mirror of
https://github.com/zerotier/ZeroTierOne.git
synced 2024-12-18 12:46:25 +00:00
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:
parent
0b03ad9a21
commit
8e6e4ede6d
@ -19,6 +19,8 @@
|
||||
#define _DEBUG(x)
|
||||
#endif
|
||||
|
||||
#include "../node/Metrics.hpp"
|
||||
|
||||
#include <deque>
|
||||
#include <set>
|
||||
#include <memory>
|
||||
@ -61,6 +63,7 @@ public:
|
||||
{
|
||||
while(m_pool.size() < m_minPoolSize){
|
||||
m_pool.push_back(m_factory->create());
|
||||
Metrics::pool_avail++;
|
||||
}
|
||||
};
|
||||
|
||||
@ -91,6 +94,7 @@ public:
|
||||
while((m_pool.size() + m_borrowed.size()) < m_minPoolSize) {
|
||||
std::shared_ptr<Connection> conn = m_factory->create();
|
||||
m_pool.push_back(conn);
|
||||
Metrics::pool_avail++;
|
||||
}
|
||||
|
||||
if(m_pool.size()==0){
|
||||
@ -99,8 +103,10 @@ public:
|
||||
try {
|
||||
std::shared_ptr<Connection> conn = m_factory->create();
|
||||
m_borrowed.insert(conn);
|
||||
Metrics::pool_in_use++;
|
||||
return std::static_pointer_cast<T>(conn);
|
||||
} catch (std::exception &e) {
|
||||
Metrics::pool_errors++;
|
||||
throw ConnectionUnavailable();
|
||||
}
|
||||
} else {
|
||||
@ -116,11 +122,13 @@ public:
|
||||
return std::static_pointer_cast<T>(conn);
|
||||
} catch(std::exception& e) {
|
||||
// Error creating a replacement connection
|
||||
Metrics::pool_errors++;
|
||||
throw ConnectionUnavailable();
|
||||
}
|
||||
}
|
||||
}
|
||||
// Nothing available
|
||||
Metrics::pool_errors++;
|
||||
throw ConnectionUnavailable();
|
||||
}
|
||||
}
|
||||
@ -128,8 +136,10 @@ public:
|
||||
// Take one off the front
|
||||
std::shared_ptr<Connection> conn = m_pool.front();
|
||||
m_pool.pop_front();
|
||||
Metrics::pool_avail--;
|
||||
// Add it to the borrowed list
|
||||
m_borrowed.insert(conn);
|
||||
Metrics::pool_in_use++;
|
||||
return std::static_pointer_cast<T>(conn);
|
||||
};
|
||||
|
||||
@ -143,7 +153,9 @@ public:
|
||||
// Lock
|
||||
std::unique_lock<std::mutex> lock(m_poolMutex);
|
||||
m_borrowed.erase(conn);
|
||||
Metrics::pool_in_use--;
|
||||
if ((m_pool.size() + m_borrowed.size()) < m_maxPoolSize) {
|
||||
Metrics::pool_avail++;
|
||||
m_pool.push_back(conn);
|
||||
}
|
||||
};
|
||||
@ -158,4 +170,4 @@ protected:
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
@ -13,6 +13,7 @@
|
||||
|
||||
#include "DB.hpp"
|
||||
#include "EmbeddedNetworkController.hpp"
|
||||
#include "../node/Metrics.hpp"
|
||||
|
||||
#include <chrono>
|
||||
#include <algorithm>
|
||||
@ -211,16 +212,19 @@ void DB::_memberChanged(nlohmann::json &old,nlohmann::json &memberConfig,bool no
|
||||
{
|
||||
std::lock_guard<std::mutex> l(_networks_l);
|
||||
auto nw2 = _networks.find(networkId);
|
||||
if (nw2 != _networks.end())
|
||||
if (nw2 != _networks.end()) {
|
||||
nw = nw2->second;
|
||||
}
|
||||
}
|
||||
if (nw) {
|
||||
std::lock_guard<std::mutex> l(nw->lock);
|
||||
if (OSUtils::jsonBool(old["activeBridge"],false))
|
||||
if (OSUtils::jsonBool(old["activeBridge"],false)) {
|
||||
nw->activeBridgeMembers.erase(memberId);
|
||||
}
|
||||
wasAuth = OSUtils::jsonBool(old["authorized"],false);
|
||||
if (wasAuth)
|
||||
if (wasAuth) {
|
||||
nw->authorizedMembers.erase(memberId);
|
||||
}
|
||||
json &ips = old["ipAssignments"];
|
||||
if (ips.is_array()) {
|
||||
for(unsigned long i=0;i<ips.size();++i) {
|
||||
@ -255,11 +259,14 @@ void DB::_memberChanged(nlohmann::json &old,nlohmann::json &memberConfig,bool no
|
||||
|
||||
nw->members[memberId] = memberConfig;
|
||||
|
||||
if (OSUtils::jsonBool(memberConfig["activeBridge"],false))
|
||||
if (OSUtils::jsonBool(memberConfig["activeBridge"],false)) {
|
||||
nw->activeBridgeMembers.insert(memberId);
|
||||
}
|
||||
isAuth = OSUtils::jsonBool(memberConfig["authorized"],false);
|
||||
if (isAuth)
|
||||
if (isAuth) {
|
||||
Metrics::member_auths++;
|
||||
nw->authorizedMembers.insert(memberId);
|
||||
}
|
||||
json &ips = memberConfig["ipAssignments"];
|
||||
if (ips.is_array()) {
|
||||
for(unsigned long i=0;i<ips.size();++i) {
|
||||
@ -303,6 +310,24 @@ void DB::_memberChanged(nlohmann::json &old,nlohmann::json &memberConfig,bool no
|
||||
}
|
||||
}
|
||||
|
||||
if (notifyListeners) {
|
||||
if(networkId != 0 && memberId != 0 && old.is_object() && !memberConfig.is_object()) {
|
||||
// member delete
|
||||
Metrics::member_count--;
|
||||
} else if (networkId != 0 && memberId != 0 && !old.is_object() && memberConfig.is_object()) {
|
||||
// new member
|
||||
Metrics::member_count++;
|
||||
}
|
||||
|
||||
if (!wasAuth && isAuth) {
|
||||
Metrics::member_auths++;
|
||||
} else if (wasAuth && !isAuth) {
|
||||
Metrics::member_deauths++;
|
||||
} else {
|
||||
Metrics::member_changes++;
|
||||
}
|
||||
}
|
||||
|
||||
if ((notifyListeners)&&((wasAuth)&&(!isAuth)&&(networkId)&&(memberId))) {
|
||||
std::lock_guard<std::mutex> ll(_changeListeners_l);
|
||||
for(auto i=_changeListeners.begin();i!=_changeListeners.end();++i) {
|
||||
@ -313,6 +338,16 @@ void DB::_memberChanged(nlohmann::json &old,nlohmann::json &memberConfig,bool no
|
||||
|
||||
void DB::_networkChanged(nlohmann::json &old,nlohmann::json &networkConfig,bool notifyListeners)
|
||||
{
|
||||
if (notifyListeners) {
|
||||
if (old.is_object() && old.contains("id") && networkConfig.is_object() && networkConfig.contains("id")) {
|
||||
Metrics::network_changes++;
|
||||
} else if (!old.is_object() && networkConfig.is_object() && networkConfig.contains("id")) {
|
||||
Metrics::network_count++;
|
||||
} else if (old.is_object() && old.contains("id") && !networkConfig.is_object()) {
|
||||
Metrics::network_count--;
|
||||
}
|
||||
}
|
||||
|
||||
if (networkConfig.is_object()) {
|
||||
const std::string ids = networkConfig["id"];
|
||||
const uint64_t networkId = Utils::hexStrToU64(ids.c_str());
|
||||
|
@ -35,6 +35,8 @@
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
#include <prometheus/simpleapi.h>
|
||||
|
||||
#define ZT_MEMBER_AUTH_TIMEOUT_NOTIFY_BEFORE 25000
|
||||
|
||||
namespace ZeroTier
|
||||
|
@ -13,6 +13,8 @@
|
||||
|
||||
#include "FileDB.hpp"
|
||||
|
||||
#include "../node/Metrics.hpp"
|
||||
|
||||
namespace ZeroTier
|
||||
{
|
||||
|
||||
@ -39,6 +41,7 @@ FileDB::FileDB(const char *path) :
|
||||
if (nwids.length() == 16) {
|
||||
nlohmann::json nullJson;
|
||||
_networkChanged(nullJson,network,false);
|
||||
Metrics::network_count++;
|
||||
std::string membersPath(_networksPath + ZT_PATH_SEPARATOR_S + nwids + ZT_PATH_SEPARATOR_S "member");
|
||||
std::vector<std::string> members(OSUtils::listDirectory(membersPath.c_str(),false));
|
||||
for(auto m=members.begin();m!=members.end();++m) {
|
||||
@ -50,6 +53,7 @@ FileDB::FileDB(const char *path) :
|
||||
if (addrs.length() == 10) {
|
||||
nlohmann::json nullJson2;
|
||||
_memberChanged(nullJson2,member,false);
|
||||
Metrics::member_count++;
|
||||
}
|
||||
} catch ( ... ) {}
|
||||
}
|
||||
@ -88,8 +92,9 @@ bool FileDB::save(nlohmann::json &record,bool notifyListeners)
|
||||
if ((!old.is_object())||(!_compareRecords(old,record))) {
|
||||
record["revision"] = OSUtils::jsonInt(record["revision"],0ULL) + 1ULL;
|
||||
OSUtils::ztsnprintf(p1,sizeof(p1),"%s" ZT_PATH_SEPARATOR_S "%.16llx.json",_networksPath.c_str(),nwid);
|
||||
if (!OSUtils::writeFile(p1,OSUtils::jsonDump(record,-1)))
|
||||
if (!OSUtils::writeFile(p1,OSUtils::jsonDump(record,-1))) {
|
||||
fprintf(stderr,"WARNING: controller unable to write to path: %s" ZT_EOL_S,p1);
|
||||
}
|
||||
_networkChanged(old,record,notifyListeners);
|
||||
modified = true;
|
||||
}
|
||||
@ -110,8 +115,9 @@ bool FileDB::save(nlohmann::json &record,bool notifyListeners)
|
||||
OSUtils::ztsnprintf(p2,sizeof(p2),"%s" ZT_PATH_SEPARATOR_S "%.16llx",_networksPath.c_str(),(unsigned long long)nwid);
|
||||
OSUtils::mkdir(p2);
|
||||
OSUtils::mkdir(pb);
|
||||
if (!OSUtils::writeFile(p1,OSUtils::jsonDump(record,-1)))
|
||||
if (!OSUtils::writeFile(p1,OSUtils::jsonDump(record,-1))) {
|
||||
fprintf(stderr,"WARNING: controller unable to write to path: %s" ZT_EOL_S,p1);
|
||||
}
|
||||
}
|
||||
_memberChanged(old,record,notifyListeners);
|
||||
modified = true;
|
||||
|
@ -119,6 +119,7 @@ MemberNotificationReceiver::MemberNotificationReceiver(PostgreSQL *p, pqxx::conn
|
||||
|
||||
void MemberNotificationReceiver::operator() (const std::string &payload, int packend_pid) {
|
||||
fprintf(stderr, "Member Notification received: %s\n", payload.c_str());
|
||||
Metrics::pgsql_mem_notification++;
|
||||
json tmp(json::parse(payload));
|
||||
json &ov = tmp["old_val"];
|
||||
json &nv = tmp["new_val"];
|
||||
@ -141,6 +142,7 @@ NetworkNotificationReceiver::NetworkNotificationReceiver(PostgreSQL *p, pqxx::co
|
||||
|
||||
void NetworkNotificationReceiver::operator() (const std::string &payload, int packend_pid) {
|
||||
fprintf(stderr, "Network Notification received: %s\n", payload.c_str());
|
||||
Metrics::pgsql_net_notification++;
|
||||
json tmp(json::parse(payload));
|
||||
json &ov = tmp["old_val"];
|
||||
json &nv = tmp["new_val"];
|
||||
@ -705,6 +707,8 @@ void PostgreSQL::initializeNetworks()
|
||||
}
|
||||
}
|
||||
|
||||
Metrics::network_count++;
|
||||
|
||||
_networkChanged(empty, config, false);
|
||||
|
||||
auto end = std::chrono::high_resolution_clock::now();
|
||||
@ -925,6 +929,8 @@ void PostgreSQL::initializeMembers()
|
||||
}
|
||||
}
|
||||
|
||||
Metrics::member_count++;
|
||||
|
||||
_memberChanged(empty, config, false);
|
||||
|
||||
memberId = "";
|
||||
@ -1034,7 +1040,6 @@ void PostgreSQL::heartbeat()
|
||||
w.commit();
|
||||
} catch (std::exception &e) {
|
||||
fprintf(stderr, "%s: Heartbeat update failed: %s\n", controllerId, e.what());
|
||||
w.abort();
|
||||
_pool->unborrow(c);
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
|
||||
continue;
|
||||
@ -1230,6 +1235,7 @@ void PostgreSQL::_networksWatcher_Redis() {
|
||||
}
|
||||
lastID = id;
|
||||
}
|
||||
Metrics::redis_net_notification++;
|
||||
}
|
||||
}
|
||||
} catch (sw::redis::Error &e) {
|
||||
@ -1788,6 +1794,7 @@ uint64_t PostgreSQL::_doRedisUpdate(sw::redis::Transaction &tx, std::string &con
|
||||
.sadd("network-nodes-all:{"+controllerId+"}:"+networkId, memberId)
|
||||
.hmset("member:{"+controllerId+"}:"+networkId+":"+memberId, record.begin(), record.end());
|
||||
++count;
|
||||
Metrics::redis_mem_notification++;
|
||||
}
|
||||
|
||||
// expire records from all-nodes and network-nodes member list
|
||||
|
@ -26,6 +26,8 @@
|
||||
#include <memory>
|
||||
#include <redis++/redis++.h>
|
||||
|
||||
#include "../node/Metrics.hpp"
|
||||
|
||||
extern "C" {
|
||||
typedef struct pg_conn PGconn;
|
||||
}
|
||||
@ -53,6 +55,7 @@ public:
|
||||
}
|
||||
|
||||
virtual std::shared_ptr<Connection> create() {
|
||||
Metrics::conn_counter++;
|
||||
auto c = std::shared_ptr<PostgresConnection>(new PostgresConnection());
|
||||
c->c = std::make_shared<pqxx::connection>(m_connString);
|
||||
return std::static_pointer_cast<Connection>(c);
|
||||
|
3
ext/prometheus-cpp-lite-1.0/.gitignore
vendored
Normal file
3
ext/prometheus-cpp-lite-1.0/.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
.vs
|
||||
bin/
|
||||
out/
|
@ -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}>")
|
@ -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.
|
@ -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;
|
||||
...
|
||||
```
|
||||
|
@ -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)
|
@ -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;
|
||||
}
|
@ -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:: */
|
34
ext/prometheus-cpp-lite-1.0/CMakeLists.txt
Normal file
34
ext/prometheus-cpp-lite-1.0/CMakeLists.txt
Normal file
@ -0,0 +1,34 @@
|
||||
project(prometheus-cpp-lite)
|
||||
cmake_minimum_required(VERSION 3.2)
|
||||
|
||||
option(PROMETHEUS_BUILD_EXAMPLES "Build with examples" OFF)
|
||||
|
||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/bin)
|
||||
|
||||
if(WIN32)
|
||||
|
||||
# it prevent create Debug/ and Release folders in Visual Studio
|
||||
foreach( OUTPUTCONFIG ${CMAKE_CONFIGURATION_TYPES} )
|
||||
string( TOUPPER ${OUTPUTCONFIG} OUTPUTCONFIG )
|
||||
set( CMAKE_RUNTIME_OUTPUT_DIRECTORY_${OUTPUTCONFIG} ${PROJECT_SOURCE_DIR}/bin )
|
||||
endforeach()
|
||||
|
||||
set (INSTALL_PATH_BIN "${PROJECT_SOURCE_DIR}/installed/bin/")
|
||||
|
||||
else() # not WIN32
|
||||
|
||||
set (INSTALL_PATH_BIN "bin/")
|
||||
|
||||
endif()
|
||||
|
||||
add_subdirectory("./core")
|
||||
|
||||
add_subdirectory("./simpleapi")
|
||||
|
||||
add_subdirectory("./3rdpatry/http-client-lite")
|
||||
|
||||
if(PROMETHEUS_BUILD_EXAMPLES)
|
||||
add_subdirectory("./examples")
|
||||
endif()
|
||||
|
||||
|
21
ext/prometheus-cpp-lite-1.0/LICENSE
Normal file
21
ext/prometheus-cpp-lite-1.0/LICENSE
Normal file
@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2021 biaks (ianiskr@gmail.com)
|
||||
|
||||
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.
|
201
ext/prometheus-cpp-lite-1.0/README.md
Normal file
201
ext/prometheus-cpp-lite-1.0/README.md
Normal file
@ -0,0 +1,201 @@
|
||||
# C++ Header-only Prometheus client library
|
||||
|
||||
It is a tool for quickly adding metrics (and profiling) functionality to C++ projects.
|
||||
|
||||
## Advantages:
|
||||
|
||||
1. Written in pure C++,
|
||||
2. Header-only,
|
||||
2. Cross-platform,
|
||||
3. Compiles with C ++ 11, C ++ 14, C ++ 17 standards,
|
||||
4. Has no third-party dependencies,
|
||||
5. Several APIs for use in your projects,
|
||||
6. Saving metrics to a file (and then works with node_exporter) or sending via http (uses built-in header-only http-client-lite library),
|
||||
7. Possiblity to use different types for storing metrics data (default is uint32_t, but you can use double or uint64_t types if you want),
|
||||
8. Five types of metrics are supported: counter, gauge, summary, histogram and benchmark,
|
||||
10. Has detailed examples of use (see examples folder)
|
||||
|
||||
## How it differs from the [jupp0r/prometheus-cpp](https://github.com/jupp0r/prometheus-cpp) project:
|
||||
1. I need a simple header only wariant library without dependencies to write metrics to a .prom file,
|
||||
2. I need the fastest possible work using integer values of counters (original project use only floating pointer values),
|
||||
3. The origianl project have problems on compilers that do not know how to do LTO optimization,
|
||||
4. I did not like the python style of the original project and the large amount of extra code in it and I wanted to make it lighter and more c++ classic.
|
||||
|
||||
## How to use it:
|
||||
The library has two API:
|
||||
1. Complex API for those who want to control everything,
|
||||
2. Simple API for those who want to quickly add metrics to their C ++ (and it is actually just a wrapper around the complex API).
|
||||
|
||||
|
||||
### Let's start with a simple API because it's simple:
|
||||
|
||||
To add it to your C++ project add these lines to your CMakeLists.txt file:
|
||||
```
|
||||
add_subdirectory("prometheus-cpp-lite/core")
|
||||
add_subdirectory("prometheus-cpp-lite/3rdpatry/http-client-lite")
|
||||
add_subdirectory("prometheus-cpp-lite/simpleapi")
|
||||
target_link_libraries(your_target prometheus-cpp-simpleapi)
|
||||
```
|
||||
|
||||
The simplest way to create a metric would be like this:
|
||||
``` c++
|
||||
prometheus::simpleapi::METRIC_metric_t metric1 { "metric1", "first simple metric without any tag" };
|
||||
prometheus::simpleapi::METRIC_metric_t metric2 { "metric2", "second simple metric without any tag" };
|
||||
```
|
||||
where ```METRIC``` can be ```counter```, ```gauge```, ```summary```, ```histogram``` or ```benchmark```.
|
||||
|
||||
If you want to access an existing metric again elsewhere in the code, you can do this:
|
||||
``` c++
|
||||
prometheus::simpleapi::METRIC_metric_t metric2_yet_another_link { "metric2", "" };
|
||||
```
|
||||
this works because when adding a metric, it checks whether there is already a metric with the same name and, if there is one, a link to it is returned.
|
||||
|
||||
You can create a family of metrics (metrics with tags) as follows:
|
||||
``` c++
|
||||
prometheus::simpleapi::METRIC_family_t family { "metric_family", "metric family" };
|
||||
prometheus::simpleapi::METRIC_metric_t metric1 { family.Add({{"name", "metric1"}}) };
|
||||
prometheus::simpleapi::METRIC_metric_t metric2 { family.Add({{"name", "metric2"}}) };
|
||||
```
|
||||
where METRIC can be ```counter```, ```gauge```, ```summary```, ```histogram``` or ```benchmark```.
|
||||
|
||||
Next, you can do the following things with metrics:
|
||||
``` c++
|
||||
metric++; // for increment it (only for counter and gauge metrics)
|
||||
metric += value; // for add value to metric (only for gauge metric)
|
||||
metric -= value; // for sub value from metric (only for gauge metric)
|
||||
metric = value; // save current value (only gauge metrics)
|
||||
metric.start(); // start calculate time (only for benchmark metric)
|
||||
metric.stop(); // stop calculate time (only for benchmark metric)
|
||||
```
|
||||
|
||||
You can change the settings of save (or send) metrics data as follows:
|
||||
``` c++
|
||||
prometheus::simpleapi::saver.set_delay(period_in_seconds); // change the period of saving (or sending) metrics data in seconds (5 seconds by default)
|
||||
prometheus::simpleapi::saver.set_out_file(filename); // change the name of the output file (metrics.prom by default)
|
||||
prometheus::simpleapi::saver.set_server_url(url); // change the name of prometheus server (unset by default)
|
||||
```
|
||||
|
||||
### Simple API complex example 1 (examples/simpleapi_example.cpp):
|
||||
|
||||
``` c++
|
||||
#include <prometheus/simpleapi.h>
|
||||
|
||||
void main() {
|
||||
|
||||
using namespace prometheus::simpleapi;
|
||||
|
||||
counter_family_t family { "simple_family", "simple family example" };
|
||||
counter_metric_t metric1 { family.Add({{"name", "counter1"}}) };
|
||||
counter_metric_t metric2 { family.Add({{"name", "counter2"}}) };
|
||||
|
||||
counter_metric_t metric3 { "simple_counter_1", "simple counter 1 without labels example" };
|
||||
counter_metric_t metric4 { "simple_counter_2", "simple counter 2 without labels example" };
|
||||
|
||||
for (;; ) {
|
||||
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||
const int random_value = std::rand();
|
||||
if (random_value & 1) metric1++;
|
||||
if (random_value & 2) metric2++;
|
||||
if (random_value & 4) metric3++;
|
||||
if (random_value & 8) metric4++;
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
Output in "metrics.prom" file (by default) will be:
|
||||
|
||||
```
|
||||
# HELP simple_family simple family example
|
||||
# TYPE simple_family counter
|
||||
simple_family{name="counter1"} 10
|
||||
simple_family{name="counter2"} 9
|
||||
# HELP simple_counter_1 simple counter 1 without labels example
|
||||
# TYPE simple_counter_1 counter
|
||||
simple_counter_1 6
|
||||
# HELP simple_counter_2 simple counter 2 without labels example
|
||||
# TYPE simple_counter_2 counter
|
||||
simple_counter_2 8
|
||||
```
|
||||
|
||||
### Simple API complex example 2 (examples/simpleapi_use_in_class_example.cpp):
|
||||
|
||||
``` c++
|
||||
#include <prometheus/simpleapi.h>
|
||||
|
||||
using namespace prometheus::simpleapi;
|
||||
|
||||
class MyClass {
|
||||
|
||||
counter_family_t metric_family { "simple_family", "simple family example" };
|
||||
counter_metric_t metric1 { metric_family.Add({{"name", "counter1"}}) };
|
||||
counter_metric_t metric2 { metric_family.Add({{"name", "counter2"}}) };
|
||||
|
||||
counter_metric_t metric3 { "simple_counter_1", "simple counter 1 without labels example" };
|
||||
counter_metric_t metric4 { "simple_counter_2", "simple counter 2 without labels example" };
|
||||
|
||||
benchmark_family_t benchmark_family { "simple_benchmark_family", "simple benchmark family example" };
|
||||
benchmark_metric_t benchmark1 { benchmark_family.Add({{"benchmark", "1"}}) };
|
||||
benchmark_metric_t benchmark2 { benchmark_family.Add({{"benchmark", "2"}}) };
|
||||
|
||||
public:
|
||||
|
||||
MyClass() = default;
|
||||
|
||||
void member_to_do_something() {
|
||||
|
||||
benchmark1.start();
|
||||
const int random_value = std::rand();
|
||||
benchmark1.stop();
|
||||
|
||||
benchmark2.start();
|
||||
if (random_value & 1) metric1++;
|
||||
if (random_value & 2) metric2++;
|
||||
if (random_value & 4) metric3++;
|
||||
if (random_value & 8) metric4++;
|
||||
benchmark2.stop();
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
void main() {
|
||||
|
||||
MyClass myClass;
|
||||
benchmark_metric_t benchmark { "simple_benchmark", "simple benchmark example" };
|
||||
|
||||
for (;; ) {
|
||||
|
||||
benchmark.start();
|
||||
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||
benchmark.stop();
|
||||
|
||||
myClass.member_to_do_something();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
Output in "metrics.prom" file (by default) will be:
|
||||
|
||||
```
|
||||
# HELP simple_family simple family example
|
||||
# TYPE simple_family counter
|
||||
simple_family{name="counter1"} 3
|
||||
simple_family{name="counter2"} 2
|
||||
# HELP simple_counter_1 simple counter 1 without labels example
|
||||
# TYPE simple_counter_1 counter
|
||||
simple_counter_1 3
|
||||
# HELP simple_counter_2 simple counter 2 without labels example
|
||||
# TYPE simple_counter_2 counter
|
||||
simple_counter_2 3
|
||||
# HELP simple_benchmark_family simple benchmark family example
|
||||
# TYPE simple_benchmark_family counter
|
||||
simple_benchmark_family{benchmark="1"} 0.0001088
|
||||
simple_benchmark_family{benchmark="2"} 1.48e-05
|
||||
# HELP simple_benchmark simple benchmark example
|
||||
# TYPE simple_benchmark counter
|
||||
simple_benchmark 6.0503248
|
||||
```
|
||||
|
20
ext/prometheus-cpp-lite-1.0/core/CMakeLists.txt
Normal file
20
ext/prometheus-cpp-lite-1.0/core/CMakeLists.txt
Normal file
@ -0,0 +1,20 @@
|
||||
project(prometheus-cpp-lite-core)
|
||||
cmake_minimum_required(VERSION 3.2)
|
||||
|
||||
file(GLOB_RECURSE PROMETHEUS_CPP_LITE_HEADERS *.h)
|
||||
|
||||
# it is header only target
|
||||
|
||||
add_library (${PROJECT_NAME} INTERFACE)
|
||||
target_sources (${PROJECT_NAME} INTERFACE ${PROMETHEUS_CPP_LITE_HEADERS})
|
||||
target_include_directories(${PROJECT_NAME} INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/include)
|
||||
add_custom_target (${PROJECT_NAME}-ide SOURCES ${PROMETHEUS_CPP_LITE_HEADERS})
|
||||
target_link_libraries (${PROJECT_NAME} INTERFACE http-client-lite)
|
||||
|
||||
set (${PROJECT_NAME}_INCLUDE ${CMAKE_CURRENT_SOURCE_DIR}/include PARENT_SCOPE)
|
||||
|
||||
# it need for save_to_file_t
|
||||
if(NOT WIN32)
|
||||
find_package(Threads)
|
||||
target_link_libraries(${PROJECT_NAME} INTERFACE ${CMAKE_THREAD_LIBS_INIT})
|
||||
endif()
|
@ -0,0 +1,40 @@
|
||||
#pragma once
|
||||
|
||||
#include <type_traits>
|
||||
#include <atomic>
|
||||
|
||||
namespace prometheus {
|
||||
|
||||
template <typename FloatingType>
|
||||
inline std::atomic<FloatingType>& atomic_add_for_floating_types(std::atomic<FloatingType>& value,
|
||||
const FloatingType& add) {
|
||||
FloatingType desired;
|
||||
FloatingType expected = value.load(std::memory_order_relaxed);
|
||||
do {
|
||||
desired = expected + add;
|
||||
} while (!value.compare_exchange_weak(expected, desired));
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
template <typename FloatingType, class = typename std::enable_if<std::is_floating_point<FloatingType>::value, int>::type>
|
||||
inline std::atomic<FloatingType>& operator++(std::atomic<FloatingType>& value) {
|
||||
return atomic_add_for_floating_types(value, 1.0);
|
||||
}
|
||||
|
||||
template <typename FloatingType, class = typename std::enable_if<std::is_floating_point<FloatingType>::value, int>::type>
|
||||
inline std::atomic<FloatingType>& operator+=(std::atomic<FloatingType>& value, const FloatingType& val) {
|
||||
return atomic_add_for_floating_types(value, val);
|
||||
}
|
||||
|
||||
template <typename FloatingType, class = typename std::enable_if<std::is_floating_point<FloatingType>::value, int>::type>
|
||||
inline std::atomic<FloatingType>& operator--(std::atomic<FloatingType>& value) {
|
||||
return atomic_add_for_floating_types(value, -1.0);
|
||||
}
|
||||
|
||||
template <typename FloatingType, class = typename std::enable_if<std::is_floating_point<FloatingType>::value, int>::type>
|
||||
inline std::atomic<FloatingType>& operator-=(std::atomic<FloatingType>& value, const FloatingType& val) {
|
||||
return atomic_add_for_floating_types(value, -val);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,72 @@
|
||||
#pragma once
|
||||
|
||||
#include "prometheus/metric.h"
|
||||
#include "prometheus/family.h"
|
||||
|
||||
#include <chrono>
|
||||
|
||||
namespace prometheus {
|
||||
|
||||
class Benchmark : public Metric {
|
||||
|
||||
#ifndef NDEBUG
|
||||
bool already_started = false;
|
||||
#endif
|
||||
|
||||
std::chrono::time_point<std::chrono::high_resolution_clock> start_;
|
||||
std::chrono::time_point<std::chrono::high_resolution_clock>::duration elapsed = std::chrono::time_point<std::chrono::high_resolution_clock>::duration::zero(); // elapsed time
|
||||
|
||||
public:
|
||||
|
||||
using Value = double;
|
||||
using Family = CustomFamily<Benchmark>;
|
||||
|
||||
static const Metric::Type static_type = Metric::Type::Counter;
|
||||
|
||||
Benchmark() : Metric(Metric::Type::Counter) {}
|
||||
|
||||
void start() {
|
||||
|
||||
#ifndef NDEBUG
|
||||
if (already_started)
|
||||
throw std::runtime_error("try to start already started counter");
|
||||
else
|
||||
already_started = true;
|
||||
#endif
|
||||
|
||||
start_ = std::chrono::high_resolution_clock::now();
|
||||
|
||||
}
|
||||
|
||||
void stop() {
|
||||
|
||||
#ifndef NDEBUG
|
||||
if (already_started == false)
|
||||
throw std::runtime_error("try to stop already stoped counter");
|
||||
#endif
|
||||
|
||||
std::chrono::time_point<std::chrono::high_resolution_clock> stop;
|
||||
stop = std::chrono::high_resolution_clock::now();
|
||||
elapsed += stop - start_;
|
||||
|
||||
#ifndef NDEBUG
|
||||
already_started = false;
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
double Get() const {
|
||||
return std::chrono::duration_cast<std::chrono::duration<double>>(elapsed).count();
|
||||
}
|
||||
|
||||
virtual ClientMetric Collect() const {
|
||||
ClientMetric metric;
|
||||
metric.counter.value = Get();
|
||||
return metric;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
} // namespace prometheus
|
@ -0,0 +1,35 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include "registry.h"
|
||||
|
||||
namespace prometheus {
|
||||
|
||||
template <typename CustomMetric>
|
||||
class Builder {
|
||||
|
||||
Family::Labels labels_;
|
||||
std::string name_;
|
||||
std::string help_;
|
||||
|
||||
public:
|
||||
Builder& Labels(const std::map<const std::string, const std::string>& labels) {
|
||||
labels_ = labels;
|
||||
return *this;
|
||||
}
|
||||
Builder& Name(const std::string& name) {
|
||||
name_ = name;
|
||||
return *this;
|
||||
}
|
||||
Builder& Help(const std::string& help) {
|
||||
help_ = help;
|
||||
return *this;
|
||||
}
|
||||
CustomFamily<CustomMetric>& Register(Registry& registry) {
|
||||
return registry.Add<CustomFamily<CustomMetric>>(name_, help_, labels_);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
@ -0,0 +1,194 @@
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
|
||||
namespace prometheus {
|
||||
|
||||
namespace detail {
|
||||
|
||||
class CKMSQuantiles {
|
||||
|
||||
public:
|
||||
|
||||
struct Quantile {
|
||||
|
||||
double quantile;
|
||||
double error;
|
||||
double u;
|
||||
double v;
|
||||
|
||||
Quantile(double quantile, double error)
|
||||
: quantile(quantile),
|
||||
error(error),
|
||||
u(2.0 * error / (1.0 - quantile)),
|
||||
v(2.0 * error / quantile) {}
|
||||
|
||||
};
|
||||
|
||||
private:
|
||||
|
||||
struct Item {
|
||||
|
||||
double value;
|
||||
int g;
|
||||
int delta;
|
||||
|
||||
Item(double value, int lower_delta, int delta)
|
||||
: value(value), g(lower_delta), delta(delta) {}
|
||||
|
||||
};
|
||||
|
||||
public:
|
||||
|
||||
explicit CKMSQuantiles(const std::vector<Quantile>& quantiles)
|
||||
: quantiles_(quantiles), count_(0), buffer_{}, buffer_count_(0) {}
|
||||
|
||||
void insert(double value) {
|
||||
buffer_[buffer_count_] = value;
|
||||
++buffer_count_;
|
||||
|
||||
if (buffer_count_ == buffer_.size()) {
|
||||
insertBatch();
|
||||
compress();
|
||||
}
|
||||
}
|
||||
|
||||
double get(double q) {
|
||||
insertBatch();
|
||||
compress();
|
||||
|
||||
if (sample_.empty()) {
|
||||
return std::numeric_limits<double>::quiet_NaN();
|
||||
}
|
||||
|
||||
int rankMin = 0;
|
||||
const auto desired = static_cast<int>(q * static_cast<double>(count_));
|
||||
const auto bound = desired + (allowableError(desired) / 2);
|
||||
|
||||
auto it = sample_.begin();
|
||||
decltype(it) prev;
|
||||
auto cur = it++;
|
||||
|
||||
while (it != sample_.end()) {
|
||||
prev = cur;
|
||||
cur = it++;
|
||||
|
||||
rankMin += prev->g;
|
||||
|
||||
if (rankMin + cur->g + cur->delta > bound) {
|
||||
return prev->value;
|
||||
}
|
||||
}
|
||||
|
||||
return sample_.back().value;
|
||||
}
|
||||
|
||||
void reset() {
|
||||
count_ = 0;
|
||||
sample_.clear();
|
||||
buffer_count_ = 0;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
double allowableError(int rank) {
|
||||
auto size = sample_.size();
|
||||
double minError = static_cast<double>(size + 1);
|
||||
|
||||
for (const auto& q : quantiles_.get()) {
|
||||
double error;
|
||||
if (static_cast<double>(rank) <= q.quantile * static_cast<double>(size)) {
|
||||
error = q.u * static_cast<double>(size - rank);
|
||||
}
|
||||
else {
|
||||
error = q.v * rank;
|
||||
}
|
||||
if (error < minError) {
|
||||
minError = error;
|
||||
}
|
||||
}
|
||||
|
||||
return minError;
|
||||
}
|
||||
|
||||
bool insertBatch() {
|
||||
if (buffer_count_ == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::sort(buffer_.begin(), buffer_.begin() + buffer_count_);
|
||||
|
||||
std::size_t start = 0;
|
||||
if (sample_.empty()) {
|
||||
sample_.emplace_back(buffer_[0], 1, 0);
|
||||
++start;
|
||||
++count_;
|
||||
}
|
||||
|
||||
std::size_t idx = 0;
|
||||
std::size_t item = idx++;
|
||||
|
||||
for (std::size_t i = start; i < buffer_count_; ++i) {
|
||||
double v = buffer_[i];
|
||||
while (idx < sample_.size() && sample_[item].value < v) {
|
||||
item = idx++;
|
||||
}
|
||||
|
||||
if (sample_[item].value > v) {
|
||||
--idx;
|
||||
}
|
||||
|
||||
int delta;
|
||||
if (idx - 1 == 0 || idx + 1 == sample_.size()) {
|
||||
delta = 0;
|
||||
}
|
||||
else {
|
||||
delta = static_cast<int>(std::floor(allowableError(static_cast<int>(idx + 1)))) + 1;
|
||||
}
|
||||
|
||||
sample_.emplace(sample_.begin() + idx, v, 1, delta);
|
||||
count_++;
|
||||
item = idx++;
|
||||
}
|
||||
|
||||
buffer_count_ = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
void compress() {
|
||||
if (sample_.size() < 2) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::size_t idx = 0;
|
||||
std::size_t prev;
|
||||
std::size_t next = idx++;
|
||||
|
||||
while (idx < sample_.size()) {
|
||||
prev = next;
|
||||
next = idx++;
|
||||
|
||||
if (sample_[prev].g + sample_[next].g + sample_[next].delta <=
|
||||
allowableError(static_cast<int>(idx - 1))) {
|
||||
sample_[next].g += sample_[prev].g;
|
||||
sample_.erase(sample_.begin() + prev);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
const std::reference_wrapper<const std::vector<Quantile>> quantiles_;
|
||||
|
||||
std::size_t count_;
|
||||
std::vector<Item> sample_;
|
||||
std::array<double, 500> buffer_;
|
||||
std::size_t buffer_count_;
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
} // namespace prometheus
|
@ -0,0 +1,94 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
#include <vector>
|
||||
|
||||
namespace prometheus {
|
||||
|
||||
// ñòðóêòóðà, â êîòîðóþ êîïèðóþòñÿ çíà÷åíèÿ ìåòðèê ïåðåä èõ ñåðèàëèçàöèåé
|
||||
struct ClientMetric {
|
||||
|
||||
// Label
|
||||
|
||||
struct Label {
|
||||
|
||||
std::string name;
|
||||
std::string value;
|
||||
|
||||
Label(const std::string name_, const std::string value_) : name(name_), value(value_) {}
|
||||
|
||||
bool operator<(const Label& rhs) const {
|
||||
return std::tie(name, value) < std::tie(rhs.name, rhs.value);
|
||||
}
|
||||
|
||||
bool operator==(const Label& rhs) const {
|
||||
return std::tie(name, value) == std::tie(rhs.name, rhs.value);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
std::vector<Label> label;
|
||||
|
||||
// Counter
|
||||
|
||||
struct Counter {
|
||||
double value = 0.0;
|
||||
};
|
||||
|
||||
Counter counter;
|
||||
|
||||
// Gauge
|
||||
|
||||
struct Gauge {
|
||||
double value = 0.0;
|
||||
};
|
||||
|
||||
Gauge gauge;
|
||||
|
||||
// Summary
|
||||
|
||||
struct Quantile {
|
||||
double quantile = 0.0;
|
||||
double value = 0.0;
|
||||
};
|
||||
|
||||
struct Summary {
|
||||
std::uint64_t sample_count = 0;
|
||||
double sample_sum = 0.0;
|
||||
std::vector<Quantile> quantile;
|
||||
};
|
||||
|
||||
Summary summary;
|
||||
|
||||
// Histogram
|
||||
|
||||
struct Bucket {
|
||||
std::uint64_t cumulative_count = 0;
|
||||
double upper_bound = 0.0;
|
||||
};
|
||||
|
||||
struct Histogram {
|
||||
std::uint64_t sample_count = 0;
|
||||
double sample_sum = 0.0;
|
||||
std::vector<Bucket> bucket;
|
||||
};
|
||||
|
||||
Histogram histogram;
|
||||
|
||||
// Untyped
|
||||
|
||||
struct Untyped {
|
||||
double value = 0;
|
||||
};
|
||||
|
||||
Untyped untyped;
|
||||
|
||||
// Timestamp
|
||||
|
||||
std::int64_t timestamp_ms = 0;
|
||||
|
||||
};
|
||||
|
||||
} // namespace prometheus
|
@ -0,0 +1,27 @@
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "prometheus/metric_family.h"
|
||||
|
||||
namespace prometheus {
|
||||
|
||||
/// @brief Interface implemented by anything that can be used by Prometheus to
|
||||
/// collect metrics.
|
||||
///
|
||||
/// A Collectable has to be registered for collection. See Registry.
|
||||
class Collectable {
|
||||
|
||||
public:
|
||||
|
||||
//Collectable() = default;
|
||||
|
||||
virtual ~Collectable() = default;
|
||||
|
||||
using MetricFamilies = std::vector<MetricFamily>;
|
||||
|
||||
/// \brief Returns a list of metrics and their samples.
|
||||
virtual MetricFamilies Collect() const = 0;
|
||||
};
|
||||
|
||||
} // namespace prometheus
|
112
ext/prometheus-cpp-lite-1.0/core/include/prometheus/counter.h
Normal file
112
ext/prometheus-cpp-lite-1.0/core/include/prometheus/counter.h
Normal file
@ -0,0 +1,112 @@
|
||||
#pragma once
|
||||
|
||||
#include "prometheus/atomic_floating.h"
|
||||
#include "prometheus/metric.h"
|
||||
#include "prometheus/family.h"
|
||||
|
||||
#include "prometheus/builder.h"
|
||||
|
||||
#include <atomic>
|
||||
|
||||
namespace prometheus {
|
||||
|
||||
/// \brief A counter metric to represent a monotonically increasing value.
|
||||
///
|
||||
/// This class represents the metric type counter:
|
||||
/// https://prometheus.io/docs/concepts/metric_types/#counter
|
||||
///
|
||||
/// The value of the counter can only increase. Example of counters are:
|
||||
/// - the number of requests served
|
||||
/// - tasks completed
|
||||
/// - errors
|
||||
///
|
||||
/// Do not use a counter to expose a value that can decrease - instead use a
|
||||
/// Gauge.
|
||||
///
|
||||
/// The class is thread-safe. No concurrent call to any API of this type causes
|
||||
/// a data race.
|
||||
template <typename Value_ = uint64_t>
|
||||
class Counter : public Metric {
|
||||
|
||||
std::atomic<Value_> value{ 0 };
|
||||
|
||||
public:
|
||||
|
||||
using Value = Value_;
|
||||
using Family = CustomFamily<Counter<Value>>;
|
||||
|
||||
static const Metric::Type static_type = Metric::Type::Counter;
|
||||
|
||||
Counter() : Metric (Metric::Type::Counter) {} ///< \brief Create a counter that starts at 0.
|
||||
|
||||
// original API
|
||||
|
||||
void Increment() { ///< \brief Increment the counter by 1.
|
||||
++value;
|
||||
}
|
||||
|
||||
void Increment(const Value& val) { ///< \brief Increment the counter by a given amount. The counter will not change if the given amount is negative.
|
||||
if (val > 0)
|
||||
value += val;
|
||||
}
|
||||
|
||||
const Value Get() const { ///< \brief Get the current value of the counter.
|
||||
return value;
|
||||
}
|
||||
|
||||
virtual ClientMetric Collect() const { ///< /// \brief Get the current value of the counter. Collect is called by the Registry when collecting metrics.
|
||||
ClientMetric metric;
|
||||
metric.counter.value = static_cast<double>(value);
|
||||
return metric;
|
||||
}
|
||||
|
||||
// new API
|
||||
|
||||
Counter& operator ++() {
|
||||
++value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Counter& operator++ (int) {
|
||||
++value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Counter& operator += (const Value& val) {
|
||||
value += val;
|
||||
return *this;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
/// \brief Return a builder to configure and register a Counter metric.
|
||||
///
|
||||
/// @copydetails Family<>::Family()
|
||||
///
|
||||
/// Example usage:
|
||||
///
|
||||
/// \code
|
||||
/// auto registry = std::make_shared<Registry>();
|
||||
/// auto& counter_family = prometheus::BuildCounter()
|
||||
/// .Name("some_name")
|
||||
/// .Help("Additional description.")
|
||||
/// .Labels({{"key", "value"}})
|
||||
/// .Register(*registry);
|
||||
///
|
||||
/// ...
|
||||
/// \endcode
|
||||
///
|
||||
/// \return An object of unspecified type T, i.e., an implementation detail
|
||||
/// except that it has the following members:
|
||||
///
|
||||
/// - Name(const std::string&) to set the metric name,
|
||||
/// - Help(const std::string&) to set an additional description.
|
||||
/// - Label(const std::map<std::string, std::string>&) to assign a set of
|
||||
/// key-value pairs (= labels) to the metric.
|
||||
///
|
||||
/// To finish the configuration of the Counter metric, register it with
|
||||
/// Register(Registry&).
|
||||
using BuildCounter = Builder<Counter<double>>;
|
||||
|
||||
|
||||
} // namespace prometheus
|
355
ext/prometheus-cpp-lite-1.0/core/include/prometheus/family.h
Normal file
355
ext/prometheus-cpp-lite-1.0/core/include/prometheus/family.h
Normal file
@ -0,0 +1,355 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
#include <cassert>
|
||||
|
||||
#include "prometheus/collectable.h"
|
||||
#include "prometheus/metric.h"
|
||||
#include "prometheus/hash.h"
|
||||
|
||||
namespace prometheus {
|
||||
|
||||
/// \brief A metric of type T with a set of labeled dimensions.
|
||||
///
|
||||
/// One of Prometheus main feature is a multi-dimensional data model with time
|
||||
/// series data identified by metric name and key/value pairs, also known as
|
||||
/// labels. A time series is a series of data points indexed (or listed or
|
||||
/// graphed) in time order (https://en.wikipedia.org/wiki/Time_series).
|
||||
///
|
||||
/// An instance of this class is exposed as multiple time series during
|
||||
/// scrape, i.e., one time series for each set of labels provided to Add().
|
||||
///
|
||||
/// For example it is possible to collect data for a metric
|
||||
/// `http_requests_total`, with two time series:
|
||||
///
|
||||
/// - all HTTP requests that used the method POST
|
||||
/// - all HTTP requests that used the method GET
|
||||
///
|
||||
/// The metric name specifies the general feature of a system that is
|
||||
/// measured, e.g., `http_requests_total`. Labels enable Prometheus's
|
||||
/// dimensional data model: any given combination of labels for the same
|
||||
/// metric name identifies a particular dimensional instantiation of that
|
||||
/// metric. For example a label for 'all HTTP requests that used the method
|
||||
/// POST' can be assigned with `method= "POST"`.
|
||||
///
|
||||
/// Given a metric name and a set of labels, time series are frequently
|
||||
/// identified using this notation:
|
||||
///
|
||||
/// <metric name> { < label name >= <label value>, ... }
|
||||
///
|
||||
/// It is required to follow the syntax of metric names and labels given by:
|
||||
/// https://prometheus.io/docs/concepts/data_model/#metric-names-and-labels
|
||||
///
|
||||
/// The following metric and label conventions are not required for using
|
||||
/// Prometheus, but can serve as both a style-guide and a collection of best
|
||||
/// practices: https://prometheus.io/docs/practices/naming/
|
||||
///
|
||||
/// tparam T One of the metric types Counter, Gauge, Histogram or Summary.
|
||||
class Family : public Collectable {
|
||||
|
||||
public:
|
||||
|
||||
using Hash = std::size_t;
|
||||
using Label = std::pair<const std::string, const std::string>;
|
||||
using Labels = std::map <const std::string, const std::string>;
|
||||
using MetricPtr = std::unique_ptr<Metric>;
|
||||
|
||||
const Metric::Type type;
|
||||
const std::string name;
|
||||
const std::string help;
|
||||
const Labels constant_labels;
|
||||
mutable std::mutex mutex;
|
||||
|
||||
std::unordered_map<Hash, MetricPtr> metrics;
|
||||
std::unordered_map<Hash, Labels> labels;
|
||||
std::unordered_map<Metric*, Hash> labels_reverse_lookup;
|
||||
|
||||
|
||||
/// \brief Compute the hash value of a map of labels.
|
||||
///
|
||||
/// \param labels The map that will be computed the hash value.
|
||||
///
|
||||
/// \returns The hash value of the given labels.
|
||||
static Hash hash_labels (const Labels& labels) {
|
||||
size_t seed = 0;
|
||||
for (const Label& label : labels)
|
||||
detail::hash_combine (&seed, label.first, label.second);
|
||||
|
||||
return seed;
|
||||
}
|
||||
|
||||
static bool isLocaleIndependentDigit (char c) { return '0' <= c && c <= '9'; }
|
||||
static bool isLocaleIndependentAlphaNumeric (char c) { return isLocaleIndependentDigit(c) || ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z'); }
|
||||
|
||||
bool nameStartsValid (const std::string& name) {
|
||||
if (name.empty()) return false; // must not be empty
|
||||
if (isLocaleIndependentDigit(name.front())) return false; // must not start with a digit
|
||||
if (name.compare(0, 2, "__") == 0) return false; // must not start with "__"
|
||||
return true;
|
||||
}
|
||||
|
||||
/// \brief Check if the metric name is valid
|
||||
///
|
||||
/// The metric name regex is "[a-zA-Z_:][a-zA-Z0-9_:]*"
|
||||
///
|
||||
/// \see https://prometheus.io/docs/concepts/data_model/
|
||||
///
|
||||
/// \param name metric name
|
||||
/// \return true is valid, false otherwise
|
||||
bool CheckMetricName (const std::string& name) {
|
||||
|
||||
if (!nameStartsValid(name))
|
||||
return false;
|
||||
|
||||
for (const char& c : name)
|
||||
if ( !isLocaleIndependentAlphaNumeric(c) && c != '_' && c != ':' )
|
||||
return false;
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
/// \brief Check if the label name is valid
|
||||
///
|
||||
/// The label name regex is "[a-zA-Z_][a-zA-Z0-9_]*"
|
||||
///
|
||||
/// \see https://prometheus.io/docs/concepts/data_model/
|
||||
///
|
||||
/// \param name label name
|
||||
/// \return true is valid, false otherwise
|
||||
bool CheckLabelName (const std::string& name) {
|
||||
|
||||
if (!nameStartsValid(name))
|
||||
return false;
|
||||
|
||||
for (const char& c : name)
|
||||
if (!isLocaleIndependentAlphaNumeric(c) && c != '_')
|
||||
return false;
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
/// \brief Create a new metric.
|
||||
///
|
||||
/// Every metric is uniquely identified by its name and a set of key-value
|
||||
/// pairs, also known as labels. Prometheus's query language allows filtering
|
||||
/// and aggregation based on metric name and these labels.
|
||||
///
|
||||
/// This example selects all time series that have the `http_requests_total`
|
||||
/// metric name:
|
||||
///
|
||||
/// http_requests_total
|
||||
///
|
||||
/// It is possible to assign labels to the metric name. These labels are
|
||||
/// propagated to each dimensional data added with Add(). For example if a
|
||||
/// label `job= "prometheus"` is provided to this constructor, it is possible
|
||||
/// to filter this time series with Prometheus's query language by appending
|
||||
/// a set of labels to match in curly braces ({})
|
||||
///
|
||||
/// http_requests_total{job= "prometheus"}
|
||||
///
|
||||
/// For further information see: [Quering Basics]
|
||||
/// (https://prometheus.io/docs/prometheus/latest/querying/basics/)
|
||||
///
|
||||
/// \param name Set the metric name.
|
||||
/// \param help Set an additional description.
|
||||
/// \param constant_labels Assign a set of key-value pairs (= labels) to the
|
||||
/// metric. All these labels are propagated to each time series within the
|
||||
/// metric.
|
||||
/// \throw std::runtime_exception on invalid metric or label names.
|
||||
Family (Metric::Type type_, const std::string& name_, const std::string& help_, const Labels& constant_labels_)
|
||||
: type(type_), name(name_), help(help_), constant_labels(constant_labels_) {
|
||||
|
||||
if (!CheckMetricName(name_))
|
||||
throw std::invalid_argument("Invalid metric name");
|
||||
|
||||
for (const Label& label_pair : constant_labels) {
|
||||
const std::string& label_name = label_pair.first;
|
||||
if (!CheckLabelName(label_name))
|
||||
throw std::invalid_argument("Invalid label name");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// \brief Remove the given dimensional data.
|
||||
///
|
||||
/// \param metric Dimensional data to be removed. The function does nothing,
|
||||
/// if the given metric was not returned by Add().
|
||||
void Remove (Metric* metric) {
|
||||
std::lock_guard<std::mutex> lock{ mutex };
|
||||
|
||||
if (labels_reverse_lookup.count(metric) == 0)
|
||||
return;
|
||||
|
||||
const Hash hash = labels_reverse_lookup.at(metric);
|
||||
metrics.erase(hash);
|
||||
labels.erase(hash);
|
||||
labels_reverse_lookup.erase(metric);
|
||||
|
||||
}
|
||||
|
||||
/// \brief Returns true if the dimensional data with the given labels exist
|
||||
///
|
||||
/// \param labels A set of key-value pairs (= labels) of the dimensional data.
|
||||
bool Has (const Labels& labels) const {
|
||||
const Hash hash = hash_labels (labels);
|
||||
std::lock_guard<std::mutex> lock{ mutex };
|
||||
return metrics.find(hash) != metrics.end();
|
||||
}
|
||||
|
||||
/// \brief Returns the name for this family.
|
||||
///
|
||||
/// \return The family name.
|
||||
const std::string& GetName() const {
|
||||
return name;
|
||||
}
|
||||
|
||||
/// \brief Returns the constant labels for this family.
|
||||
///
|
||||
/// \return All constant labels as key-value pairs.
|
||||
const Labels& GetConstantLabels() const {
|
||||
return constant_labels;
|
||||
}
|
||||
|
||||
/// \brief Returns the current value of each dimensional data.
|
||||
///
|
||||
/// Collect is called by the Registry when collecting metrics.
|
||||
///
|
||||
/// \return Zero or more samples for each dimensional data.
|
||||
MetricFamilies Collect() const override {
|
||||
std::lock_guard<std::mutex> lock{ mutex };
|
||||
|
||||
if (metrics.empty())
|
||||
return {};
|
||||
|
||||
MetricFamily family = MetricFamily{};
|
||||
family.type = type;
|
||||
family.name = name;
|
||||
family.help = help;
|
||||
family.metric.reserve(metrics.size());
|
||||
|
||||
for (const std::pair<const Hash, MetricPtr>& metric_pair : metrics) {
|
||||
|
||||
ClientMetric collected = metric_pair.second->Collect();
|
||||
for (const Label& constant_label : constant_labels)
|
||||
collected.label.emplace_back(ClientMetric::Label(constant_label.first, constant_label.second));
|
||||
|
||||
const Labels& metric_labels = labels.at(metric_pair.first);
|
||||
for (const Label& metric_label : metric_labels)
|
||||
collected.label.emplace_back(ClientMetric::Label(metric_label.first, metric_label.second));
|
||||
|
||||
family.metric.push_back(std::move(collected));
|
||||
|
||||
}
|
||||
|
||||
return { family };
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
template <typename CustomMetric>
|
||||
class CustomFamily : public Family {
|
||||
|
||||
public:
|
||||
|
||||
static const Metric::Type static_type = CustomMetric::static_type;
|
||||
|
||||
CustomFamily(const std::string& name, const std::string& help, const Family::Labels& constant_labels)
|
||||
: Family(static_type, name, help, constant_labels) {}
|
||||
|
||||
/// \brief Add a new dimensional data.
|
||||
///
|
||||
/// Each new set of labels adds a new dimensional data and is exposed in
|
||||
/// Prometheus as a time series. It is possible to filter the time series
|
||||
/// with Prometheus's query language by appending a set of labels to match in
|
||||
/// curly braces ({})
|
||||
///
|
||||
/// http_requests_total{job= "prometheus",method= "POST"}
|
||||
///
|
||||
/// \param labels Assign a set of key-value pairs (= labels) to the
|
||||
/// dimensional data. The function does nothing, if the same set of labels
|
||||
/// already exists.
|
||||
/// \param args Arguments are passed to the constructor of metric type T. See
|
||||
/// Counter, Gauge, Histogram or Summary for required constructor arguments.
|
||||
/// \return Return the newly created dimensional data or - if a same set of
|
||||
/// labels already exists - the already existing dimensional data.
|
||||
/// \throw std::runtime_exception on invalid label names.
|
||||
template <typename... Args>
|
||||
CustomMetric& Add (const Labels& new_labels, Args&&... args) {
|
||||
const Hash hash = hash_labels (new_labels);
|
||||
std::lock_guard<std::mutex> lock{ mutex };
|
||||
|
||||
// try to find existing one
|
||||
auto metrics_iter = metrics.find(hash);
|
||||
if (metrics_iter != metrics.end()) {
|
||||
#ifndef NDEBUG
|
||||
// check that we have stored labels for this existing metric
|
||||
auto labels_iter = labels.find(hash);
|
||||
assert(labels_iter != labels.end());
|
||||
const Labels& stored_labels = labels_iter->second;
|
||||
assert(new_labels == stored_labels);
|
||||
#endif
|
||||
return dynamic_cast<CustomMetric&>(*metrics_iter->second);
|
||||
}
|
||||
|
||||
// check labels before create the new one
|
||||
for (const Label& label_pair : new_labels) {
|
||||
const std::string& label_name = label_pair.first;
|
||||
if (!CheckLabelName(label_name))
|
||||
throw std::invalid_argument("Invalid label name");
|
||||
if (constant_labels.count(label_name))
|
||||
throw std::invalid_argument("Label name already present in constant labels");
|
||||
}
|
||||
|
||||
// create new one
|
||||
std::unique_ptr<CustomMetric> metric_ptr (new CustomMetric(std::forward<Args>(args)...));
|
||||
CustomMetric& metric = *metric_ptr;
|
||||
|
||||
const auto stored_metric = metrics.insert(std::make_pair(hash, std::move(metric_ptr)));
|
||||
assert(stored_metric.second);
|
||||
labels.insert({ hash, new_labels });
|
||||
labels_reverse_lookup.insert({ stored_metric.first->second.get(), hash });
|
||||
|
||||
return metric;
|
||||
}
|
||||
|
||||
/// \brief Return a builder to configure and register a Counter metric.
|
||||
///
|
||||
/// @copydetails family_base_t<>::family_base_t()
|
||||
///
|
||||
/// Example usage:
|
||||
///
|
||||
/// \code
|
||||
/// auto registry = std::make_shared<Registry>();
|
||||
/// auto& counter_family = prometheus::Counter_family::build("some_name", "Additional description.", {{"key", "value"}}, *registry);
|
||||
///
|
||||
/// ...
|
||||
/// \endcode
|
||||
///
|
||||
/// \return An object of unspecified type T, i.e., an implementation detail
|
||||
/// except that it has the following members:
|
||||
///
|
||||
/// - Name(const std::string&) to set the metric name,
|
||||
/// - Help(const std::string&) to set an additional description.
|
||||
/// - Label(const std::map<std::string, std::string>&) to assign a set of
|
||||
/// key-value pairs (= labels) to the metric.
|
||||
///
|
||||
/// To finish the configuration of the Counter metric, register it with
|
||||
/// Register(Registry&).
|
||||
template <typename Registry>
|
||||
static CustomFamily& Build(Registry& registry, const std::string& name, const std::string& help, const Family::Labels& labels = Family::Labels()) {
|
||||
return registry.template Add<CustomFamily>(name, help, labels);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
} // namespace prometheus
|
205
ext/prometheus-cpp-lite-1.0/core/include/prometheus/gateway.h
Normal file
205
ext/prometheus-cpp-lite-1.0/core/include/prometheus/gateway.h
Normal file
@ -0,0 +1,205 @@
|
||||
#pragma once
|
||||
|
||||
#include "prometheus/collectable.h"
|
||||
#include "prometheus/text_serializer.h"
|
||||
#include "prometheus/metric_family.h"
|
||||
|
||||
#include <jdl/httpclientlite.h>
|
||||
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <future>
|
||||
#include <algorithm>
|
||||
#include <utility>
|
||||
|
||||
|
||||
namespace prometheus {
|
||||
|
||||
class Gateway {
|
||||
using CollectableEntry = std::pair<std::weak_ptr<Collectable>, std::string>;
|
||||
|
||||
std::string job_uri_;
|
||||
std::string labels_;
|
||||
|
||||
std::mutex mutex_;
|
||||
|
||||
std::vector<CollectableEntry> collectables_;
|
||||
|
||||
enum class HttpMethod : uint8_t{
|
||||
Post,
|
||||
Put,
|
||||
Delete,
|
||||
};
|
||||
|
||||
public:
|
||||
using Labels = std::map<std::string, std::string>;
|
||||
|
||||
Gateway(const std::string host, const std::string port,
|
||||
const std::string jobname, const Labels& labels = {})
|
||||
: job_uri_(host + ':' + port + std::string("/metrics/job/") + jobname)
|
||||
, labels_{}
|
||||
{
|
||||
std::stringstream label_strm;
|
||||
for (const auto& label : labels) {
|
||||
label_strm << "/" << label.first << "/" << label.second;
|
||||
}
|
||||
labels_ = label_strm.str();
|
||||
}
|
||||
|
||||
void RegisterCollectable(const std::weak_ptr<Collectable>& collectable,
|
||||
const Labels* labels = nullptr) {
|
||||
std::stringstream label_strm;
|
||||
|
||||
if (labels != nullptr) {
|
||||
for (const auto& label : *labels) {
|
||||
label_strm << "/" << label.first << "/" << label.second;
|
||||
}
|
||||
}
|
||||
|
||||
CleanupStalePointers(collectables_);
|
||||
collectables_.emplace_back(std::make_pair(collectable, label_strm.str()));
|
||||
}
|
||||
|
||||
|
||||
static const Labels GetInstanceLabel(const std::string& hostname) {
|
||||
if (hostname.empty()) {
|
||||
return Gateway::Labels{};
|
||||
}
|
||||
|
||||
return Gateway::Labels{{"instance", hostname}};
|
||||
}
|
||||
|
||||
|
||||
// Push metrics to the given pushgateway.
|
||||
int Push() {
|
||||
return push(HttpMethod::Post);
|
||||
}
|
||||
|
||||
|
||||
std::future<int> AsyncPush() {
|
||||
return async_push(HttpMethod::Post);
|
||||
}
|
||||
|
||||
|
||||
// PushAdd metrics to the given pushgateway.
|
||||
int PushAdd() {
|
||||
return push(HttpMethod::Put);
|
||||
}
|
||||
|
||||
|
||||
std::future<int> AsyncPushAdd() {
|
||||
return async_push(HttpMethod::Put);
|
||||
}
|
||||
|
||||
|
||||
// Delete metrics from the given pushgateway.
|
||||
int Delete() {
|
||||
return performHttpRequest(HttpMethod::Delete, job_uri_, {});
|
||||
}
|
||||
|
||||
// Delete metrics from the given pushgateway.
|
||||
std::future<int> AsyncDelete() {
|
||||
return std::async(std::launch::async, [&] { return Delete(); });
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
std::string getUri(const CollectableEntry& collectable) const {
|
||||
return (job_uri_ + labels_ + collectable.second);
|
||||
}
|
||||
|
||||
|
||||
int performHttpRequest(HttpMethod /*method*/, const std::string& uri_str, const std::string& body) {
|
||||
std::lock_guard<std::mutex> l(mutex_);
|
||||
|
||||
/* Stub function. The implementation will be later, after connecting the
|
||||
* additional library of HTTP requests. */
|
||||
|
||||
jdl::URI uri(uri_str);
|
||||
jdl::HTTPResponse response = jdl::HTTPClient::request(jdl::HTTPClient::m_post, uri, body);
|
||||
|
||||
return std::stoi(response.response);
|
||||
}
|
||||
|
||||
|
||||
int push(HttpMethod method) {
|
||||
const auto serializer = TextSerializer{};
|
||||
|
||||
for (const auto& wcollectable : collectables_) {
|
||||
auto collectable = wcollectable.first.lock();
|
||||
if (!collectable) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto metrics = collectable->Collect();
|
||||
auto uri = getUri(wcollectable);
|
||||
|
||||
std::stringstream body;
|
||||
serializer.Serialize(body, metrics);
|
||||
std::string body_str = body.str();
|
||||
|
||||
auto status_code = performHttpRequest(method, uri, body_str);
|
||||
|
||||
if (status_code < 100 || status_code >= 400) {
|
||||
return status_code;
|
||||
}
|
||||
}
|
||||
|
||||
return 200;
|
||||
}
|
||||
|
||||
|
||||
std::future<int> async_push(HttpMethod method) {
|
||||
const auto serializer = TextSerializer{};
|
||||
std::vector<std::future<int>> futures;
|
||||
|
||||
for (const auto& wcollectable : collectables_) {
|
||||
auto collectable = wcollectable.first.lock();
|
||||
if (!collectable) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto metrics = collectable->Collect();
|
||||
auto uri = getUri(wcollectable);
|
||||
|
||||
std::stringstream body;
|
||||
serializer.Serialize(body, metrics);
|
||||
auto body_ptr = std::make_shared<std::string>(body.str());
|
||||
|
||||
futures.emplace_back(std::async(std::launch::async, [method, &uri, &body_ptr, this] {
|
||||
return performHttpRequest(method, uri, *body_ptr);
|
||||
}));
|
||||
}
|
||||
|
||||
const auto reduceFutures = [](std::vector<std::future<int>> lfutures) {
|
||||
auto final_status_code = 200;
|
||||
|
||||
for (auto& future : lfutures) {
|
||||
auto status_code = future.get();
|
||||
|
||||
if (status_code < 100 || status_code >= 400) {
|
||||
final_status_code = status_code;
|
||||
}
|
||||
}
|
||||
|
||||
return final_status_code;
|
||||
};
|
||||
|
||||
return std::async(std::launch::async, reduceFutures, std::move(futures));
|
||||
}
|
||||
|
||||
|
||||
static void CleanupStalePointers(std::vector<CollectableEntry>& collectables) {
|
||||
collectables.erase(std::remove_if(std::begin(collectables), std::end(collectables),
|
||||
[](const CollectableEntry& candidate) {
|
||||
return candidate.first.expired();
|
||||
}),
|
||||
std::end(collectables));
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace prometheus
|
128
ext/prometheus-cpp-lite-1.0/core/include/prometheus/gauge.h
Normal file
128
ext/prometheus-cpp-lite-1.0/core/include/prometheus/gauge.h
Normal file
@ -0,0 +1,128 @@
|
||||
#pragma once
|
||||
|
||||
#include "prometheus/atomic_floating.h"
|
||||
#include "prometheus/metric.h"
|
||||
#include "prometheus/family.h"
|
||||
|
||||
#include "prometheus/builder.h"
|
||||
|
||||
#include <atomic>
|
||||
#include <ctime>
|
||||
|
||||
namespace prometheus {
|
||||
|
||||
/// \brief A gauge metric to represent a value that can arbitrarily go up and
|
||||
/// down.
|
||||
///
|
||||
/// The class represents the metric type gauge:
|
||||
/// https://prometheus.io/docs/concepts/metric_types/#gauge
|
||||
///
|
||||
/// Gauges are typically used for measured values like temperatures or current
|
||||
/// memory usage, but also "counts" that can go up and down, like the number of
|
||||
/// running processes.
|
||||
///
|
||||
/// The class is thread-safe. No concurrent call to any API of this type causes
|
||||
/// a data race.
|
||||
template <typename Value_ = uint64_t>
|
||||
class Gauge : public Metric {
|
||||
|
||||
std::atomic<Value_> value { 0 };
|
||||
|
||||
public:
|
||||
|
||||
using Value = Value_;
|
||||
using Family = CustomFamily<Gauge<Value>>;
|
||||
|
||||
static const Metric::Type static_type = Metric::Type::Gauge;
|
||||
|
||||
|
||||
Gauge() : Metric (static_type) {} ///< \brief Create a gauge that starts at 0.
|
||||
Gauge(const Value value_) : Metric(static_type), value{ value_ } {} ///< \brief Create a gauge that starts at the given amount.
|
||||
|
||||
// original API
|
||||
|
||||
void Increment() { ++value; } ///< \brief Increment the gauge by 1.
|
||||
void Increment(const Value& val) { value += val; } ///< \brief Increment the gauge by the given amount.
|
||||
|
||||
void Decrement() { --value; } ///< \brief Decrement the gauge by 1.
|
||||
void Decrement(const Value& val) { value -= val; } ///< \brief Decrement the gauge by the given amount.
|
||||
|
||||
void SetToCurrentTime() { ///< \brief Set the gauge to the current unixtime in seconds.
|
||||
const time_t time = std::time(nullptr);
|
||||
value = static_cast<Value>(time);
|
||||
}
|
||||
void Set(const Value& val) { value = val; } ///< \brief Set the gauge to the given value.
|
||||
const Value Get() const { return value; } ///< \brief Get the current value of the gauge.
|
||||
|
||||
virtual ClientMetric Collect() const { ///< \brief Get the current value of the gauge. Collect is called by the Registry when collecting metrics.
|
||||
ClientMetric metric;
|
||||
metric.gauge.value = static_cast<double>(value);
|
||||
return metric;
|
||||
}
|
||||
|
||||
// new API
|
||||
|
||||
Gauge& operator ++() {
|
||||
++value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Gauge& operator++ (int) {
|
||||
++value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Gauge& operator --() {
|
||||
--value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Gauge& operator-- (int) {
|
||||
--value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Gauge& operator+=(const Value& val) {
|
||||
value += val;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Gauge& operator-=(const Value& val) {
|
||||
value -= val;
|
||||
return *this;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
/// \brief Return a builder to configure and register a Gauge metric.
|
||||
///
|
||||
/// @copydetails Family<>::Family()
|
||||
///
|
||||
/// Example usage:
|
||||
///
|
||||
/// \code
|
||||
/// auto registry = std::make_shared<Registry>();
|
||||
/// auto& gauge_family = prometheus::BuildGauge()
|
||||
/// .Name("some_name")
|
||||
/// .Help("Additional description.")
|
||||
/// .Labels({{"key", "value"}})
|
||||
/// .Register(*registry);
|
||||
///
|
||||
/// ...
|
||||
/// \endcode
|
||||
///
|
||||
/// \return An object of unspecified type T, i.e., an implementation detail
|
||||
/// except that it has the following members:
|
||||
///
|
||||
/// - Name(const std::string&) to set the metric name,
|
||||
/// - Help(const std::string&) to set an additional description.
|
||||
/// - Label(const std::map<std::string, std::string>&) to assign a set of
|
||||
/// key-value pairs (= labels) to the metric.
|
||||
///
|
||||
/// To finish the configuration of the Gauge metric register it with
|
||||
/// Register(Registry&).
|
||||
using BuildGauge = Builder<Gauge<double>>;
|
||||
|
||||
|
||||
} // namespace prometheus
|
50
ext/prometheus-cpp-lite-1.0/core/include/prometheus/hash.h
Normal file
50
ext/prometheus-cpp-lite-1.0/core/include/prometheus/hash.h
Normal file
@ -0,0 +1,50 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include <functional>
|
||||
|
||||
namespace prometheus {
|
||||
|
||||
namespace detail {
|
||||
|
||||
/// \brief Combine a hash value with nothing.
|
||||
/// It's the boundary condition of this serial functions.
|
||||
///
|
||||
/// \param seed Not effect.
|
||||
inline void hash_combine(std::size_t *) {}
|
||||
|
||||
/// \brief Combine the given hash value with another obeject.
|
||||
///
|
||||
/// \param seed The given hash value. It's a input/output parameter.
|
||||
/// \param value The object that will be combined with the given hash value.
|
||||
template <typename T>
|
||||
inline void hash_combine(std::size_t *seed, const T &value) {
|
||||
*seed ^= std::hash<T>{}(value) + 0x9e3779b9 + (*seed << 6) + (*seed >> 2);
|
||||
}
|
||||
|
||||
/// \brief Combine the given hash value with another objects. It's a recursion。
|
||||
///
|
||||
/// \param seed The give hash value. It's a input/output parameter.
|
||||
/// \param value The object that will be combined with the given hash value.
|
||||
/// \param args The objects that will be combined with the given hash value.
|
||||
template <typename T, typename... Types>
|
||||
inline void hash_combine(std::size_t *seed, const T &value,
|
||||
const Types &...args) {
|
||||
hash_combine(seed, value);
|
||||
hash_combine(seed, args...);
|
||||
}
|
||||
|
||||
/// \brief Compute a hash value of the given args.
|
||||
///
|
||||
/// \param args The arguments that will be computed hash value.
|
||||
/// \return The hash value of the given args.
|
||||
template <typename... Types>
|
||||
inline std::size_t hash_value(const Types &...args) {
|
||||
std::size_t seed = 0;
|
||||
hash_combine(&seed, args...);
|
||||
return seed;
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
} // namespace prometheus
|
154
ext/prometheus-cpp-lite-1.0/core/include/prometheus/histogram.h
Normal file
154
ext/prometheus-cpp-lite-1.0/core/include/prometheus/histogram.h
Normal file
@ -0,0 +1,154 @@
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <cassert>
|
||||
#include <algorithm>
|
||||
|
||||
#include "prometheus/metric.h"
|
||||
#include "prometheus/family.h"
|
||||
#include "prometheus/counter.h"
|
||||
|
||||
namespace prometheus {
|
||||
|
||||
/// \brief A histogram metric to represent aggregatable distributions of events.
|
||||
///
|
||||
/// This class represents the metric type histogram:
|
||||
/// https://prometheus.io/docs/concepts/metric_types/#histogram
|
||||
///
|
||||
/// A histogram tracks the number of observations and the sum of the observed
|
||||
/// values, allowing to calculate the average of the observed values.
|
||||
///
|
||||
/// At its core a histogram has a counter per bucket. The sum of observations
|
||||
/// also behaves like a counter as long as there are no negative observations.
|
||||
///
|
||||
/// See https://prometheus.io/docs/practices/histograms/ for detailed
|
||||
/// explanations of histogram usage and differences to summaries.
|
||||
///
|
||||
/// The class is thread-safe. No concurrent call to any API of this type causes
|
||||
/// a data race.
|
||||
template <typename Value_ = uint64_t>
|
||||
class Histogram : Metric {
|
||||
|
||||
using BucketBoundaries = std::vector<Value_>;
|
||||
|
||||
const BucketBoundaries bucket_boundaries_;
|
||||
std::vector<Counter<Value_>> bucket_counts_;
|
||||
Gauge<Value_> sum_;
|
||||
|
||||
public:
|
||||
using Value = Value_;
|
||||
using Family = CustomFamily<Histogram<Value>>;
|
||||
|
||||
static const Metric::Type static_type = Metric::Type::Histogram;
|
||||
|
||||
/// \brief Create a histogram with manually chosen buckets.
|
||||
///
|
||||
/// The BucketBoundaries are a list of monotonically increasing values
|
||||
/// representing the bucket boundaries. Each consecutive pair of values is
|
||||
/// interpreted as a half-open interval [b_n, b_n+1) which defines one bucket.
|
||||
///
|
||||
/// There is no limitation on how the buckets are divided, i.e, equal size,
|
||||
/// exponential etc..
|
||||
///
|
||||
/// The bucket boundaries cannot be changed once the histogram is created.
|
||||
Histogram (const BucketBoundaries& buckets)
|
||||
: Metric(static_type), bucket_boundaries_{ buckets }, bucket_counts_{ buckets.size() + 1 }, sum_{} {
|
||||
assert(std::is_sorted(std::begin(bucket_boundaries_),
|
||||
std::end(bucket_boundaries_)));
|
||||
}
|
||||
|
||||
/// \brief Observe the given amount.
|
||||
///
|
||||
/// The given amount selects the 'observed' bucket. The observed bucket is
|
||||
/// chosen for which the given amount falls into the half-open interval [b_n,
|
||||
/// b_n+1). The counter of the observed bucket is incremented. Also the total
|
||||
/// sum of all observations is incremented.
|
||||
void Observe(const Value value) {
|
||||
// TODO: determine bucket list size at which binary search would be faster
|
||||
const auto bucket_index = static_cast<std::size_t>(std::distance(
|
||||
bucket_boundaries_.begin(),
|
||||
std::find_if(
|
||||
std::begin(bucket_boundaries_), std::end(bucket_boundaries_),
|
||||
[value](const double boundary) { return boundary >= value; })));
|
||||
sum_.Increment(value);
|
||||
bucket_counts_[bucket_index].Increment();
|
||||
}
|
||||
|
||||
/// \brief Observe multiple data points.
|
||||
///
|
||||
/// Increments counters given a count for each bucket. (i.e. the caller of
|
||||
/// this function must have already sorted the values into buckets).
|
||||
/// Also increments the total sum of all observations by the given value.
|
||||
void ObserveMultiple(const std::vector<Value>& bucket_increments,
|
||||
const Value sum_of_values) {
|
||||
|
||||
if (bucket_increments.size() != bucket_counts_.size()) {
|
||||
throw std::length_error(
|
||||
"The size of bucket_increments was not equal to"
|
||||
"the number of buckets in the histogram.");
|
||||
}
|
||||
|
||||
sum_.Increment(sum_of_values);
|
||||
|
||||
for (std::size_t i{ 0 }; i < bucket_counts_.size(); ++i) {
|
||||
bucket_counts_[i].Increment(bucket_increments[i]);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// \brief Get the current value of the counter.
|
||||
///
|
||||
/// Collect is called by the Registry when collecting metrics.
|
||||
virtual ClientMetric Collect() const {
|
||||
auto metric = ClientMetric{};
|
||||
|
||||
auto cumulative_count = 0ULL;
|
||||
metric.histogram.bucket.reserve(bucket_counts_.size());
|
||||
for (std::size_t i{0}; i < bucket_counts_.size(); ++i) {
|
||||
cumulative_count += static_cast<std::size_t>(bucket_counts_[i].Value());
|
||||
auto bucket = ClientMetric::Bucket{};
|
||||
bucket.cumulative_count = cumulative_count;
|
||||
bucket.upper_bound = (i == bucket_boundaries_.size()
|
||||
? std::numeric_limits<double>::infinity()
|
||||
: bucket_boundaries_[i]);
|
||||
metric.histogram.bucket.push_back(std::move(bucket));
|
||||
}
|
||||
metric.histogram.sample_count = cumulative_count;
|
||||
metric.histogram.sample_sum = sum_.Get();
|
||||
|
||||
return metric;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
/// \brief Return a builder to configure and register a Histogram metric.
|
||||
///
|
||||
/// @copydetails Family<>::Family()
|
||||
///
|
||||
/// Example usage:
|
||||
///
|
||||
/// \code
|
||||
/// auto registry = std::make_shared<Registry>();
|
||||
/// auto& histogram_family = prometheus::BuildHistogram()
|
||||
/// .Name("some_name")
|
||||
/// .Help("Additional description.")
|
||||
/// .Labels({{"key", "value"}})
|
||||
/// .Register(*registry);
|
||||
///
|
||||
/// ...
|
||||
/// \endcode
|
||||
///
|
||||
/// \return An object of unspecified type T, i.e., an implementation detail
|
||||
/// except that it has the following members:
|
||||
///
|
||||
/// - Name(const std::string&) to set the metric name,
|
||||
/// - Help(const std::string&) to set an additional description.
|
||||
/// - Label(const std::map<std::string, std::string>&) to assign a set of
|
||||
/// key-value pairs (= labels) to the metric.
|
||||
///
|
||||
/// To finish the configuration of the Histogram metric register it with
|
||||
/// Register(Registry&).
|
||||
using BuildHistogram = Builder<Histogram<double>>;
|
||||
|
||||
|
||||
} // namespace prometheus
|
29
ext/prometheus-cpp-lite-1.0/core/include/prometheus/metric.h
Normal file
29
ext/prometheus-cpp-lite-1.0/core/include/prometheus/metric.h
Normal file
@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "client_metric.h"
|
||||
|
||||
namespace prometheus {
|
||||
|
||||
class Metric {
|
||||
|
||||
public:
|
||||
enum class Type {
|
||||
Counter,
|
||||
Gauge,
|
||||
Summary,
|
||||
Histogram,
|
||||
Untyped,
|
||||
};
|
||||
|
||||
Type type;
|
||||
|
||||
Metric (Type type_) : type(type_) {}
|
||||
virtual ~Metric() = default;
|
||||
|
||||
virtual ClientMetric Collect() const = 0;
|
||||
|
||||
};
|
||||
|
||||
} // namespace prometheus
|
@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "metric.h"
|
||||
#include "prometheus/client_metric.h"
|
||||
|
||||
namespace prometheus {
|
||||
|
||||
struct MetricFamily {
|
||||
Metric::Type type;
|
||||
std::string name;
|
||||
std::string help;
|
||||
std::vector<ClientMetric> metric;
|
||||
};
|
||||
|
||||
} // namespace prometheus
|
@ -0,0 +1,86 @@
|
||||
#pragma once
|
||||
|
||||
#include <thread>
|
||||
#include <chrono>
|
||||
#include <string>
|
||||
|
||||
#include "registry.h"
|
||||
#include "text_serializer.h"
|
||||
|
||||
#include <jdl/httpclientlite.h>
|
||||
|
||||
|
||||
namespace prometheus {
|
||||
class PushToServer {
|
||||
std::chrono::seconds period { 1 };
|
||||
std::string uri { "" };
|
||||
std::thread worker_thread { &PushToServer::worker_function, this };
|
||||
std::shared_ptr<Registry> registry_ptr { nullptr };
|
||||
bool must_die { false };
|
||||
|
||||
void push_data() {
|
||||
if (registry_ptr) {
|
||||
if (!uri.empty()) {
|
||||
std::stringstream body_strm;
|
||||
TextSerializer::Serialize(body_strm, registry_ptr->Collect());
|
||||
|
||||
std::string body = body_strm.str();
|
||||
jdl::HTTPResponse response = jdl::HTTPClient::request(jdl::HTTPClient::m_post, jdl::URI(uri), body);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void worker_function() {
|
||||
// it need for fast shutdown this thread when SaveToFile destructor is called
|
||||
const uint64_t divider = 100;
|
||||
uint64_t fraction = divider;
|
||||
|
||||
for (;;) {
|
||||
std::chrono::milliseconds period_ms
|
||||
= std::chrono::duration_cast<std::chrono::milliseconds>(period);
|
||||
std::this_thread::sleep_for( period_ms / divider );
|
||||
|
||||
if (must_die) {
|
||||
push_data();
|
||||
return;
|
||||
}
|
||||
|
||||
if (--fraction == 0) {
|
||||
fraction = divider;
|
||||
push_data();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
PushToServer() {
|
||||
jdl::init_socket();
|
||||
}
|
||||
|
||||
~PushToServer() {
|
||||
must_die = true;
|
||||
worker_thread.join();
|
||||
jdl::deinit_socket();
|
||||
}
|
||||
|
||||
PushToServer(std::shared_ptr<Registry>& registry_, const std::chrono::seconds& period_, const std::string& uri_) {
|
||||
set_registry(registry_);
|
||||
set_delay(period_);
|
||||
set_uri(uri_);
|
||||
}
|
||||
|
||||
void set_delay (const std::chrono::seconds& new_period) {
|
||||
period = new_period;
|
||||
}
|
||||
|
||||
|
||||
void set_uri (const std::string& uri_) {
|
||||
uri = std::move(uri_);
|
||||
}
|
||||
|
||||
void set_registry (std::shared_ptr<Registry>& new_registry_ptr) {
|
||||
registry_ptr = new_registry_ptr;
|
||||
}
|
||||
|
||||
};
|
||||
}
|
123
ext/prometheus-cpp-lite-1.0/core/include/prometheus/registry.h
Normal file
123
ext/prometheus-cpp-lite-1.0/core/include/prometheus/registry.h
Normal file
@ -0,0 +1,123 @@
|
||||
#pragma once
|
||||
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "prometheus/collectable.h"
|
||||
#include "prometheus/family.h"
|
||||
|
||||
namespace prometheus {
|
||||
|
||||
/// \brief Manages the collection of a number of metrics.
|
||||
///
|
||||
/// The Registry is responsible to expose data to a class/method/function
|
||||
/// "bridge", which returns the metrics in a format Prometheus supports.
|
||||
///
|
||||
/// The key class is the Collectable. This has a method - called Collect() -
|
||||
/// that returns zero or more metrics and their samples. The metrics are
|
||||
/// represented by the class Family<>, which implements the Collectable
|
||||
/// interface. A new metric is registered with BuildCounter(), BuildGauge(),
|
||||
/// BuildHistogram() or BuildSummary().
|
||||
///
|
||||
/// The class is thread-safe. No concurrent call to any API of this type causes
|
||||
/// a data race.
|
||||
class Registry : public Collectable {
|
||||
|
||||
public:
|
||||
|
||||
/// \brief How to deal with repeatedly added family names for a type.
|
||||
///
|
||||
/// Adding a family with the same name but different types is always an error
|
||||
/// and will lead to an exception.
|
||||
enum class InsertBehavior {
|
||||
/// \brief If a family with the same name and labels already exists return
|
||||
/// the existing one. If no family with that name exists create it.
|
||||
/// Otherwise throw.
|
||||
Merge,
|
||||
/// \brief Throws if a family with the same name already exists.
|
||||
Throw,
|
||||
/// \brief Never merge and always create a new family. This violates the
|
||||
/// prometheus specification but was the default behavior in earlier
|
||||
/// versions
|
||||
NonStandardAppend,
|
||||
};
|
||||
|
||||
using FamilyPtr = std::unique_ptr<Family>;
|
||||
using Families = std::vector<FamilyPtr>;
|
||||
|
||||
const InsertBehavior insert_behavior;
|
||||
mutable std::mutex mutex;
|
||||
Families families;
|
||||
|
||||
/// \brief name Create a new registry.
|
||||
///
|
||||
/// \param insert_behavior How to handle families with the same name.
|
||||
Registry (InsertBehavior insert_behavior_ = InsertBehavior::Merge)
|
||||
: insert_behavior(insert_behavior_) {}
|
||||
|
||||
/// \brief Returns a list of metrics and their samples.
|
||||
///
|
||||
/// Every time the Registry is scraped it calls each of the metrics Collect
|
||||
/// function.
|
||||
///
|
||||
/// \return Zero or more metrics and their samples.
|
||||
virtual MetricFamilies Collect() const {
|
||||
|
||||
std::lock_guard<std::mutex> lock{ mutex };
|
||||
|
||||
MetricFamilies results;
|
||||
|
||||
for (const FamilyPtr& family_ptr : families) {
|
||||
MetricFamilies metrics = family_ptr->Collect();
|
||||
results.insert(results.end(), std::make_move_iterator(metrics.begin()), std::make_move_iterator(metrics.end()));
|
||||
}
|
||||
|
||||
return results;
|
||||
|
||||
}
|
||||
|
||||
template <typename CustomFamily>
|
||||
CustomFamily& Add (const std::string& name, const std::string& help, const Family::Labels& labels) {
|
||||
|
||||
std::lock_guard<std::mutex> lock{ mutex };
|
||||
|
||||
bool found_one_but_not_merge = false;
|
||||
for (const FamilyPtr& family_ptr : families) {
|
||||
if (family_ptr->GetName() == name) {
|
||||
|
||||
if (family_ptr->type != CustomFamily::static_type) // found family with this name and with different type
|
||||
throw std::invalid_argument("Family name already exists with different type");
|
||||
|
||||
else { // found family with this name and the same type
|
||||
switch (insert_behavior) {
|
||||
case InsertBehavior::Throw:
|
||||
throw std::invalid_argument("Family name already exists");
|
||||
case InsertBehavior::Merge:
|
||||
if (family_ptr->GetConstantLabels() == labels)
|
||||
return dynamic_cast<CustomFamily&>(*family_ptr);
|
||||
else // this strange rule was in previos version prometheus cpp
|
||||
found_one_but_not_merge = true;
|
||||
case InsertBehavior::NonStandardAppend:
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if (found_one_but_not_merge) // this strange rule was in previos version prometheus cpp
|
||||
throw std::invalid_argument("Family name already exists with different labels");
|
||||
|
||||
std::unique_ptr<CustomFamily> new_family_ptr (new CustomFamily(name, help, labels));
|
||||
CustomFamily& new_family = *new_family_ptr;
|
||||
families.push_back(std::move(new_family_ptr));
|
||||
return new_family;
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
} // namespace prometheus
|
@ -0,0 +1,83 @@
|
||||
#pragma once
|
||||
|
||||
#include <thread>
|
||||
#include <chrono>
|
||||
#include <string>
|
||||
#include <fstream>
|
||||
#include <memory>
|
||||
|
||||
#include "registry.h"
|
||||
#include "text_serializer.h"
|
||||
|
||||
namespace prometheus {
|
||||
class SaveToFile {
|
||||
std::chrono::seconds period { 1 };
|
||||
std::string filename;
|
||||
std::thread worker_thread { &SaveToFile::worker_function, this };
|
||||
std::shared_ptr<Registry> registry_ptr { nullptr };
|
||||
bool must_die { false };
|
||||
|
||||
void save_data() {
|
||||
if (registry_ptr) {
|
||||
std::fstream out_file_stream;
|
||||
out_file_stream.open(filename, std::fstream::out | std::fstream::binary);
|
||||
if (out_file_stream.is_open()) {
|
||||
TextSerializer::Serialize(out_file_stream, registry_ptr->Collect());
|
||||
out_file_stream.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void worker_function() {
|
||||
// it need for fast shutdown this thread when SaveToFile destructor is called
|
||||
const uint64_t divider = 100;
|
||||
uint64_t fraction = divider;
|
||||
for (;;) {
|
||||
std::chrono::milliseconds period_ms
|
||||
= std::chrono::duration_cast<std::chrono::milliseconds>(period);
|
||||
std::this_thread::sleep_for( period_ms / divider );
|
||||
if (must_die) {
|
||||
save_data();
|
||||
return;
|
||||
}
|
||||
if (--fraction == 0) {
|
||||
fraction = divider;
|
||||
save_data();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
SaveToFile() = default;
|
||||
|
||||
~SaveToFile() {
|
||||
must_die = true;
|
||||
worker_thread.join();
|
||||
}
|
||||
|
||||
SaveToFile(std::shared_ptr<Registry>& registry_, const std::chrono::seconds& period_, const std::string& filename_) {
|
||||
set_registry(registry_);
|
||||
set_delay(period_);
|
||||
set_out_file(filename_);
|
||||
}
|
||||
|
||||
void set_delay (const std::chrono::seconds& new_period) {
|
||||
period = new_period;
|
||||
}
|
||||
|
||||
|
||||
bool set_out_file (const std::string& filename_) {
|
||||
filename = filename_;
|
||||
std::fstream out_file_stream;
|
||||
out_file_stream.open(filename, std::fstream::out | std::fstream::binary);
|
||||
bool open_success = out_file_stream.is_open();
|
||||
out_file_stream.close();
|
||||
return open_success;
|
||||
}
|
||||
|
||||
void set_registry (std::shared_ptr<Registry>& new_registry_ptr) {
|
||||
registry_ptr = new_registry_ptr;
|
||||
}
|
||||
|
||||
};
|
||||
}
|
154
ext/prometheus-cpp-lite-1.0/core/include/prometheus/summary.h
Normal file
154
ext/prometheus-cpp-lite-1.0/core/include/prometheus/summary.h
Normal file
@ -0,0 +1,154 @@
|
||||
#pragma once
|
||||
|
||||
#include <chrono>
|
||||
#include <cstdint>
|
||||
#include <mutex>
|
||||
#include <vector>
|
||||
|
||||
#include "prometheus/metric.h"
|
||||
#include "prometheus/family.h"
|
||||
|
||||
#include "prometheus/detail/ckms_quantiles.h"
|
||||
#include "prometheus/detail/time_window_quantiles.h"
|
||||
|
||||
#include "prometheus/builder.h"
|
||||
|
||||
namespace prometheus {
|
||||
|
||||
/// \brief A summary metric samples observations over a sliding window of time.
|
||||
///
|
||||
/// This class represents the metric type summary:
|
||||
/// https://prometheus.io/docs/instrumenting/writing_clientlibs/#summary
|
||||
///
|
||||
/// A summary provides a total count of observations and a sum of all observed
|
||||
/// values. In contrast to a histogram metric it also calculates configurable
|
||||
/// Phi-quantiles over a sliding window of time.
|
||||
///
|
||||
/// The essential difference between summaries and histograms is that summaries
|
||||
/// calculate streaming Phi-quantiles on the client side and expose them
|
||||
/// directly, while histograms expose bucketed observation counts and the
|
||||
/// calculation of quantiles from the buckets of a histogram happens on the
|
||||
/// server side:
|
||||
/// https://prometheus.io/docs/prometheus/latest/querying/functions/#histogram_quantile.
|
||||
///
|
||||
/// Note that Phi designates the probability density function of the standard
|
||||
/// Gaussian distribution.
|
||||
///
|
||||
/// See https://prometheus.io/docs/practices/histograms/ for detailed
|
||||
/// explanations of Phi-quantiles, summary usage, and differences to histograms.
|
||||
///
|
||||
/// The class is thread-safe. No concurrent call to any API of this type causes
|
||||
/// a data race.
|
||||
class Summary : Metric {
|
||||
|
||||
public:
|
||||
|
||||
using Value = double;
|
||||
using Family = CustomFamily<Summary>;
|
||||
|
||||
static const Metric::Type static_type = Metric::Type::Summary;
|
||||
|
||||
using Quantiles = std::vector<detail::CKMSQuantiles::Quantile>;
|
||||
|
||||
const Quantiles quantiles_;
|
||||
mutable std::mutex mutex_;
|
||||
std::uint64_t count_;
|
||||
double sum_;
|
||||
detail::TimeWindowQuantiles quantile_values_;
|
||||
|
||||
public:
|
||||
|
||||
/// \brief Create a summary metric.
|
||||
///
|
||||
/// \param quantiles A list of 'targeted' Phi-quantiles. A targeted
|
||||
/// Phi-quantile is specified in the form of a Phi-quantile and tolerated
|
||||
/// error. For example a Quantile{0.5, 0.1} means that the median (= 50th
|
||||
/// percentile) should be returned with 10 percent error or a Quantile{0.2,
|
||||
/// 0.05} means the 20th percentile with 5 percent tolerated error. Note that
|
||||
/// percentiles and quantiles are the same concept, except percentiles are
|
||||
/// expressed as percentages. The Phi-quantile must be in the interval [0, 1].
|
||||
/// Note that a lower tolerated error for a Phi-quantile results in higher
|
||||
/// usage of resources (memory and cpu) to calculate the summary.
|
||||
///
|
||||
/// The Phi-quantiles are calculated over a sliding window of time. The
|
||||
/// sliding window of time is configured by max_age and age_buckets.
|
||||
///
|
||||
/// \param max_age Set the duration of the time window, i.e., how long
|
||||
/// observations are kept before they are discarded. The default value is 60
|
||||
/// seconds.
|
||||
///
|
||||
/// \param age_buckets Set the number of buckets of the time window. It
|
||||
/// determines the number of buckets used to exclude observations that are
|
||||
/// older than max_age from the summary, e.g., if max_age is 60 seconds and
|
||||
/// age_buckets is 5, buckets will be switched every 12 seconds. The value is
|
||||
/// a trade-off between resources (memory and cpu for maintaining the bucket)
|
||||
/// and how smooth the time window is moved. With only one age bucket it
|
||||
/// effectively results in a complete reset of the summary each time max_age
|
||||
/// has passed. The default value is 5.
|
||||
Summary(const Quantiles& quantiles, std::chrono::milliseconds max_age = std::chrono::seconds{ 60 }, int age_buckets = 5)
|
||||
: Metric(static_type), quantiles_{ quantiles }, count_{ 0 }, sum_{ 0 }, quantile_values_(quantiles_, max_age, age_buckets) {}
|
||||
|
||||
/// \brief Observe the given amount.
|
||||
void Observe(const double value) {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
|
||||
count_ += 1;
|
||||
sum_ += value;
|
||||
quantile_values_.insert(value);
|
||||
|
||||
}
|
||||
|
||||
/// \brief Get the current value of the summary.
|
||||
///
|
||||
/// Collect is called by the Registry when collecting metrics.
|
||||
virtual ClientMetric Collect() const {
|
||||
auto metric = ClientMetric{};
|
||||
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
|
||||
metric.summary.quantile.reserve(quantiles_.size());
|
||||
for (const auto& quantile : quantiles_) {
|
||||
auto metricQuantile = ClientMetric::Quantile{};
|
||||
metricQuantile.quantile = quantile.quantile;
|
||||
metricQuantile.value = quantile_values_.get(quantile.quantile);
|
||||
metric.summary.quantile.push_back(std::move(metricQuantile));
|
||||
}
|
||||
metric.summary.sample_count = count_;
|
||||
metric.summary.sample_sum = sum_;
|
||||
|
||||
return metric;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/// \brief Return a builder to configure and register a Summary metric.
|
||||
///
|
||||
/// @copydetails Family<>::Family()
|
||||
///
|
||||
/// Example usage:
|
||||
///
|
||||
/// \code
|
||||
/// auto registry = std::make_shared<Registry>();
|
||||
/// auto& summary_family = prometheus::BuildSummary()
|
||||
/// .Name("some_name")
|
||||
/// .Help("Additional description.")
|
||||
/// .Labels({{"key", "value"}})
|
||||
/// .Register(*registry);
|
||||
///
|
||||
/// ...
|
||||
/// \endcode
|
||||
///
|
||||
/// \return An object of unspecified type T, i.e., an implementation detail
|
||||
/// except that it has the following members:
|
||||
///
|
||||
/// - Name(const std::string&) to set the metric name,
|
||||
/// - Help(const std::string&) to set an additional description.
|
||||
/// - Label(const std::map<std::string, std::string>&) to assign a set of
|
||||
/// key-value pairs (= labels) to the metric.
|
||||
///
|
||||
/// To finish the configuration of the Summary metric register it with
|
||||
/// Register(Registry&).
|
||||
using BuildSummary = Builder<Summary>;
|
||||
|
||||
|
||||
} // namespace prometheus
|
@ -0,0 +1,211 @@
|
||||
#pragma once
|
||||
|
||||
#include <iosfwd>
|
||||
#include <vector>
|
||||
#include <array>
|
||||
#include <math.h>
|
||||
#include <ostream>
|
||||
|
||||
#include "prometheus/metric_family.h"
|
||||
|
||||
#if __cpp_lib_to_chars >= 201611L
|
||||
#include <charconv>
|
||||
#endif
|
||||
|
||||
namespace prometheus {
|
||||
|
||||
class TextSerializer {
|
||||
|
||||
// Write a double as a string, with proper formatting for infinity and NaN
|
||||
static void WriteValue (std::ostream& out, double value) {
|
||||
if (std::isnan(value))
|
||||
out << "Nan";
|
||||
else if (std::isinf(value))
|
||||
out << (value < 0 ? "-Inf" : "+Inf");
|
||||
|
||||
else {
|
||||
|
||||
std::array<char, 128> buffer;
|
||||
|
||||
#if __cpp_lib_to_chars >= 201611L
|
||||
auto [ptr, ec] = std::to_chars(buffer.data(), buffer.data() + buffer.size(), value);
|
||||
if (ec != std::errc()) {
|
||||
throw std::runtime_error("Could not convert double to string: " +
|
||||
std::make_error_code(ec).message());
|
||||
}
|
||||
out.write(buffer.data(), ptr - buffer.data());
|
||||
#else
|
||||
int wouldHaveWritten = std::snprintf(buffer.data(), buffer.size(), "%.*g", std::numeric_limits<double>::max_digits10 - 1, value);
|
||||
if (wouldHaveWritten <= 0 || static_cast<std::size_t>(wouldHaveWritten) >= buffer.size()) {
|
||||
throw std::runtime_error("Could not convert double to string");
|
||||
}
|
||||
out.write(buffer.data(), wouldHaveWritten);
|
||||
#endif
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
static void WriteValue(std::ostream& out, const std::string& value) {
|
||||
for (auto c : value) {
|
||||
switch (c) {
|
||||
case '\n': out << '\\' << 'n'; break;
|
||||
case '\\': out << '\\' << c; break;
|
||||
case '"': out << '\\' << c; break;
|
||||
default: out << c; break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Write a line header: metric name and labels
|
||||
template <typename T = std::string>
|
||||
static void WriteHead(
|
||||
std::ostream& out,
|
||||
const MetricFamily& family,
|
||||
const ClientMetric& metric,
|
||||
const std::string& suffix = "",
|
||||
const std::string& extraLabelName = "",
|
||||
const T& extraLabelValue = T()) {
|
||||
|
||||
out << family.name << suffix;
|
||||
|
||||
if (!metric.label.empty() || !extraLabelName.empty()) {
|
||||
|
||||
out << "{";
|
||||
const char* prefix = "";
|
||||
|
||||
for (auto& lp : metric.label) {
|
||||
out << prefix << lp.name << "=\"";
|
||||
WriteValue(out, lp.value);
|
||||
out << "\"";
|
||||
prefix = ",";
|
||||
}
|
||||
if (!extraLabelName.empty()) {
|
||||
out << prefix << extraLabelName << "=\"";
|
||||
WriteValue(out, extraLabelValue);
|
||||
out << "\"";
|
||||
}
|
||||
out << "}";
|
||||
}
|
||||
out << " ";
|
||||
}
|
||||
|
||||
// Write a line trailer: timestamp
|
||||
static void WriteTail(std::ostream& out, const ClientMetric& metric) {
|
||||
if (metric.timestamp_ms != 0) {
|
||||
out << " " << metric.timestamp_ms;
|
||||
}
|
||||
out << "\n";
|
||||
}
|
||||
|
||||
static void SerializeCounter(std::ostream& out, const MetricFamily& family, const ClientMetric& metric) {
|
||||
WriteHead(out, family, metric);
|
||||
WriteValue(out, metric.counter.value);
|
||||
WriteTail(out, metric);
|
||||
}
|
||||
|
||||
static void SerializeGauge(std::ostream& out, const MetricFamily& family, const ClientMetric& metric) {
|
||||
WriteHead(out, family, metric);
|
||||
WriteValue(out, metric.gauge.value);
|
||||
WriteTail(out, metric);
|
||||
}
|
||||
|
||||
static void SerializeSummary(std::ostream& out, const MetricFamily& family, const ClientMetric& metric) {
|
||||
auto& sum = metric.summary;
|
||||
WriteHead(out, family, metric, "_count");
|
||||
out << sum.sample_count;
|
||||
WriteTail(out, metric);
|
||||
|
||||
WriteHead(out, family, metric, "_sum");
|
||||
WriteValue(out, sum.sample_sum);
|
||||
WriteTail(out, metric);
|
||||
|
||||
for (auto& q : sum.quantile) {
|
||||
WriteHead(out, family, metric, "", "quantile", q.quantile);
|
||||
WriteValue(out, q.value);
|
||||
WriteTail(out, metric);
|
||||
}
|
||||
}
|
||||
|
||||
static void SerializeUntyped(std::ostream& out, const MetricFamily& family, const ClientMetric& metric) {
|
||||
WriteHead(out, family, metric);
|
||||
WriteValue(out, metric.untyped.value);
|
||||
WriteTail(out, metric);
|
||||
}
|
||||
|
||||
static void SerializeHistogram(std::ostream& out, const MetricFamily& family, const ClientMetric& metric) {
|
||||
auto& hist = metric.histogram;
|
||||
WriteHead(out, family, metric, "_count");
|
||||
out << hist.sample_count;
|
||||
WriteTail(out, metric);
|
||||
|
||||
WriteHead(out, family, metric, "_sum");
|
||||
WriteValue(out, hist.sample_sum);
|
||||
WriteTail(out, metric);
|
||||
|
||||
double last = -std::numeric_limits<double>::infinity();
|
||||
for (auto& b : hist.bucket) {
|
||||
WriteHead(out, family, metric, "_bucket", "le", b.upper_bound);
|
||||
last = b.upper_bound;
|
||||
out << b.cumulative_count;
|
||||
WriteTail(out, metric);
|
||||
}
|
||||
|
||||
if (last != std::numeric_limits<double>::infinity()) {
|
||||
WriteHead(out, family, metric, "_bucket", "le", "+Inf");
|
||||
out << hist.sample_count;
|
||||
WriteTail(out, metric);
|
||||
}
|
||||
}
|
||||
|
||||
static void SerializeFamily(std::ostream& out, const MetricFamily& family) {
|
||||
if (!family.help.empty()) {
|
||||
out << "# HELP " << family.name << " " << family.help << "\n";
|
||||
}
|
||||
switch (family.type) {
|
||||
case Metric::Type::Counter:
|
||||
out << "# TYPE " << family.name << " counter\n";
|
||||
for (auto& metric : family.metric) {
|
||||
SerializeCounter(out, family, metric);
|
||||
}
|
||||
break;
|
||||
case Metric::Type::Gauge:
|
||||
out << "# TYPE " << family.name << " gauge\n";
|
||||
for (auto& metric : family.metric) {
|
||||
SerializeGauge(out, family, metric);
|
||||
}
|
||||
break;
|
||||
case Metric::Type::Summary:
|
||||
out << "# TYPE " << family.name << " summary\n";
|
||||
for (auto& metric : family.metric) {
|
||||
SerializeSummary(out, family, metric);
|
||||
}
|
||||
break;
|
||||
case Metric::Type::Untyped:
|
||||
out << "# TYPE " << family.name << " untyped\n";
|
||||
for (auto& metric : family.metric) {
|
||||
SerializeUntyped(out, family, metric);
|
||||
}
|
||||
break;
|
||||
case Metric::Type::Histogram:
|
||||
out << "# TYPE " << family.name << " histogram\n";
|
||||
for (auto& metric : family.metric) {
|
||||
SerializeHistogram(out, family, metric);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
static void Serialize (std::ostream& out, const std::vector<MetricFamily>& metrics) {
|
||||
std::locale saved_locale = out.getloc();
|
||||
out.imbue(std::locale::classic());
|
||||
for (auto& family : metrics) {
|
||||
SerializeFamily(out, family);
|
||||
}
|
||||
out.imbue(saved_locale);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
} // namespace prometheus
|
@ -0,0 +1,62 @@
|
||||
#pragma once
|
||||
|
||||
#include <chrono>
|
||||
#include <cstddef>
|
||||
#include <vector>
|
||||
|
||||
#include "prometheus/detail/ckms_quantiles.h"
|
||||
|
||||
namespace prometheus {
|
||||
namespace detail {
|
||||
|
||||
class TimeWindowQuantiles {
|
||||
|
||||
using Clock = std::chrono::steady_clock;
|
||||
|
||||
public:
|
||||
TimeWindowQuantiles(const std::vector<CKMSQuantiles::Quantile>& quantiles,
|
||||
const Clock::duration max_age, const int age_buckets)
|
||||
: quantiles_(quantiles),
|
||||
ckms_quantiles_(age_buckets, CKMSQuantiles(quantiles_)),
|
||||
current_bucket_(0),
|
||||
last_rotation_(Clock::now()),
|
||||
rotation_interval_(max_age / age_buckets) {}
|
||||
|
||||
double get(double q) const {
|
||||
CKMSQuantiles& current_bucket = rotate();
|
||||
return current_bucket.get(q);
|
||||
}
|
||||
|
||||
void insert(double value) {
|
||||
rotate();
|
||||
for (auto& bucket : ckms_quantiles_) {
|
||||
bucket.insert(value);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
CKMSQuantiles& rotate() const {
|
||||
auto delta = Clock::now() - last_rotation_;
|
||||
while (delta > rotation_interval_) {
|
||||
ckms_quantiles_[current_bucket_].reset();
|
||||
|
||||
if (++current_bucket_ >= ckms_quantiles_.size()) {
|
||||
current_bucket_ = 0;
|
||||
}
|
||||
|
||||
delta -= rotation_interval_;
|
||||
last_rotation_ += rotation_interval_;
|
||||
}
|
||||
return ckms_quantiles_[current_bucket_];
|
||||
}
|
||||
|
||||
const std::vector<CKMSQuantiles::Quantile>& quantiles_;
|
||||
mutable std::vector<CKMSQuantiles> ckms_quantiles_;
|
||||
mutable std::size_t current_bucket_;
|
||||
|
||||
mutable Clock::time_point last_rotation_;
|
||||
const Clock::duration rotation_interval_;
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
} // namespace prometheus
|
31
ext/prometheus-cpp-lite-1.0/examples/CMakeLists.txt
Normal file
31
ext/prometheus-cpp-lite-1.0/examples/CMakeLists.txt
Normal file
@ -0,0 +1,31 @@
|
||||
|
||||
add_executable (original_example "original_example.cpp" )
|
||||
target_link_libraries(original_example prometheus-cpp-lite-core)
|
||||
|
||||
add_executable (modern_example "modern_example.cpp" )
|
||||
target_link_libraries(modern_example prometheus-cpp-lite-core)
|
||||
|
||||
add_executable (use_counters_in_class_example "use_counters_in_class_example.cpp" )
|
||||
target_link_libraries(use_counters_in_class_example prometheus-cpp-lite-core)
|
||||
|
||||
add_executable (use_gauge_in_class_example "use_gauge_in_class_example.cpp" )
|
||||
target_link_libraries(use_gauge_in_class_example prometheus-cpp-lite-core)
|
||||
|
||||
add_executable (use_benchmark_in_class_example "use_benchmark_in_class_example.cpp" )
|
||||
target_link_libraries(use_benchmark_in_class_example prometheus-cpp-lite-core)
|
||||
|
||||
add_executable (save_to_file_example "save_to_file_example.cpp" )
|
||||
target_link_libraries(save_to_file_example prometheus-cpp-lite-core)
|
||||
|
||||
add_executable (push_to_server_example "push_to_server_example.cpp" )
|
||||
target_link_libraries(push_to_server_example prometheus-cpp-lite-core)
|
||||
|
||||
add_executable (gateway_example "gateway_example.cpp" )
|
||||
target_link_libraries(gateway_example prometheus-cpp-lite-core)
|
||||
|
||||
|
||||
add_executable (simpleapi_example "simpleapi_example.cpp")
|
||||
target_link_libraries(simpleapi_example prometheus-cpp-simpleapi)
|
||||
|
||||
add_executable (simpleapi_use_in_class_example "simpleapi_use_in_class_example.cpp")
|
||||
target_link_libraries(simpleapi_use_in_class_example prometheus-cpp-simpleapi)
|
64
ext/prometheus-cpp-lite-1.0/examples/gateway_example.cpp
Normal file
64
ext/prometheus-cpp-lite-1.0/examples/gateway_example.cpp
Normal file
@ -0,0 +1,64 @@
|
||||
#include <chrono>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
|
||||
#include "prometheus/client_metric.h"
|
||||
#include "prometheus/counter.h"
|
||||
#include "prometheus/family.h"
|
||||
#include "prometheus/gateway.h"
|
||||
#include "prometheus/registry.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <Winsock2.h>
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
static std::string GetHostName() {
|
||||
char hostname[1024];
|
||||
|
||||
if (::gethostname(hostname, sizeof(hostname))) {
|
||||
return {};
|
||||
}
|
||||
return hostname;
|
||||
}
|
||||
|
||||
int main() {
|
||||
using namespace prometheus;
|
||||
|
||||
// create a push gateway
|
||||
const auto labels = Gateway::GetInstanceLabel(GetHostName());
|
||||
|
||||
Gateway gateway{"127.0.0.1", "9091", "sample_client", labels};
|
||||
|
||||
// create a metrics registry with component=main labels applied to all its
|
||||
// metrics
|
||||
auto registry = std::make_shared<Registry>();
|
||||
|
||||
// add a new counter family to the registry (families combine values with the
|
||||
// same name, but distinct label dimensions)
|
||||
auto& counter_family = BuildCounter()
|
||||
.Name("time_running_seconds_total")
|
||||
.Help("How many seconds is this server running?")
|
||||
.Labels({{"label", "value"}})
|
||||
.Register(*registry);
|
||||
|
||||
// add a counter to the metric family
|
||||
auto& second_counter = counter_family.Add(
|
||||
{{"another_label", "value"}, {"yet_another_label", "value"}});
|
||||
|
||||
// ask the pusher to push the metrics to the pushgateway
|
||||
gateway.RegisterCollectable(registry);
|
||||
|
||||
for (;;) {
|
||||
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||
// increment the counter by one (second)
|
||||
second_counter.Increment();
|
||||
|
||||
// push metrics
|
||||
auto returnCode = gateway.Push();
|
||||
std::cout << "returnCode is " << returnCode << std::endl;
|
||||
}
|
||||
}
|
65
ext/prometheus-cpp-lite-1.0/examples/modern_example.cpp
Normal file
65
ext/prometheus-cpp-lite-1.0/examples/modern_example.cpp
Normal file
@ -0,0 +1,65 @@
|
||||
#include <prometheus/registry.h>
|
||||
#include <prometheus/counter.h>
|
||||
#include <prometheus/text_serializer.h>
|
||||
|
||||
#include <array>
|
||||
#include <chrono>
|
||||
#include <cstdlib>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <iostream>
|
||||
|
||||
int main() {
|
||||
|
||||
using namespace prometheus;
|
||||
|
||||
// for clarity, we deduce the required types
|
||||
using IntegerCounter = Counter<uint64_t>;
|
||||
using FloatingCounter = Counter<double>;
|
||||
|
||||
using IntegerCounterFamily = CustomFamily<IntegerCounter>;
|
||||
using FloatingCounterFamily = CustomFamily<FloatingCounter>;
|
||||
|
||||
// create a metrics registry
|
||||
// @note it's the users responsibility to keep the object alive
|
||||
Registry registry;
|
||||
|
||||
// add a new counter family to the registry (families combine values with the
|
||||
// same name, but distinct label dimensions)
|
||||
//
|
||||
// @note please follow the metric-naming best-practices:
|
||||
// https://prometheus.io/docs/practices/naming/
|
||||
FloatingCounterFamily& packet_counter{ FloatingCounter::Family::Build(registry, "observed_packets_total", "Number of observed packets") };
|
||||
|
||||
// add and remember dimensional data, incrementing those is very cheap
|
||||
FloatingCounter& tcp_rx_counter{ packet_counter.Add({ {"protocol", "tcp"}, {"direction", "rx"} }) };
|
||||
FloatingCounter& tcp_tx_counter{ packet_counter.Add({ {"protocol", "tcp"}, {"direction", "tx"} }) };
|
||||
FloatingCounter& udp_rx_counter{ packet_counter.Add({ {"protocol", "udp"}, {"direction", "rx"} }) };
|
||||
FloatingCounter& udp_tx_counter{ packet_counter.Add({ {"protocol", "udp"}, {"direction", "tx"} }) };
|
||||
|
||||
// add a counter whose dimensional data is not known at compile time
|
||||
// nevertheless dimensional values should only occur in low cardinality:
|
||||
// https://prometheus.io/docs/practices/naming/#labels
|
||||
IntegerCounterFamily& http_requests_counter = IntegerCounter::Family::Build(registry, "http_requests_total", "Number of HTTP requests");
|
||||
|
||||
for (;; ) {
|
||||
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||
const int random_value = std::rand();
|
||||
|
||||
if (random_value & 1) tcp_rx_counter++;
|
||||
if (random_value & 2) ++tcp_tx_counter;
|
||||
if (random_value & 4) udp_rx_counter += 0.5;
|
||||
if (random_value & 8) udp_tx_counter += 0.7;
|
||||
|
||||
const std::array<std::string, 4> methods = { "GET", "PUT", "POST", "HEAD" };
|
||||
const std::string& method = methods.at(static_cast<std::size_t>(random_value) % methods.size());
|
||||
|
||||
// dynamically calling Family<T>.Add() works but is slow and should be avoided
|
||||
http_requests_counter.Add({ {"method", method} }) += 10;
|
||||
|
||||
TextSerializer text_serializer;
|
||||
text_serializer.Serialize(std::cout, registry.Collect());
|
||||
|
||||
}
|
||||
}
|
67
ext/prometheus-cpp-lite-1.0/examples/original_example.cpp
Normal file
67
ext/prometheus-cpp-lite-1.0/examples/original_example.cpp
Normal file
@ -0,0 +1,67 @@
|
||||
|
||||
#include <prometheus/registry.h>
|
||||
#include <prometheus/counter.h>
|
||||
#include <prometheus/text_serializer.h>
|
||||
|
||||
#include <array>
|
||||
#include <chrono>
|
||||
#include <cstdlib>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <iostream>
|
||||
|
||||
int main() {
|
||||
|
||||
using namespace prometheus;
|
||||
|
||||
// create a metrics registry
|
||||
// @note it's the users responsibility to keep the object alive
|
||||
auto registry = std::make_shared<Registry>();
|
||||
|
||||
// add a new counter family to the registry (families combine values with the
|
||||
// same name, but distinct label dimensions)
|
||||
//
|
||||
// @note please follow the metric-naming best-practices:
|
||||
// https://prometheus.io/docs/practices/naming/
|
||||
auto& packet_counter = BuildCounter()
|
||||
.Name("observed_packets_total")
|
||||
.Help("Number of observed packets")
|
||||
.Register(*registry);
|
||||
|
||||
// add and remember dimensional data, incrementing those is very cheap
|
||||
auto& tcp_rx_counter = packet_counter.Add({ {"protocol", "tcp"}, {"direction", "rx"} });
|
||||
auto& tcp_tx_counter = packet_counter.Add({ {"protocol", "tcp"}, {"direction", "tx"} });
|
||||
auto& udp_rx_counter = packet_counter.Add({ {"protocol", "udp"}, {"direction", "rx"} });
|
||||
auto& udp_tx_counter = packet_counter.Add({ {"protocol", "udp"}, {"direction", "tx"} });
|
||||
|
||||
// add a counter whose dimensional data is not known at compile time
|
||||
// nevertheless dimensional values should only occur in low cardinality:
|
||||
// https://prometheus.io/docs/practices/naming/#labels
|
||||
auto& http_requests_counter = BuildCounter()
|
||||
.Name("http_requests_total")
|
||||
.Help("Number of HTTP requests")
|
||||
.Register(*registry);
|
||||
|
||||
// ask the exposer to scrape the registry on incoming HTTP requests
|
||||
//exposer.RegisterCollectable(registry);
|
||||
|
||||
for ( ;; ) {
|
||||
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||
const auto random_value = std::rand();
|
||||
|
||||
if (random_value & 1) tcp_rx_counter.Increment();
|
||||
if (random_value & 2) tcp_tx_counter.Increment();
|
||||
if (random_value & 4) udp_rx_counter.Increment(10);
|
||||
if (random_value & 8) udp_tx_counter.Increment(10);
|
||||
|
||||
const std::array<std::string, 4> methods = { "GET", "PUT", "POST", "HEAD" };
|
||||
auto method = methods.at(static_cast<std::size_t>(random_value) % methods.size());
|
||||
// dynamically calling Family<T>.Add() works but is slow and should be avoided
|
||||
http_requests_counter.Add({ {"method", method} }).Increment();
|
||||
|
||||
TextSerializer text_serializer;
|
||||
text_serializer.Serialize(std::cout, registry->Collect());
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,44 @@
|
||||
#include <prometheus/registry.h>
|
||||
#include <prometheus/counter.h>
|
||||
#include <prometheus/push_to_server.h>
|
||||
|
||||
#include <array>
|
||||
#include <chrono>
|
||||
#include <cstdlib>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <iostream>
|
||||
|
||||
int main() {
|
||||
|
||||
using namespace prometheus;
|
||||
|
||||
// for clarity, we deduce the required types
|
||||
using Metric = Counter<uint64_t>;
|
||||
|
||||
using Family = Metric::Family;
|
||||
|
||||
// create a metrics registry
|
||||
// @note it's the users responsibility to keep the object alive
|
||||
std::shared_ptr<Registry> registry_ptr = std::make_shared<Registry>();
|
||||
|
||||
PushToServer pusher(registry_ptr, std::chrono::seconds(5),
|
||||
std::string("http://127.0.0.1:9091/metrics/job/samples/instance/test") );
|
||||
|
||||
// add a new counter family to the registry (families combine values with the
|
||||
// same name, but distinct label dimensions)
|
||||
//
|
||||
// @note please follow the metric-naming best-practices:
|
||||
// https://prometheus.io/docs/practices/naming/
|
||||
Family& family { Family::Build(*registry_ptr, "our_metric", "some metric") };
|
||||
|
||||
// add and remember dimensional data, incrementing those is very cheap
|
||||
Metric& metric { family.Add({}) };
|
||||
|
||||
for (;; ) {
|
||||
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||
const int random_value = std::rand();
|
||||
metric += random_value % 10;
|
||||
}
|
||||
}
|
@ -0,0 +1,43 @@
|
||||
#include <prometheus/registry.h>
|
||||
#include <prometheus/counter.h>
|
||||
#include <prometheus/save_to_file.h>
|
||||
|
||||
#include <array>
|
||||
#include <chrono>
|
||||
#include <cstdlib>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <iostream>
|
||||
|
||||
int main() {
|
||||
|
||||
using namespace prometheus;
|
||||
|
||||
// for clarity, we deduce the required types
|
||||
using Metric = Counter<uint64_t>;
|
||||
|
||||
using Family = Metric::Family;
|
||||
|
||||
// create a metrics registry
|
||||
// @note it's the users responsibility to keep the object alive
|
||||
std::shared_ptr<Registry> registry_ptr = std::make_shared<Registry>();
|
||||
|
||||
SaveToFile saver( registry_ptr, std::chrono::seconds(5), "./metrics.prom" );
|
||||
|
||||
// add a new counter family to the registry (families combine values with the
|
||||
// same name, but distinct label dimensions)
|
||||
//
|
||||
// @note please follow the metric-naming best-practices:
|
||||
// https://prometheus.io/docs/practices/naming/
|
||||
Family& family { Family::Build(*registry_ptr, "our_metric", "some metric") };
|
||||
|
||||
// add and remember dimensional data, incrementing those is very cheap
|
||||
Metric& metric { family.Add({}) };
|
||||
|
||||
for (;; ) {
|
||||
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||
const int random_value = std::rand();
|
||||
metric += random_value % 10;
|
||||
}
|
||||
}
|
26
ext/prometheus-cpp-lite-1.0/examples/simpleapi_example.cpp
Normal file
26
ext/prometheus-cpp-lite-1.0/examples/simpleapi_example.cpp
Normal file
@ -0,0 +1,26 @@
|
||||
|
||||
#include <prometheus/simpleapi.h>
|
||||
|
||||
int main() {
|
||||
|
||||
using namespace prometheus::simpleapi;
|
||||
|
||||
counter_family_t family { "simple_family", "simple family example" };
|
||||
counter_metric_t metric1 { family.Add({{"name", "counter1"}}) };
|
||||
counter_metric_t metric2 { family.Add({{"name", "counter2"}}) };
|
||||
|
||||
counter_metric_t metric3 { "simple_counter_1", "simple counter 1 without labels example" };
|
||||
counter_metric_t metric4 { "simple_counter_2", "simple counter 2 without labels example" };
|
||||
|
||||
for (;; ) {
|
||||
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||
const int random_value = std::rand();
|
||||
if (random_value & 1) metric1++;
|
||||
if (random_value & 2) metric2++;
|
||||
if (random_value & 4) metric3++;
|
||||
if (random_value & 8) metric4++;
|
||||
}
|
||||
|
||||
//return 0;
|
||||
|
||||
}
|
@ -0,0 +1,57 @@
|
||||
|
||||
#include <prometheus/simpleapi.h>
|
||||
|
||||
// use prometheus namespace
|
||||
using namespace prometheus::simpleapi;
|
||||
|
||||
class MyClass {
|
||||
|
||||
counter_family_t metric_family { "simple_family", "simple family example" };
|
||||
counter_metric_t metric1 { metric_family.Add({{"name", "counter1"}}) };
|
||||
counter_metric_t metric2 { metric_family.Add({{"name", "counter2"}}) };
|
||||
|
||||
counter_metric_t metric3 { "simple_counter_1", "simple counter 1 without labels example" };
|
||||
counter_metric_t metric4 { "simple_counter_2", "simple counter 2 without labels example" };
|
||||
|
||||
benchmark_family_t benchmark_family { "simple_benchmark_family", "simple benchmark family example" };
|
||||
benchmark_metric_t benchmark1 { benchmark_family.Add({{"benchmark", "1"}}) };
|
||||
benchmark_metric_t benchmark2 { benchmark_family.Add({{"benchmark", "2"}}) };
|
||||
|
||||
public:
|
||||
|
||||
MyClass() = default;
|
||||
|
||||
void member_to_do_something() {
|
||||
|
||||
benchmark1.start();
|
||||
const int random_value = std::rand();
|
||||
benchmark1.stop();
|
||||
|
||||
benchmark2.start();
|
||||
if (random_value & 1) metric1++;
|
||||
if (random_value & 2) metric2++;
|
||||
if (random_value & 4) metric3++;
|
||||
if (random_value & 8) metric4++;
|
||||
benchmark2.stop();
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
int main() {
|
||||
|
||||
MyClass myClass;
|
||||
benchmark_metric_t benchmark { "simple_benchmark", "simple benchmark example" };
|
||||
|
||||
for (;; ) {
|
||||
|
||||
benchmark.start();
|
||||
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||
benchmark.stop();
|
||||
|
||||
myClass.member_to_do_something();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,59 @@
|
||||
#include <prometheus/registry.h>
|
||||
#include <prometheus/benchmark.h>
|
||||
#include <prometheus/text_serializer.h>
|
||||
|
||||
#include <array>
|
||||
#include <chrono>
|
||||
#include <cstdlib>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <iostream>
|
||||
|
||||
// use prometheus namespace
|
||||
using namespace prometheus;
|
||||
|
||||
// create global registry for use it from our classes
|
||||
static Registry globalRegistry;
|
||||
|
||||
class MyClass {
|
||||
|
||||
Benchmark::Family& benchmarkFamily { Benchmark::Family::Build(globalRegistry,
|
||||
"benchmark_family", "family for check benchmark functionality") };
|
||||
|
||||
Benchmark& benchmark1 { benchmarkFamily.Add({{"number", "1"}}) };
|
||||
Benchmark& benchmark2 { benchmarkFamily.Add({{"number", "2"}}) };
|
||||
|
||||
public:
|
||||
|
||||
MyClass() = default;
|
||||
|
||||
void member_to_do_something() {
|
||||
|
||||
benchmark1.start();
|
||||
benchmark2.start();
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(500));
|
||||
benchmark1.stop();
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(500));
|
||||
benchmark2.stop();
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
int main() {
|
||||
|
||||
MyClass myClass;
|
||||
|
||||
for (;; ) {
|
||||
|
||||
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||
|
||||
myClass.member_to_do_something();
|
||||
|
||||
TextSerializer text_serializer;
|
||||
text_serializer.Serialize(std::cout, globalRegistry.Collect());
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,79 @@
|
||||
#include <prometheus/registry.h>
|
||||
#include <prometheus/counter.h>
|
||||
#include <prometheus/text_serializer.h>
|
||||
|
||||
#include <array>
|
||||
#include <chrono>
|
||||
#include <cstdlib>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <iostream>
|
||||
|
||||
// use prometheus namespace
|
||||
using namespace prometheus;
|
||||
|
||||
// for clarity, we deduce the required types
|
||||
using IntegerCounter = Counter<uint64_t>;
|
||||
using FloatingCounter = Counter<double>;
|
||||
|
||||
using IntegerCounterFamily = CustomFamily<IntegerCounter>;
|
||||
using FloatingCounterFamily = CustomFamily<FloatingCounter>;
|
||||
|
||||
// create global registry for use it from our classes
|
||||
static Registry globalRegistry;
|
||||
|
||||
class MyClass {
|
||||
|
||||
IntegerCounterFamily& counterFamily1 { IntegerCounter::Family::Build(globalRegistry,
|
||||
"counter_family_1", "counter for check integer functionality",
|
||||
{{"type","integer"}} ) };
|
||||
|
||||
IntegerCounter& counter11{ counterFamily1.Add({{"number", "1"}}) };
|
||||
IntegerCounter& counter12{ counterFamily1.Add({{"number", "2"}}) };
|
||||
IntegerCounter& counter13{ counterFamily1.Add({{"number", "3"}}) };
|
||||
|
||||
|
||||
FloatingCounterFamily& counterFamily2 { FloatingCounter::Family::Build(globalRegistry,
|
||||
"counter_family_2", "counter for check floating functionality",
|
||||
{{"type","float"}} ) };
|
||||
|
||||
FloatingCounter& counter21{ counterFamily2.Add({{"number", "1"}}) };
|
||||
FloatingCounter& counter22{ counterFamily2.Add({{"number", "2"}}) };
|
||||
FloatingCounter& counter23{ counterFamily2.Add({{"number", "3"}}) };
|
||||
|
||||
public:
|
||||
|
||||
MyClass() = default;
|
||||
|
||||
void member_to_do_something() {
|
||||
|
||||
const int random_value = std::rand();
|
||||
|
||||
if (random_value & 1) counter11++;
|
||||
if (random_value & 2) counter12++;
|
||||
if (random_value & 4) counter13++;
|
||||
if (random_value & 8) counter21++;
|
||||
if (random_value & 16) counter22++;
|
||||
if (random_value & 32) counter23++;
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
int main() {
|
||||
|
||||
MyClass myClass;
|
||||
|
||||
for (;; ) {
|
||||
|
||||
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||
|
||||
myClass.member_to_do_something();
|
||||
|
||||
TextSerializer text_serializer;
|
||||
text_serializer.Serialize(std::cout, globalRegistry.Collect());
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,79 @@
|
||||
#include <prometheus/registry.h>
|
||||
#include <prometheus/gauge.h>
|
||||
#include <prometheus/text_serializer.h>
|
||||
|
||||
#include <array>
|
||||
#include <chrono>
|
||||
#include <cstdlib>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <iostream>
|
||||
|
||||
// use prometheus namespace
|
||||
using namespace prometheus;
|
||||
|
||||
// for clarity, we deduce the required types
|
||||
using IntegerGauge = Gauge<int64_t>;
|
||||
using FloatingGauge = Gauge<double>;
|
||||
|
||||
using IntegerGaugeFamily = CustomFamily<IntegerGauge>;
|
||||
using FloatingGaugeFamily = CustomFamily<FloatingGauge>;
|
||||
|
||||
// create global registry for use it from our classes
|
||||
static Registry globalRegistry;
|
||||
|
||||
class MyClass {
|
||||
|
||||
IntegerGaugeFamily& gaugeFamily1 { IntegerGauge::Family::Build(globalRegistry,
|
||||
"gauge_family_1", "gauge for check integer functionality",
|
||||
{{"type","integer"}} ) };
|
||||
|
||||
IntegerGauge& gauge11{ gaugeFamily1.Add({{"number", "1"}}) };
|
||||
IntegerGauge& gauge12{ gaugeFamily1.Add({{"number", "2"}}) };
|
||||
IntegerGauge& gauge13{ gaugeFamily1.Add({{"number", "3"}}) };
|
||||
|
||||
|
||||
FloatingGaugeFamily& gaugeFamily2 { FloatingGauge::Family::Build(globalRegistry,
|
||||
"gauge_family_2", "gauge for check floating functionality",
|
||||
{{"type","float"}} ) };
|
||||
|
||||
FloatingGauge& gauge21{ gaugeFamily2.Add({{"number", "1"}}) };
|
||||
FloatingGauge& gauge22{ gaugeFamily2.Add({{"number", "2"}}) };
|
||||
FloatingGauge& gauge23{ gaugeFamily2.Add({{"number", "3"}}) };
|
||||
|
||||
public:
|
||||
|
||||
MyClass() = default;
|
||||
|
||||
void member_to_do_something() {
|
||||
|
||||
const int random_value = std::rand();
|
||||
|
||||
if (random_value & 1 ) gauge11++; else gauge11--;
|
||||
if (random_value & (1 << 1)) gauge12++; else gauge12--;
|
||||
if (random_value & (1 << 2)) gauge13++; else gauge13--;
|
||||
if (random_value & (1 << 3)) gauge21++; else gauge21--;
|
||||
if (random_value & (1 << 4)) gauge22++; else gauge22--;
|
||||
if (random_value & (1 << 5)) gauge23++; else gauge23--;
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
int main() {
|
||||
|
||||
MyClass myClass;
|
||||
|
||||
for (;; ) {
|
||||
|
||||
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||
|
||||
myClass.member_to_do_something();
|
||||
|
||||
TextSerializer text_serializer;
|
||||
text_serializer.Serialize(std::cout, globalRegistry.Collect());
|
||||
|
||||
}
|
||||
}
|
||||
|
7
ext/prometheus-cpp-lite-1.0/simpleapi/CMakeLists.txt
Normal file
7
ext/prometheus-cpp-lite-1.0/simpleapi/CMakeLists.txt
Normal file
@ -0,0 +1,7 @@
|
||||
project(prometheus-cpp-simpleapi)
|
||||
cmake_minimum_required(VERSION 3.2)
|
||||
|
||||
add_library (${PROJECT_NAME} STATIC "./src/simpleapi.cpp" )
|
||||
target_sources (${PROJECT_NAME} PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include/prometheus/simpleapi.h")
|
||||
target_include_directories(${PROJECT_NAME} PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include")
|
||||
target_link_libraries (${PROJECT_NAME} PUBLIC prometheus-cpp-lite-core)
|
@ -0,0 +1,156 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <prometheus/family.h>
|
||||
#include <prometheus/registry.h>
|
||||
#include <prometheus/counter.h>
|
||||
#include <prometheus/gauge.h>
|
||||
#include <prometheus/benchmark.h>
|
||||
|
||||
#include <prometheus/registry.h>
|
||||
#include <prometheus/save_to_file.h>
|
||||
|
||||
#include <thread>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <functional>
|
||||
|
||||
namespace prometheus {
|
||||
namespace simpleapi {
|
||||
|
||||
extern Registry& registry;
|
||||
extern SaveToFile saver;
|
||||
|
||||
|
||||
template <typename CustomWrapper>
|
||||
class family_wrapper_t {
|
||||
|
||||
typename CustomWrapper::Family* family_{ nullptr };
|
||||
|
||||
public:
|
||||
|
||||
// make new family: family_t family {"family_name", "Family description"}
|
||||
family_wrapper_t(const std::string& name, const std::string& description)
|
||||
: family_(&CustomWrapper::Family::Build(registry, name, description)) {}
|
||||
|
||||
// make new metric into existing family: metric_t metric {family.Add({{"tag_name", "tag_value"}})}
|
||||
CustomWrapper Add(const typename CustomWrapper::Family::Labels& labels) {
|
||||
return CustomWrapper(family_, family_->Add(labels));
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
class counter_metric_t {
|
||||
|
||||
public:
|
||||
|
||||
using Metric = Counter<uint64_t>;
|
||||
using Family = Metric::Family;
|
||||
|
||||
private:
|
||||
|
||||
Family* family_ { nullptr };
|
||||
Metric* metric_ { nullptr };
|
||||
|
||||
friend family_wrapper_t<counter_metric_t>;
|
||||
counter_metric_t(typename Metric::Family* family, Metric& metric)
|
||||
: family_(family), metric_(&metric) {}
|
||||
|
||||
public:
|
||||
|
||||
// fake empty metric
|
||||
counter_metric_t() = default;
|
||||
|
||||
// make new counter as simple metric without tags and with hidden family included: metric_t metric {"counter_name", "Counter description"}
|
||||
counter_metric_t(const std::string& name, const std::string& description)
|
||||
: family_(&Metric::Family::Build(registry, name, description)), metric_(&family_->Add({})) {}
|
||||
|
||||
void operator++ () { metric_->Increment(); }
|
||||
void operator++ (int) { metric_->Increment(); }
|
||||
void operator+= (typename Metric::Value val) { metric_->Increment(val); }
|
||||
|
||||
uint64_t value() const { return metric_->Get(); }
|
||||
|
||||
};
|
||||
|
||||
using counter_family_t = family_wrapper_t<counter_metric_t>;
|
||||
|
||||
|
||||
class gauge_metric_t {
|
||||
|
||||
public:
|
||||
|
||||
using Metric = Gauge<int64_t>;
|
||||
using Family = Metric::Family;
|
||||
|
||||
private:
|
||||
|
||||
Family* family_ { nullptr };
|
||||
Metric* metric_ { nullptr };
|
||||
|
||||
friend family_wrapper_t<gauge_metric_t>;
|
||||
gauge_metric_t(typename Metric::Family* family, Metric& metric)
|
||||
: family_(family), metric_(&metric) {}
|
||||
|
||||
public:
|
||||
|
||||
// fake empty metric
|
||||
gauge_metric_t() = default;
|
||||
|
||||
// make new gauge as simple metric without tags and with hidden family included: metric {"counter_name", "Counter description"}
|
||||
gauge_metric_t(const std::string& name, const std::string& description)
|
||||
: family_(&Metric::Family::Build(registry, name, description)), metric_(&family_->Add({})) {}
|
||||
|
||||
void operator++ () { metric_->Increment(); }
|
||||
void operator++ (int) { metric_->Increment(); }
|
||||
void operator+= (typename Metric::Value val) { metric_->Increment(val); }
|
||||
|
||||
void operator-- () { metric_->Decrement(); }
|
||||
void operator-- (int) { metric_->Decrement(); }
|
||||
void operator-= (typename Metric::Value val) { metric_->Decrement(val); }
|
||||
void operator= (typename Metric::Value val) { metric_->Set(val); }
|
||||
|
||||
int64_t value() const { return metric_->Get(); }
|
||||
|
||||
};
|
||||
|
||||
using gauge_family_t = family_wrapper_t<gauge_metric_t>;
|
||||
|
||||
|
||||
class benchmark_metric_t {
|
||||
|
||||
public:
|
||||
|
||||
using Metric = Benchmark;
|
||||
using Family = Metric::Family;
|
||||
|
||||
private:
|
||||
|
||||
Family* family_ { nullptr };
|
||||
Metric* metric_ { nullptr };
|
||||
|
||||
friend family_wrapper_t<benchmark_metric_t>;
|
||||
benchmark_metric_t(typename Metric::Family* family, Metric& metric)
|
||||
: family_(family), metric_(&metric) {}
|
||||
|
||||
public:
|
||||
|
||||
// fake empty metric
|
||||
benchmark_metric_t() = default;
|
||||
|
||||
// make new benchmark as simple metric without tags and with hidden family included: metric {"counter_name", "Counter description"}
|
||||
benchmark_metric_t(const std::string& name, const std::string& description)
|
||||
: family_(&Metric::Family::Build(registry, name, description)), metric_(&family_->Add({})) {}
|
||||
|
||||
void start() { metric_->start(); }
|
||||
void stop() { metric_->stop(); }
|
||||
|
||||
double value() const { return metric_->Get(); }
|
||||
|
||||
};
|
||||
|
||||
using benchmark_family_t = family_wrapper_t<benchmark_metric_t>;
|
||||
|
||||
}
|
||||
}
|
13
ext/prometheus-cpp-lite-1.0/simpleapi/src/simpleapi.cpp
Normal file
13
ext/prometheus-cpp-lite-1.0/simpleapi/src/simpleapi.cpp
Normal file
@ -0,0 +1,13 @@
|
||||
#include "prometheus/simpleapi.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace prometheus {
|
||||
namespace simpleapi {
|
||||
|
||||
std::shared_ptr<Registry> registry_ptr = std::make_shared<Registry>();
|
||||
Registry& registry = *registry_ptr;
|
||||
SaveToFile saver(registry_ptr, std::chrono::seconds(5), std::string("./metrics.prom"));
|
||||
|
||||
}
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
# This requires GNU make, which is typically "gmake" on BSD systems
|
||||
|
||||
INCLUDES=-isystem ext
|
||||
INCLUDES=-isystem ext -Iext/prometheus-cpp-lite-1.0/core/include -Iext/prometheus-cpp-lite-1.0/simpleapi/include
|
||||
DEFS=
|
||||
LIBS=
|
||||
|
||||
|
@ -9,7 +9,7 @@ ifeq ($(origin CXX),default)
|
||||
CXX:=$(shell if [ -e /opt/rh/devtoolset-8/root/usr/bin/g++ ]; then echo /opt/rh/devtoolset-8/root/usr/bin/g++; else echo $(CXX); fi)
|
||||
endif
|
||||
|
||||
INCLUDES?=-Izeroidc/target -isystem ext
|
||||
INCLUDES?=-Izeroidc/target -isystem ext -Iext/prometheus-cpp-lite-1.0/core/include -Iext-prometheus-cpp-lite-1.0/3rdparty/http-client-lite/include -Iext/prometheus-cpp-lite-1.0/simpleapi/include
|
||||
DEFS?=
|
||||
LDLIBS?=
|
||||
DESTDIR?=
|
||||
|
@ -2,7 +2,7 @@ CC=clang
|
||||
CXX=clang++
|
||||
TOPDIR=$(shell PWD)
|
||||
|
||||
INCLUDES=-I$(shell PWD)/zeroidc/target -isystem $(TOPDIR)/ext
|
||||
INCLUDES=-I$(shell PWD)/zeroidc/target -isystem $(TOPDIR)/ext -I$(TOPDIR)/ext/prometheus-cpp-lite-1.0/core/include -I$(TOPDIR)/ext-prometheus-cpp-lite-1.0/3rdparty/http-client-lite/include -I$(TOPDIR)/ext/prometheus-cpp-lite-1.0/simpleapi/include
|
||||
DEFS=
|
||||
LIBS=
|
||||
ARCH_FLAGS=-arch x86_64 -arch arm64
|
||||
|
75
node/Metrics.cpp
Normal file
75
node/Metrics.cpp
Normal file
@ -0,0 +1,75 @@
|
||||
/*
|
||||
* Copyright (c)2013-2023 ZeroTier, Inc.
|
||||
*
|
||||
* Use of this software is governed by the Business Source License included
|
||||
* in the LICENSE.TXT file in the project's root directory.
|
||||
*
|
||||
* Change Date: 2025-01-01
|
||||
*
|
||||
* On the date above, in accordance with the Business Source License, use
|
||||
* of this software will be governed by version 2.0 of the Apache License.
|
||||
*/
|
||||
|
||||
#include <prometheus/simpleapi.h>
|
||||
|
||||
namespace prometheus {
|
||||
namespace simpleapi {
|
||||
std::shared_ptr<Registry> registry_ptr = std::make_shared<Registry>();
|
||||
Registry& registry = *registry_ptr;
|
||||
SaveToFile saver;
|
||||
}
|
||||
}
|
||||
|
||||
namespace ZeroTier {
|
||||
namespace Metrics {
|
||||
// Data Sent/Received Metrics
|
||||
prometheus::simpleapi::counter_metric_t udp_send
|
||||
{ "zt_udp_data_sent", "number of bytes ZeroTier has sent via UDP" };
|
||||
prometheus::simpleapi::counter_metric_t udp_recv
|
||||
{ "zt_udp_data_recv", "number of bytes ZeroTier has received via UDP" };
|
||||
prometheus::simpleapi::counter_metric_t tcp_send
|
||||
{ "zt_tcp_data_sent", "number of bytes ZeroTier has sent via TCP" };
|
||||
prometheus::simpleapi::counter_metric_t tcp_recv
|
||||
{ "zt_tcp_data_recv", "number of bytes ZeroTier has received via TCP" };
|
||||
|
||||
// General Controller Metrics
|
||||
prometheus::simpleapi::gauge_metric_t network_count
|
||||
{"controller_network_count", "number of networks the controller is serving"};
|
||||
prometheus::simpleapi::gauge_metric_t member_count
|
||||
{"controller_member_count", "number of network members the controller is serving"};
|
||||
prometheus::simpleapi::counter_metric_t network_changes
|
||||
{"controller_network_change_count", "number of times a network configuration is changed"};
|
||||
prometheus::simpleapi::counter_metric_t member_changes
|
||||
{"controller_member_change_count", "number of times a network member configuration is changed"};
|
||||
prometheus::simpleapi::counter_metric_t member_auths
|
||||
{"controller_member_auth_count", "number of network member auths"};
|
||||
prometheus::simpleapi::counter_metric_t member_deauths
|
||||
{"controller_member_deauth_count", "number of network member deauths"};
|
||||
|
||||
#ifdef ZT_CONTROLLER_USE_LIBPQ
|
||||
// Central Controller Metrics
|
||||
prometheus::simpleapi::counter_metric_t pgsql_mem_notification
|
||||
{ "controller_pgsql_member_notifications_received", "number of member change notifications received via pgsql NOTIFY" };
|
||||
prometheus::simpleapi::counter_metric_t pgsql_net_notification
|
||||
{ "controller_pgsql_network_notifications_received", "number of network change notifications received via pgsql NOTIFY" };
|
||||
prometheus::simpleapi::counter_metric_t redis_mem_notification
|
||||
{ "controller_redis_member_notifications_received", "number of member change notifications received via redis" };
|
||||
prometheus::simpleapi::counter_metric_t redis_net_notification
|
||||
{ "controller_redis_network_notifications_received", "number of network change notifications received via redis" };
|
||||
|
||||
// Central DB Pool Metrics
|
||||
prometheus::simpleapi::counter_metric_t conn_counter
|
||||
{ "controller_pgsql_connections_created", "number of pgsql connections created"};
|
||||
prometheus::simpleapi::counter_metric_t max_pool_size
|
||||
{ "controller_pgsql_max_conn_pool_size", "max connection pool size for postgres"};
|
||||
prometheus::simpleapi::counter_metric_t min_pool_size
|
||||
{ "controller_pgsql_min_conn_pool_size", "minimum connection pool size for postgres" };
|
||||
prometheus::simpleapi::gauge_metric_t pool_avail
|
||||
{ "controller_pgsql_available_conns", "number of available postgres connections" };
|
||||
prometheus::simpleapi::gauge_metric_t pool_in_use
|
||||
{ "controller_pgsql_in_use_conns", "number of postgres database connections in use" };
|
||||
prometheus::simpleapi::counter_metric_t pool_errors
|
||||
{ "controller_pgsql_connection_errors", "number of connection errors the connection pool has seen" };
|
||||
#endif
|
||||
}
|
||||
}
|
57
node/Metrics.hpp
Normal file
57
node/Metrics.hpp
Normal file
@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright (c)2013-2023 ZeroTier, Inc.
|
||||
*
|
||||
* Use of this software is governed by the Business Source License included
|
||||
* in the LICENSE.TXT file in the project's root directory.
|
||||
*
|
||||
* Change Date: 2025-01-01
|
||||
*
|
||||
* On the date above, in accordance with the Business Source License, use
|
||||
* of this software will be governed by version 2.0 of the Apache License.
|
||||
*/
|
||||
#ifndef METRICS_H_
|
||||
#define METRICS_H_
|
||||
|
||||
#include <prometheus/simpleapi.h>
|
||||
|
||||
namespace prometheus {
|
||||
namespace simpleapi {
|
||||
extern std::shared_ptr<Registry> registry_ptr;
|
||||
}
|
||||
}
|
||||
|
||||
namespace ZeroTier {
|
||||
namespace Metrics {
|
||||
// Data Sent/Received Metrics
|
||||
extern prometheus::simpleapi::counter_metric_t udp_send;
|
||||
extern prometheus::simpleapi::counter_metric_t udp_recv;
|
||||
extern prometheus::simpleapi::counter_metric_t tcp_send;
|
||||
extern prometheus::simpleapi::counter_metric_t tcp_recv;
|
||||
|
||||
// General Controller Metrics
|
||||
extern prometheus::simpleapi::gauge_metric_t network_count;
|
||||
extern prometheus::simpleapi::gauge_metric_t member_count;
|
||||
extern prometheus::simpleapi::counter_metric_t network_changes;
|
||||
extern prometheus::simpleapi::counter_metric_t member_changes;
|
||||
extern prometheus::simpleapi::counter_metric_t member_auths;
|
||||
extern prometheus::simpleapi::counter_metric_t member_deauths;
|
||||
|
||||
#ifdef ZT_CONTROLLER_USE_LIBPQ
|
||||
// Central Controller Metrics
|
||||
extern prometheus::simpleapi::counter_metric_t pgsql_mem_notification;
|
||||
extern prometheus::simpleapi::counter_metric_t pgsql_net_notification;
|
||||
extern prometheus::simpleapi::counter_metric_t redis_mem_notification;
|
||||
extern prometheus::simpleapi::counter_metric_t redis_net_notification;
|
||||
|
||||
// Central DB Pool Metrics
|
||||
extern prometheus::simpleapi::counter_metric_t conn_counter;
|
||||
extern prometheus::simpleapi::counter_metric_t max_pool_size;
|
||||
extern prometheus::simpleapi::counter_metric_t min_pool_size;
|
||||
extern prometheus::simpleapi::gauge_metric_t pool_avail;
|
||||
extern prometheus::simpleapi::gauge_metric_t pool_in_use;
|
||||
extern prometheus::simpleapi::counter_metric_t pool_errors;
|
||||
#endif
|
||||
} // namespace Metrics
|
||||
}// namespace ZeroTier
|
||||
|
||||
#endif // METRICS_H_
|
@ -10,6 +10,7 @@ CORE_OBJS=\
|
||||
node/IncomingPacket.o \
|
||||
node/InetAddress.o \
|
||||
node/Membership.o \
|
||||
node/Metrics.o \
|
||||
node/Multicaster.o \
|
||||
node/Network.o \
|
||||
node/NetworkConfig.o \
|
||||
|
@ -50,6 +50,8 @@
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/tcp.h>
|
||||
|
||||
#include "../node/Metrics.hpp"
|
||||
|
||||
#if defined(__linux__) || defined(linux) || defined(__LINUX__) || defined(__linux)
|
||||
#ifndef IPV6_DONTFRAG
|
||||
#define IPV6_DONTFRAG 62
|
||||
@ -452,9 +454,13 @@ public:
|
||||
{
|
||||
PhySocketImpl &sws = *(reinterpret_cast<PhySocketImpl *>(sock));
|
||||
#if defined(_WIN32) || defined(_WIN64)
|
||||
return ((long)::sendto(sws.sock,reinterpret_cast<const char *>(data),len,0,remoteAddress,(remoteAddress->sa_family == AF_INET6) ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in)) == (long)len);
|
||||
int sent = ((long)::sendto(sws.sock,reinterpret_cast<const char *>(data),len,0,remoteAddress,(remoteAddress->sa_family == AF_INET6) ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in)) == (long)len);
|
||||
Metrics::udp_send += sent;
|
||||
return sent;
|
||||
#else
|
||||
return ((long)::sendto(sws.sock,data,len,0,remoteAddress,(remoteAddress->sa_family == AF_INET6) ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in)) == (long)len);
|
||||
ssize_t sent = ((long)::sendto(sws.sock,data,len,0,remoteAddress,(remoteAddress->sa_family == AF_INET6) ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in)) == (long)len);
|
||||
Metrics::udp_send += sent;
|
||||
return sent;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -87,6 +87,8 @@
|
||||
#include "../ext/http-parser/http_parser.h"
|
||||
#endif
|
||||
|
||||
#include "../node/Metrics.hpp"
|
||||
|
||||
#if ZT_VAULT_SUPPORT
|
||||
extern "C" {
|
||||
#include <curl/curl.h>
|
||||
@ -846,6 +848,10 @@ public:
|
||||
_ports[1] = 0;
|
||||
_ports[2] = 0;
|
||||
|
||||
prometheus::simpleapi::saver.set_registry(prometheus::simpleapi::registry_ptr);
|
||||
prometheus::simpleapi::saver.set_delay(std::chrono::seconds(5));
|
||||
prometheus::simpleapi::saver.set_out_file(_homePath + ZT_PATH_SEPARATOR + "metrics.prom");
|
||||
|
||||
#if ZT_VAULT_SUPPORT
|
||||
curl_global_init(CURL_GLOBAL_DEFAULT);
|
||||
#endif
|
||||
@ -1667,7 +1673,15 @@ public:
|
||||
|
||||
} else scode = 404;
|
||||
_node->freeQueryResult((void *)pl);
|
||||
} else scode = 500;
|
||||
} else scode = 500;\
|
||||
} else if (ps[0] == "metrics") {
|
||||
std::string statspath = _homePath + ZT_PATH_SEPARATOR + "metrics.prom";
|
||||
if (!OSUtils::readFile(statspath.c_str(), responseBody)) {
|
||||
scode = 500;
|
||||
} else {
|
||||
scode = 200;
|
||||
responseContentType = "text/plain";
|
||||
}
|
||||
} else {
|
||||
if (_controller) {
|
||||
scode = _controller->handleControlPlaneHttpGET(std::vector<std::string>(ps.begin()+1,ps.end()),urlArgs,headers,body,responseBody,responseContentType);
|
||||
@ -2456,9 +2470,11 @@ public:
|
||||
if (_forceTcpRelay) {
|
||||
return;
|
||||
}
|
||||
Metrics::udp_recv += len;
|
||||
const uint64_t now = OSUtils::now();
|
||||
if ((len >= 16)&&(reinterpret_cast<const InetAddress *>(from)->ipScope() == InetAddress::IP_SCOPE_GLOBAL))
|
||||
if ((len >= 16)&&(reinterpret_cast<const InetAddress *>(from)->ipScope() == InetAddress::IP_SCOPE_GLOBAL)) {
|
||||
_lastDirectReceiveFromGlobal = now;
|
||||
}
|
||||
const ZT_ResultCode rc = _node->processWirePacket(nullptr,now,reinterpret_cast<int64_t>(sock),reinterpret_cast<const struct sockaddr_storage *>(from),data,len,&_nextBackgroundTaskDeadline);
|
||||
if (ZT_ResultCode_isFatal(rc)) {
|
||||
char tmp[256];
|
||||
@ -2545,6 +2561,7 @@ public:
|
||||
{
|
||||
try {
|
||||
if (!len) return; // sanity check, should never happen
|
||||
Metrics::tcp_recv += len;
|
||||
TcpConnection *tc = reinterpret_cast<TcpConnection *>(*uptr);
|
||||
tc->lastReceive = OSUtils::now();
|
||||
switch(tc->type) {
|
||||
@ -2683,6 +2700,7 @@ public:
|
||||
Mutex::Lock _l(tc->writeq_m);
|
||||
if (tc->writeq.length() > 0) {
|
||||
long sent = (long)_phy.streamSend(sock,tc->writeq.data(),(unsigned long)tc->writeq.length(),true);
|
||||
Metrics::tcp_send += sent;
|
||||
if (sent > 0) {
|
||||
if ((unsigned long)sent >= (unsigned long)tc->writeq.length()) {
|
||||
tc->writeq.clear();
|
||||
@ -3221,9 +3239,13 @@ public:
|
||||
// working we can instantly "fail forward" to it and stop using TCP
|
||||
// proxy fallback, which is slow.
|
||||
if ((localSocket != -1)&&(localSocket != 0)&&(_binder.isUdpSocketValid((PhySocket *)((uintptr_t)localSocket)))) {
|
||||
if ((ttl)&&(addr->ss_family == AF_INET)) _phy.setIp4UdpTtl((PhySocket *)((uintptr_t)localSocket),ttl);
|
||||
if ((ttl)&&(addr->ss_family == AF_INET)) {
|
||||
_phy.setIp4UdpTtl((PhySocket *)((uintptr_t)localSocket),ttl);
|
||||
}
|
||||
const bool r = _phy.udpSend((PhySocket *)((uintptr_t)localSocket),(const struct sockaddr *)addr,data,len);
|
||||
if ((ttl)&&(addr->ss_family == AF_INET)) _phy.setIp4UdpTtl((PhySocket *)((uintptr_t)localSocket),255);
|
||||
if ((ttl)&&(addr->ss_family == AF_INET)) {
|
||||
_phy.setIp4UdpTtl((PhySocket *)((uintptr_t)localSocket),255);
|
||||
}
|
||||
return ((r) ? 0 : -1);
|
||||
} else {
|
||||
return ((_binder.udpSendAll(_phy,addr,data,len,ttl)) ? 0 : -1);
|
||||
|
@ -81,6 +81,7 @@
|
||||
<ClCompile Include="..\..\node\IncomingPacket.cpp" />
|
||||
<ClCompile Include="..\..\node\InetAddress.cpp" />
|
||||
<ClCompile Include="..\..\node\Membership.cpp" />
|
||||
<ClCompile Include="..\..\node\Metrics.cpp" />
|
||||
<ClCompile Include="..\..\node\Multicaster.cpp" />
|
||||
<ClCompile Include="..\..\node\Network.cpp" />
|
||||
<ClCompile Include="..\..\node\NetworkConfig.cpp" />
|
||||
@ -210,6 +211,7 @@
|
||||
<ClInclude Include="..\..\node\InetAddress.hpp" />
|
||||
<ClInclude Include="..\..\node\MAC.hpp" />
|
||||
<ClInclude Include="..\..\node\Membership.hpp" />
|
||||
<ClInclude Include="..\..\node\Metrics.hpp" />
|
||||
<ClInclude Include="..\..\node\Multicaster.hpp" />
|
||||
<ClInclude Include="..\..\node\MulticastGroup.hpp" />
|
||||
<ClInclude Include="..\..\node\Mutex.hpp" />
|
||||
@ -412,7 +414,7 @@
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)\..\ext;$(SolutionDir)\..\zeroidc\target;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)\..\ext;$(SolutionDir)\..\ext\prometheus-cpp-lite-1.0\core\include;$(SolutionDir)\..\ext\prometheus-cpp-lite-1.0\simpleapi\include;$(SolutionDir)\..\zeroidc\target;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<PreprocessorDefinitions>ZT_SSO_ENABLED=1;ZT_EXPORT;FD_SETSIZE=1024;NOMINMAX;STATICLIB;WIN32;ZT_TRACE;ZT_USE_MINIUPNPC;MINIUPNP_STATICLIB;ZT_SOFTWARE_UPDATE_DEFAULT="disable";%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<DisableSpecificWarnings>4996</DisableSpecificWarnings>
|
||||
<RuntimeTypeInfo>false</RuntimeTypeInfo>
|
||||
@ -434,7 +436,7 @@
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)\..\ext;$(SolutionDir)\..\zeroidc\target;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)\..\ext;$(SolutionDir)\..\ext\prometheus-cpp-lite-1.0\core\include;$(SolutionDir)\..\ext\prometheus-cpp-lite-1.0\simpleapi\include;$(SolutionDir)\..\zeroidc\target;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<PreprocessorDefinitions>ZT_SSO_ENABLED=1;ZT_EXPORT;FD_SETSIZE=1024;NOMINMAX;STATICLIB;WIN32;ZT_TRACE;ZT_USE_MINIUPNPC;MINIUPNP_STATICLIB;ZT_SOFTWARE_UPDATE_DEFAULT="disable";%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<DisableSpecificWarnings>4996</DisableSpecificWarnings>
|
||||
<RuntimeTypeInfo>false</RuntimeTypeInfo>
|
||||
@ -455,7 +457,7 @@
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)\..\ext;$(SolutionDir)\..\zeroidc\target;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)\..\ext;$(SolutionDir)\..\ext\prometheus-cpp-lite-1.0\core\include;$(SolutionDir)\..\ext\prometheus-cpp-lite-1.0\simpleapi\include;$(SolutionDir)\..\zeroidc\target;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<PreprocessorDefinitions>ZT_SSO_ENABLED=1;ZT_EXPORT;FD_SETSIZE=1024;NOMINMAX;STATICLIB;WIN32;ZT_TRACE;ZT_RULES_ENGINE_DEBUGGING;ZT_USE_MINIUPNPC;MINIUPNP_STATICLIB;ZT_SOFTWARE_UPDATE_DEFAULT="disable";%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||
<DisableSpecificWarnings>4996</DisableSpecificWarnings>
|
||||
@ -501,7 +503,7 @@
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)\..\ext;$(SolutionDir)\..\zeroidc\target;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)\..\ext;$(SolutionDir)\..\ext\prometheus-cpp-lite-1.0\core\include;$(SolutionDir)\..\ext\prometheus-cpp-lite-1.0\simpleapi\include;$(SolutionDir)\..\zeroidc\target;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<PreprocessorDefinitions>ZT_SSO_ENABLED=1;ZT_EXPORT;FD_SETSIZE=1024;NOMINMAX;STATICLIB;WIN32;ZT_USE_MINIUPNPC;MINIUPNP_STATICLIB;ZT_SOFTWARE_UPDATE_DEFAULT="disable";%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||
<DisableSpecificWarnings>4996</DisableSpecificWarnings>
|
||||
@ -547,7 +549,7 @@
|
||||
<FunctionLevelLinking>false</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)\..\ext;$(SolutionDir)\..\zeroidc\target;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)\..\ext;$(SolutionDir)\..\ext\prometheus-cpp-lite-1.0\core\include;$(SolutionDir)\..\ext\prometheus-cpp-lite-1.0\simpleapi\include;$(SolutionDir)\..\zeroidc\target;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<PreprocessorDefinitions>ZT_SSO_ENABLED=1;ZT_EXPORT;FD_SETSIZE=1024;STATICLIB;ZT_SALSA20_SSE;ZT_USE_MINIUPNPC;MINIUPNP_STATICLIB;WIN32;NOMINMAX;ZT_SOFTWARE_UPDATE_DEFAULT="apply";ZT_BUILD_PLATFORM=2;ZT_BUILD_ARCHITECTURE=1;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||
<StringPooling>true</StringPooling>
|
||||
@ -583,7 +585,7 @@
|
||||
<FunctionLevelLinking>false</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)\..\ext;$(SolutionDir)\..\zeroidc\target;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)\..\ext;$(SolutionDir)\..\ext\prometheus-cpp-lite-1.0\core\include;$(SolutionDir)\..\ext\prometheus-cpp-lite-1.0\simpleapi\include;$(SolutionDir)\..\zeroidc\target;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<PreprocessorDefinitions>ZT_SSO_ENABLED=1;ZT_EXPORT;FD_SETSIZE=1024;STATICLIB;ZT_SOFTWARE_UPDATE_DEFAULT="apply";ZT_SALSA20_SSE;ZT_USE_MINIUPNPC;MINIUPNP_STATICLIB;WIN32;NOMINMAX;ZT_BUILD_PLATFORM=2;ZT_BUILD_ARCHITECTURE=2;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||
<EnableEnhancedInstructionSet>NotSet</EnableEnhancedInstructionSet>
|
||||
|
@ -291,6 +291,9 @@
|
||||
<ClCompile Include="..\..\osdep\WinFWHelper.cpp">
|
||||
<Filter>Source Files\osdep</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\node\Metrics.cpp">
|
||||
<Filter>Source Files\node</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="resource.h">
|
||||
@ -557,6 +560,9 @@
|
||||
<ClInclude Include="..\..\osdep\WinFWHelper.hpp">
|
||||
<Filter>Header Files\osdep</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\node\Metrics.hpp">
|
||||
<Filter>Header Files\node</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ResourceCompile Include="ZeroTierOne.rc">
|
||||
|
@ -15,7 +15,7 @@ url = "2.3"
|
||||
reqwest = "0.11"
|
||||
jwt = {version = "0.16", git = "https://github.com/glimberg/rust-jwt"}
|
||||
serde = "1.0"
|
||||
time = { version = "0.3", features = ["formatting"] }
|
||||
time = { version = "~0.3", features = ["formatting"] }
|
||||
bytes = "1.3"
|
||||
thiserror = "1"
|
||||
tokio = { version = ">=1.24" }
|
||||
|
Loading…
Reference in New Issue
Block a user