From 003092a2020c927408fb173dfd9dcdf2dfc35775 Mon Sep 17 00:00:00 2001 From: Makarand Dharmapurikar Date: Tue, 21 Feb 2017 14:08:01 -0800 Subject: [PATCH] code and BUILD file for creating a grpcz client The client binary can be built with 'bazel build //tools/grpcz:grpcz_client' and can be invoked with bazel-bin/tools/grpcz/grpcz_client --server SERVER_ADDR:PORT You can see the stats page at http://localhost:8000/grpcz --- WORKSPACE | 26 +- tools/grpcz/BUILD | 73 +++++ tools/grpcz/any.proto | 139 +++++++++ tools/grpcz/census.proto | 318 +++++++++++++++++++++ tools/grpcz/empty.proto | 52 ++++ tools/grpcz/grpcz_client.cc | 174 +++++++++++ tools/grpcz/monitoring.proto | 131 +++++++++ tools/run_tests/sanity/check_submodules.sh | 2 +- 8 files changed, 910 insertions(+), 5 deletions(-) create mode 100644 tools/grpcz/BUILD create mode 100644 tools/grpcz/any.proto create mode 100644 tools/grpcz/census.proto create mode 100644 tools/grpcz/empty.proto create mode 100644 tools/grpcz/grpcz_client.cc create mode 100644 tools/grpcz/monitoring.proto diff --git a/WORKSPACE b/WORKSPACE index 4f90f06d881..5d163f78e81 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -33,26 +33,44 @@ bind( actual = "@submodule_gtest//:gtest", ) +bind( + name = "gflags", + actual = "@com_github_gflags_gflags//:gflags", +) + new_local_repository( name = "submodule_boringssl", - path = "third_party/boringssl-with-bazel", build_file = "third_party/boringssl-with-bazel/BUILD", + path = "third_party/boringssl-with-bazel", ) new_local_repository( name = "submodule_zlib", - path = "third_party/zlib", build_file = "third_party/zlib.BUILD", + path = "third_party/zlib", ) new_local_repository( name = "submodule_protobuf", - path = "third_party/protobuf", build_file = "third_party/protobuf/BUILD", + path = "third_party/protobuf", ) new_local_repository( name = "submodule_gtest", - path = "third_party/googletest", build_file = "third_party/gtest.BUILD", + path = "third_party/googletest", +) + +local_repository( + name = "com_github_gflags_gflags", + path = "third_party/gflags", +) +# used for tools/grpcz/grpcz_client +git_repository( + name = "mongoose_repo", + commit = "21b9ddd490783e3afaa0fa9b45d6c1133eb922dc", + remote = "https://github.com/makdharma/mongoose.git" ) + + diff --git a/tools/grpcz/BUILD b/tools/grpcz/BUILD new file mode 100644 index 00000000000..34bd072d004 --- /dev/null +++ b/tools/grpcz/BUILD @@ -0,0 +1,73 @@ +# Copyright 2017, Google Inc. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following disclaimer +# in the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +licenses(["notice"]) # 3-clause BSD + +package(default_visibility = ["//visibility:public"]) + +load("//:bazel/generate_cc.bzl", "generate_cc") + +proto_library ( + name = "monitoring_proto_local_copy", + srcs = [ + # TODO (erikgribkoff) : remove the local copies of these protos + "monitoring.proto", + "empty.proto", + "any.proto", + "census.proto", + ], +) + +generate_cc( + name = "monitoring_codegen", + srcs = [":monitoring_proto_local_copy"], +) + +generate_cc( + name = "monitoring_grpc_codegen", + srcs = [":monitoring_proto_local_copy"], + plugin = "//:grpc_cpp_plugin", +) + +cc_library( + name = "proto_lib", + srcs = [":monitoring_codegen", ":monitoring_grpc_codegen"], + hdrs = [":monitoring_codegen", ":monitoring_grpc_codegen"], + deps = ["//:grpc++", "//:grpc++_codegen_proto", "//external:protobuf"], +) + +cc_binary( + name = "grpcz_client", + srcs = ["grpcz_client.cc",], + deps = [ + "proto_lib", + "//external:gflags", + "@mongoose_repo//:mongoose_lib", + ], +) diff --git a/tools/grpcz/any.proto b/tools/grpcz/any.proto new file mode 100644 index 00000000000..a224a2b596e --- /dev/null +++ b/tools/grpcz/any.proto @@ -0,0 +1,139 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +syntax = "proto3"; + +//package google.protobuf; + +option csharp_namespace = "Google.Protobuf.WellKnownTypes"; +option go_package = "github.com/golang/protobuf/ptypes/any"; +option java_package = "com.google.protobuf"; +option java_outer_classname = "AnyProto"; +option java_multiple_files = true; +option objc_class_prefix = "GPB"; + +// `Any` contains an arbitrary serialized protocol buffer message along with a +// URL that describes the type of the serialized message. +// +// Protobuf library provides support to pack/unpack Any values in the form +// of utility functions or additional generated methods of the Any type. +// +// Example 1: Pack and unpack a message in C++. +// +// Foo foo = ...; +// Any any; +// any.PackFrom(foo); +// ... +// if (any.UnpackTo(&foo)) { +// ... +// } +// +// Example 2: Pack and unpack a message in Java. +// +// Foo foo = ...; +// Any any = Any.pack(foo); +// ... +// if (any.is(Foo.class)) { +// foo = any.unpack(Foo.class); +// } +// +// Example 3: Pack and unpack a message in Python. +// +// foo = Foo(...) +// any = Any() +// any.Pack(foo) +// ... +// if any.Is(Foo.DESCRIPTOR): +// any.Unpack(foo) +// ... +// +// The pack methods provided by protobuf library will by default use +// 'type.googleapis.com/full.type.name' as the type URL and the unpack +// methods only use the fully qualified type name after the last '/' +// in the type URL, for example "foo.bar.com/x/y.z" will yield type +// name "y.z". +// +// +// JSON +// ==== +// The JSON representation of an `Any` value uses the regular +// representation of the deserialized, embedded message, with an +// additional field `@type` which contains the type URL. Example: +// +// package google.profile; +// message Person { +// string first_name = 1; +// string last_name = 2; +// } +// +// { +// "@type": "type.googleapis.com/google.profile.Person", +// "firstName": , +// "lastName": +// } +// +// If the embedded message type is well-known and has a custom JSON +// representation, that representation will be embedded adding a field +// `value` which holds the custom JSON in addition to the `@type` +// field. Example (for message [google.protobuf.Duration][]): +// +// { +// "@type": "type.googleapis.com/google.protobuf.Duration", +// "value": "1.212s" +// } +// +message Any { + // A URL/resource name whose content describes the type of the + // serialized protocol buffer message. + // + // For URLs which use the scheme `http`, `https`, or no scheme, the + // following restrictions and interpretations apply: + // + // * If no scheme is provided, `https` is assumed. + // * The last segment of the URL's path must represent the fully + // qualified name of the type (as in `path/google.protobuf.Duration`). + // The name should be in a canonical form (e.g., leading "." is + // not accepted). + // * An HTTP GET on the URL must yield a [google.protobuf.Type][] + // value in binary format, or produce an error. + // * Applications are allowed to cache lookup results based on the + // URL, or have them precompiled into a binary to avoid any + // lookup. Therefore, binary compatibility needs to be preserved + // on changes to types. (Use versioned type names to manage + // breaking changes.) + // + // Schemes other than `http`, `https` (or the empty scheme) might be + // used with implementation specific semantics. + // + string type_url = 1; + + // Must be a valid serialized protocol buffer of the above specified type. + bytes value = 2; +} diff --git a/tools/grpcz/census.proto b/tools/grpcz/census.proto new file mode 100644 index 00000000000..d1ff69400b0 --- /dev/null +++ b/tools/grpcz/census.proto @@ -0,0 +1,318 @@ +// Copyright 2017, Google Inc. +// 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. + +//TODO(ericgribkoff) Depend on this directly from the instrumentation-proto +//repository. + +syntax = "proto3"; + +package google.instrumentation; + +option java_package = "com.google.instrumentation.stats.proto"; +option java_outer_classname = "CensusProto"; + +// All the census protos. +// +// Nomenclature notes: +// * Capitalized names below (like View) are protos. +// * Protos which describe types are named with a Descriptor suffix (e.g. +// MesurementDescriptor). +// +// Census lets you define the type and description of the data being measured +// (e.g. the latency of an RPC or the number of CPU cycles spent on an +// operation using MeasurementDescriptor. As individual measurements (a double +// value) for are recorded, they are aggregated together into an +// Aggregation. There are two Aggregation types available: Distribution +// (describes the distribution of all measurements, possibly with a histogram) +// and IntervalStats (the count and mean of measurements across specified time +// periods). An Aggregation is described by an AggregationDescriptor. +// +// You can define how your measurements (described by a MeasurementDescriptor) +// are broken down by Tag values and which Aggregations to use through a +// ViewDescriptor. The output (all measurements broken down by tag values into +// specific Aggregations) is called a View. + + +// The following two types are copied from +// google/protobuf/{duration,timestamp}.proto. Ideally, we would be able to +// import them, but this causes compilation issues on C-based systems +// (e.g. https://koti.kapsi.fi/jpa/nanopb/), which cannot process the C++ +// headers generated from the standard protobuf distribution. See the relevant +// proto files for full documentation of these types. + +message Duration { + // Signed seconds of the span of time. Must be from -315,576,000,000 + // to +315,576,000,000 inclusive. + int64 seconds = 1; + + // Signed fractions of a second at nanosecond resolution of the span + // of time. Durations less than one second are represented with a 0 + // `seconds` field and a positive or negative `nanos` field. For durations + // of one second or more, a non-zero value for the `nanos` field must be + // of the same sign as the `seconds` field. Must be from -999,999,999 + // to +999,999,999 inclusive. + int32 nanos = 2; +} + +message Timestamp { + // Represents seconds of UTC time since Unix epoch + // 1970-01-01T00:00:00Z. Must be from from 0001-01-01T00:00:00Z to + // 9999-12-31T23:59:59Z inclusive. + int64 seconds = 1; + + // Non-negative fractions of a second at nanosecond resolution. Negative + // second values with fractions must still have non-negative nanos values + // that count forward in time. Must be from 0 to 999,999,999 + // inclusive. + int32 nanos = 2; +} + +// MeasurementDescriptor describes a data point (measurement) type. +message MeasurementDescriptor { + // A descriptive name, e.g. rpc_latency, cpu. Must be unique. + string name = 1; + + // More detailed description of the resource, used in documentation. + string description = 2; + + // Fundamental units of measurement supported by Census + // TODO(aveitch): expand this to include other S.I. units? + enum BasicUnit { + UNKNOWN = 0; // Implementations should not use this + SCALAR = 1; // Dimensionless + BITS = 2; // A single bit + BYTES = 3; // An 8-bit byte + SECONDS = 4; // S.I. unit + CORES = 5; // CPU core usage + MAX_UNITS = 6; // Last defined value; implementations should only use + // this for validation. + } + + // MeasurementUnit lets you build compound units of the form + // 10^n * (A * B * ...) / (X * Y * ...), + // where the elements in the numerator and denominator are all BasicUnits. A + // MeasurementUnit must have at least one BasicUnit in its numerator. + // + // To specify multiplication in the numerator or denominator, simply specify + // multiple numerator or denominator fields. For example: + // + // - byte-seconds (i.e. bytes * seconds): + // numerator: BYTES + // numerator: SECS + // + // - events/sec^2 (i.e. rate of change of events/sec): + // numerator: SCALAR + // denominator: SECS + // denominator: SECS + // + // To specify multiples (in power of 10) of units, specify a non-zero + // 'power10' value, for example: + // + // - MB/s (i.e. megabytes / s): + // power10: 6 + // numerator: BYTES + // denominator: SECS + // + // - nanoseconds + // power10: -9 + // numerator: SECS + message MeasurementUnit { + int32 power10 = 1; + repeated BasicUnit numerators = 2; + repeated BasicUnit denominators = 3; + } + + // The units used by this type of measurement. + MeasurementUnit unit = 3; +} + +// An aggregation summarizes a series of individual measurements. There are +// two types of aggregation (IntervalAggregation and DistributionAggregation), +// unique types of each can be set using descriptors for each. + +// DistributionAggregation contains summary statistics for a population of +// values and, optionally, a histogram representing the distribution of those +// values across a specified set of histogram buckets, as defined in +// DistributionAggregationDescriptor.bucket_bounds. +// +// The summary statistics are the count, mean, minimum, and the maximum of the +// set of population of values. +// +// Although it is not forbidden, it is generally a bad idea to include +// non-finite values (infinities or NaNs) in the population of values, as this +// will render the `mean` field meaningless. +message DistributionAggregation { + // The number of values in the population. Must be non-negative. + int64 count = 1; + + // The arithmetic mean of the values in the population. If `count` is zero + // then this field must be zero. + double mean = 2; + + // The sum of the values in the population. If `count` is zero then this + // field must be zero. + double sum = 3; + + // Describes a range of population values. + message Range { + // The minimum of the population values. + double min = 1; + // The maximum of the population values. + double max = 2; + } + + // The range of the population values. If `count` is zero, this field will not + // be defined. + Range range = 4; + + // A Distribution may optionally contain a histogram of the values in the + // population. The histogram is given in `bucket_count` as counts of values + // that fall into one of a sequence of non-overlapping buckets, as described + // by `DistributionAggregationDescriptor.bucket_boundaries`. The sum of the + // values in `bucket_counts` must equal the value in `count`. + // + // Bucket counts are given in order under the numbering scheme described + // above (the underflow bucket has number 0; the finite buckets, if any, + // have numbers 1 through N-2; the overflow bucket has number N-1). + // + // The size of `bucket_count` must be no greater than N as defined in + // `bucket_boundaries`. + // + // Any suffix of trailing zero bucket_count fields may be omitted. + repeated int64 bucket_counts = 5; + + // Tags associated with this DistributionAggregation. These will be filled + // in based on the View specification. + repeated Tag tags = 6; +} + +message DistributionAggregationDescriptor { + // A Distribution may optionally contain a histogram of the values in the + // population. The bucket boundaries for that histogram are described by + // `bucket_bounds`. This defines `size(bucket_bounds) + 1` (= N) + // buckets. The boundaries for bucket index i are: + // + // [-infinity, bucket_bounds[i]) for i == 0 + // [bucket_bounds[i-1], bucket_bounds[i]) for 0 < i < N-2 + // [bucket_bounds[i-1], +infinity) for i == N-1 + // + // i.e. an underflow bucket (number 0), zero or more finite buckets (1 + // through N - 2, and an overflow bucket (N - 1), with inclusive lower + // bounds and exclusive upper bounds. + // + // If `bucket_bounds` has no elements (zero size), then there is no + // histogram associated with the Distribution. If `bucket_bounds` has only + // one element, there are no finite buckets, and that single element is the + // common boundary of the overflow and underflow buckets. The values must + // be monotonically increasing. + repeated double bucket_bounds = 1; +} + +// An IntervalAggreation records summary stats over various time +// windows. These stats are approximate, with the degree of accuracy +// controlled by setting the n_sub_intervals parameter in the +// IntervalAggregationDescriptor. +message IntervalAggregation { + // Summary statistic over a single time interval. + message Interval { + // The interval duration. Must be positive. + Duration interval_size = 1; + // Approximate number of measurements recorded in this interval. + double count = 2; + // The cumulative sum of measurements in this interval. + double sum = 3; + } + + // Full set of intervals for this aggregation. + repeated Interval intervals = 1; + + // Tags associated with this IntervalAggregation. These will be filled in + // based on the View specification. + repeated Tag tags = 2; +} + +// An IntervalAggreationDescriptor specifies time intervals for an +// IntervalAggregation. +message IntervalAggregationDescriptor { + // Number of internal sub-intervals to use when collecting stats for each + // interval. The max error in interval measurements will be approximately + // 1/n_sub_intervals (although in practice, this will only be approached in + // the presence of very large and bursty workload changes), and underlying + // memory usage will be roughly proportional to the value of this + // field. Must be in the range [2, 20]. A value of 5 will be used if this is + // unspecified. + int32 n_sub_intervals = 1; + + // The size of each interval, as a time duration. Must have at least one + // element. + repeated Duration interval_sizes = 2; +} + +// A Tag: key-value pair. +message Tag { + string key = 1; + string value = 2; +} + +// A ViewDescriptor specifies an AggregationDescriptor and a set of tag +// keys. Views instantiated from this descriptor will contain Aggregations +// broken down by the unique set of matching tag values for each measurement. +message ViewDescriptor { + // Name of view. Must be unique. + string name = 1; + + // More detailed description, for documentation purposes. + string description = 2; + + // Name of a MeasurementDescriptor to be used for this view. + string measurement_descriptor_name = 3; + + // Aggregation type to associate with View. + oneof aggregation { + IntervalAggregationDescriptor interval_aggregation = 4; + DistributionAggregationDescriptor distribution_aggregation = 5; + } + + // Tag keys to match with a given measurement. If no keys are specified, + // then all stats are recorded. Keys must be unique. + repeated string tag_keys = 6; +} + +// DistributionView contains all aggregations for a view specified using a +// DistributionAggregationDescriptor. +message DistributionView { + // Aggregations - each will have a unique set of tag values for the tag_keys + // associated with the corresponding View. + repeated DistributionAggregation aggregations = 1; + + // Start and end timestamps over which aggregations was accumulated. + Timestamp start = 2; + Timestamp end = 3; +} + +// IntervalView contains all aggregations for a view specified using a +// IntervalAggregationDescriptor. +message IntervalView { + // Aggregations - each will have a unique set of tag values for the tag_keys + // associated with the corresponding View. + repeated IntervalAggregation aggregations = 1; +} + +// A View contains the aggregations based on a ViewDescriptor. +message View { + // ViewDescriptor name associated with this set of View. + string view_name = 1; + + oneof view { + DistributionView distribution_view = 2; + IntervalView interval_view = 3; + } +} diff --git a/tools/grpcz/empty.proto b/tools/grpcz/empty.proto new file mode 100644 index 00000000000..03cacd23308 --- /dev/null +++ b/tools/grpcz/empty.proto @@ -0,0 +1,52 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +syntax = "proto3"; + +package google.protobuf; + +option csharp_namespace = "Google.Protobuf.WellKnownTypes"; +option go_package = "github.com/golang/protobuf/ptypes/empty"; +option java_package = "com.google.protobuf"; +option java_outer_classname = "EmptyProto"; +option java_multiple_files = true; +option objc_class_prefix = "GPB"; +option cc_enable_arenas = true; + +// A generic empty message that you can re-use to avoid defining duplicated +// empty messages in your APIs. A typical example is to use it as the request +// or the response type of an API method. For instance: +// +// service Foo { +// rpc Bar(google.protobuf.Empty) returns (google.protobuf.Empty); +// } +// +// The JSON representation for `Empty` is empty JSON object `{}`. +message Empty {} diff --git a/tools/grpcz/grpcz_client.cc b/tools/grpcz/grpcz_client.cc new file mode 100644 index 00000000000..afca3b0532f --- /dev/null +++ b/tools/grpcz/grpcz_client.cc @@ -0,0 +1,174 @@ +/* + * + * Copyright 2017, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include +#include +#include + +#include +#include +#include + +#include "gflags/gflags.h" +#include "mongoose.h" + +// TODO (makdharma): remove local copies of these protos +#include "tools/grpcz/census.grpc.pb.h" +#include "tools/grpcz/monitoring.grpc.pb.h" + +DEFINE_string(server, "127.0.0.1:50052", + "file path (or host:port) where grpcz server is running"); +DEFINE_string(http_port, "8000", + "Port id for accessing the HTTP server that renders /grpcz page"); +DEFINE_bool(print, false, "only print the output and quit"); + +using grpc::Channel; +using grpc::ClientContext; +using grpc::Status; + +using ::grpc::instrumentation::v1alpha::CanonicalRpcStats; +using ::grpc::instrumentation::v1alpha::Monitoring; + +static const std::string static_html_header = + " \ +\ +

GRPCZ FTW

\ + "; + +class GrpczClient { + public: + GrpczClient(std::shared_ptr channel) + : stub_(Monitoring::NewStub(channel)) {} + + std::string GetStatsAsJson() { + const ::google::protobuf::Empty request; + CanonicalRpcStats reply; + ClientContext context; + Status status = stub_->GetCanonicalRpcStats(&context, request, &reply); + + if (status.ok()) { + std::string json_str; + ::google::protobuf::util::MessageToJsonString(reply, &json_str); + return json_str; + } else { + static const std::string error_message_json = + "{\"grpcz Access Error\"\ + :{\"view\":{\"viewName\":\"grpcz Access Error\",\ + \"intervalView\":\"Server not running?\"}}}"; + std::cout << status.error_code() << ":= " << status.error_message() + << std::endl; + return error_message_json; + } + } + + private: + std::unique_ptr stub_; +}; + +static struct mg_serve_http_opts s_http_server_opts; +std::unique_ptr g_grpcz_client; + +static void ev_handler(struct mg_connection *nc, int ev, void *p) { + if (ev == MG_EV_HTTP_REQUEST) { + mg_serve_http(nc, (struct http_message *)p, s_http_server_opts); + } +} + +static void grpcz_handler(struct mg_connection *nc, int ev, void *ev_data) { + (void)ev; + (void)ev_data; + gpr_log(GPR_INFO, "fetching grpcz stats from %s", FLAGS_server.c_str()); + std::string json_str = g_grpcz_client->GetStatsAsJson(); + std::string rendered_html = + static_html_header + json_str + static_html_footer; + mg_printf(nc, "HTTP/1.0 200 OK\r\n\r\n%s", rendered_html.c_str()); + nc->flags |= MG_F_SEND_AND_CLOSE; +} + +int main(int argc, char **argv) { + gflags::ParseCommandLineFlags(&argc, &argv, true); + + // Create a client + g_grpcz_client.reset(new GrpczClient( + grpc::CreateChannel(FLAGS_server, grpc::InsecureChannelCredentials()))); + if (FLAGS_print) { + g_grpcz_client->GetStatsAsJson(); + return 0; + } + + // Set up a mongoose webserver handler + struct mg_mgr mgr; + mg_mgr_init(&mgr, NULL); + gpr_log(GPR_INFO, "Starting grpcz web server on port %s\n", + FLAGS_http_port.c_str()); + + struct mg_connection *nc = mg_bind(&mgr, FLAGS_http_port.c_str(), ev_handler); + if (nc == NULL) { + gpr_log(GPR_ERROR, "Failed to create listener on port %s\n", + FLAGS_http_port.c_str()); + return -11; + } + mg_register_http_endpoint(nc, "/grpcz", grpcz_handler); + mg_set_protocol_http_websocket(nc); + + // Poll in a loop and serve /grpcz pages + for (;;) { + mg_mgr_poll(&mgr, 100); + } + mg_mgr_free(&mgr); + return 0; +} diff --git a/tools/grpcz/monitoring.proto b/tools/grpcz/monitoring.proto new file mode 100644 index 00000000000..ec3dfe9f74c --- /dev/null +++ b/tools/grpcz/monitoring.proto @@ -0,0 +1,131 @@ +// Copyright 2017, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// This file defines an interface for exporting monitoring information +// out of gRPC servers. +syntax = "proto3"; + +// TODO(ericgribkoff) Figure out how to manage the external Census proto +// dependency. +import "tools/grpcz/census.proto"; +import "tools/grpcz/empty.proto"; +import "tools/grpcz/any.proto"; + +package grpc.instrumentation.v1alpha; + +option java_multiple_files = true; +option java_package = "io.grpc.instrumentation.v1alpha"; +option java_outer_classname = "MonitoringProto"; + +service Monitoring { + // Return canonical RPC stats + rpc GetCanonicalRpcStats(google.protobuf.Empty) returns (CanonicalRpcStats) { + } + + // Query the server for specific stats + rpc GetStats(StatsRequest) returns (StatsResponse) { + // TODO(aveitch, ericgribkoff): Pease define the stats response message + // StatsRequest would specifically identify the stats to be returned. + } + + // Request the server to stream back snapshots of the requested stats + rpc WatchStats(StatsRequest) returns (stream StatsResponse) { + } + + + // Return request traces. + rpc GetRequestTraces(TraceRequest) returns(TraceResponse) { + // TODO(aveitch): Please define the messages here + } + + // Return application-defined groups of monitoring data. + // This is a low level facility to allow extension of the monitoring API to + // application-specific monitoring data. Frameworks may use this to define + // additional groups of monitoring data made available by servers. + rpc GetCustomMonitoringData(MonitoringDataGroup) + returns (CustomMonitoringData) { + } + +} + +// Canonical RPC stats exported by gRPC. +message CanonicalRpcStats { + // Wrapper combining View and ViewDescriptor. + message View { + google.instrumentation.MeasurementDescriptor measurement_descriptor = 1; + google.instrumentation.ViewDescriptor view_descriptor = 2; + google.instrumentation.View view = 3; + } + + View rpc_client_errors = 1; + View rpc_client_completed_rpcs = 2; + View rpc_client_started_rpcs = 3; + View rpc_client_elapsed_time = 4; + View rpc_client_server_elapsed_time = 5; + View rpc_client_request_bytes = 6; + View rpc_client_response_bytes = 7; + View rpc_client_request_count = 8; + View rpc_client_response_count = 9; + View rpc_server_errors = 10; + View rpc_server_completed_rpcs = 11; + View rpc_server_server_elapsed_time = 12; + View rpc_server_request_bytes = 13; + View rpc_server_response_bytes = 14; + View rpc_server_request_count = 15; + View rpc_server_response_count = 16; + View rpc_server_elapsed_time = 17; + //TODO(ericgribkoff) Add minute-hour interval stats. +} + +message StatsRequest { + // TODO(aveitch): Complete definition of this type +} + +message StatsResponse { + // TODO(aveitch): Complete definition of this type +} + +message TraceRequest { + // TODO(aveitch): Complete definition of this type +} + +message TraceResponse { + // TODO(aveitch): Complete definition of this type +} + +message MonitoringDataGroup { + string name = 1; // name of a group of monitoring data +} + +// The wrapper for custom monitoring data. +message CustomMonitoringData { + // can be any application specific monitoring data + Any contents = 1; +} + diff --git a/tools/run_tests/sanity/check_submodules.sh b/tools/run_tests/sanity/check_submodules.sh index 0b68319d290..cfe4e2731c0 100755 --- a/tools/run_tests/sanity/check_submodules.sh +++ b/tools/run_tests/sanity/check_submodules.sh @@ -44,7 +44,7 @@ cat << EOF | awk '{ print $1 }' | sort > $want_submodules 44c25c892a6229b20db7cd9dc05584ea865896de third_party/benchmark (v0.1.0-343-g44c25c8) 78684e5b222645828ca302e56b40b9daff2b2d27 third_party/boringssl (78684e5) 886e7d75368e3f4fab3f4d0d3584e4abfc557755 third_party/boringssl-with-bazel (version_for_cocoapods_7.0-857-g886e7d7) - f8a0efe03aa69b3336d8e228b37d4ccb17324b88 third_party/gflags (v2.2.0) + 30dbc81fb5ffdc98ea9b14b1918bfe4e8779b26e third_party/gflags (v2.2.0) c99458533a9b4c743ed51537e25989ea55944908 third_party/googletest (release-1.7.0) a428e42072765993ff674fda72863c9f1aa2d268 third_party/protobuf (v3.1.0-alpha-1) bcad91771b7f0bff28a1cac1981d7ef2b9bcef3c third_party/thrift (bcad917)