-
Notifications
You must be signed in to change notification settings - Fork 440
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Implement periodic exporting metric reader (#1286)
- Loading branch information
Showing
10 changed files
with
272 additions
and
15 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
72 changes: 72 additions & 0 deletions
72
sdk/include/opentelemetry/sdk/metrics/export/periodic_exporting_metric_reader.h
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
// Copyright The OpenTelemetry Authors | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
#pragma once | ||
#ifndef ENABLE_METRICS_PREVIEW | ||
|
||
# include "opentelemetry/sdk/metrics/metric_reader.h" | ||
# include "opentelemetry/version.h" | ||
|
||
# include <atomic> | ||
# include <chrono> | ||
# include <condition_variable> | ||
# include <thread> | ||
|
||
OPENTELEMETRY_BEGIN_NAMESPACE | ||
namespace sdk | ||
{ | ||
namespace metrics | ||
{ | ||
|
||
class MetricExporter; | ||
/** | ||
* Struct to hold PeriodicExortingMetricReader options. | ||
*/ | ||
|
||
constexpr std::chrono::milliseconds kExportIntervalMillis = std::chrono::milliseconds(60000); | ||
constexpr std::chrono::milliseconds kExportTimeOutMillis = std::chrono::milliseconds(30000); | ||
struct PeriodicExportingMetricReaderOptions | ||
{ | ||
|
||
/* The time interval between two consecutive exports. */ | ||
std::chrono::milliseconds export_interval_millis = | ||
std::chrono::milliseconds(kExportIntervalMillis); | ||
|
||
/* how long the export can run before it is cancelled. */ | ||
std::chrono::milliseconds export_timeout_millis = std::chrono::milliseconds(kExportTimeOutMillis); | ||
}; | ||
|
||
class PeriodicExportingMetricReader : public MetricReader | ||
{ | ||
|
||
public: | ||
PeriodicExportingMetricReader( | ||
std::unique_ptr<MetricExporter> exporter, | ||
const PeriodicExportingMetricReaderOptions &option, | ||
AggregationTemporality aggregation_temporality = AggregationTemporality::kCumulative); | ||
|
||
private: | ||
bool OnForceFlush(std::chrono::microseconds timeout) noexcept override; | ||
|
||
bool OnShutDown(std::chrono::microseconds timeout) noexcept override; | ||
|
||
void OnInitialized() noexcept override; | ||
|
||
std::unique_ptr<MetricExporter> exporter_; | ||
std::chrono::milliseconds export_interval_millis_; | ||
std::chrono::milliseconds export_timeout_millis_; | ||
|
||
void DoBackgroundWork(); | ||
|
||
/* The background worker thread */ | ||
std::thread worker_thread_; | ||
|
||
/* Synchronization primitives */ | ||
std::condition_variable cv_; | ||
std::mutex cv_m_; | ||
}; | ||
|
||
} // namespace metrics | ||
} // namespace sdk | ||
OPENTELEMETRY_END_NAMESPACE | ||
#endif |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
101 changes: 101 additions & 0 deletions
101
sdk/src/metrics/export/periodic_exporting_metric_reader.cc
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,101 @@ | ||
// Copyright The OpenTelemetry Authors | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
#ifndef ENABLE_METRICS_PREVIEW | ||
# include "opentelemetry/sdk/metrics/export/periodic_exporting_metric_reader.h" | ||
# include "opentelemetry/sdk/common/global_log_handler.h" | ||
# include "opentelemetry/sdk/metrics/metric_exporter.h" | ||
|
||
# include <chrono> | ||
# include <future> | ||
|
||
OPENTELEMETRY_BEGIN_NAMESPACE | ||
namespace sdk | ||
{ | ||
namespace metrics | ||
{ | ||
|
||
PeriodicExportingMetricReader::PeriodicExportingMetricReader( | ||
std::unique_ptr<MetricExporter> exporter, | ||
const PeriodicExportingMetricReaderOptions &option, | ||
AggregationTemporality aggregation_temporality) | ||
: MetricReader(aggregation_temporality), | ||
exporter_{std::move(exporter)}, | ||
export_interval_millis_{option.export_interval_millis}, | ||
export_timeout_millis_{option.export_timeout_millis} | ||
{ | ||
if (export_interval_millis_ <= export_timeout_millis_) | ||
{ | ||
OTEL_INTERNAL_LOG_WARN( | ||
"[Periodic Exporting Metric Reader] Invalid configuration: " | ||
"export_interval_millis_ should be less than export_timeout_millis_, using default values"); | ||
export_interval_millis_ = kExportIntervalMillis; | ||
export_timeout_millis_ = kExportTimeOutMillis; | ||
} | ||
} | ||
|
||
void PeriodicExportingMetricReader::OnInitialized() noexcept | ||
{ | ||
worker_thread_ = std::thread(&PeriodicExportingMetricReader::DoBackgroundWork, this); | ||
} | ||
|
||
void PeriodicExportingMetricReader::DoBackgroundWork() | ||
{ | ||
std::unique_lock<std::mutex> lk(cv_m_); | ||
do | ||
{ | ||
if (IsShutdown()) | ||
{ | ||
break; | ||
} | ||
std::atomic<bool> cancel_export_for_timeout{false}; | ||
auto start = std::chrono::steady_clock::now(); | ||
auto future_receive = std::async(std::launch::async, [this, &cancel_export_for_timeout] { | ||
Collect([this, &cancel_export_for_timeout](MetricData data) { | ||
if (cancel_export_for_timeout) | ||
{ | ||
OTEL_INTERNAL_LOG_ERROR( | ||
"[Periodic Exporting Metric Reader] Collect took longer configured time: " | ||
<< export_timeout_millis_.count() << " ms, and timed out"); | ||
return false; | ||
} | ||
this->exporter_->Export(data); | ||
return true; | ||
}); | ||
}); | ||
std::future_status status; | ||
do | ||
{ | ||
status = future_receive.wait_for(std::chrono::milliseconds(export_timeout_millis_)); | ||
if (status == std::future_status::timeout) | ||
{ | ||
cancel_export_for_timeout = true; | ||
break; | ||
} | ||
} while (status != std::future_status::ready); | ||
auto end = std::chrono::steady_clock::now(); | ||
auto export_time_ms = std::chrono::duration_cast<std::chrono::milliseconds>(end - start); | ||
auto remaining_wait_interval_ms = export_interval_millis_ - export_time_ms; | ||
cv_.wait_for(lk, remaining_wait_interval_ms); | ||
} while (true); | ||
} | ||
|
||
bool PeriodicExportingMetricReader::OnForceFlush(std::chrono::microseconds timeout) noexcept | ||
{ | ||
return exporter_->ForceFlush(timeout); | ||
} | ||
|
||
bool PeriodicExportingMetricReader::OnShutDown(std::chrono::microseconds timeout) noexcept | ||
{ | ||
if (worker_thread_.joinable()) | ||
{ | ||
cv_.notify_one(); | ||
worker_thread_.join(); | ||
} | ||
return exporter_->Shutdown(timeout); | ||
} | ||
|
||
} // namespace metrics | ||
} // namespace sdk | ||
OPENTELEMETRY_END_NAMESPACE | ||
#endif |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
// Copyright The OpenTelemetry Authors | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
#ifndef ENABLE_METRICS_PREVIEW | ||
|
||
# include "opentelemetry/sdk/metrics/export/periodic_exporting_metric_reader.h" | ||
# include "opentelemetry/sdk/metrics/export/metric_producer.h" | ||
# include "opentelemetry/sdk/metrics/metric_exporter.h" | ||
|
||
# include <gtest/gtest.h> | ||
|
||
using namespace opentelemetry; | ||
using namespace opentelemetry::sdk::instrumentationlibrary; | ||
using namespace opentelemetry::sdk::metrics; | ||
|
||
class MockPushMetricExporter : public MetricExporter | ||
{ | ||
public: | ||
opentelemetry::sdk::common::ExportResult Export(const MetricData &record) noexcept override | ||
{ | ||
records_.push_back(record); | ||
return opentelemetry::sdk::common::ExportResult::kSuccess; | ||
} | ||
|
||
bool ForceFlush( | ||
std::chrono::microseconds timeout = (std::chrono::microseconds::max)()) noexcept override | ||
{ | ||
return false; | ||
} | ||
|
||
bool Shutdown(std::chrono::microseconds timeout = std::chrono::microseconds(0)) noexcept override | ||
{ | ||
return true; | ||
} | ||
|
||
size_t GetDataCount() { return records_.size(); } | ||
|
||
private: | ||
std::vector<MetricData> records_; | ||
}; | ||
|
||
class MockMetricProducer : public MetricProducer | ||
{ | ||
public: | ||
MockMetricProducer(std::chrono::microseconds sleep_ms = std::chrono::microseconds::zero()) | ||
: sleep_ms_{sleep_ms}, data_sent_size_(0) | ||
{} | ||
|
||
bool Collect(nostd::function_ref<bool(MetricData)> callback) noexcept override | ||
{ | ||
std::this_thread::sleep_for(sleep_ms_); | ||
data_sent_size_++; | ||
MetricData data; | ||
callback(data); | ||
return true; | ||
} | ||
|
||
size_t GetDataCount() { return data_sent_size_; } | ||
|
||
private: | ||
std::chrono::microseconds sleep_ms_; | ||
size_t data_sent_size_; | ||
}; | ||
|
||
TEST(PeriodicExporingMetricReader, BasicTests) | ||
{ | ||
std::unique_ptr<MetricExporter> exporter(new MockPushMetricExporter()); | ||
PeriodicExportingMetricReaderOptions options; | ||
options.export_timeout_millis = std::chrono::milliseconds(200); | ||
options.export_interval_millis = std::chrono::milliseconds(500); | ||
auto exporter_ptr = exporter.get(); | ||
PeriodicExportingMetricReader reader(std::move(exporter), options); | ||
MockMetricProducer producer; | ||
reader.SetMetricProducer(&producer); | ||
std::this_thread::sleep_for(std::chrono::milliseconds(2000)); | ||
reader.Shutdown(); | ||
EXPECT_EQ(static_cast<MockPushMetricExporter *>(exporter_ptr)->GetDataCount(), | ||
static_cast<MockMetricProducer *>(&producer)->GetDataCount()); | ||
} | ||
|
||
#endif |
33d9c62
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.
Possible performance regression was detected for benchmark 'OpenTelemetry-cpp sdk Benchmark'.
Benchmark result of this commit is worse than the previous benchmark result exceeding threshold
2
.BM_BaselineBuffer/1
18955085.277557373
ns/iter470928.69069080986
ns/iter40.25
BM_LockFreeBuffer/1
3767801.7616271973
ns/iter343715.10234399297
ns/iter10.96
This comment was automatically generated by workflow using github-action-benchmark.