如何在C++ OpenTelemetry的析构函数中强制触发指标观测回调?
如何在C++ OpenTelemetry的析构函数中强制触发指标观测回调?
我完全懂你现在的头疼之处——想用OpenTelemetry在程序 shutdown(也就是MyPublisher的析构函数里)触发一次指标观测,但SDK里到处都是std::move,把对象所有权转出去之后,自己手里啥都没剩下,调用ForceFlush()或者Shutdown()都找不到正确的对象,对吧?我来给你拆解问题,再给你改好能用的代码。
先说说你现有代码的核心问题
你在init()方法里创建了reader、context、provider这些关键对象,但全部用std::move把所有权转出去之后,没有在MyPublisher类里保留任何强引用。最后你只设置了全局的metrics_api::Provider,但它本质上是个弱引用,析构时可能已经被销毁了,自然调用不了任何方法触发观测。
另外,你原来的main函数用了exit(0),这会直接终止程序,根本不会调用MyPublisher的析构函数,这也是一个隐藏的坑!
解决思路:保留关键对象的强引用,用Shutdown()替代ForceFlush()
OpenTelemetry的SDK里,Shutdown()方法比ForceFlush()更适合 shutdown 场景:它会自动触发最后一次指标观测(也就是你的PublishStatus回调)、聚合、导出,还会清理所有关联资源。我们只需要在MyPublisher里保留对MeterProvider的强引用,析构时调用它的Shutdown()就行。
具体修改步骤+完整代码示例
1. 给MyPublisher类添加成员变量,保留强引用
在类的私有部分添加:
private: // 保留MeterProvider的强引用,确保它和MyPublisher同生命周期 std::shared_ptr<metrics_sdk::MeterProvider> meter_provider_;
2. 修改init()方法,不要丢了对象所有权
修改init()里创建provider的逻辑,把它存到成员变量里:
void init() { otlp_exporter::OtlpGrpcMetricExporterOptions exporterOptions; auto exporter = metrics_exporter::OStreamMetricExporterFactory::Create(); std::cout << "made exporter" << std::endl; metrics_sdk::PeriodicExportingMetricReaderOptions readerOptions; readerOptions.export_interval_millis = std::chrono::milliseconds(1000); readerOptions.export_timeout_millis = std::chrono::milliseconds(500); auto reader = metrics_sdk::PeriodicExportingMetricReaderFactory::Create(std::move(exporter), readerOptions); auto context = opentelemetry::sdk::metrics::MeterContextFactory::Create(); context->AddMetricReader(std::move(reader)); // 把provider存到成员变量里,不要直接move就丢了 auto provider = metrics_sdk::MeterProviderFactory::Create(std::move(context)); meter_provider_ = std::move(provider); std::shared_ptr<metrics_api::MeterProvider> api_provider(meter_provider_); metrics_api::Provider::SetMeterProvider(api_provider); }
3. 在析构函数里调用Shutdown()触发最后一次观测
修改析构函数:
~MyPublisher() { if (meter_provider_) { // 触发最后一次指标观测+导出,并清理资源 meter_provider_->Shutdown(); std::cout << "Final metrics exported during shutdown" << std::endl; } };
4. 修复main函数的退出方式
把exit(0)改成return 0,确保MyPublisher的析构函数能正常执行:
int main(void) { MyPublisher myPublisher("Bob"); myPublisher.addStatus(); myPublisher.setStatus(6); // 用return让析构函数正常执行 return 0; }
完整修改后的代码
#include <opentelemetry/exporters/ostream/metric_exporter_factory.h> #include <opentelemetry/sdk/metrics/meter_context_factory.h> #include <opentelemetry/exporters/otlp/otlp_grpc_metric_exporter_factory.h> #include <opentelemetry/metrics/provider.h> #include <opentelemetry/sdk/metrics/export/periodic_exporting_metric_reader.h> #include <opentelemetry/sdk/metrics/export/periodic_exporting_metric_reader_factory.h> #include <opentelemetry/sdk/metrics/meter_provider.h> #include <opentelemetry/sdk/metrics/meter_provider_factory.h> #include <opentelemetry/common/key_value_iterable_view.h> #include <iostream> #include <map> namespace metrics_api = opentelemetry::metrics; namespace metrics_exporter = opentelemetry::exporter::metrics; namespace metrics_sdk = opentelemetry::sdk::metrics; namespace otel_common = opentelemetry::common; namespace otlp_exporter = opentelemetry::exporter::otlp; using ObservableInstrumentSPtr = opentelemetry::v1::nostd::shared_ptr<metrics_api::ObservableInstrument>; void PublishStatus(opentelemetry::metrics::ObserverResult result, void* context); class MyPublisher { public: MyPublisher(std::string name) { myName = name; init(); }; ~MyPublisher() { if (meter_provider_) { meter_provider_->Shutdown(); std::cout << "Final metrics exported during shutdown" << std::endl; } }; long GetStatus() { return status; }; std::string GetName() { return myName; }; std::string myName; long status = 1337; void init() { otlp_exporter::OtlpGrpcMetricExporterOptions exporterOptions; auto exporter = metrics_exporter::OStreamMetricExporterFactory::Create(); std::cout << "made exporter" << std::endl; metrics_sdk::PeriodicExportingMetricReaderOptions readerOptions; readerOptions.export_interval_millis = std::chrono::milliseconds(1000); readerOptions.export_timeout_millis = std::chrono::milliseconds(500); auto reader = metrics_sdk::PeriodicExportingMetricReaderFactory::Create(std::move(exporter), readerOptions); auto context = opentelemetry::sdk::metrics::MeterContextFactory::Create(); context->AddMetricReader(std::move(reader)); // 保留provider的强引用到成员变量 auto provider = metrics_sdk::MeterProviderFactory::Create(std::move(context)); meter_provider_ = std::move(provider); std::shared_ptr<metrics_api::MeterProvider> api_provider(meter_provider_); metrics_api::Provider::SetMeterProvider(api_provider); } void addStatus() { auto provider = metrics_api::Provider::GetMeterProvider(); if (!provider) { std::cout << "Failed to retrieve provider" << std::endl; exit(-1); } opentelemetry::nostd::shared_ptr<metrics_api::Meter> meter = provider->GetMeter("cat.status"); if (!meter) { std::cout << "Failed to make meter" << std::endl; exit(-1); } ObservableInstrumentSPtr observable = ObservableInstrumentSPtr(meter->CreateInt64ObservableGauge(myName, "My status", "1")); if (!observable) { std::cout << "Failed to get api_provider" << std::endl; exit(-1); } observable->AddCallback(PublishStatus, this); std::cout << "added status" << std::endl; } void setStatus(long val) { status = val; }; private: // 新增:保留MeterProvider的强引用 std::shared_ptr<metrics_sdk::MeterProvider> meter_provider_; std::string myName; long status = 1337; }; using ObserverResultSPtr = opentelemetry::nostd::shared_ptr<opentelemetry::metrics::ObserverResultT<long>>; void PublishStatus(opentelemetry::metrics::ObserverResult result, void* context) { std::cout << "publishing" << std::endl; auto myPublisher = static_cast<MyPublisher*>(context); std::map<std::string, std::string> attributes; attributes["name"] = myPublisher->GetName(); long val = myPublisher->GetStatus(); std::cout << "got val: " << val << std::endl; ObserverResultSPtr res = opentelemetry::nostd::get<ObserverResultSPtr>(result); std::cout << "got res" << std::endl; res->Observe(val, opentelemetry::common::KeyValueIterableView<decltype(attributes)>{attributes}); } int main(void) { MyPublisher myPublisher("Bob"); myPublisher.addStatus(); myPublisher.setStatus(6); return 0; }
几个关键注意点
return 0vsexit(0):exit(0)会直接终止程序,跳过所有对象的析构函数,必须改成return 0才能让MyPublisher的析构函数正常执行,触发Shutdown()。- 为什么
Shutdown()有效:MeterProvider::Shutdown()会遍历所有关联的MetricReader,调用它们的Shutdown(),而PeriodicExportingMetricReader::Shutdown()会主动触发一次完整的指标采集流程——包括调用你的PublishStatus回调函数,正好满足你的需求。 - 强引用的重要性:如果不保留
meter_provider_这个强引用,MeterProvider可能会被提前销毁,析构时调用Shutdown()就会失效。
内容来源于stack exchange
相关产品推荐
相关产品推荐

