You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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;
}

几个关键注意点

  1. return 0 vs exit(0):exit(0)会直接终止程序,跳过所有对象的析构函数,必须改成return 0才能让MyPublisher的析构函数正常执行,触发Shutdown()。
  2. 为什么Shutdown()有效:MeterProvider::Shutdown()会遍历所有关联的MetricReader,调用它们的Shutdown(),而PeriodicExportingMetricReader::Shutdown()会主动触发一次完整的指标采集流程——包括调用你的PublishStatus回调函数,正好满足你的需求。
  3. 强引用的重要性:如果不保留meter_provider_这个强引用,MeterProvider可能会被提前销毁,析构时调用Shutdown()就会失效。

内容来源于stack exchange

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.07 08:32:59