Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add OTLP exporter #152

Merged
merged 13 commits into from
Jul 8, 2020
25 changes: 25 additions & 0 deletions WORKSPACE
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,31 @@ workspace(name = "io_opentelemetry_cpp")

load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")

# Load gRPC dependency
# Note that this dependency needs to be loaded first due to
# https://github.com/bazelbuild/bazel/issues/6664
http_archive(
name = "com_github_grpc_grpc",
sha256 = "d6af0859d3ae4693b1955e972aa2e590d6f4d44baaa82651467c6beea453e30e",
strip_prefix = "grpc-1.26.0-pre1",
urls = [
"https://github.com/grpc/grpc/archive/v1.26.0-pre1.tar.gz",
],
)

load("@com_github_grpc_grpc//bazel:grpc_deps.bzl", "grpc_deps")

grpc_deps()

# Load extra gRPC dependencies due to https://github.com/grpc/grpc/issues/20511
load("@com_github_grpc_grpc//bazel:grpc_extra_deps.bzl", "grpc_extra_deps")

grpc_extra_deps()

load("@upb//bazel:repository_defs.bzl", "bazel_version_repository")

bazel_version_repository(name = "upb_bazel_version")

# Uses older protobuf version because of
# https://github.com/protocolbuffers/protobuf/issues/7179
http_archive(
Expand Down
24 changes: 24 additions & 0 deletions bazel/opentelemetry_proto.BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
package(default_visibility = ["//visibility:public"])

load("@rules_proto//proto:defs.bzl", "proto_library")
load("@com_github_grpc_grpc//bazel:cc_grpc_library.bzl", "cc_grpc_library")

proto_library(
name = "common_proto",
Expand Down Expand Up @@ -58,3 +59,26 @@ cc_proto_library(
name = "trace_proto_cc",
deps = [":trace_proto"],
)

proto_library(
name = "trace_service_proto",
srcs = [
"opentelemetry/proto/collector/trace/v1/trace_service.proto",
],
deps = [
":trace_proto",
],
)

cc_proto_library(
name = "trace_service_proto_cc",
deps = [":trace_service_proto"],
)

cc_grpc_library(
name = "trace_service_grpc_cc",
srcs = [":trace_service_proto"],
grpc_only = True,
deps = [":trace_service_proto_cc"],
generate_mocks = True,
)
29 changes: 29 additions & 0 deletions exporters/otlp/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,30 @@ cc_library(
include_prefix = "exporters/otlp",
deps = [
"//sdk/src/trace",
"//sdk/src/common:random",
"@com_github_opentelemetry_proto//:trace_proto_cc",
],
)

cc_library(
name = "otlp_exporter",
srcs = [
'otlp_exporter.h',
nadiaciobanu marked this conversation as resolved.
Show resolved Hide resolved
'otlp_exporter.cc',
],
deps = [
":recordable",
"//api",
"//sdk/src/trace",
"@com_github_opentelemetry_proto//:trace_proto_cc",
"@com_github_opentelemetry_proto//:trace_service_proto_cc",

# For gRPC
"@com_github_opentelemetry_proto//:trace_service_grpc_cc",
"@com_github_grpc_grpc//:grpc++",
],
)

cc_test(
name = "recordable_test",
srcs = ["recordable_test.cc"],
Expand All @@ -37,3 +57,12 @@ cc_test(
"@com_google_googletest//:gtest_main",
],
)

cc_test(
name = "otlp_exporter_test",
srcs = ["otlp_exporter_test.cc"],
deps = [
":otlp_exporter",
"@com_google_googletest//:gtest_main",
],
)
71 changes: 71 additions & 0 deletions exporters/otlp/otlp_exporter.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
#include "otlp_exporter.h"

OPENTELEMETRY_BEGIN_NAMESPACE
namespace exporter
{
namespace otlp
{

const std::string KCollectorAddress = "localhost:55678";
nadiaciobanu marked this conversation as resolved.
Show resolved Hide resolved

// ----------------------------- Helper functions ------------------------------

// Add span protobufs contained in recordables to request
void PopulateRequest(const nostd::span<std::unique_ptr<sdk::trace::Recordable>> &spans,
proto::collector::trace::v1::ExportTraceServiceRequest *request)
{
auto resource_span = request->add_resource_spans();
auto instrumentation_lib = resource_span->add_instrumentation_library_spans();

for (auto &recordable : spans) {
auto rec = std::unique_ptr<Recordable>(
static_cast<Recordable *>(recordable.release()));

proto::trace::v1::Span* span = instrumentation_lib->add_spans();
nadiaciobanu marked this conversation as resolved.
Show resolved Hide resolved
span->CopyFrom(rec->span());
}
}

// Establish connection to OpenTelemetry Collector
std::unique_ptr<proto::collector::trace::v1::TraceService::Stub> MakeServiceStub()
{
auto channel = grpc::CreateChannel(KCollectorAddress, grpc::InsecureChannelCredentials());
return proto::collector::trace::v1::TraceService::NewStub(channel);
}

// -------------------------------- Contructors --------------------------------

OtlpExporter::OtlpExporter(): OtlpExporter(MakeServiceStub()) {}

OtlpExporter::OtlpExporter(
std::unique_ptr<proto::collector::trace::v1::TraceService::StubInterface> stub):
trace_service_stub_(std::move(stub)) {}

// ----------------------------- Exporter methods ------------------------------

std::unique_ptr<sdk::trace::Recordable> OtlpExporter::MakeRecordable() noexcept
{
return std::unique_ptr<sdk::trace::Recordable>(new Recordable);
}

sdk::trace::ExportResult OtlpExporter::Export(
const nostd::span<std::unique_ptr<sdk::trace::Recordable>> &spans) noexcept
{
proto::collector::trace::v1::ExportTraceServiceRequest request;

PopulateRequest(spans, &request);

grpc::ClientContext context;
proto::collector::trace::v1::ExportTraceServiceResponse response;

grpc::Status status = trace_service_stub_->Export(&context, request, &response);

if(!status.ok()){
std::cerr << "OTLP trace exporter: Export() failed\n";
nadiaciobanu marked this conversation as resolved.
Show resolved Hide resolved
return sdk::trace::ExportResult::kFailure;
}
return sdk::trace::ExportResult::kSuccess;
}
} // namespace otlp
} // namespace exporter
OPENTELEMETRY_END_NAMESPACE
41 changes: 41 additions & 0 deletions exporters/otlp/otlp_exporter.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
#pragma once

#include "opentelemetry/sdk/trace/exporter.h"
#include "opentelemetry/sdk/trace/span_data.h"
#include "opentelemetry/proto/collector/trace/v1/trace_service.pb.h"
#include "opentelemetry/proto/collector/trace/v1/trace_service.grpc.pb.h"
#include "opentelemetry/proto/trace/v1/trace.pb.h"
#include "src/common/random.h"
nadiaciobanu marked this conversation as resolved.
Show resolved Hide resolved
#include "recordable.h"

#include <grpcpp/grpcpp.h>
#include <iostream>
nadiaciobanu marked this conversation as resolved.
Show resolved Hide resolved

OPENTELEMETRY_BEGIN_NAMESPACE
namespace exporter
{
namespace otlp
{
class OtlpExporter final : public opentelemetry::sdk::trace::SpanExporter
nadiaciobanu marked this conversation as resolved.
Show resolved Hide resolved
{
public:
OtlpExporter();
nadiaciobanu marked this conversation as resolved.
Show resolved Hide resolved

std::unique_ptr<sdk::trace::Recordable> MakeRecordable() noexcept override;

sdk::trace::ExportResult Export(
const nostd::span<std::unique_ptr<sdk::trace::Recordable>> &spans) noexcept override;

void Shutdown(std::chrono::microseconds timeout = std::chrono::microseconds(0)) noexcept override {};

private:
// For testing
friend class OtlpExporterTestPeer;

std::unique_ptr<proto::collector::trace::v1::TraceService::StubInterface> trace_service_stub_;

OtlpExporter(std::unique_ptr<proto::collector::trace::v1::TraceService::StubInterface> stub);
};
} // namespace otlp
} // namespace exporter
OPENTELEMETRY_END_NAMESPACE
74 changes: 74 additions & 0 deletions exporters/otlp/otlp_exporter_test.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
#include "otlp_exporter.h"
#include "opentelemetry/sdk/trace/simple_processor.h"
#include "opentelemetry/sdk/trace/tracer_provider.h"
#include "opentelemetry/trace/provider.h"
#include "opentelemetry/proto/collector/trace/v1/trace_service_mock.grpc.pb.h"

#include <gtest/gtest.h>

using namespace testing;

OPENTELEMETRY_BEGIN_NAMESPACE
namespace exporter
{
namespace otlp
{

class OtlpExporterTestPeer : public ::testing::Test
{
public:
std::unique_ptr<OtlpExporter> GetExporter(
proto::collector::trace::v1::TraceService::StubInterface* mock_stub)
nadiaciobanu marked this conversation as resolved.
Show resolved Hide resolved
{
return std::unique_ptr<OtlpExporter>(
new OtlpExporter(std::unique_ptr<proto::collector::trace::v1::TraceService::StubInterface>(mock_stub)));
}
};

// Call Export() directly
TEST_F(OtlpExporterTestPeer, ExportUnitTest)
{
auto mock_stub = new proto::collector::trace::v1::MockTraceServiceStub();
auto exporter = std::shared_ptr<sdk::trace::SpanExporter>(GetExporter(mock_stub));

auto recordable_1 = exporter->MakeRecordable();
recordable_1->SetName("Test span 1");
auto recordable_2 = exporter->MakeRecordable();
recordable_2->SetName("Test span 2");

// Test successful RPC
nostd::span<std::unique_ptr<sdk::trace::Recordable>> batch_1(&recordable_1, 1);
EXPECT_CALL(*mock_stub, Export(_,_,_)).Times(Exactly(1)).WillOnce(Return(grpc::Status::OK));
auto result = exporter->Export(batch_1);
EXPECT_EQ(sdk::trace::ExportResult::kSuccess, result);

// Test failed RPC
nostd::span<std::unique_ptr<sdk::trace::Recordable>> batch_2(&recordable_2, 1);
EXPECT_CALL(*mock_stub, Export(_,_,_)).Times(Exactly(1)).WillOnce(Return(grpc::Status::CANCELLED));
result = exporter->Export(batch_2);
EXPECT_EQ(sdk::trace::ExportResult::kFailure, result);
}

// Create spans, let processor call Export()
TEST_F(OtlpExporterTestPeer, ExportIntegrationTest)
{
auto mock_stub = new proto::collector::trace::v1::MockTraceServiceStub();
auto exporter = std::unique_ptr<sdk::trace::SpanExporter>(GetExporter(mock_stub));

auto processor = std::shared_ptr<sdk::trace::SpanProcessor>(
new sdk::trace::SimpleSpanProcessor(std::move(exporter)));
auto provider = nostd::shared_ptr<trace::TracerProvider>(new sdk::trace::TracerProvider(processor));
trace::Provider::SetTracerProvider(provider);

nostd::shared_ptr<trace::Tracer> tracer = provider->GetTracer("test");

EXPECT_CALL(*mock_stub, Export(_,_,_)).Times(AtLeast(1)).WillRepeatedly(Return(grpc::Status::OK));

auto parent_span = tracer->StartSpan("Test parent span");
auto child_span = tracer->StartSpan("Test child span");
child_span->End();
parent_span->End();
}
} // namespace otlp
} // namespace exporter
OPENTELEMETRY_END_NAMESPACE