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
|
||||
lib/src/AOPAdvice.cc
|
||||
lib/src/AccessLogger.cc
|
||||
lib/src/CacheFile.cc
|
||||
lib/src/ConfigAdapterManager.cc
|
||||
lib/src/ConfigLoader.cc
|
||||
lib/src/Cookie.cc
|
||||
lib/src/DrClassMap.cc
|
||||
lib/src/DrTemplateBase.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/HttpBinder.cc
|
||||
lib/src/HttpClientImpl.cc
|
||||
@ -251,33 +257,29 @@ set(DROGON_SOURCES
|
||||
lib/src/HttpUtils.cc
|
||||
lib/src/HttpViewData.cc
|
||||
lib/src/IntranetIpFilter.cc
|
||||
lib/src/JsonConfigAdapter.cc
|
||||
lib/src/ListenerManager.cc
|
||||
lib/src/LocalHostFilter.cc
|
||||
lib/src/MultiPart.cc
|
||||
lib/src/NotFound.cc
|
||||
lib/src/PluginsManager.cc
|
||||
lib/src/PromExporter.cc
|
||||
lib/src/RangeParser.cc
|
||||
lib/src/SecureSSLRedirector.cc
|
||||
lib/src/GlobalFilters.cc
|
||||
lib/src/AccessLogger.cc
|
||||
lib/src/RateLimiter.cc
|
||||
lib/src/RealIpResolver.cc
|
||||
lib/src/SecureSSLRedirector.cc
|
||||
lib/src/SessionManager.cc
|
||||
lib/src/SlashRemover.cc
|
||||
lib/src/SlidingWindowRateLimiter.cc
|
||||
lib/src/StaticFileRouter.cc
|
||||
lib/src/TaskTimeoutFlag.cc
|
||||
lib/src/TokenBucketRateLimiter.cc
|
||||
lib/src/Utilities.cc
|
||||
lib/src/WebSocketClientImpl.cc
|
||||
lib/src/WebSocketConnectionImpl.cc
|
||||
lib/src/WebsocketControllersRouter.cc
|
||||
lib/src/RateLimiter.cc
|
||||
lib/src/FixedWindowRateLimiter.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)
|
||||
lib/src/YamlConfigAdapter.cc
|
||||
lib/src/drogon_test.cc)
|
||||
set(private_headers
|
||||
lib/src/AOPAdvice.h
|
||||
lib/src/CacheFile.h
|
||||
@ -689,10 +691,23 @@ set(DROGON_UTIL_HEADERS
|
||||
lib/inc/drogon/utils/FunctionTraits.h
|
||||
lib/inc/drogon/utils/HttpConstraint.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}
|
||||
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
|
||||
lib/inc/drogon/plugins/Plugin.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/Hodor.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}
|
||||
DESTINATION ${INSTALL_INCLUDE_DIR}/drogon/plugins)
|
||||
@ -712,7 +728,8 @@ target_sources(${PROJECT_NAME} PRIVATE
|
||||
${ORM_HEADERS}
|
||||
${DROGON_UTIL_HEADERS}
|
||||
${DROGON_PLUGIN_HEADERS}
|
||||
${NOSQL_HEADERS})
|
||||
${NOSQL_HEADERS}
|
||||
${DROGON_MONITORING_HEADERS})
|
||||
|
||||
source_group("Public API"
|
||||
FILES
|
||||
@ -720,7 +737,8 @@ source_group("Public API"
|
||||
${ORM_HEADERS}
|
||||
${DROGON_UTIL_HEADERS}
|
||||
${DROGON_PLUGIN_HEADERS}
|
||||
${NOSQL_HEADERS})
|
||||
${NOSQL_HEADERS}
|
||||
${DROGON_MONITORING_HEADERS})
|
||||
source_group("Private Headers"
|
||||
FILES
|
||||
${private_headers})
|
||||
|
@ -244,19 +244,19 @@
|
||||
//0 means cache forever, the negative value means no cache
|
||||
"static_files_cache_time": 5,
|
||||
//simple_controllers_map: Used to configure mapping from path to simple controller
|
||||
"simple_controllers_map": [
|
||||
{
|
||||
"path": "/path/name",
|
||||
"controller": "controllerClassName",
|
||||
"http_methods": [
|
||||
"get",
|
||||
"post"
|
||||
],
|
||||
"filters": [
|
||||
"FilterClassName"
|
||||
]
|
||||
}
|
||||
],
|
||||
//"simple_controllers_map": [
|
||||
// {
|
||||
// "path": "/path/name",
|
||||
// "controller": "controllerClassName",
|
||||
// "http_methods": [
|
||||
// "get",
|
||||
// "post"
|
||||
// ],
|
||||
// "filters": [
|
||||
// "FilterClassName"
|
||||
// ]
|
||||
// }
|
||||
//],
|
||||
//idle_connection_timeout: Defaults to 60 seconds, the lifetime
|
||||
//of the connection without read or write
|
||||
"idle_connection_timeout": 60,
|
||||
@ -307,16 +307,13 @@
|
||||
"plugins": [
|
||||
{
|
||||
//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": [],
|
||||
//config: The configuration of the plugin. This json object is the parameter to initialize the plugin.
|
||||
//It can be commented out
|
||||
"config": {
|
||||
"ssl_redirect_exempt": [
|
||||
".*\\.jpg"
|
||||
],
|
||||
"secure_ssl_host": "localhost:8849"
|
||||
"path": "/metrics"
|
||||
}
|
||||
}
|
||||
],
|
||||
|
@ -115,6 +115,16 @@ class HttpBinder : public HttpBinderBase
|
||||
const HttpRequestPtr &req,
|
||||
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));
|
||||
}
|
||||
|
||||
|
@ -247,6 +247,13 @@ class DROGON_EXPORT HttpRequest
|
||||
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 size_t matchedPathPatternLength() const = 0;
|
||||
|
||||
|
@ -33,6 +33,7 @@
|
||||
#include <drogon/plugins/Hodor.h>
|
||||
#include <drogon/plugins/SlashRemover.h>
|
||||
#include <drogon/plugins/GlobalFilters.h>
|
||||
#include <drogon/plugins/PromExporter.h>
|
||||
#include <drogon/IntranetIpFilter.h>
|
||||
#include <drogon/LocalHostFilter.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;
|
||||
}
|
||||
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())
|
||||
{
|
||||
for (auto &observer : postRoutingObservers_)
|
||||
@ -639,41 +675,11 @@ void HttpControllersRouter::doControllerHandler(
|
||||
}
|
||||
}
|
||||
|
||||
std::deque<std::string> params(ctrlBinderPtr->parameterPlaces_.size());
|
||||
|
||||
for (size_t j = 1; j < matchResult.size(); ++j)
|
||||
auto ¶msVector = req->getRoutingParameters();
|
||||
std::deque<std::string> params(paramsVector.size());
|
||||
for (int i = 0; i < paramsVector.size(); i++)
|
||||
{
|
||||
if (!matchResult[j].matched)
|
||||
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{};
|
||||
}
|
||||
}
|
||||
params[i] = paramsVector[i];
|
||||
}
|
||||
ctrlBinderPtr->binderPtr_->handleHttpRequest(
|
||||
params,
|
||||
|
@ -583,6 +583,7 @@ void HttpRequestImpl::swap(HttpRequestImpl &that) noexcept
|
||||
swap(loop_, that.loop_);
|
||||
swap(flagForParsingContentType_, that.flagForParsingContentType_);
|
||||
swap(jsonParsingErrorPtr_, that.jsonParsingErrorPtr_);
|
||||
swap(routingParams_, that.routingParams_);
|
||||
}
|
||||
|
||||
const char *HttpRequestImpl::versionString() const
|
||||
|
@ -79,6 +79,7 @@ class HttpRequestImpl : public HttpRequest
|
||||
keepAlive_ = true;
|
||||
jsonParsingErrorPtr_.reset();
|
||||
peerCertificate_.reset();
|
||||
routingParams_.clear();
|
||||
}
|
||||
|
||||
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
|
||||
{
|
||||
path_ = path;
|
||||
@ -614,6 +625,7 @@ class HttpRequestImpl : public HttpRequest
|
||||
bool keepAlive_{true};
|
||||
bool isOnSecureConnection_{false};
|
||||
bool passThrough_{false};
|
||||
std::vector<std::string> routingParams_;
|
||||
|
||||
protected:
|
||||
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)
|
||||
{
|
||||
// write your application logic here
|
||||
counter_->increment();
|
||||
LOG_WARN << req->matchedPathPatternData();
|
||||
LOG_DEBUG << "index=" << threadIndex_.getThreadData();
|
||||
++(threadIndex_.getThreadData());
|
||||
|
@ -1,6 +1,9 @@
|
||||
#pragma once
|
||||
#include <drogon/HttpSimpleController.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;
|
||||
|
||||
namespace example
|
||||
@ -23,9 +26,18 @@ class TestController : public drogon::HttpSimpleController<TestController>
|
||||
TestController()
|
||||
{
|
||||
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:
|
||||
drogon::IOThreadStorage<int> threadIndex_;
|
||||
std::shared_ptr<drogon::monitoring::Counter> counter_;
|
||||
};
|
||||
} // namespace example
|
||||
|
Loading…
x
Reference in New Issue
Block a user