Skip to content

Commit

Permalink
Add Stackdriver access logger (envoyproxy#2429)
Browse files Browse the repository at this point in the history
* add Stackdriver access logger

* address comments

* fix test

* format

* try to remove DEBUG macro to make mac happy

* sigh, remove the test

* address comment

* clean up

* add back macos test. add -c opt to circle build args

* address comment

* format

* switch back to request queue

* remove deconstructor, flush should happen in RootContext onDone.

* clean up
  • Loading branch information
bianpengyuan authored and istio-testing committed Oct 10, 2019
1 parent ff0a139 commit c0af782
Show file tree
Hide file tree
Showing 14 changed files with 761 additions and 40 deletions.
2 changes: 1 addition & 1 deletion .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ jobs:
xcode: "11.0.0"
environment:
- BAZEL_STARTUP_ARGS: "--output_base /Users/distiller/.cache/bazel"
- BAZEL_BUILD_ARGS: "--local_ram_resources=12288 --local_cpu_resources=8 --verbose_failures --test_env=ENVOY_IP_TEST_VERSIONS=v4only --test_output=all"
- BAZEL_BUILD_ARGS: "--local_ram_resources=12288 --local_cpu_resources=8 --verbose_failures --test_env=ENVOY_IP_TEST_VERSIONS=v4only --test_output=all -c opt "
- CC: clang
- CXX: clang++
steps:
Expand Down
21 changes: 21 additions & 0 deletions extensions/stackdriver/common/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,27 @@ cc_library(
visibility = [
"//extensions/stackdriver:__pkg__",
"//extensions/stackdriver/edges:__pkg__",
"//extensions/stackdriver/log:__pkg__",
"//extensions/stackdriver/metric:__pkg__",
],
)

cc_library(
name = "utils",
srcs = [
"utils.cc",
],
hdrs = [
"utils.h",
],
visibility = [
"//extensions/stackdriver:__pkg__",
"//extensions/stackdriver/log:__pkg__",
"//extensions/stackdriver/metric:__pkg__",
],
deps = [
":constants",
"//extensions/common:context",
"@com_google_googleapis//google/monitoring/v3:monitoring_cc_proto",
],
)
1 change: 1 addition & 0 deletions extensions/stackdriver/common/constants.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ constexpr char kGCPProjectKey[] = "gcp_project";

// Misc
constexpr char kIstioProxyContainerName[] = "istio-proxy";
constexpr double kNanosecondsPerMillisecond = 1000000.0;

} // namespace Common
} // namespace Stackdriver
Expand Down
53 changes: 53 additions & 0 deletions extensions/stackdriver/common/utils.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/* Copyright 2019 Istio Authors. All Rights Reserved.
*
* 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 "extensions/stackdriver/common/utils.h"
#include "extensions/stackdriver/common/constants.h"

namespace Extensions {
namespace Stackdriver {
namespace Common {

using google::api::MonitoredResource;

void getMonitoredResource(const std::string &monitored_resource_type,
const ::wasm::common::NodeInfo &local_node_info,
MonitoredResource *monitored_resource) {
if (!monitored_resource) {
return;
}
monitored_resource->set_type(monitored_resource_type);
auto platform_metadata = local_node_info.platform_metadata();
(*monitored_resource->mutable_labels())[kProjectIDLabel] =
platform_metadata[kGCPProjectKey];
(*monitored_resource->mutable_labels())[kLocationLabel] =
platform_metadata[kGCPClusterLocationKey];
(*monitored_resource->mutable_labels())[kClusterNameLabel] =
platform_metadata[kGCPClusterNameKey];
(*monitored_resource->mutable_labels())[kNamespaceNameLabel] =
local_node_info.namespace_();
(*monitored_resource->mutable_labels())[kPodNameLabel] =
local_node_info.name();

if (monitored_resource_type == kContainerMonitoredResource) {
// Fill in container_name of k8s_container monitored resource.
(*monitored_resource->mutable_labels())[kContainerNameLabel] =
kIstioProxyContainerName;
}
}

} // namespace Common
} // namespace Stackdriver
} // namespace Extensions
32 changes: 32 additions & 0 deletions extensions/stackdriver/common/utils.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/* Copyright 2019 Istio Authors. All Rights Reserved.
*
* 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 "extensions/common/context.h"
#include "google/api/monitored_resource.pb.h"

namespace Extensions {
namespace Stackdriver {
namespace Common {

// Gets monitored resource proto based on the type and node metadata info.
// Only two types of monitored resource could be returned: k8s_container or
// k8s_pod.
void getMonitoredResource(const std::string &monitored_resource_type,
const ::wasm::common::NodeInfo &local_node_info,
google::api::MonitoredResource *monitored_resource);

} // namespace Common
} // namespace Stackdriver
} // namespace Extensions
73 changes: 73 additions & 0 deletions extensions/stackdriver/log/BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
# Copyright 2019 Istio Authors. All Rights Reserved.
#
# 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.
#
################################################################################
#

licenses(["notice"]) # Apache 2

load(
"@envoy//bazel:envoy_build_system.bzl",
"envoy_cc_library",
"envoy_cc_test",
)

envoy_cc_library(
name = "logger",
srcs = [
"logger.cc",
],
hdrs = [
"logger.h",
],
repository = "@envoy",
visibility = [
"//extensions/stackdriver:__pkg__",
],
deps = [
":exporter",
"//extensions/common:context",
"//extensions/stackdriver/common:constants",
"//extensions/stackdriver/common:utils",
],
)

envoy_cc_library(
name = "exporter",
srcs = [
"exporter.cc",
],
hdrs = [
"exporter.h",
],
repository = "@envoy",
visibility = [
"//extensions/stackdriver:__pkg__",
],
deps = [
"@com_google_googleapis//google/logging/v2:logging_cc_proto",
"@envoy//source/extensions/common/wasm/null:null_plugin_lib",
],
)

envoy_cc_test(
name = "logger_test",
size = "small",
srcs = ["logger_test.cc"],
repository = "@envoy",
deps = [
":logger",
"@envoy//source/extensions/common/wasm:wasm_lib",
],
)
102 changes: 102 additions & 0 deletions extensions/stackdriver/log/exporter.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
/* Copyright 2019 Istio Authors. All Rights Reserved.
*
* 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 "extensions/stackdriver/log/exporter.h"

#ifdef NULL_PLUGIN
namespace Envoy {
namespace Extensions {
namespace Common {
namespace Wasm {
namespace Null {
namespace Plugin {

using envoy::api::v2::core::GrpcService;
using Envoy::Extensions::Common::Wasm::Null::Plugin::GrpcStatus;
using Envoy::Extensions::Common::Wasm::Null::Plugin::logDebug;
using Envoy::Extensions::Common::Wasm::Null::Plugin::logInfo;
using Envoy::Extensions::Common::Wasm::Null::Plugin::StringView;

#endif

constexpr char kGoogleStackdriverLoggingAddress[] = "logging.googleapis.com";
constexpr char kGoogleLoggingService[] = "google.logging.v2.LoggingServiceV2";
constexpr char kGoogleWriteLogEntriesMethod[] = "WriteLogEntries";
constexpr char kDefaultRootCertFile[] = "/etc/ssl/certs/ca-certificates.crt";
constexpr int kDefaultTimeoutMillisecond = 10000;

namespace Extensions {
namespace Stackdriver {
namespace Log {

ExporterImpl::ExporterImpl(RootContext* root_context,
const std::string& logging_service_endpoint) {
context_ = root_context;
success_callback_ = [](google::protobuf::Empty&&) {
logDebug("successfully sent Stackdriver logging request");
};

failure_callback_ = [](GrpcStatus status, StringView message) {
// TODO(bianpengyuan): add retry.
logWarn("Stackdriver logging api call error: " +
std::to_string(static_cast<int>(status)) + std::string(message));
};

// Construct grpc_service for the Stackdriver gRPC call.
GrpcService grpc_service;
if (logging_service_endpoint.empty()) {
grpc_service.mutable_google_grpc()->set_target_uri(
kGoogleStackdriverLoggingAddress);
grpc_service.mutable_google_grpc()
->add_call_credentials()
->mutable_google_compute_engine();
grpc_service.mutable_google_grpc()
->mutable_channel_credentials()
->mutable_ssl_credentials()
->mutable_root_certs()
->set_filename(kDefaultRootCertFile);
} else {
// Do not set credential if target uri is provided. This should happen in
// test.
grpc_service.mutable_google_grpc()->set_target_uri(
logging_service_endpoint);
}
grpc_service.SerializeToString(&grpc_service_string_);
}

void ExporterImpl::exportLogs(
const std::vector<
std::unique_ptr<const google::logging::v2::WriteLogEntriesRequest>>&
requests) const {
for (const auto& req : requests) {
context_->grpcSimpleCall(grpc_service_string_, kGoogleLoggingService,
kGoogleWriteLogEntriesMethod, *req,
kDefaultTimeoutMillisecond, success_callback_,
failure_callback_);
}
}

} // namespace Log
} // namespace Stackdriver
} // namespace Extensions

#ifdef NULL_PLUGIN
} // namespace Plugin
} // namespace Null
} // namespace Wasm
} // namespace Common
} // namespace Extensions
} // namespace Envoy
#endif
90 changes: 90 additions & 0 deletions extensions/stackdriver/log/exporter.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
/* Copyright 2019 Istio Authors. All Rights Reserved.
*
* 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.
*/

#pragma once

#include <string>

#include "google/logging/v2/logging.pb.h"

#ifndef NULL_PLUGIN
#include "api/wasm/cpp/proxy_wasm_intrinsics.h"
#else

#include "extensions/common/wasm/null/null_plugin.h"

namespace Envoy {
namespace Extensions {
namespace Common {
namespace Wasm {
namespace Null {
namespace Plugin {
#endif

namespace Extensions {
namespace Stackdriver {
namespace Log {

// Log exporter interface.
class Exporter {
public:
virtual ~Exporter() {}

virtual void exportLogs(
const std::vector<
std::unique_ptr<const google::logging::v2::WriteLogEntriesRequest>>&)
const = 0;
};

// Exporter writes Stackdriver access log to the backend. It uses WebAssembly
// gRPC API.
class ExporterImpl : public Exporter {
public:
// root_context is the wasm runtime context that this instance runs with.
// logging_service_endpoint is an optional param which should be used for test
// only.
ExporterImpl(RootContext* root_context,
const std::string& logging_service_endpoint);

// exportLogs exports the given log request to Stackdriver.
void exportLogs(
const std::vector<
std::unique_ptr<const google::logging::v2::WriteLogEntriesRequest>>&
req) const override;

private:
// Wasm context that outbound calls are attached to.
RootContext* context_ = nullptr;

// Serialized string of Stackdriver logging service
std::string grpc_service_string_;

// Callbacks for gRPC calls.
std::function<void(google::protobuf::Empty&&)> success_callback_;
std::function<void(GrpcStatus, StringView)> failure_callback_;
};

} // namespace Log
} // namespace Stackdriver
} // namespace Extensions

#ifdef NULL_PLUGIN
} // namespace Plugin
} // namespace Null
} // namespace Wasm
} // namespace Common
} // namespace Extensions
} // namespace Envoy
#endif
Loading

0 comments on commit c0af782

Please sign in to comment.