mirror of
https://github.com/drogonframework/drogon.git
synced 2025-07-18 00:00:46 -04:00
Compare commits
2 Commits
e2e5d6d57f
...
cedeeb59f4
Author | SHA1 | Date | |
---|---|---|---|
|
cedeeb59f4 | ||
|
4e5638fdcd |
@ -230,12 +230,18 @@ endif (BUILD_BROTLI)
|
|||||||
|
|
||||||
set(DROGON_SOURCES
|
set(DROGON_SOURCES
|
||||||
lib/src/AOPAdvice.cc
|
lib/src/AOPAdvice.cc
|
||||||
|
lib/src/AccessLogger.cc
|
||||||
lib/src/CacheFile.cc
|
lib/src/CacheFile.cc
|
||||||
|
lib/src/ConfigAdapterManager.cc
|
||||||
lib/src/ConfigLoader.cc
|
lib/src/ConfigLoader.cc
|
||||||
lib/src/Cookie.cc
|
lib/src/Cookie.cc
|
||||||
lib/src/DrClassMap.cc
|
lib/src/DrClassMap.cc
|
||||||
lib/src/DrTemplateBase.cc
|
lib/src/DrTemplateBase.cc
|
||||||
lib/src/FiltersFunction.cc
|
lib/src/FiltersFunction.cc
|
||||||
|
lib/src/FixedWindowRateLimiter.cc
|
||||||
|
lib/src/GlobalFilters.cc
|
||||||
|
lib/src/Histogram.cc
|
||||||
|
lib/src/Hodor.cc
|
||||||
lib/src/HttpAppFrameworkImpl.cc
|
lib/src/HttpAppFrameworkImpl.cc
|
||||||
lib/src/HttpBinder.cc
|
lib/src/HttpBinder.cc
|
||||||
lib/src/HttpClientImpl.cc
|
lib/src/HttpClientImpl.cc
|
||||||
@ -251,33 +257,29 @@ set(DROGON_SOURCES
|
|||||||
lib/src/HttpUtils.cc
|
lib/src/HttpUtils.cc
|
||||||
lib/src/HttpViewData.cc
|
lib/src/HttpViewData.cc
|
||||||
lib/src/IntranetIpFilter.cc
|
lib/src/IntranetIpFilter.cc
|
||||||
|
lib/src/JsonConfigAdapter.cc
|
||||||
lib/src/ListenerManager.cc
|
lib/src/ListenerManager.cc
|
||||||
lib/src/LocalHostFilter.cc
|
lib/src/LocalHostFilter.cc
|
||||||
lib/src/MultiPart.cc
|
lib/src/MultiPart.cc
|
||||||
lib/src/NotFound.cc
|
lib/src/NotFound.cc
|
||||||
lib/src/PluginsManager.cc
|
lib/src/PluginsManager.cc
|
||||||
|
lib/src/PromExporter.cc
|
||||||
lib/src/RangeParser.cc
|
lib/src/RangeParser.cc
|
||||||
lib/src/SecureSSLRedirector.cc
|
lib/src/RateLimiter.cc
|
||||||
lib/src/GlobalFilters.cc
|
|
||||||
lib/src/AccessLogger.cc
|
|
||||||
lib/src/RealIpResolver.cc
|
lib/src/RealIpResolver.cc
|
||||||
|
lib/src/SecureSSLRedirector.cc
|
||||||
lib/src/SessionManager.cc
|
lib/src/SessionManager.cc
|
||||||
|
lib/src/SlashRemover.cc
|
||||||
|
lib/src/SlidingWindowRateLimiter.cc
|
||||||
lib/src/StaticFileRouter.cc
|
lib/src/StaticFileRouter.cc
|
||||||
lib/src/TaskTimeoutFlag.cc
|
lib/src/TaskTimeoutFlag.cc
|
||||||
|
lib/src/TokenBucketRateLimiter.cc
|
||||||
lib/src/Utilities.cc
|
lib/src/Utilities.cc
|
||||||
lib/src/WebSocketClientImpl.cc
|
lib/src/WebSocketClientImpl.cc
|
||||||
lib/src/WebSocketConnectionImpl.cc
|
lib/src/WebSocketConnectionImpl.cc
|
||||||
lib/src/WebsocketControllersRouter.cc
|
lib/src/WebsocketControllersRouter.cc
|
||||||
lib/src/RateLimiter.cc
|
lib/src/YamlConfigAdapter.cc
|
||||||
lib/src/FixedWindowRateLimiter.cc
|
lib/src/drogon_test.cc)
|
||||||
lib/src/SlidingWindowRateLimiter.cc
|
|
||||||
lib/src/TokenBucketRateLimiter.cc
|
|
||||||
lib/src/Hodor.cc
|
|
||||||
lib/src/SlashRemover.cc
|
|
||||||
lib/src/drogon_test.cc
|
|
||||||
lib/src/ConfigAdapterManager.cc
|
|
||||||
lib/src/JsonConfigAdapter.cc
|
|
||||||
lib/src/YamlConfigAdapter.cc)
|
|
||||||
set(private_headers
|
set(private_headers
|
||||||
lib/src/AOPAdvice.h
|
lib/src/AOPAdvice.h
|
||||||
lib/src/CacheFile.h
|
lib/src/CacheFile.h
|
||||||
@ -689,10 +691,23 @@ set(DROGON_UTIL_HEADERS
|
|||||||
lib/inc/drogon/utils/FunctionTraits.h
|
lib/inc/drogon/utils/FunctionTraits.h
|
||||||
lib/inc/drogon/utils/HttpConstraint.h
|
lib/inc/drogon/utils/HttpConstraint.h
|
||||||
lib/inc/drogon/utils/OStringStream.h
|
lib/inc/drogon/utils/OStringStream.h
|
||||||
lib/inc/drogon/utils/Utilities.h)
|
lib/inc/drogon/utils/Utilities.h
|
||||||
|
lib/inc/drogon/utils/monitoring.h)
|
||||||
install(FILES ${DROGON_UTIL_HEADERS}
|
install(FILES ${DROGON_UTIL_HEADERS}
|
||||||
DESTINATION ${INSTALL_INCLUDE_DIR}/drogon/utils)
|
DESTINATION ${INSTALL_INCLUDE_DIR}/drogon/utils)
|
||||||
|
|
||||||
|
set(DROGON_MONITORING_HEADERS
|
||||||
|
lib/inc/drogon/utils/monitoring/Counter.h
|
||||||
|
lib/inc/drogon/utils/monitoring/Metric.h
|
||||||
|
lib/inc/drogon/utils/monitoring/Registry.h
|
||||||
|
lib/inc/drogon/utils/monitoring/Collector.h
|
||||||
|
lib/inc/drogon/utils/monitoring/Sample.h
|
||||||
|
lib/inc/drogon/utils/monitoring/Gauge.h
|
||||||
|
lib/inc/drogon/utils/monitoring/Histogram.h)
|
||||||
|
|
||||||
|
install(FILES ${DROGON_MONITORING_HEADERS}
|
||||||
|
DESTINATION ${INSTALL_INCLUDE_DIR}/drogon/utils/monitoring)
|
||||||
|
|
||||||
set(DROGON_PLUGIN_HEADERS
|
set(DROGON_PLUGIN_HEADERS
|
||||||
lib/inc/drogon/plugins/Plugin.h
|
lib/inc/drogon/plugins/Plugin.h
|
||||||
lib/inc/drogon/plugins/SecureSSLRedirector.h
|
lib/inc/drogon/plugins/SecureSSLRedirector.h
|
||||||
@ -700,7 +715,8 @@ set(DROGON_PLUGIN_HEADERS
|
|||||||
lib/inc/drogon/plugins/RealIpResolver.h
|
lib/inc/drogon/plugins/RealIpResolver.h
|
||||||
lib/inc/drogon/plugins/Hodor.h
|
lib/inc/drogon/plugins/Hodor.h
|
||||||
lib/inc/drogon/plugins/SlashRemover.h
|
lib/inc/drogon/plugins/SlashRemover.h
|
||||||
lib/inc/drogon/plugins/GlobalFilters.h)
|
lib/inc/drogon/plugins/GlobalFilters.h
|
||||||
|
lib/inc/drogon/plugins/PromExporter.h)
|
||||||
|
|
||||||
install(FILES ${DROGON_PLUGIN_HEADERS}
|
install(FILES ${DROGON_PLUGIN_HEADERS}
|
||||||
DESTINATION ${INSTALL_INCLUDE_DIR}/drogon/plugins)
|
DESTINATION ${INSTALL_INCLUDE_DIR}/drogon/plugins)
|
||||||
@ -712,7 +728,8 @@ target_sources(${PROJECT_NAME} PRIVATE
|
|||||||
${ORM_HEADERS}
|
${ORM_HEADERS}
|
||||||
${DROGON_UTIL_HEADERS}
|
${DROGON_UTIL_HEADERS}
|
||||||
${DROGON_PLUGIN_HEADERS}
|
${DROGON_PLUGIN_HEADERS}
|
||||||
${NOSQL_HEADERS})
|
${NOSQL_HEADERS}
|
||||||
|
${DROGON_MONITORING_HEADERS})
|
||||||
|
|
||||||
source_group("Public API"
|
source_group("Public API"
|
||||||
FILES
|
FILES
|
||||||
@ -720,7 +737,8 @@ source_group("Public API"
|
|||||||
${ORM_HEADERS}
|
${ORM_HEADERS}
|
||||||
${DROGON_UTIL_HEADERS}
|
${DROGON_UTIL_HEADERS}
|
||||||
${DROGON_PLUGIN_HEADERS}
|
${DROGON_PLUGIN_HEADERS}
|
||||||
${NOSQL_HEADERS})
|
${NOSQL_HEADERS}
|
||||||
|
${DROGON_MONITORING_HEADERS})
|
||||||
source_group("Private Headers"
|
source_group("Private Headers"
|
||||||
FILES
|
FILES
|
||||||
${private_headers})
|
${private_headers})
|
||||||
|
@ -244,19 +244,19 @@
|
|||||||
//0 means cache forever, the negative value means no cache
|
//0 means cache forever, the negative value means no cache
|
||||||
"static_files_cache_time": 5,
|
"static_files_cache_time": 5,
|
||||||
//simple_controllers_map: Used to configure mapping from path to simple controller
|
//simple_controllers_map: Used to configure mapping from path to simple controller
|
||||||
"simple_controllers_map": [
|
//"simple_controllers_map": [
|
||||||
{
|
// {
|
||||||
"path": "/path/name",
|
// "path": "/path/name",
|
||||||
"controller": "controllerClassName",
|
// "controller": "controllerClassName",
|
||||||
"http_methods": [
|
// "http_methods": [
|
||||||
"get",
|
// "get",
|
||||||
"post"
|
// "post"
|
||||||
],
|
// ],
|
||||||
"filters": [
|
// "filters": [
|
||||||
"FilterClassName"
|
// "FilterClassName"
|
||||||
]
|
// ]
|
||||||
}
|
// }
|
||||||
],
|
//],
|
||||||
//idle_connection_timeout: Defaults to 60 seconds, the lifetime
|
//idle_connection_timeout: Defaults to 60 seconds, the lifetime
|
||||||
//of the connection without read or write
|
//of the connection without read or write
|
||||||
"idle_connection_timeout": 60,
|
"idle_connection_timeout": 60,
|
||||||
@ -307,16 +307,13 @@
|
|||||||
"plugins": [
|
"plugins": [
|
||||||
{
|
{
|
||||||
//name: The class name of the plugin
|
//name: The class name of the plugin
|
||||||
//"name": "drogon::plugin::SecureSSLRedirector",
|
"name": "drogon::plugin::PromExporter",
|
||||||
//dependencies: Plugins that the plugin depends on. It can be commented out
|
//dependencies: Plugins that the plugin depends on. It can be commented out
|
||||||
"dependencies": [],
|
"dependencies": [],
|
||||||
//config: The configuration of the plugin. This json object is the parameter to initialize the plugin.
|
//config: The configuration of the plugin. This json object is the parameter to initialize the plugin.
|
||||||
//It can be commented out
|
//It can be commented out
|
||||||
"config": {
|
"config": {
|
||||||
"ssl_redirect_exempt": [
|
"path": "/metrics"
|
||||||
".*\\.jpg"
|
|
||||||
],
|
|
||||||
"secure_ssl_host": "localhost:8849"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
@ -115,6 +115,16 @@ class HttpBinder : public HttpBinderBase
|
|||||||
const HttpRequestPtr &req,
|
const HttpRequestPtr &req,
|
||||||
std::function<void(const HttpResponsePtr &)> &&callback) override
|
std::function<void(const HttpResponsePtr &)> &&callback) override
|
||||||
{
|
{
|
||||||
|
if (!pathArguments.empty())
|
||||||
|
{
|
||||||
|
std::vector<std::string> args;
|
||||||
|
args.reserve(pathArguments.size());
|
||||||
|
for (auto &arg : pathArguments)
|
||||||
|
{
|
||||||
|
args.emplace_back(arg);
|
||||||
|
}
|
||||||
|
req->setRoutingParameters(std::move(args));
|
||||||
|
}
|
||||||
run(pathArguments, req, std::move(callback));
|
run(pathArguments, req, std::move(callback));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -247,6 +247,13 @@ class DROGON_EXPORT HttpRequest
|
|||||||
matchedPathPatternLength());
|
matchedPathPatternLength());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the matched path pattern after routing (including matched parameters
|
||||||
|
/// in the query string)
|
||||||
|
virtual const std::vector<std::string> &getRoutingParameters() const = 0;
|
||||||
|
|
||||||
|
/// This method usually is called by the framework.
|
||||||
|
virtual void setRoutingParameters(std::vector<std::string> &¶ms) = 0;
|
||||||
|
|
||||||
virtual const char *matchedPathPatternData() const = 0;
|
virtual const char *matchedPathPatternData() const = 0;
|
||||||
virtual size_t matchedPathPatternLength() const = 0;
|
virtual size_t matchedPathPatternLength() const = 0;
|
||||||
|
|
||||||
|
@ -33,6 +33,7 @@
|
|||||||
#include <drogon/plugins/Hodor.h>
|
#include <drogon/plugins/Hodor.h>
|
||||||
#include <drogon/plugins/SlashRemover.h>
|
#include <drogon/plugins/SlashRemover.h>
|
||||||
#include <drogon/plugins/GlobalFilters.h>
|
#include <drogon/plugins/GlobalFilters.h>
|
||||||
|
#include <drogon/plugins/PromExporter.h>
|
||||||
#include <drogon/IntranetIpFilter.h>
|
#include <drogon/IntranetIpFilter.h>
|
||||||
#include <drogon/LocalHostFilter.h>
|
#include <drogon/LocalHostFilter.h>
|
||||||
#include <drogon/Cookie.h>
|
#include <drogon/Cookie.h>
|
||||||
|
98
lib/inc/drogon/plugins/PromExporter.h
Normal file
98
lib/inc/drogon/plugins/PromExporter.h
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
/**
|
||||||
|
* @file PromExporter.h
|
||||||
|
* @author An Tao
|
||||||
|
*
|
||||||
|
* Copyright 2018, An Tao. All rights reserved.
|
||||||
|
* https://github.com/an-tao/drogon
|
||||||
|
* Use of this source code is governed by a MIT license
|
||||||
|
* that can be found in the License file.
|
||||||
|
*
|
||||||
|
* Drogon
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include <drogon/plugins/Plugin.h>
|
||||||
|
#include <drogon/utils/monitoring/Registry.h>
|
||||||
|
#include <drogon/utils/monitoring/Collector.h>
|
||||||
|
#include <memory>
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
|
namespace drogon
|
||||||
|
{
|
||||||
|
namespace plugin
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @brief The PromExporter plugin implements a prometheus exporter.
|
||||||
|
* The json configuration is as follows:
|
||||||
|
* @code
|
||||||
|
{
|
||||||
|
"name": "drogon::plugin::PromExporter",
|
||||||
|
"dependencies": [],
|
||||||
|
"config": {
|
||||||
|
// The path of the metrics. the default value is "/metrics".
|
||||||
|
"path": "/metrics",
|
||||||
|
// The list of collectors.
|
||||||
|
"collectors":[
|
||||||
|
{
|
||||||
|
// The name of the collector.
|
||||||
|
"name": "http_requests_total",
|
||||||
|
// The help message of the collector.
|
||||||
|
"help": "The total number of http requests",
|
||||||
|
// The type of the collector. The default value is "counter".
|
||||||
|
// The other possible value is as following:
|
||||||
|
// "gauge", "histogram".
|
||||||
|
"type": "counter",
|
||||||
|
// The labels of the collector.
|
||||||
|
"labels": ["method", "status"]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@endcode
|
||||||
|
* */
|
||||||
|
class DROGON_EXPORT PromExporter
|
||||||
|
: public drogon::Plugin<PromExporter>,
|
||||||
|
public std::enable_shared_from_this<PromExporter>,
|
||||||
|
public drogon::monitoring::Registry
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
PromExporter()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void initAndStart(const Json::Value &config) override;
|
||||||
|
|
||||||
|
void shutdown() override
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
~PromExporter() override
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void registerCollector(
|
||||||
|
const std::shared_ptr<drogon::monitoring::CollectorBase> &collector)
|
||||||
|
override;
|
||||||
|
|
||||||
|
std::shared_ptr<drogon::monitoring::CollectorBase> getCollector(
|
||||||
|
const std::string &name) const noexcept(false);
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
std::shared_ptr<drogon::monitoring::Collector<T>> getCollector(
|
||||||
|
const std::string &name) const
|
||||||
|
{
|
||||||
|
return std::dynamic_pointer_cast<drogon::monitoring::Collector<T>>(
|
||||||
|
getCollector(name));
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
mutable std::mutex mutex_;
|
||||||
|
std::unordered_map<std::string,
|
||||||
|
std::shared_ptr<drogon::monitoring::CollectorBase>>
|
||||||
|
collectors_;
|
||||||
|
std::string path_{"/metrics"};
|
||||||
|
std::string exportMetrics();
|
||||||
|
};
|
||||||
|
} // namespace plugin
|
||||||
|
} // namespace drogon
|
18
lib/inc/drogon/utils/monitoring.h
Normal file
18
lib/inc/drogon/utils/monitoring.h
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
* monitoring.h
|
||||||
|
* An Tao
|
||||||
|
*
|
||||||
|
* Copyright 2018, An Tao. All rights reserved.
|
||||||
|
* https://github.com/an-tao/drogon
|
||||||
|
* Use of this source code is governed by a MIT license
|
||||||
|
* that can be found in the License file.
|
||||||
|
*
|
||||||
|
* Drogon
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#include <drogon/utils/monitoring/Metric.h>
|
||||||
|
#include <drogon/utils/monitoring/Registry.h>
|
||||||
|
#include <drogon/utils/monitoring/Collector.h>
|
||||||
|
#include <drogon/utils/monitoring/Sample.h>
|
132
lib/inc/drogon/utils/monitoring/Collector.h
Normal file
132
lib/inc/drogon/utils/monitoring/Collector.h
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
* Collector.h
|
||||||
|
* An Tao
|
||||||
|
*
|
||||||
|
* Copyright 2018, An Tao. All rights reserved.
|
||||||
|
* https://github.com/an-tao/drogon
|
||||||
|
* Use of this source code is governed by a MIT license
|
||||||
|
* that can be found in the License file.
|
||||||
|
*
|
||||||
|
* Drogon
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include <trantor/utils/Date.h>
|
||||||
|
#include <drogon/utils/monitoring/Sample.h>
|
||||||
|
#include <drogon/utils/monitoring/Metric.h>
|
||||||
|
#include <drogon/utils/monitoring/Registry.h>
|
||||||
|
#include <string>
|
||||||
|
#include <string_view>
|
||||||
|
#include <vector>
|
||||||
|
#include <mutex>
|
||||||
|
#include <map>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
namespace drogon
|
||||||
|
{
|
||||||
|
namespace monitoring
|
||||||
|
{
|
||||||
|
struct SamplesGroup
|
||||||
|
{
|
||||||
|
std::shared_ptr<Metric> metric;
|
||||||
|
std::vector<Sample> samples;
|
||||||
|
};
|
||||||
|
|
||||||
|
class CollectorBase : public std::enable_shared_from_this<CollectorBase>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual ~CollectorBase() = default;
|
||||||
|
virtual std::vector<SamplesGroup> collect() const = 0;
|
||||||
|
virtual const std::string &name() const = 0;
|
||||||
|
virtual const std::string &help() const = 0;
|
||||||
|
virtual const std::string_view type() const = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief The Collector class template is used to collect samples from a group
|
||||||
|
* of metric.
|
||||||
|
*/
|
||||||
|
template <typename T>
|
||||||
|
class Collector : public CollectorBase
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Collector(const std::string &name,
|
||||||
|
const std::string &help,
|
||||||
|
const std::vector<std::string> &labelNames)
|
||||||
|
: name_(name), help_(help), labelsNames_(labelNames)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::shared_ptr<T> &metric(
|
||||||
|
const std::vector<std::string> &labelValues) noexcept(false)
|
||||||
|
{
|
||||||
|
if (labelValues.size() != labelsNames_.size())
|
||||||
|
{
|
||||||
|
throw std::runtime_error(
|
||||||
|
"The number of label values is not equal to the number of "
|
||||||
|
"label names!");
|
||||||
|
}
|
||||||
|
std::lock_guard<std::mutex> guard(mutex_);
|
||||||
|
auto iter = metrics_.find(labelValues);
|
||||||
|
if (iter != metrics_.end())
|
||||||
|
{
|
||||||
|
return iter->second;
|
||||||
|
}
|
||||||
|
auto metric = std::make_shared<T>(name_, labelsNames_, labelValues);
|
||||||
|
metrics_[labelValues] = metric;
|
||||||
|
return metrics_[labelValues];
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<SamplesGroup> collect() const override
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> guard(mutex_);
|
||||||
|
std::vector<SamplesGroup> samples;
|
||||||
|
for (auto &pair : metrics_)
|
||||||
|
{
|
||||||
|
SamplesGroup samplesGroup;
|
||||||
|
auto &metric = pair.second;
|
||||||
|
samplesGroup.metric = metric;
|
||||||
|
auto metricSamples = metric->collect();
|
||||||
|
samplesGroup.samples = std::move(metricSamples);
|
||||||
|
samples.emplace_back(std::move(samplesGroup));
|
||||||
|
}
|
||||||
|
return samples;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string &name() const override
|
||||||
|
{
|
||||||
|
return name_;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string &help() const override
|
||||||
|
{
|
||||||
|
return help_;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string_view type() const override
|
||||||
|
{
|
||||||
|
return T::type();
|
||||||
|
}
|
||||||
|
|
||||||
|
void registerTo(Registry ®istry)
|
||||||
|
{
|
||||||
|
registry.registerCollector(shared_from_this());
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::vector<std::string> &labelsNames() const
|
||||||
|
{
|
||||||
|
return labelsNames_;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
const std::string name_;
|
||||||
|
const std::string help_;
|
||||||
|
const std::vector<std::string> labelsNames_;
|
||||||
|
std::map<std::vector<std::string>, std::shared_ptr<T>> metrics_;
|
||||||
|
mutable std::mutex mutex_;
|
||||||
|
};
|
||||||
|
} // namespace monitoring
|
||||||
|
} // namespace drogon
|
82
lib/inc/drogon/utils/monitoring/Counter.h
Normal file
82
lib/inc/drogon/utils/monitoring/Counter.h
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
* Counter.h
|
||||||
|
* An Tao
|
||||||
|
*
|
||||||
|
* Copyright 2018, An Tao. All rights reserved.
|
||||||
|
* https://github.com/an-tao/drogon
|
||||||
|
* Use of this source code is governed by a MIT license
|
||||||
|
* that can be found in the License file.
|
||||||
|
*
|
||||||
|
* Drogon
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include <drogon/utils/monitoring/Metric.h>
|
||||||
|
#include <string_view>
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
|
namespace drogon
|
||||||
|
{
|
||||||
|
namespace monitoring
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* This class is used to collect samples for a counter metric.
|
||||||
|
* */
|
||||||
|
class Counter : public Metric
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Counter(const std::string &name,
|
||||||
|
const std::vector<std::string> &labelNames,
|
||||||
|
const std::vector<std::string> &labelValues) noexcept(false)
|
||||||
|
: Metric(name, labelNames, labelValues)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<Sample> collect() const override
|
||||||
|
{
|
||||||
|
Sample s;
|
||||||
|
s.name = name_;
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(mutex_);
|
||||||
|
s.value = value_;
|
||||||
|
}
|
||||||
|
return {s};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Increment the counter by 1.
|
||||||
|
* */
|
||||||
|
void increment()
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(mutex_);
|
||||||
|
value_++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Increment the counter by the given value.
|
||||||
|
* */
|
||||||
|
void increment(double value)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(mutex_);
|
||||||
|
value_ += value;
|
||||||
|
}
|
||||||
|
|
||||||
|
void reset()
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(mutex_);
|
||||||
|
value_ = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::string_view type()
|
||||||
|
{
|
||||||
|
return "counter";
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
mutable std::mutex mutex_;
|
||||||
|
double value_{0};
|
||||||
|
};
|
||||||
|
} // namespace monitoring
|
||||||
|
} // namespace drogon
|
109
lib/inc/drogon/utils/monitoring/Gauge.h
Normal file
109
lib/inc/drogon/utils/monitoring/Gauge.h
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
* Gauge.h
|
||||||
|
* An Tao
|
||||||
|
*
|
||||||
|
* Copyright 2018, An Tao. All rights reserved.
|
||||||
|
* https://github.com/an-tao/drogon
|
||||||
|
* Use of this source code is governed by a MIT license
|
||||||
|
* that can be found in the License file.
|
||||||
|
*
|
||||||
|
* Drogon
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include <drogon/utils/monitoring/Metric.h>
|
||||||
|
#include <string_view>
|
||||||
|
#include <atomic>
|
||||||
|
|
||||||
|
namespace drogon
|
||||||
|
{
|
||||||
|
namespace monitoring
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* This class is used to collect samples for a gauge metric.
|
||||||
|
* */
|
||||||
|
class Gauge : public Metric
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Construct a gauge metric with a name and a help string.
|
||||||
|
* */
|
||||||
|
Gauge(const std::string &name,
|
||||||
|
const std::vector<std::string> &labelNames,
|
||||||
|
const std::vector<std::string> &labelValues) noexcept(false)
|
||||||
|
: Metric(name, labelNames, labelValues)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<Sample> collect() const override
|
||||||
|
{
|
||||||
|
Sample s;
|
||||||
|
std::lock_guard<std::mutex> lock(mutex_);
|
||||||
|
s.name = name_;
|
||||||
|
s.value = value_;
|
||||||
|
s.timestamp = timestamp_;
|
||||||
|
return {s};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Increment the counter by 1.
|
||||||
|
* */
|
||||||
|
void increment()
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(mutex_);
|
||||||
|
value_ += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void decrement()
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(mutex_);
|
||||||
|
value_ -= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void decrement(double value)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(mutex_);
|
||||||
|
value_ -= value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Increment the counter by the given value.
|
||||||
|
* */
|
||||||
|
void increment(double value)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(mutex_);
|
||||||
|
value_ += value;
|
||||||
|
}
|
||||||
|
|
||||||
|
void reset()
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(mutex_);
|
||||||
|
value_ = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void set(double value)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(mutex_);
|
||||||
|
value_ = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::string_view type()
|
||||||
|
{
|
||||||
|
return "counter";
|
||||||
|
}
|
||||||
|
|
||||||
|
void setToCurrentTime()
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(mutex_);
|
||||||
|
timestamp_ = trantor::Date::now();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
mutable std::mutex mutex_;
|
||||||
|
double value_{0};
|
||||||
|
trantor::Date timestamp_{0};
|
||||||
|
};
|
||||||
|
} // namespace monitoring
|
||||||
|
} // namespace drogon
|
122
lib/inc/drogon/utils/monitoring/Histogram.h
Normal file
122
lib/inc/drogon/utils/monitoring/Histogram.h
Normal file
@ -0,0 +1,122 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
* Histogram.h
|
||||||
|
* An Tao
|
||||||
|
*
|
||||||
|
* Copyright 2018, An Tao. All rights reserved.
|
||||||
|
* https://github.com/an-tao/drogon
|
||||||
|
* Use of this source code is governed by a MIT license
|
||||||
|
* that can be found in the License file.
|
||||||
|
*
|
||||||
|
* Drogon
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include <drogon/exports.h>
|
||||||
|
#include <drogon/utils/monitoring/Metric.h>
|
||||||
|
#include <trantor/net/EventLoopThread.h>
|
||||||
|
#include <string_view>
|
||||||
|
#include <atomic>
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
|
namespace drogon
|
||||||
|
{
|
||||||
|
namespace monitoring
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* This class is used to collect samples for a counter metric.
|
||||||
|
* */
|
||||||
|
class DROGON_EXPORT Histogram : public Metric
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
struct TimeBucket
|
||||||
|
{
|
||||||
|
std::vector<uint64_t> buckets;
|
||||||
|
uint64_t count{0};
|
||||||
|
double sum{0};
|
||||||
|
};
|
||||||
|
|
||||||
|
Histogram(const std::string &name,
|
||||||
|
const std::vector<std::string> &labelNames,
|
||||||
|
const std::vector<std::string> &labelValues,
|
||||||
|
const std::vector<double> &bucketBoundaries,
|
||||||
|
const std::chrono::duration<double> &maxAge,
|
||||||
|
uint64_t timeBucketsCount,
|
||||||
|
trantor::EventLoop *loop = nullptr) noexcept(false)
|
||||||
|
: Metric(name, labelNames, labelValues),
|
||||||
|
maxAge_(maxAge),
|
||||||
|
timeBucketCount_(timeBucketsCount),
|
||||||
|
bucketBoundaries_(bucketBoundaries)
|
||||||
|
{
|
||||||
|
if (loop == nullptr)
|
||||||
|
{
|
||||||
|
loopThreadPtr_ = std::make_unique<trantor::EventLoopThread>();
|
||||||
|
loopPtr_ = loopThreadPtr_->getLoop();
|
||||||
|
loopThreadPtr_->run();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
loopPtr_ = loop;
|
||||||
|
}
|
||||||
|
if (maxAge > std::chrono::seconds(0))
|
||||||
|
{
|
||||||
|
if (timeBucketsCount == 0)
|
||||||
|
{
|
||||||
|
throw std::runtime_error(
|
||||||
|
"timeBucketsCount must be greater than 0");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
timeBuckets_.emplace_back();
|
||||||
|
// check the bucket boundaries are sorted
|
||||||
|
for (size_t i = 1; i < bucketBoundaries.size(); i++)
|
||||||
|
{
|
||||||
|
if (bucketBoundaries[i] <= bucketBoundaries[i - 1])
|
||||||
|
{
|
||||||
|
throw std::runtime_error(
|
||||||
|
"The bucket boundaries must be sorted");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void observe(double value);
|
||||||
|
std::vector<Sample> collect() const override;
|
||||||
|
|
||||||
|
~Histogram() override
|
||||||
|
{
|
||||||
|
if (timerId_ != trantor::InvalidTimerId)
|
||||||
|
{
|
||||||
|
loopPtr_->invalidateTimer(timerId_);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::string_view type()
|
||||||
|
{
|
||||||
|
return "histogram";
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::deque<TimeBucket> timeBuckets_;
|
||||||
|
std::unique_ptr<trantor::EventLoopThread> loopThreadPtr_;
|
||||||
|
trantor::EventLoop *loopPtr_{nullptr};
|
||||||
|
mutable std::mutex mutex_;
|
||||||
|
std::chrono::duration<double> maxAge_;
|
||||||
|
trantor::TimerId timerId_{trantor::InvalidTimerId};
|
||||||
|
size_t timeBucketCount_{0};
|
||||||
|
const std::vector<double> bucketBoundaries_;
|
||||||
|
|
||||||
|
void rotateTimeBuckets()
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> guard(mutex_);
|
||||||
|
TimeBucket bucket;
|
||||||
|
bucket.buckets.resize(bucketBoundaries_.size() + 1);
|
||||||
|
timeBuckets_.emplace_back(std::move(bucket));
|
||||||
|
if (timeBuckets_.size() > timeBucketCount_)
|
||||||
|
{
|
||||||
|
auto expiredTimeBucket = timeBuckets_.front();
|
||||||
|
timeBuckets_.erase(timeBuckets_.begin());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} // namespace monitoring
|
||||||
|
} // namespace drogon
|
77
lib/inc/drogon/utils/monitoring/Metric.h
Normal file
77
lib/inc/drogon/utils/monitoring/Metric.h
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
* Metric.h
|
||||||
|
* An Tao
|
||||||
|
*
|
||||||
|
* Copyright 2018, An Tao. All rights reserved.
|
||||||
|
* https://github.com/an-tao/drogon
|
||||||
|
* Use of this source code is governed by a MIT license
|
||||||
|
* that can be found in the License file.
|
||||||
|
*
|
||||||
|
* Drogon
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <drogon/utils/monitoring/Sample.h>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include <memory>
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
|
namespace drogon
|
||||||
|
{
|
||||||
|
namespace monitoring
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* This class is used to collect samples for a metric.
|
||||||
|
* */
|
||||||
|
class Metric : public std::enable_shared_from_this<Metric>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Construct a metric with a name and a help string.
|
||||||
|
* */
|
||||||
|
|
||||||
|
Metric(const std::string &name,
|
||||||
|
const std::vector<std::string> &labelNames,
|
||||||
|
const std::vector<std::string> &labelValues) noexcept(false)
|
||||||
|
: name_(name)
|
||||||
|
{
|
||||||
|
if (labelNames.size() != labelValues.size())
|
||||||
|
{
|
||||||
|
throw std::runtime_error(
|
||||||
|
"The number of label names is not equal to the number of label "
|
||||||
|
"values!");
|
||||||
|
}
|
||||||
|
labels_.resize(labelNames.size());
|
||||||
|
for (size_t i = 0; i < labelNames.size(); i++)
|
||||||
|
{
|
||||||
|
labels_[i].first = labelNames[i];
|
||||||
|
labels_[i].second = labelValues[i];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const std::string &name() const
|
||||||
|
{
|
||||||
|
return name_;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::vector<std::pair<std::string, std::string>> &labels() const
|
||||||
|
{
|
||||||
|
return labels_;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual ~Metric() = default;
|
||||||
|
virtual std::vector<Sample> collect() const = 0;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
const std::string name_;
|
||||||
|
std::vector<std::pair<std::string, std::string>> labels_;
|
||||||
|
};
|
||||||
|
|
||||||
|
using MetricPtr = std::shared_ptr<Metric>;
|
||||||
|
|
||||||
|
} // namespace monitoring
|
||||||
|
} // namespace drogon
|
35
lib/inc/drogon/utils/monitoring/Registry.h
Normal file
35
lib/inc/drogon/utils/monitoring/Registry.h
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
* Registry.h
|
||||||
|
* An Tao
|
||||||
|
*
|
||||||
|
* Copyright 2018, An Tao. All rights reserved.
|
||||||
|
* https://github.com/an-tao/drogon
|
||||||
|
* Use of this source code is governed by a MIT license
|
||||||
|
* that can be found in the License file.
|
||||||
|
*
|
||||||
|
* Drogon
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
namespace drogon
|
||||||
|
{
|
||||||
|
namespace monitoring
|
||||||
|
{
|
||||||
|
class CollectorBase;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class is used to register metrics.
|
||||||
|
* */
|
||||||
|
class Registry
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual ~Registry() = default;
|
||||||
|
virtual void registerCollector(
|
||||||
|
const std::shared_ptr<CollectorBase> &collector) = 0;
|
||||||
|
};
|
||||||
|
} // namespace monitoring
|
||||||
|
} // namespace drogon
|
35
lib/inc/drogon/utils/monitoring/Sample.h
Normal file
35
lib/inc/drogon/utils/monitoring/Sample.h
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
* Sample.h
|
||||||
|
* An Tao
|
||||||
|
*
|
||||||
|
* Copyright 2018, An Tao. All rights reserved.
|
||||||
|
* https://github.com/an-tao/drogon
|
||||||
|
* Use of this source code is governed by a MIT license
|
||||||
|
* that can be found in the License file.
|
||||||
|
*
|
||||||
|
* Drogon
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include <trantor/utils/Date.h>
|
||||||
|
#include <vector>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace drogon
|
||||||
|
{
|
||||||
|
namespace monitoring
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* This class is used to collect samples for a metric.
|
||||||
|
* */
|
||||||
|
struct Sample
|
||||||
|
{
|
||||||
|
double value{0};
|
||||||
|
trantor::Date timestamp{0};
|
||||||
|
std::string name;
|
||||||
|
std::vector<std::pair<std::string, std::string>> exLabels;
|
||||||
|
};
|
||||||
|
} // namespace monitoring
|
||||||
|
} // namespace drogon
|
75
lib/inc/drogon/utils/monitoring/StopWatch.h
Normal file
75
lib/inc/drogon/utils/monitoring/StopWatch.h
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
* StopWatch.h
|
||||||
|
* An Tao
|
||||||
|
*
|
||||||
|
* Copyright 2018, An Tao. All rights reserved.
|
||||||
|
* https://github.com/an-tao/drogon
|
||||||
|
* Use of this source code is governed by a MIT license
|
||||||
|
* that can be found in the License file.
|
||||||
|
*
|
||||||
|
* Drogon
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <chrono>
|
||||||
|
#include <functional>
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
namespace drogon
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @brief This class is used to measure the elapsed time.
|
||||||
|
*/
|
||||||
|
class StopWatch
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
StopWatch() : start_(std::chrono::steady_clock::now())
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
~StopWatch()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Reset the start time.
|
||||||
|
*/
|
||||||
|
void reset()
|
||||||
|
{
|
||||||
|
start_ = std::chrono::steady_clock::now();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get the elapsed time in seconds.
|
||||||
|
*/
|
||||||
|
double elapsed() const
|
||||||
|
{
|
||||||
|
return std::chrono::duration_cast<std::chrono::duration<double>>(
|
||||||
|
std::chrono::steady_clock::now() - start_)
|
||||||
|
.count();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::chrono::steady_clock::time_point start_;
|
||||||
|
};
|
||||||
|
|
||||||
|
class LifeTimeWatch
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
LifeTimeWatch(std::function<void(double)> callback)
|
||||||
|
: stopWatch_(), callback_(std::move(callback))
|
||||||
|
{
|
||||||
|
assert(callback_);
|
||||||
|
}
|
||||||
|
|
||||||
|
~LifeTimeWatch()
|
||||||
|
{
|
||||||
|
callback_(stopWatch_.elapsed());
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
StopWatch stopWatch_;
|
||||||
|
std::function<void(double)> callback_;
|
||||||
|
};
|
||||||
|
} // namespace drogon
|
80
lib/src/Histogram.cc
Normal file
80
lib/src/Histogram.cc
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
#include <drogon/utils/monitoring/Histogram.h>
|
||||||
|
using namespace drogon;
|
||||||
|
using namespace drogon::monitoring;
|
||||||
|
|
||||||
|
void Histogram::observe(double value)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(mutex_);
|
||||||
|
if (maxAge_ > std::chrono::seconds(0) &&
|
||||||
|
timerId_ == trantor::InvalidTimerId)
|
||||||
|
{
|
||||||
|
std::weak_ptr<Histogram> weakPtr =
|
||||||
|
std::dynamic_pointer_cast<Histogram>(shared_from_this());
|
||||||
|
timerId_ = loopPtr_->runEvery(maxAge_ / timeBucketCount_, [weakPtr]() {
|
||||||
|
auto thisPtr = weakPtr.lock();
|
||||||
|
if (!thisPtr)
|
||||||
|
return;
|
||||||
|
thisPtr->rotateTimeBuckets();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
auto ¤tBucket = timeBuckets_.back();
|
||||||
|
currentBucket.sum += value;
|
||||||
|
currentBucket.count += 1;
|
||||||
|
for (size_t i = 0; i < bucketBoundaries_.size(); i++)
|
||||||
|
{
|
||||||
|
if (value <= bucketBoundaries_[i])
|
||||||
|
{
|
||||||
|
currentBucket.buckets[i] += 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (value > bucketBoundaries_.back())
|
||||||
|
{
|
||||||
|
currentBucket.buckets.back() += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<Sample> Histogram::collect() const
|
||||||
|
{
|
||||||
|
std::vector<Sample> samples;
|
||||||
|
std::lock_guard<std::mutex> guard(mutex_);
|
||||||
|
size_t count{0};
|
||||||
|
for (size_t i = 0; i < bucketBoundaries_.size(); i++)
|
||||||
|
{
|
||||||
|
Sample sample;
|
||||||
|
for (auto &bucket : timeBuckets_)
|
||||||
|
{
|
||||||
|
count += bucket.buckets[i];
|
||||||
|
}
|
||||||
|
sample.name = name_ + "_bucket";
|
||||||
|
sample.exLabels.emplace_back("le",
|
||||||
|
std::to_string(bucketBoundaries_[i]));
|
||||||
|
sample.value = count;
|
||||||
|
samples.emplace_back(std::move(sample));
|
||||||
|
}
|
||||||
|
Sample sample;
|
||||||
|
for (auto &bucket : timeBuckets_)
|
||||||
|
{
|
||||||
|
count += bucket.buckets.back();
|
||||||
|
}
|
||||||
|
sample.name = name_ + "_bucket";
|
||||||
|
sample.exLabels.emplace_back("le", "+Inf");
|
||||||
|
sample.value = count;
|
||||||
|
samples.emplace_back(std::move(sample));
|
||||||
|
double sum{0};
|
||||||
|
uint64_t totalCount{0};
|
||||||
|
for (auto &bucket : timeBuckets_)
|
||||||
|
{
|
||||||
|
sum += bucket.sum;
|
||||||
|
totalCount += bucket.count;
|
||||||
|
}
|
||||||
|
Sample sumSample;
|
||||||
|
sumSample.name = name_ + "_sum";
|
||||||
|
sumSample.value = sum;
|
||||||
|
samples.emplace_back(std::move(sumSample));
|
||||||
|
Sample countSample;
|
||||||
|
countSample.name = name_ + "_count";
|
||||||
|
countSample.value = totalCount;
|
||||||
|
samples.emplace_back(std::move(countSample));
|
||||||
|
return samples;
|
||||||
|
}
|
@ -524,6 +524,42 @@ void HttpControllersRouter::route(
|
|||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
std::vector<std::string> params;
|
||||||
|
for (size_t j = 1; j < result.size(); ++j)
|
||||||
|
{
|
||||||
|
if (!result[j].matched)
|
||||||
|
continue;
|
||||||
|
size_t place = j;
|
||||||
|
if (j <= binder->parameterPlaces_.size())
|
||||||
|
{
|
||||||
|
place = binder->parameterPlaces_[j - 1];
|
||||||
|
}
|
||||||
|
if (place > params.size())
|
||||||
|
params.resize(place);
|
||||||
|
params[place - 1] = result[j].str();
|
||||||
|
LOG_TRACE << "place=" << place << " para:" << params[place - 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!binder->queryParametersPlaces_.empty())
|
||||||
|
{
|
||||||
|
auto &queryPara = req->getParameters();
|
||||||
|
for (auto const ¶Place : binder->queryParametersPlaces_)
|
||||||
|
{
|
||||||
|
auto place = paraPlace.second;
|
||||||
|
if (place > params.size())
|
||||||
|
params.resize(place);
|
||||||
|
auto iter = queryPara.find(paraPlace.first);
|
||||||
|
if (iter != queryPara.end())
|
||||||
|
{
|
||||||
|
params[place - 1] = iter->second;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
params[place - 1] = std::string{};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
req->setRoutingParameters(std::move(params));
|
||||||
if (!postRoutingObservers_.empty())
|
if (!postRoutingObservers_.empty())
|
||||||
{
|
{
|
||||||
for (auto &observer : postRoutingObservers_)
|
for (auto &observer : postRoutingObservers_)
|
||||||
@ -639,41 +675,11 @@ void HttpControllersRouter::doControllerHandler(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::deque<std::string> params(ctrlBinderPtr->parameterPlaces_.size());
|
auto ¶msVector = req->getRoutingParameters();
|
||||||
|
std::deque<std::string> params(paramsVector.size());
|
||||||
for (size_t j = 1; j < matchResult.size(); ++j)
|
for (int i = 0; i < paramsVector.size(); i++)
|
||||||
{
|
{
|
||||||
if (!matchResult[j].matched)
|
params[i] = paramsVector[i];
|
||||||
continue;
|
|
||||||
size_t place = j;
|
|
||||||
if (j <= ctrlBinderPtr->parameterPlaces_.size())
|
|
||||||
{
|
|
||||||
place = ctrlBinderPtr->parameterPlaces_[j - 1];
|
|
||||||
}
|
|
||||||
if (place > params.size())
|
|
||||||
params.resize(place);
|
|
||||||
params[place - 1] = matchResult[j].str();
|
|
||||||
LOG_TRACE << "place=" << place << " para:" << params[place - 1];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!ctrlBinderPtr->queryParametersPlaces_.empty())
|
|
||||||
{
|
|
||||||
auto &queryPara = req->getParameters();
|
|
||||||
for (auto const ¶Place : ctrlBinderPtr->queryParametersPlaces_)
|
|
||||||
{
|
|
||||||
auto place = paraPlace.second;
|
|
||||||
if (place > params.size())
|
|
||||||
params.resize(place);
|
|
||||||
auto iter = queryPara.find(paraPlace.first);
|
|
||||||
if (iter != queryPara.end())
|
|
||||||
{
|
|
||||||
params[place - 1] = iter->second;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
params[place - 1] = std::string{};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
ctrlBinderPtr->binderPtr_->handleHttpRequest(
|
ctrlBinderPtr->binderPtr_->handleHttpRequest(
|
||||||
params,
|
params,
|
||||||
|
@ -583,6 +583,7 @@ void HttpRequestImpl::swap(HttpRequestImpl &that) noexcept
|
|||||||
swap(loop_, that.loop_);
|
swap(loop_, that.loop_);
|
||||||
swap(flagForParsingContentType_, that.flagForParsingContentType_);
|
swap(flagForParsingContentType_, that.flagForParsingContentType_);
|
||||||
swap(jsonParsingErrorPtr_, that.jsonParsingErrorPtr_);
|
swap(jsonParsingErrorPtr_, that.jsonParsingErrorPtr_);
|
||||||
|
swap(routingParams_, that.routingParams_);
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *HttpRequestImpl::versionString() const
|
const char *HttpRequestImpl::versionString() const
|
||||||
|
@ -79,6 +79,7 @@ class HttpRequestImpl : public HttpRequest
|
|||||||
keepAlive_ = true;
|
keepAlive_ = true;
|
||||||
jsonParsingErrorPtr_.reset();
|
jsonParsingErrorPtr_.reset();
|
||||||
peerCertificate_.reset();
|
peerCertificate_.reset();
|
||||||
|
routingParams_.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
trantor::EventLoop *getLoop()
|
trantor::EventLoop *getLoop()
|
||||||
@ -143,6 +144,16 @@ class HttpRequestImpl : public HttpRequest
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const std::vector<std::string> &getRoutingParameters() const override
|
||||||
|
{
|
||||||
|
return routingParams_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setRoutingParameters(std::vector<std::string> &¶ms)
|
||||||
|
{
|
||||||
|
routingParams_ = std::move(params);
|
||||||
|
}
|
||||||
|
|
||||||
void setPath(const std::string &path) override
|
void setPath(const std::string &path) override
|
||||||
{
|
{
|
||||||
path_ = path;
|
path_ = path;
|
||||||
@ -614,6 +625,7 @@ class HttpRequestImpl : public HttpRequest
|
|||||||
bool keepAlive_{true};
|
bool keepAlive_{true};
|
||||||
bool isOnSecureConnection_{false};
|
bool isOnSecureConnection_{false};
|
||||||
bool passThrough_{false};
|
bool passThrough_{false};
|
||||||
|
std::vector<std::string> routingParams_;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
std::string content_;
|
std::string content_;
|
||||||
|
207
lib/src/PromExporter.cc
Normal file
207
lib/src/PromExporter.cc
Normal file
@ -0,0 +1,207 @@
|
|||||||
|
#include <drogon/plugins/PromExporter.h>
|
||||||
|
#include <drogon/HttpAppFramework.h>
|
||||||
|
#include <drogon/utils/monitoring/Counter.h>
|
||||||
|
#include <drogon/utils/monitoring/Gauge.h>
|
||||||
|
#include <drogon/utils/monitoring/Histogram.h>
|
||||||
|
#include <drogon/utils/monitoring/Collector.h>
|
||||||
|
|
||||||
|
using namespace drogon;
|
||||||
|
using namespace drogon::monitoring;
|
||||||
|
using namespace drogon::plugin;
|
||||||
|
|
||||||
|
void PromExporter::initAndStart(const Json::Value &config)
|
||||||
|
{
|
||||||
|
path_ = config.get("path", path_).asString();
|
||||||
|
LOG_ERROR << path_;
|
||||||
|
auto &app = drogon::app();
|
||||||
|
std::weak_ptr<PromExporter> weakPtr = shared_from_this();
|
||||||
|
app.registerHandler(
|
||||||
|
path_,
|
||||||
|
[weakPtr](const HttpRequestPtr &req,
|
||||||
|
std::function<void(const HttpResponsePtr &)> &&callback) {
|
||||||
|
auto thisPtr = weakPtr.lock();
|
||||||
|
if (!thisPtr)
|
||||||
|
{
|
||||||
|
auto resp = HttpResponse::newNotFoundResponse();
|
||||||
|
callback(resp);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto resp = HttpResponse::newHttpResponse();
|
||||||
|
resp->setBody(thisPtr->exportMetrics());
|
||||||
|
resp->setExpiredTime(5);
|
||||||
|
callback(resp);
|
||||||
|
},
|
||||||
|
{Get, Options},
|
||||||
|
"PromExporter");
|
||||||
|
if (config.isMember("collectors"))
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> guard(mutex_);
|
||||||
|
auto &collectors = config["collectors"];
|
||||||
|
if (collectors.isArray())
|
||||||
|
{
|
||||||
|
for (auto const &collector : collectors)
|
||||||
|
{
|
||||||
|
if (collector.isObject())
|
||||||
|
{
|
||||||
|
auto name = collector["name"].asString();
|
||||||
|
auto type = collector["type"].asString();
|
||||||
|
auto help = collector["help"].asString();
|
||||||
|
auto labels = collector["labels"];
|
||||||
|
if (labels.isArray())
|
||||||
|
{
|
||||||
|
std::vector<std::string> labelNames;
|
||||||
|
for (auto const &label : labels)
|
||||||
|
{
|
||||||
|
if (label.isString())
|
||||||
|
{
|
||||||
|
labelNames.push_back(label.asString());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
LOG_ERROR << "label name must be a string!";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (type == "counter")
|
||||||
|
{
|
||||||
|
auto counterCollector =
|
||||||
|
std::make_shared<Collector<Counter>>(
|
||||||
|
name, help, labelNames);
|
||||||
|
collectors_.insert(
|
||||||
|
std::make_pair(name, counterCollector));
|
||||||
|
}
|
||||||
|
else if (type == "gauge")
|
||||||
|
{
|
||||||
|
auto gaugeCollector =
|
||||||
|
std::make_shared<Collector<Gauge>>(name,
|
||||||
|
help,
|
||||||
|
labelNames);
|
||||||
|
collectors_.insert(
|
||||||
|
std::make_pair(name, gaugeCollector));
|
||||||
|
}
|
||||||
|
else if (type == "histogram")
|
||||||
|
{
|
||||||
|
auto histogramCollector =
|
||||||
|
std::make_shared<Collector<Histogram>>(
|
||||||
|
name, help, labelNames);
|
||||||
|
collectors_.insert(
|
||||||
|
std::make_pair(name, histogramCollector));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
LOG_ERROR << "Unknown collector type: " << type;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
LOG_ERROR << "labels must be an array!";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
LOG_ERROR << "collector must be an object!";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
LOG_ERROR << "collectors must be an array!";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::string exportCollector(
|
||||||
|
const std::shared_ptr<CollectorBase> &collector)
|
||||||
|
{
|
||||||
|
auto sampleGroups = collector->collect();
|
||||||
|
std::string res;
|
||||||
|
res.append("# HELP ")
|
||||||
|
.append(collector->name())
|
||||||
|
.append(" ")
|
||||||
|
.append(collector->help())
|
||||||
|
.append("\r\n");
|
||||||
|
res.append("# TYPE ")
|
||||||
|
.append(collector->name())
|
||||||
|
.append(" ")
|
||||||
|
.append(collector->type())
|
||||||
|
.append("\r\n");
|
||||||
|
for (auto const &sampleGroup : sampleGroups)
|
||||||
|
{
|
||||||
|
auto const &metricPtr = sampleGroup.metric;
|
||||||
|
auto const &samples = sampleGroup.samples;
|
||||||
|
for (auto &sample : samples)
|
||||||
|
{
|
||||||
|
res.append(metricPtr->name());
|
||||||
|
if (!sample.exLabels.empty() || !metricPtr->labels().empty())
|
||||||
|
{
|
||||||
|
res.append("{");
|
||||||
|
for (auto const &label : metricPtr->labels())
|
||||||
|
{
|
||||||
|
res.append(label.first)
|
||||||
|
.append("=\"")
|
||||||
|
.append(label.second)
|
||||||
|
.append("\",");
|
||||||
|
}
|
||||||
|
for (auto const &label : sample.exLabels)
|
||||||
|
{
|
||||||
|
res.append(label.first)
|
||||||
|
.append("=\"")
|
||||||
|
.append(label.second)
|
||||||
|
.append("\",");
|
||||||
|
}
|
||||||
|
res.pop_back();
|
||||||
|
res.append("}");
|
||||||
|
}
|
||||||
|
res.append(" ").append(std::to_string(sample.value));
|
||||||
|
if (sample.timestamp.microSecondsSinceEpoch() > 0)
|
||||||
|
{
|
||||||
|
res.append(" ")
|
||||||
|
.append(std::to_string(
|
||||||
|
sample.timestamp.microSecondsSinceEpoch() / 1000))
|
||||||
|
.append("\r\n");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
res.append("\r\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string PromExporter::exportMetrics()
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> guard(mutex_);
|
||||||
|
std::string result;
|
||||||
|
for (auto const &collector : collectors_)
|
||||||
|
{
|
||||||
|
result.append(exportCollector(collector.second));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PromExporter::registerCollector(
|
||||||
|
const std::shared_ptr<drogon::monitoring::CollectorBase> &collector)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> guard(mutex_);
|
||||||
|
if (collectors_.find(collector->name()) != collectors_.end())
|
||||||
|
{
|
||||||
|
throw std::runtime_error("The collector named " + collector->name() +
|
||||||
|
" has been registered!");
|
||||||
|
}
|
||||||
|
collectors_.insert(std::make_pair(collector->name(), collector));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<drogon::monitoring::CollectorBase> PromExporter::getCollector(
|
||||||
|
const std::string &name) const noexcept(false)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> guard(mutex_);
|
||||||
|
auto iter = collectors_.find(name);
|
||||||
|
if (iter != collectors_.end())
|
||||||
|
{
|
||||||
|
return iter->second;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw std::runtime_error("Can't find the collector named " + name);
|
||||||
|
}
|
||||||
|
}
|
@ -6,6 +6,7 @@ void TestController::asyncHandleHttpRequest(
|
|||||||
std::function<void(const HttpResponsePtr &)> &&callback)
|
std::function<void(const HttpResponsePtr &)> &&callback)
|
||||||
{
|
{
|
||||||
// write your application logic here
|
// write your application logic here
|
||||||
|
counter_->increment();
|
||||||
LOG_WARN << req->matchedPathPatternData();
|
LOG_WARN << req->matchedPathPatternData();
|
||||||
LOG_DEBUG << "index=" << threadIndex_.getThreadData();
|
LOG_DEBUG << "index=" << threadIndex_.getThreadData();
|
||||||
++(threadIndex_.getThreadData());
|
++(threadIndex_.getThreadData());
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include <drogon/HttpSimpleController.h>
|
#include <drogon/HttpSimpleController.h>
|
||||||
#include <drogon/IOThreadStorage.h>
|
#include <drogon/IOThreadStorage.h>
|
||||||
|
#include <drogon/utils/monitoring/Counter.h>
|
||||||
|
#include <drogon/utils/monitoring/Collector.h>
|
||||||
|
#include <drogon/plugins/PromExporter.h>
|
||||||
using namespace drogon;
|
using namespace drogon;
|
||||||
|
|
||||||
namespace example
|
namespace example
|
||||||
@ -23,9 +26,18 @@ class TestController : public drogon::HttpSimpleController<TestController>
|
|||||||
TestController()
|
TestController()
|
||||||
{
|
{
|
||||||
LOG_DEBUG << "TestController constructor";
|
LOG_DEBUG << "TestController constructor";
|
||||||
|
auto collector = std::make_shared<
|
||||||
|
drogon::monitoring::Collector<drogon::monitoring::Counter>>(
|
||||||
|
"test_counter",
|
||||||
|
"The counter for requests to the root url",
|
||||||
|
std::vector<std::string>());
|
||||||
|
counter_ = collector->metric(std::vector<std::string>());
|
||||||
|
collector->registerTo(
|
||||||
|
*app().getSharedPlugin<drogon::plugin::PromExporter>());
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
drogon::IOThreadStorage<int> threadIndex_;
|
drogon::IOThreadStorage<int> threadIndex_;
|
||||||
|
std::shared_ptr<drogon::monitoring::Counter> counter_;
|
||||||
};
|
};
|
||||||
} // namespace example
|
} // namespace example
|
||||||
|
Loading…
x
Reference in New Issue
Block a user