diff --git a/opencensus/exporters/stats/stackdriver/README.md b/opencensus/exporters/stats/stackdriver/README.md index 0e70899e..63d0bf85 100644 --- a/opencensus/exporters/stats/stackdriver/README.md +++ b/opencensus/exporters/stats/stackdriver/README.md @@ -19,18 +19,24 @@ In order to be able to push your stats to [Stackdriver Monitoring](stackdriver-m These steps enable the API but don't require that your app is hosted on Google Cloud Platform. ### Setup authentication + The Stackdriver exporter uses gRPC, which requires a certificate (`etc/roots.pem` in the gRPC repository) copied to to `/usr/share/grpc/roots.pem`. If your application runs on Google Cloud Platform, it can automatically determine credentials to authenticate to Stackdriver from the VM environment. + Otherwise, create a -[Google Cloud service account](https://cloud.google.com/iam/docs/creating-managing-service-accounts) -with the "Monitoring Editor" role, create and download a service account key, +[Google Cloud service account](https://cloud.google.com/iam/docs/creating-managing-service-accounts), +create and download a service account key, and set the environment variable `GOOGLE_APPLICATION_CREDENTIALS` to the path to that key. +Please refer to the [Stats access controls](https://cloud.google.com/monitoring/access-control) +and [Trace access controls](https://cloud.google.com/trace/docs/iam) +documentation for configuring roles. + ### Register the exporter `#include opencensus/exporters/stats/stackdriver/stackdriver_exporter.h` (if diff --git a/opencensus/exporters/trace/stackdriver/BUILD b/opencensus/exporters/trace/stackdriver/BUILD index 5b6c82bd..9f1fb985 100644 --- a/opencensus/exporters/trace/stackdriver/BUILD +++ b/opencensus/exporters/trace/stackdriver/BUILD @@ -16,7 +16,7 @@ load("//opencensus:copts.bzl", "DEFAULT_COPTS", "TEST_COPTS") licenses(["notice"]) # Apache License 2.0 -package(default_visibility = ["//visibility:public"]) +package(default_visibility = ["//visibility:private"]) # Libraries # ========================================================================= # @@ -26,27 +26,12 @@ cc_library( srcs = ["internal/stackdriver_exporter.cc"], hdrs = ["stackdriver_exporter.h"], copts = DEFAULT_COPTS, + visibility = ["//visibility:public"], deps = [ "//google/devtools/cloudtrace/v2:tracing_proto", "//opencensus/trace", "@com_github_grpc_grpc//:grpc++", - "@com_google_absl//absl/base:core_headers", "@com_google_absl//absl/memory", "@com_google_absl//absl/strings", ], ) - -# Tests -# ========================================================================= # - -cc_test( - name = "stackdriver_exporter_test", - srcs = ["internal/stackdriver_exporter_test.cc"], - copts = TEST_COPTS, - deps = [ - ":stackdriver_exporter", - "//opencensus/trace", - "@com_github_grpc_grpc//test/core/util:gpr_test_util", - "@com_google_googletest//:gtest_main", - ], -) diff --git a/opencensus/exporters/trace/stackdriver/README.md b/opencensus/exporters/trace/stackdriver/README.md new file mode 100644 index 00000000..f219d63c --- /dev/null +++ b/opencensus/exporters/trace/stackdriver/README.md @@ -0,0 +1,3 @@ +Please refer to the +[Stackdriver Stats Exporter instructions](../../stats/stackdriver/README.md) for +setup. diff --git a/opencensus/exporters/trace/stackdriver/internal/stackdriver_exporter.cc b/opencensus/exporters/trace/stackdriver/internal/stackdriver_exporter.cc index e2bfe684..cb27ef52 100644 --- a/opencensus/exporters/trace/stackdriver/internal/stackdriver_exporter.cc +++ b/opencensus/exporters/trace/stackdriver/internal/stackdriver_exporter.cc @@ -21,6 +21,10 @@ #include "absl/memory/memory.h" #include "absl/strings/str_cat.h" #include "absl/time/clock.h" +#include "google/devtools/cloudtrace/v2/tracing.grpc.pb.h" +#include "include/grpc++/grpc++.h" +#include "opencensus/trace/exporter/span_data.h" +#include "opencensus/trace/exporter/span_exporter.h" using grpc::ClientContext; using grpc::Status; @@ -28,18 +32,21 @@ using grpc::Status; namespace opencensus { namespace exporters { namespace trace { - -static constexpr size_t kAttributeStringLen = 256; -static constexpr size_t kAnnotationStringLen = 256; -static constexpr size_t kDisplayNameStringLen = 128; -static constexpr char kGoogleStackDriverTraceAddress[] = - "cloudtrace.googleapis.com"; - namespace { +constexpr size_t kAttributeStringLen = 256; +constexpr size_t kAnnotationStringLen = 256; +constexpr size_t kDisplayNameStringLen = 128; +constexpr char kGoogleStackDriverTraceAddress[] = "cloudtrace.googleapis.com"; + constexpr char kAgentKey[] = "g.co/agent"; constexpr char kAgentValue[] = "opencensus-cpp"; +std::string ToString(const grpc::Status& status) { + return absl::StrCat("status code ", status.error_code(), " details \"", + status.error_message(), "\""); +} + gpr_timespec ConvertToTimespec(absl::Time time) { gpr_timespec g_time; int64_t secs = absl::ToUnixSeconds(time); @@ -243,64 +250,44 @@ void ConvertSpans( } } -} // namespace +class Handler : public ::opencensus::trace::exporter::SpanExporter::Handler { + public: + Handler(absl::string_view project_id, + const std::shared_ptr& channel) + : project_id_(project_id), + stub_(::google::devtools::cloudtrace::v2::TraceService::NewStub( + channel)) {} -Status StackdriverExporter::TraceClient::BatchWriteSpans( - const ::google::devtools::cloudtrace::v2::BatchWriteSpansRequest& request) { - ::google::protobuf::Empty response; + void Export(const std::vector<::opencensus::trace::exporter::SpanData>& spans) + override; - // Context for the client. It could be used to convey extra information to - // the server and/or tweak certain RPC behaviors. Deadline set for 3000 - // milliseconds. - ClientContext context; - context.set_deadline( - ConvertToTimespec(absl::Now() + absl::Milliseconds(3000))); - - // The actual RPC that sends the span information to Stackdriver. - return stub_->BatchWriteSpans(&context, request, &response); -} - -void StackdriverExporter::Register(absl::string_view project_id) { - StackdriverExporter* exporter = new StackdriverExporter(project_id); - auto creds = grpc::GoogleDefaultCredentials(); - auto channel = ::grpc::CreateChannel(kGoogleStackDriverTraceAddress, creds); - exporter->trace_client_ = absl::make_unique(channel); - ::opencensus::trace::exporter::SpanExporter::RegisterHandler( - absl::WrapUnique<::opencensus::trace::exporter::SpanExporter::Handler>( - exporter)); -} + private: + const std::string project_id_; + std::unique_ptr stub_; +}; -void StackdriverExporter::Export( +void Handler::Export( const std::vector<::opencensus::trace::exporter::SpanData>& spans) { ::google::devtools::cloudtrace::v2::BatchWriteSpansRequest request; request.set_name(absl::StrCat("projects/", project_id_)); ConvertSpans(spans, project_id_, &request); - - Status status = trace_client_->BatchWriteSpans(request); - // Act upon its status. + ::google::protobuf::Empty response; + ClientContext context; + context.set_deadline( + ConvertToTimespec(absl::Now() + absl::Milliseconds(3000))); + Status status = stub_->BatchWriteSpans(&context, request, &response); if (!status.ok()) { - // TODO: log error. + std::cerr << "BatchWriteSpans failed: " << ToString(status) << "\n"; } } -void StackdriverExporter::ExportForTesting( - absl::string_view project_id, - const std::vector<::opencensus::trace::exporter::SpanData>& spans) { +} // namespace + +void StackdriverExporter::Register(absl::string_view project_id) { auto creds = grpc::GoogleDefaultCredentials(); auto channel = ::grpc::CreateChannel(kGoogleStackDriverTraceAddress, creds); - std::unique_ptr trace_client = - absl::make_unique(channel); - - ::google::devtools::cloudtrace::v2::BatchWriteSpansRequest request; - request.set_name(absl::StrCat("projects/", project_id)); - ConvertSpans(spans, project_id, &request); - - Status status = trace_client->BatchWriteSpans(request); - // Act upon its status. - if (!status.ok()) { - std::cerr << "BatchWriteSpans failed with code " << status.error_code() - << ": " << status.error_message() << "\n"; - } + ::opencensus::trace::exporter::SpanExporter::RegisterHandler( + absl::make_unique(project_id, channel)); } } // namespace trace diff --git a/opencensus/exporters/trace/stackdriver/internal/stackdriver_exporter_test.cc b/opencensus/exporters/trace/stackdriver/internal/stackdriver_exporter_test.cc deleted file mode 100644 index 5d8d0ed7..00000000 --- a/opencensus/exporters/trace/stackdriver/internal/stackdriver_exporter_test.cc +++ /dev/null @@ -1,84 +0,0 @@ -// Copyright 2018, OpenCensus Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "opencensus/exporters/trace/stackdriver/stackdriver_exporter.h" - -#include "absl/time/clock.h" -#include "gtest/gtest.h" -#include "include/grpc++/grpc++.h" -#include "opencensus/trace/internal/local_span_store.h" -#include "opencensus/trace/span.h" -#include "test/core/util/test_config.h" - -namespace opencensus { -namespace exporters { -namespace trace { - -class StackdriverExporterTestPeer : public ::testing::Test { - public: - StackdriverExporterTestPeer() : handler_("test") {} - - void ExportSpans( - const std::vector<::opencensus::trace::exporter::SpanData>& spans) { - // TODO: Fix this test to work with mock handlers. It cannot rely - // on actually talking to stackdriver. - // handler_.ExportForTesting("test", spans); - } - - protected: - StackdriverExporter handler_; -}; - -TEST_F(StackdriverExporterTestPeer, ExportTrace) { - ::opencensus::trace::AlwaysSampler sampler; - ::opencensus::trace::StartSpanOptions opts = {&sampler}; - - auto span1 = ::opencensus::trace::Span::StartSpan("Span1", nullptr, opts); - span1.AddAnnotation( - "Root span", - {{"str_attr", "hello"}, {"int_attr", 123}, {"bool_attr", true}}); - span1.AddAttribute("Number", 123); - span1.AddReceivedMessageEvent(3, 4, 5); - absl::SleepFor(absl::Milliseconds(100)); - auto span2 = ::opencensus::trace::Span::StartSpan("Span2", &span1, opts); - span2.AddAnnotation("First child span"); - span2.AddSentMessageEvent(0, 4, 5); - absl::SleepFor(absl::Milliseconds(100)); - auto span3 = ::opencensus::trace::Span::StartSpan("Span3", &span2, opts); - span3.AddAnnotation("Second child span"); - absl::SleepFor(absl::Milliseconds(100)); - span3.End(); - absl::SleepFor(absl::Milliseconds(100)); - span2.End(); - absl::SleepFor(absl::Milliseconds(100)); - span1.End(); - std::vector<::opencensus::trace::exporter::SpanData> spans = - ::opencensus::trace::exporter::LocalSpanStore::GetSpans(); - - ExportSpans(spans); -} - -} // namespace trace -} // namespace exporters -} // namespace opencensus - -int main(int argc, char** argv) { - grpc_test_init(argc, argv); - ::testing::InitGoogleTest(&argc, argv); - grpc_init(); - int status = RUN_ALL_TESTS(); - - grpc_shutdown(); - return status; -} diff --git a/opencensus/exporters/trace/stackdriver/stackdriver_exporter.h b/opencensus/exporters/trace/stackdriver/stackdriver_exporter.h index 825e32dc..0f30e197 100644 --- a/opencensus/exporters/trace/stackdriver/stackdriver_exporter.h +++ b/opencensus/exporters/trace/stackdriver/stackdriver_exporter.h @@ -15,62 +15,19 @@ #ifndef OPENCENSUS_EXPORTERS_TRACE_STACKDRIVER_STACKDRIVER_EXPORTER_H_ #define OPENCENSUS_EXPORTERS_TRACE_STACKDRIVER_STACKDRIVER_EXPORTER_H_ -#include -#include -#include - -#include "google/devtools/cloudtrace/v2/tracing.grpc.pb.h" -#include "include/grpc++/grpc++.h" -#include "opencensus/trace/exporter/span_data.h" -#include "opencensus/trace/exporter/span_exporter.h" +#include "absl/strings/string_view.h" namespace opencensus { namespace exporters { namespace trace { -class StackdriverExporter - : public ::opencensus::trace::exporter::SpanExporter::Handler { +class StackdriverExporter { public: - StackdriverExporter(absl::string_view project_id) : project_id_(project_id) {} - void Export(const std::vector<::opencensus::trace::exporter::SpanData>& spans) - override; - - // Registers the exporter and sets the project Id. The following are required - // to communicate with stackdriver: a valid project Id, a valid authentication - // key which has been generated for that project, and the server security - // credentials. An authentication key can be generated for your project in the - // gcp/stackdriver settings for your account. Grpc will look for the key using - // the path defined by GOOGLE_APPLICATION_CREDENTIALS environment variable - // (export GOOGLE_APPLICATION_CREDENTIALS=). The server security - // credentials located in /etc/roots.pem needs to be copied to - // /usr/share/grpc/roots.pem + // Registers the exporter and sets the project ID. static void Register(absl::string_view project_id); private: - class TraceClient { - public: - TraceClient(const std::shared_ptr& channel) - : stub_(google::devtools::cloudtrace::v2::TraceService::NewStub( - channel)) {} - - // Packages a batch of spans into a single request and writes it to - // stackdriver. Returns the status of the operation. - grpc::Status BatchWriteSpans( - const ::google::devtools::cloudtrace::v2::BatchWriteSpansRequest& - request); - - private: - std::unique_ptr stub_; - }; - - friend class StackdriverExporterTestPeer; - - static void ExportForTesting( - absl::string_view project_id, - const std::vector<::opencensus::trace::exporter::SpanData>& spans); - - const std::string project_id_; - std::unique_ptr trace_client_; + StackdriverExporter() = delete; }; } // namespace trace