-
Notifications
You must be signed in to change notification settings - Fork 5.5k
stats: add support for custom prefixes #3029
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 8 commits
55fd426
7c51e0c
a75b05d
2e0cd2c
2a021a8
f472f78
40d503b
807f7bf
34bc646
1399b2b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -38,28 +38,30 @@ void Writer::write(const std::string& message) { | |
| } | ||
|
|
||
| UdpStatsdSink::UdpStatsdSink(ThreadLocal::SlotAllocator& tls, | ||
| Network::Address::InstanceConstSharedPtr address, const bool use_tag) | ||
| : tls_(tls.allocateSlot()), server_address_(std::move(address)), use_tag_(use_tag) { | ||
| Network::Address::InstanceConstSharedPtr address, const bool use_tag, | ||
| const std::string& prefix) | ||
| : tls_(tls.allocateSlot()), server_address_(std::move(address)), use_tag_(use_tag), | ||
| prefix_(prefix.empty() ? Statsd::getDefaultPrefix() : prefix) { | ||
| tls_->set([this](Event::Dispatcher&) -> ThreadLocal::ThreadLocalObjectSharedPtr { | ||
| return std::make_shared<Writer>(this->server_address_); | ||
| }); | ||
| } | ||
|
|
||
| void UdpStatsdSink::flushCounter(const Stats::Counter& counter, uint64_t delta) { | ||
| const std::string message( | ||
| fmt::format("envoy.{}:{}|c{}", getName(counter), delta, buildTagStr(counter.tags()))); | ||
| fmt::format("{}.{}:{}|c{}", prefix_, getName(counter), delta, buildTagStr(counter.tags()))); | ||
| tls_->getTyped<Writer>().write(message); | ||
| } | ||
|
|
||
| void UdpStatsdSink::flushGauge(const Stats::Gauge& gauge, uint64_t value) { | ||
| const std::string message( | ||
| fmt::format("envoy.{}:{}|g{}", getName(gauge), value, buildTagStr(gauge.tags()))); | ||
| fmt::format("{}.{}:{}|g{}", prefix_, getName(gauge), value, buildTagStr(gauge.tags()))); | ||
| tls_->getTyped<Writer>().write(message); | ||
| } | ||
|
|
||
| void UdpStatsdSink::onHistogramComplete(const Stats::Histogram& histogram, uint64_t value) { | ||
| // For statsd histograms are all timers. | ||
| const std::string message(fmt::format("envoy.{}:{}|ms{}", getName(histogram), | ||
| const std::string message(fmt::format("{}.{}:{}|ms{}", prefix_, getName(histogram), | ||
| std::chrono::milliseconds(value).count(), | ||
| buildTagStr(histogram.tags()))); | ||
| tls_->getTyped<Writer>().write(message); | ||
|
|
@@ -86,13 +88,12 @@ const std::string UdpStatsdSink::buildTagStr(const std::vector<Stats::Tag>& tags | |
| return "|#" + StringUtil::join(tag_strings, ","); | ||
| } | ||
|
|
||
| char TcpStatsdSink::STAT_PREFIX[] = "envoy."; | ||
|
|
||
| TcpStatsdSink::TcpStatsdSink(const LocalInfo::LocalInfo& local_info, | ||
| const std::string& cluster_name, ThreadLocal::SlotAllocator& tls, | ||
| Upstream::ClusterManager& cluster_manager, Stats::Scope& scope) | ||
| : tls_(tls.allocateSlot()), cluster_manager_(cluster_manager), | ||
| cx_overflow_stat_(scope.counter("statsd.cx_overflow")) { | ||
| Upstream::ClusterManager& cluster_manager, Stats::Scope& scope, | ||
| const std::string& prefix) | ||
| : prefix_(prefix.empty() ? Statsd::getDefaultPrefix() : prefix), tls_(tls.allocateSlot()), | ||
| cluster_manager_(cluster_manager), cx_overflow_stat_(scope.counter("statsd.cx_overflow")) { | ||
|
|
||
| Config::Utility::checkClusterAndLocalInfo("tcp statsd", cluster_name, cluster_manager, | ||
| local_info); | ||
|
|
@@ -124,8 +125,8 @@ void TcpStatsdSink::TlsSink::beginFlush(bool expect_empty_buffer) { | |
|
|
||
| void TcpStatsdSink::TlsSink::commonFlush(const std::string& name, uint64_t value, char stat_type) { | ||
| ASSERT(current_slice_mem_ != nullptr); | ||
| // 40 > 6 (prefix) + 4 (random chars) + 30 for number (bigger than it will ever be) | ||
| const uint32_t max_size = name.size() + 40; | ||
| // 34 > 4 (random chars) + 30 for number (bigger than it will ever be) | ||
| const uint32_t max_size = name.size() + parent_.getPrefix().size() + 40; | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Shouldn't this be
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yep, updated. |
||
| if (current_buffer_slice_.len_ - usedBuffer() < max_size) { | ||
| endFlush(false); | ||
| beginFlush(false); | ||
|
|
@@ -135,8 +136,10 @@ void TcpStatsdSink::TlsSink::commonFlush(const std::string& name, uint64_t value | |
| // This written this way for maximum perf since with a large number of stats and at a high flush | ||
| // rate this can become expensive. | ||
| const char* snapped_current = current_slice_mem_; | ||
| memcpy(current_slice_mem_, STAT_PREFIX, sizeof(STAT_PREFIX) - 1); | ||
| current_slice_mem_ += sizeof(STAT_PREFIX) - 1; | ||
| memcpy(current_slice_mem_, parent_.getPrefix().c_str(), parent_.getPrefix().size()); | ||
| current_slice_mem_ += parent_.getPrefix().size(); | ||
| *current_slice_mem_ = '.'; | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: simplify to |
||
| current_slice_mem_ += 1; | ||
| memcpy(current_slice_mem_, name.c_str(), name.size()); | ||
| current_slice_mem_ += name.size(); | ||
| *current_slice_mem_++ = ':'; | ||
|
|
@@ -178,7 +181,8 @@ void TcpStatsdSink::TlsSink::onTimespanComplete(const std::string& name, | |
| // Ultimately it would be nice to perf optimize this path also, but it's not very frequent. It's | ||
| // also currently not possible that this interleaves with any counter/gauge flushing. | ||
| ASSERT(current_slice_mem_ == nullptr); | ||
| Buffer::OwnedImpl buffer(fmt::format("envoy.{}:{}|ms\n", name, ms.count())); | ||
| Buffer::OwnedImpl buffer( | ||
| fmt::format("{}.{}:{}|ms\n", parent_.getPrefix().c_str(), name, ms.count())); | ||
| write(buffer); | ||
| } | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -7,13 +7,16 @@ | |
| #include "envoy/upstream/cluster_manager.h" | ||
|
|
||
| #include "common/buffer/buffer_impl.h" | ||
| #include "common/common/macros.h" | ||
|
|
||
| namespace Envoy { | ||
| namespace Extensions { | ||
| namespace StatSinks { | ||
| namespace Common { | ||
| namespace Statsd { | ||
|
|
||
| static const std::string& getDefaultPrefix() { CONSTRUCT_ON_FIRST_USE(std::string, "envoy"); } | ||
|
|
||
| /** | ||
| * This is a simple UDP localhost writer for statsd messages. | ||
| */ | ||
|
|
@@ -38,11 +41,12 @@ class Writer : public ThreadLocal::ThreadLocalObject { | |
| class UdpStatsdSink : public Stats::Sink { | ||
| public: | ||
| UdpStatsdSink(ThreadLocal::SlotAllocator& tls, Network::Address::InstanceConstSharedPtr address, | ||
| const bool use_tag); | ||
| const bool use_tag, const std::string& prefix = getDefaultPrefix()); | ||
| // For testing. | ||
| UdpStatsdSink(ThreadLocal::SlotAllocator& tls, const std::shared_ptr<Writer>& writer, | ||
| const bool use_tag) | ||
| : tls_(tls.allocateSlot()), use_tag_(use_tag) { | ||
| const bool use_tag, const std::string& prefix = getDefaultPrefix()) | ||
| : tls_(tls.allocateSlot()), use_tag_(use_tag), | ||
| prefix_(prefix.empty() ? getDefaultPrefix() : prefix) { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is there a reason we want to disallow an empty prefix since the user already has the option to not provide the argument and get the default? (same for TCP)
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. An empty prefix doesn't make sense, imo. If an empty prefix is passed in, we just use the default instead. Also, if a prefix isn't specified
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. SGTM - I didn't realize this was coming directly from the proto, so there is no way to specify a default. |
||
| tls_->set( | ||
| [writer](Event::Dispatcher&) -> ThreadLocal::ThreadLocalObjectSharedPtr { return writer; }); | ||
| } | ||
|
|
@@ -57,6 +61,7 @@ class UdpStatsdSink : public Stats::Sink { | |
| // Called in unit test to validate writer construction and address. | ||
| int getFdForTests() { return tls_->getTyped<Writer>().getFdForTests(); } | ||
| bool getUseTagForTest() { return use_tag_; } | ||
| const std::string getPrefix() { return prefix_; } | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. fixed |
||
|
|
||
| private: | ||
| const std::string getName(const Stats::Metric& metric); | ||
|
|
@@ -65,6 +70,8 @@ class UdpStatsdSink : public Stats::Sink { | |
| ThreadLocal::SlotPtr tls_; | ||
| Network::Address::InstanceConstSharedPtr server_address_; | ||
| const bool use_tag_; | ||
| // Prefix for all flushed stats. | ||
| const std::string prefix_; | ||
| }; | ||
|
|
||
| /** | ||
|
|
@@ -74,7 +81,7 @@ class TcpStatsdSink : public Stats::Sink { | |
| public: | ||
| TcpStatsdSink(const LocalInfo::LocalInfo& local_info, const std::string& cluster_name, | ||
| ThreadLocal::SlotAllocator& tls, Upstream::ClusterManager& cluster_manager, | ||
| Stats::Scope& scope); | ||
| Stats::Scope& scope, const std::string& prefix = getDefaultPrefix()); | ||
|
|
||
| // Stats::Sink | ||
| void beginFlush() override { tls_->getTyped<TlsSink>().beginFlush(true); } | ||
|
|
@@ -95,6 +102,8 @@ class TcpStatsdSink : public Stats::Sink { | |
| std::chrono::milliseconds(value)); | ||
| } | ||
|
|
||
| const std::string getPrefix() { return prefix_; } | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fixed. |
||
|
|
||
| private: | ||
| struct TlsSink : public ThreadLocal::ThreadLocalObject, public Network::ConnectionCallbacks { | ||
| TlsSink(TcpStatsdSink& parent, Event::Dispatcher& dispatcher); | ||
|
|
@@ -130,7 +139,7 @@ class TcpStatsdSink : public Stats::Sink { | |
| static constexpr uint32_t FLUSH_SLICE_SIZE_BYTES = (1024 * 16); | ||
|
|
||
| // Prefix for all flushed stats. | ||
| static char STAT_PREFIX[]; | ||
| const std::string prefix_; | ||
|
|
||
| Upstream::ClusterInfoConstSharedPtr cluster_info_; | ||
| ThreadLocal::SlotPtr tls_; | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you update this comment to:
34 > 4 (postfix chars, e.g., "|ms\n") + 30 for number (bigger than it will ever be)There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done!