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
pull/9810/head
Makarand Dharmapurikar 8 years ago
parent 1096672527
commit 003092a202
  1. 26
      WORKSPACE
  2. 73
      tools/grpcz/BUILD
  3. 139
      tools/grpcz/any.proto
  4. 318
      tools/grpcz/census.proto
  5. 52
      tools/grpcz/empty.proto
  6. 174
      tools/grpcz/grpcz_client.cc
  7. 131
      tools/grpcz/monitoring.proto
  8. 2
      tools/run_tests/sanity/check_submodules.sh

@ -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"
)

@ -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",
],
)

@ -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": <string>,
// "lastName": <string>
// }
//
// 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;
}

@ -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;
}
}

@ -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 {}

@ -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 <iostream>
#include <memory>
#include <string>
#include <google/protobuf/util/json_util.h>
#include <grpc++/grpc++.h>
#include <grpc/support/log.h>
#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 =
"<!DOCTYPE html> <html> <head> <style> \
table { border-collapse: collapse; width: 100%; } \
table, td, th { border: 1px solid black; } \
</style> </head> <body>\
<div id='stats' stats='";
static const std::string static_html_footer =
"' class='hidden'></div>\
<h1> GRPCZ FTW </h1> <div id='table'> </div> \
<script> \
var canonical_stats = JSON.parse(\
document.getElementById('stats').getAttribute('stats')); \
var table = document.createElement('table'); \
for (var key in canonical_stats) { \
name = canonical_stats[key]['view']['viewName']; \
distribution = canonical_stats[key]['view']['distributionView']; \
interval = canonical_stats[key]['view']['intervalView']; \
value = (interval == undefined) ? \
JSON.stringify(distribution, null, ' ') : \
JSON.stringify(interval, null, ' '); \
var row = table.insertRow(-1); \
var col1 = row.insertCell(0); \
var col2 = row.insertCell(1); \
col1.innerHTML = name; \
col2.innerHTML = '<pre>' + value + '</pre>'; \
} \
document.getElementById('table').appendChild(table); \
</script> </body> </html>";
class GrpczClient {
public:
GrpczClient(std::shared_ptr<Channel> 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<Monitoring::Stub> stub_;
};
static struct mg_serve_http_opts s_http_server_opts;
std::unique_ptr<GrpczClient> 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;
}

@ -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;
}

@ -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)

Loading…
Cancel
Save