Skip to content

Commit

Permalink
Updated OpenTelemetry tests to use their own SpanExporter rather th…
Browse files Browse the repository at this point in the history
…an depend on the `InMemorySpanExporter`. (#4042)

* Removed version override for opentelemetry

* Disable deprecation warnings for open telemety tests to guard against future deprecations

* Added explanation for 4996 error; removed InMemoryExporter from documentation

* Checkpoint adding manual telemetry exporter

* Switch from using OpenTelemetry's InMemoryExporter (which is an internal-to-opentelemetry test hook) to our own SpanExporter

* removed unused variables because gcc doesn't like them

* Fixed doxygen comments

* Removed a bunch of unnecessary headers

* Update sdk/core/azure-core-tracing-opentelemetry/test/ut/test_exporter.hpp

Co-authored-by: Rick Winter <rick.winter@microsoft.com>

Co-authored-by: Rick Winter <rick.winter@microsoft.com>
  • Loading branch information
LarryOsterman and RickWinter authored Oct 24, 2022
1 parent 6c225cd commit 66fd7da
Show file tree
Hide file tree
Showing 2 changed files with 244 additions and 39 deletions.
Original file line number Diff line number Diff line change
@@ -1,36 +1,22 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// SPDX-License-Identifier: MIT

#define USE_MEMORY_EXPORTER 1
#include "azure/core/internal/tracing/service_tracing.hpp"
#include "azure/core/tracing/opentelemetry/opentelemetry.hpp"
#include <gtest/gtest.h>

#include <chrono>
#include <regex>

#include <azure/core/internal/http/pipeline.hpp>
#include <azure/core/internal/json/json.hpp>
#include <azure/core/internal/tracing/service_tracing.hpp>
#include <azure/core/test/test_base.hpp>
#include <azure/core/tracing/opentelemetry/opentelemetry.hpp>

#if defined(_MSC_VER)
// The OpenTelemetry headers generate a couple of warnings on MSVC in the OTel 1.2 package, suppress
// the warnings across the includes.
#pragma warning(push)
#pragma warning(disable : 4100)
#pragma warning(disable : 4244)
#pragma warning(disable : 6323) // Disable "Use of arithmetic operator on Boolean type" warning.
#endif
#include <opentelemetry/exporters/memory/in_memory_span_data.h>
#include <opentelemetry/exporters/memory/in_memory_span_exporter.h>
#include <opentelemetry/exporters/ostream/span_exporter.h>
#include "test_exporter.hpp" // Span Exporter used for OpenTelemetry tests.
#include <opentelemetry/sdk/common/global_log_handler.h>
#include <opentelemetry/sdk/trace/exporter.h>
#include <opentelemetry/sdk/trace/processor.h>
#include <opentelemetry/sdk/trace/simple_processor.h>
#include <opentelemetry/sdk/trace/tracer_provider.h>
#include <opentelemetry/trace/propagation/http_trace_context.h>
#if defined(_MSC_VER)
#pragma warning(pop)
#endif
#include <chrono>
#include <gtest/gtest.h>
#include <regex>

using namespace Azure::Core::Http::Policies;
using namespace Azure::Core::Http::Policies::_internal;
Expand Down Expand Up @@ -95,18 +81,13 @@ class CustomLogHandler : public opentelemetry::sdk::common::internal_log::LogHan
class OpenTelemetryServiceTests : public Azure::Core::Test::TestBase {
private:
protected:
std::shared_ptr<opentelemetry::exporter::memory::InMemorySpanData> m_spanData;
std::shared_ptr<TestExporter::TestData> m_spanData;

opentelemetry::nostd::shared_ptr<opentelemetry::trace::TracerProvider>
CreateOpenTelemetryProvider()
{
#if USE_MEMORY_EXPORTER
auto exporter = std::make_unique<opentelemetry::exporter::memory::InMemorySpanExporter>();
m_spanData = exporter->GetData();
#else
// logging exporter
auto exporter = std::make_unique<opentelemetry::exporter::trace::OStreamSpanExporter>();
#endif
auto exporter = std::make_unique<TestExporter>();
m_spanData = exporter->GetTestData();

// simple processor
auto simple_processor = std::unique_ptr<opentelemetry::sdk::trace::SpanProcessor>(
Expand Down Expand Up @@ -143,7 +124,7 @@ class OpenTelemetryServiceTests : public Azure::Core::Test::TestBase {
}

bool VerifySpan(
std::unique_ptr<opentelemetry::sdk::trace::SpanData> const& span,
std::unique_ptr<RecordedSpan> const& span,
std::string const& expectedSpanContentsJson)
{
Azure::Core::Json::_internal::json expectedSpanContents(
Expand Down Expand Up @@ -199,7 +180,7 @@ class OpenTelemetryServiceTests : public Azure::Core::Test::TestBase {

EXPECT_EQ(expectedAttributes.size(), attributes.size());

for (const auto& foundAttribute : attributes)
for (auto const& foundAttribute : attributes)
{
EXPECT_TRUE(expectedAttributes.contains(foundAttribute.first));
switch (foundAttribute.second.index())
Expand All @@ -219,7 +200,7 @@ class OpenTelemetryServiceTests : public Azure::Core::Test::TestBase {
std::regex expectedRegex(expectedVal);
GTEST_LOG_(INFO) << "expected Regex: " << expectedVal << std::endl;
GTEST_LOG_(INFO) << "actual val: " << actualVal << std::endl;
EXPECT_TRUE(std::regex_match(actualVal, expectedRegex));
EXPECT_TRUE(std::regex_match(std::string(actualVal), expectedRegex));
break;
}
case opentelemetry::common::kTypeDouble: {
Expand Down Expand Up @@ -327,7 +308,7 @@ TEST_F(OpenTelemetryServiceTests, CreateWithExplicitProvider)
EXPECT_FALSE(contextAndSpan.Context.IsCancelled());
}
// Now let's verify what was logged via OpenTelemetry.
auto spans = m_spanData->GetSpans();
auto const& spans = m_spanData->ExtractSpans();
EXPECT_EQ(1ul, spans.size());

VerifySpan(spans[0], R"(
Expand Down Expand Up @@ -367,7 +348,7 @@ TEST_F(OpenTelemetryServiceTests, CreateWithImplicitProvider)
}

// Now let's verify what was logged via OpenTelemetry.
auto spans = m_spanData->GetSpans();
auto const& spans = m_spanData->ExtractSpans();
EXPECT_EQ(1ul, spans.size());

VerifySpan(spans[0], R"(
Expand Down Expand Up @@ -415,7 +396,7 @@ TEST_F(OpenTelemetryServiceTests, CreateSpanWithOptions)
}

// Now let's verify what was logged via OpenTelemetry.
auto spans = m_spanData->GetSpans();
auto const& spans = m_spanData->ExtractSpans();
EXPECT_EQ(1ul, spans.size());

VerifySpan(spans[0], R"(
Expand Down Expand Up @@ -476,7 +457,7 @@ TEST_F(OpenTelemetryServiceTests, NestSpans)
}
}
// Now let's verify what was logged via OpenTelemetry.
auto spans = m_spanData->GetSpans();
auto const& spans = m_spanData->ExtractSpans();
EXPECT_EQ(2ul, spans.size());

// Because Nested API goes out of scope before My API, it will be logged first in the
Expand Down Expand Up @@ -676,7 +657,7 @@ TEST_F(OpenTelemetryServiceTests, ServiceApiImplementation)
myServiceClient.GetConfigurationString("Fred");
}
// Now let's verify what was logged via OpenTelemetry.
auto spans = m_spanData->GetSpans();
auto const& spans = m_spanData->ExtractSpans();
EXPECT_EQ(2ul, spans.size());

VerifySpan(spans[0], R"(
Expand Down Expand Up @@ -722,7 +703,7 @@ TEST_F(OpenTelemetryServiceTests, ServiceApiImplementation)
myServiceClient.GetConfigurationString("George");
}
// Now let's verify what was logged via OpenTelemetry.
auto spans = m_spanData->GetSpans();
auto const& spans = m_spanData->ExtractSpans();
EXPECT_EQ(0ul, spans.size());
}
}
224 changes: 224 additions & 0 deletions sdk/core/azure-core-tracing-opentelemetry/test/ut/test_exporter.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,224 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// SPDX-License-Identifier: MIT

#pragma once

#include <memory>
#include <opentelemetry/sdk/trace/exporter.h>

class RecordedSpan : public opentelemetry::sdk::trace::Recordable {
struct Event
{
std::string Name;
std::chrono::system_clock::time_point Timestamp;
opentelemetry::sdk::common::AttributeMap Attributes;
};
opentelemetry::trace::SpanId m_parentSpan;
opentelemetry::trace::SpanId m_spanId;
opentelemetry::sdk::common::AttributeMap m_attributes;
std::vector<Event> m_events;
opentelemetry::trace::StatusCode m_statusCode{};
std::string m_statusDescription;
std::string m_name;
opentelemetry::trace::SpanKind m_spanKind{};
std::chrono::system_clock::time_point m_startTime;
std::chrono::nanoseconds m_duration{};
std::unique_ptr<opentelemetry::sdk::instrumentationscope::InstrumentationScope> m_scope;
std::unique_ptr<opentelemetry::sdk::resource::Resource> m_resource;

public:
~RecordedSpan() = default;

/**
* Set the span context and parent span id
* @param span_context the span context to set
* @param parent_span_id the parent span id to set
*/
void SetIdentity(
const opentelemetry::trace::SpanContext& span_context,
opentelemetry::trace::SpanId parent_span_id) noexcept override
{
m_parentSpan = parent_span_id;
m_spanId = span_context.span_id();
};

/**
* Set an attribute of a span.
* @param key the name of the attribute
* @param value the attribute value
*/
void SetAttribute(
opentelemetry::nostd::string_view key,
const opentelemetry::common::AttributeValue& value) noexcept override
{
m_attributes.SetAttribute(key, value);
};

/**
* Add an event to a span.
* @param name the name of the event
* @param timestamp the timestamp of the event
* @param attributes the attributes associated with the event
*/
void AddEvent(
opentelemetry::nostd::string_view name,
opentelemetry::common::SystemTimestamp timestamp,
const opentelemetry::common::KeyValueIterable& attributes) noexcept override
{
Event event;
event.Name = std::string(name);
event.Timestamp = timestamp;

attributes.ForEachKeyValue(
[&event](
opentelemetry::nostd::string_view name, opentelemetry::common::AttributeValue value) {
event.Attributes.SetAttribute(name, value);
return true;
});
m_events.push_back(event);
};

/**
* Add a link to a span.
*/
void AddLink(
const opentelemetry::trace::SpanContext&,
const opentelemetry::common::KeyValueIterable&) noexcept override{
// TODO, when we use this, we need to test this.
// NO-OP since this exporter silences link data.
};

/**
* Set the status of the span.
* @param code the status code
* @param description a description of the status
*/
void SetStatus(
opentelemetry::trace::StatusCode code,
opentelemetry::nostd::string_view description) noexcept override
{
m_statusCode = code;
m_statusDescription = std::string(description);
};

/**
* Set the name of the span.
* @param name the name to set
*/
void SetName(opentelemetry::nostd::string_view name) noexcept override
{
m_name = std::string(name);
};

/**
* Set the spankind of the span.
* @param span_kind the spankind to set
*/
void SetSpanKind(opentelemetry::trace::SpanKind span_kind) noexcept override
{
m_spanKind = span_kind;
};

/**
* Set Resource of the span
* @param resource the resource to set
*/
void SetResource(const opentelemetry::sdk::resource::Resource& resource) noexcept override
{
m_resource = std::make_unique<opentelemetry::sdk::resource::Resource>(resource);
};

/**
* Set the start time of the span.
* @param start_time the start time to set
*/
void SetStartTime(opentelemetry::common::SystemTimestamp start_time) noexcept override
{
m_startTime = start_time;
};

/**
* Set the duration of the span.
* @param duration the duration to set
*/
void SetDuration(std::chrono::nanoseconds duration) noexcept override { m_duration = duration; }

/**
* Set the instrumentation scope of the span.
* @param instrumentation_scope the instrumentation scope to set
*/
void SetInstrumentationScope(const opentelemetry::sdk::instrumentationscope::InstrumentationScope&
instrumentation_scope) noexcept override
{
m_scope = std::make_unique<opentelemetry::sdk::instrumentationscope::InstrumentationScope>(
instrumentation_scope);
};

std::string GetName() { return m_name; }
opentelemetry::trace::StatusCode GetStatus() { return m_statusCode; }
opentelemetry::trace::SpanId GetParentSpanId() { return m_parentSpan; }
opentelemetry::trace::SpanKind GetSpanKind() { return m_spanKind; }
opentelemetry::trace::SpanId GetSpanId() { return m_spanId; }
opentelemetry::sdk::common::AttributeMap const& GetAttributes() { return m_attributes; }
opentelemetry::sdk::instrumentationscope::InstrumentationScope& GetInstrumentationScope()
{
return *m_scope;
}
};

class TestExporter final : public opentelemetry::sdk::trace::SpanExporter {

public:
class TestData {
std::vector<std::unique_ptr<RecordedSpan>> m_spans;

public:
// Returns a copy of the recorded spans and clears the set of recorded spans.
std::vector<std::unique_ptr<RecordedSpan>> const ExtractSpans() { return std::move(m_spans); }
void AddSpan(std::unique_ptr<RecordedSpan>&& span) { m_spans.push_back(std::move(span)); }
};
std::shared_ptr<TestData> const& GetTestData() { return m_testData; }

TestExporter() : m_testData{std::make_shared<TestData>()} {}
virtual ~TestExporter() = default;

/**
* Create a span recordable. This object will be used to record span data and
* will subsequently be passed to SpanExporter::Export. Vendors can implement
* custom recordables or use the default SpanData recordable provided by the
* SDK.
* @return a newly initialized Recordable object
*
* Note: This method must be callable from multiple threads.
*/
std::unique_ptr<opentelemetry::sdk::trace::Recordable> MakeRecordable() noexcept override
{
return std::unique_ptr<opentelemetry::sdk::trace::Recordable>(new (std::nothrow) RecordedSpan);
}

/**
* Exports a batch of span recordables. This method must not be called
* concurrently for the same exporter instance.
* @param spans a span of unique pointers to span recordables
*/
opentelemetry::sdk::common::ExportResult Export(
const opentelemetry::nostd::span<std::unique_ptr<opentelemetry::sdk::trace::Recordable>>&
spans) noexcept override
{
for (auto& recordable : spans)
{
auto span = std::unique_ptr<RecordedSpan>(static_cast<RecordedSpan*>(recordable.release()));
m_testData->AddSpan(std::move(span));
}
return opentelemetry::sdk::common::ExportResult::kSuccess;
}

/**
* Shut down the exporter.
* @return return the status of the operation.
*/
bool Shutdown(std::chrono::microseconds) noexcept override { return true; }

private:
std::shared_ptr<TestData> m_testData;
};

0 comments on commit 66fd7da

Please sign in to comment.