diff --git a/cpp/src/arrow/telemetry/logging.cc b/cpp/src/arrow/telemetry/logging.cc index 79114a187939b..d264f8ccd67b7 100644 --- a/cpp/src/arrow/telemetry/logging.cc +++ b/cpp/src/arrow/telemetry/logging.cc @@ -38,9 +38,6 @@ #include #include #include -#include -#include -#include #include #include @@ -57,7 +54,7 @@ namespace telemetry { namespace { -namespace SemanticConventions = otel::sdk::resource::SemanticConventions; +using internal::LogExporterOptions; constexpr const char kLoggingBackendEnvVar[] = "ARROW_LOGGING_BACKEND"; @@ -150,12 +147,12 @@ std::unique_ptr MakeExporter( } std::unique_ptr MakeExporterFromEnv( - const LoggerProviderOptions& options) { + const LogExporterOptions& exporter_options) { auto maybe_env_var = arrow::internal::GetEnvVar(kLoggingBackendEnvVar); if (maybe_env_var.ok()) { auto env_var = maybe_env_var.ValueOrDie(); auto* default_ostream = - options.default_export_stream ? options.default_export_stream : &std::cerr; + exporter_options.default_stream ? exporter_options.default_stream : &std::cerr; if (env_var == "ostream") { // TODO: Currently disabled as the log records returned by otel's ostream exporter // don't maintain copies of their attributes, leading to lifetime issues. If/when @@ -191,36 +188,13 @@ std::unique_ptr MakeLogRecordProcessor( options); } -otel::sdk::resource::Resource MakeResource(const ServiceAttributes& service_attributes) { - // TODO: We could also include process info... - // - SemanticConventions::kProcessPid - // - SemanticConventions::kProcessExecutableName - // - SemanticConventions::kProcessExecutablePath - // - SemanticConventions::kProcessOwner - // - SemanticConventions::kProcessCommandArgs - otel::sdk::resource::ResourceAttributes resource_attributes{}; - - auto set_attr = [&](const char* key, const std::optional& val) { - if (val.has_value()) resource_attributes.SetAttribute(key, val.value()); - }; - set_attr(SemanticConventions::kServiceName, service_attributes.name); - set_attr(SemanticConventions::kServiceNamespace, service_attributes.name_space); - set_attr(SemanticConventions::kServiceInstanceId, service_attributes.instance_id); - set_attr(SemanticConventions::kServiceVersion, service_attributes.version); - - auto resource = otel::sdk::resource::Resource::Create(resource_attributes); - auto env_resource = otel::sdk::resource::OTELResourceDetector().Detect(); - return resource.Merge(env_resource); -} - otel_shared_ptr MakeLoggerProvider( - const LoggerProviderOptions& options) { - auto exporter = MakeExporterFromEnv(options); + const LogExporterOptions& exporter_options) { + auto exporter = MakeExporterFromEnv(exporter_options); if (exporter) { auto processor = MakeLogRecordProcessor(std::move(exporter)); - auto resource = MakeResource(options.service_attributes); return otel_shared_ptr( - new otel::sdk::logs::LoggerProvider(std::move(processor), resource)); + new otel::sdk::logs::LoggerProvider(std::move(processor))); } return otel_shared_ptr( new otel::logs::NoopLoggerProvider{}); @@ -274,7 +248,7 @@ class OtelLogger : public Logger { } bool Flush(std::chrono::microseconds timeout) override { - return GlobalLoggerProvider::Flush(timeout); + return OtelLoggerProvider::Flush(timeout); } bool is_enabled() const override { return true; } @@ -295,21 +269,7 @@ class OtelLogger : public Logger { } // namespace -Status GlobalLoggerProvider::Initialize(const LoggerProviderOptions& options) { - otel::logs::Provider::SetLoggerProvider(MakeLoggerProvider(options)); - return Status::OK(); -} - -bool GlobalLoggerProvider::ShutDown() { - auto provider = otel::logs::Provider::GetLoggerProvider(); - if (auto sdk_provider = - dynamic_cast(provider.get())) { - return sdk_provider->Shutdown(); - } - return false; -} - -bool GlobalLoggerProvider::Flush(std::chrono::microseconds timeout) { +bool OtelLoggerProvider::Flush(std::chrono::microseconds timeout) { auto provider = otel::logs::Provider::GetLoggerProvider(); if (auto sdk_provider = dynamic_cast(provider.get())) { @@ -318,7 +278,7 @@ bool GlobalLoggerProvider::Flush(std::chrono::microseconds timeout) { return false; } -Result> GlobalLoggerProvider::MakeLogger( +Result> OtelLoggerProvider::MakeLogger( std::string_view name, const LoggingOptions& options, const AttributeHolder& attributes) { auto ot_logger = otel::logs::Provider::GetLoggerProvider()->GetLogger( @@ -327,10 +287,28 @@ Result> GlobalLoggerProvider::MakeLogger( return std::make_shared(options, std::move(ot_logger)); } -Result> GlobalLoggerProvider::MakeLogger( +Result> OtelLoggerProvider::MakeLogger( std::string_view name, const LoggingOptions& options) { return MakeLogger(name, options, EmptyAttributeHolder{}); } +namespace internal { + +Status InitializeOtelLoggerProvider(const LogExporterOptions& exporter_options) { + otel::logs::Provider::SetLoggerProvider(MakeLoggerProvider(exporter_options)); + return Status::OK(); +} + +bool ShutdownOtelLoggerProvider() { + auto provider = otel::logs::Provider::GetLoggerProvider(); + if (auto sdk_provider = + dynamic_cast(provider.get())) { + return sdk_provider->Shutdown(); + } + return false; +} + +} // namespace internal + } // namespace telemetry } // namespace arrow diff --git a/cpp/src/arrow/telemetry/logging.h b/cpp/src/arrow/telemetry/logging.h index 1a8e40dfaf726..3c16dc939fa7e 100644 --- a/cpp/src/arrow/telemetry/logging.h +++ b/cpp/src/arrow/telemetry/logging.h @@ -20,13 +20,11 @@ #include #include #include -#include #include #include #include "arrow/result.h" #include "arrow/status.h" -#include "arrow/util/config.h" #include "arrow/util/logging.h" #include "arrow/util/logging_v2.h" #include "arrow/util/macros.h" @@ -39,41 +37,9 @@ class AttributeHolder; using LogLevel = util::ArrowLogLevel; -/// \brief Attributes to be set in an OpenTelemetry resource -/// -/// The OTEL_RESOURCE_ATTRIBUTES envvar can be used to set additional attributes -struct ServiceAttributes { - static constexpr char kDefaultName[] = "arrow.unknown_service"; - static constexpr char kDefaultNamespace[] = "org.apache"; - static constexpr char kDefaultInstanceId[] = "arrow.unknown_id"; - static constexpr char kDefaultVersion[] = ARROW_VERSION_STRING; - - std::optional name = kDefaultName; - std::optional name_space = kDefaultNamespace; - std::optional instance_id = kDefaultInstanceId; - std::optional version = kDefaultVersion; - - static ServiceAttributes Defaults() { return ServiceAttributes{}; } -}; - -struct LoggerProviderOptions { - /// \brief Attributes to set for the LoggerProvider's Resource - ServiceAttributes service_attributes = ServiceAttributes::Defaults(); - - /// \brief Default stream to use for the ostream/arrow_otlp_ostream log record exporters - /// - /// If null, stderr will be used - std::ostream* default_export_stream = NULLPTR; - - static LoggerProviderOptions Defaults() { return LoggerProviderOptions{}; } -}; - -constexpr LogLevel kDefaultSeverityThreshold = LogLevel::ARROW_WARNING; -constexpr LogLevel kDefaultSeverity = LogLevel::ARROW_INFO; - struct LoggingOptions { /// \brief Minimum severity required to emit an OpenTelemetry log record - LogLevel severity_threshold = kDefaultSeverityThreshold; + LogLevel severity_threshold = LogLevel::ARROW_INFO; /// \brief Minimum severity required to immediately attempt to flush pending log records LogLevel flush_severity = LogLevel::ARROW_ERROR; @@ -96,7 +62,7 @@ struct EventId { }; struct LogDescriptor { - LogLevel severity = kDefaultSeverity; + LogLevel severity = LogLevel::ARROW_INFO; std::chrono::system_clock::time_point timestamp = std::chrono::system_clock::now(); @@ -159,13 +125,15 @@ class ARROW_EXPORT Logger : public util::Logger { virtual std::string_view name() const = 0; }; -class ARROW_EXPORT GlobalLoggerProvider { +/// \brief A wrapper interface for `opentelemetry::logs::Provider` +/// \details Application authors will typically want to set the global OpenTelemetry +/// logger provider themselves after configuring an exporter, processor, resource etc. +/// This API will then defer to the provider returned by +/// `opentelemetry::logs::Provider::GetLoggerProvider` +class ARROW_EXPORT OtelLoggerProvider { public: - static Status Initialize( - const LoggerProviderOptions& = LoggerProviderOptions::Defaults()); - - static bool ShutDown(); - + /// \brief Attempt to flush the log record processor associated with the provider + /// \return `true` if the flush occured static bool Flush(std::chrono::microseconds timeout = std::chrono::microseconds::max()); static Result> MakeLogger( @@ -175,5 +143,28 @@ class ARROW_EXPORT GlobalLoggerProvider { const AttributeHolder& attributes); }; +namespace internal { + +// These utilities are primarily intended for Arrow developers + +struct LogExporterOptions { + /// \brief Default stream to use for the ostream/arrow_otlp_ostream log record exporters + /// \details If null, stderr will be used + std::ostream* default_stream = NULLPTR; + + static LogExporterOptions Defaults() { return LogExporterOptions{}; } +}; + +/// \brief Initialize the global OpenTelemetry logger provider with a default exporter +/// (based on the ARROW_LOGGING_BACKEND envvar) and batch processor +ARROW_EXPORT Status InitializeOtelLoggerProvider( + const LogExporterOptions& exporter_options = LogExporterOptions::Defaults()); + +/// \brief Attempt to shut down the global OpenTelemetry logger provider +/// \return `true` if shutdown was successful +ARROW_EXPORT bool ShutdownOtelLoggerProvider(); + +} // namespace internal + } // namespace telemetry } // namespace arrow diff --git a/cpp/src/arrow/telemetry/telemetry_test.cc b/cpp/src/arrow/telemetry/telemetry_test.cc index 96546663ab5c8..407b8dd561bfe 100644 --- a/cpp/src/arrow/telemetry/telemetry_test.cc +++ b/cpp/src/arrow/telemetry/telemetry_test.cc @@ -49,20 +49,19 @@ class OtelEnvironment : public ::testing::Environment { otel::nostd::shared_ptr( new otel::trace::propagation::HttpTraceContext())); - auto provider_options = LoggerProviderOptions::Defaults(); - ASSERT_OK(GlobalLoggerProvider::Initialize(provider_options)); + ASSERT_OK(internal::InitializeOtelLoggerProvider()); auto logging_options = LoggingOptions::Defaults(); logging_options.severity_threshold = LogLevel::ARROW_TRACE; logging_options.flush_severity = LogLevel::ARROW_TRACE; ASSERT_OK_AND_ASSIGN( auto logger, - GlobalLoggerProvider::MakeLogger( + OtelLoggerProvider::MakeLogger( kLoggerName, logging_options, AttributeList{Attribute{"fooInt", 42}, Attribute{"barStr", "fourty two"}})); ASSERT_OK(util::LoggerRegistry::RegisterLogger(logger->name(), logger)); } - void TearDown() override { EXPECT_TRUE(GlobalLoggerProvider::ShutDown()); } + void TearDown() override { EXPECT_TRUE(internal::ShutdownOtelLoggerProvider()); } }; static ::testing::Environment* kOtelEnvironment = @@ -79,7 +78,7 @@ void Log(Args&&... args) { class TestLogging : public ::testing::Test { public: void SetUp() override { - tracer_ = internal::tracing::GetTracer(); + tracer_ = arrow::internal::tracing::GetTracer(); span_ = tracer_->StartSpan("test-logging"); }