// Copyright 2016, 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.

syntax = "proto3";

package google.census;

// All the census protos.
//
// Nomenclature note: capitalized names below (like Resource) are protos.
//
// Census lets you define a Resource - something which can be measured, like the
// latency of an RPC, the number of CPU cycles spent on an operation, or
// anything else you care to measure. You can record individual instances of
// measurements (a double value) for every Resource of interest. These
// individual measurements 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 stats are broken down by Tag values and which
// Aggregations to use through a View. The corresponding combination of
// Resource/View/Aggregation which is available to census clients is called a
// Metric.


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

// Describes a Resource.
message Resource {
  // name of resource, 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;
    BITS = 1;
    BYTES = 2;
    SECS = 3;
    CORES = 4;
    MAX_UNITS = 5;
  }

  // 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: COUNT
  //     denominator: SECS
  //     denominator: SECS
  //
  // To specify multiples (in power of 10) of units, specify a non-zero prefix
  // value, for example:
  //
  // - MB/s (i.e. megabytes / s):
  //     prefix: 6
  //     numerator: BYTES
  //     denominator: SECS
  //
  // - nanoseconds
  //     prefix: -9
  //     numerator: SECS
  message MeasurementUnit {
    int32 prefix = 1;
    repeated BasicUnit numerator = 2;
    repeated BasicUnit denominator = 3;
  }

  // The units in which Resource values are measured.
  MeasurementUnit unit = 3;
}

// An Aggregation summarizes a series of individual Resource measurements, an
// AggregationDescriptor describes an Aggregation.
message AggregationDescriptor {
  enum AggregationType {
    // Unspecified. Should not be used.
    UNKNOWN = 0;
    // A count of measurements made.
    COUNT = 1;
    // A Distribution.
    DISTRIBUTION = 2;
    // Counts over fixed time intervals.
    INTERVAL = 3;
  }
  // The type of Aggregation.
  AggregationType type = 1;

  // At most one set of options. It is illegal to specifiy an option for
  // COUNT Aggregations. interval_boundaries must be set for INTERVAL types.
  // bucket_boundaries are optional for DISTRIBUTION types.
  oneof options {
    // Defines histogram bucket boundaries for Distributions.
    BucketBoundaries bucket_boundaries = 2;
    // Defines the time windows to record for IntervalStats.
    IntervalBoundaries interval_boundaries = 3;
  }

  // A Distribution may optionally contain a histogram of the values in the
  // population. The bucket boundaries for that histogram are described by
  // `bucket_boundaries`. This defines `size(bounds) + 1` (= N) buckets. The
  // boundaries for bucket index i are:
  //
  // [-infinity, bounds[i]) for i == 0
  // [bounds[i-1], bounds[i]) for 0 < i < N-2
  // [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.
  //
  // There must be at least one element in `bounds`.  If `bounds` has only one
  // element, there are no finite buckets, and that single element is the
  // common boundary of the overflow and underflow buckets.
  message BucketBoundaries {
    // The values must be monotonically increasing.
    repeated double bounds = 1;
  }

  // For Interval stats, describe the size of each window.
  message IntervalBoundaries {
    // For each time window, specify a duration in seconds.
    repeated double window_size = 1;
  }
}

// Distribution 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
// Aggregation.bucket_options.
//
// 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 Distribution {
  // 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;

  // 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 = 3;

  // 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 `AggregationDescriptor.options.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_count = 4;
}

// Record summary stats over various time windows.
message IntervalStats {
  // Summary statistic over a single time window.
  message Window {
    // The window duration. Must be positive.
    Duration window_size = 1;
    // The number of measurements in this window.
    int64 count = 2;
    // The arithmetic mean of all measurements in the window.
    double mean = 3;
  }

  // Full set of windows for this aggregation.
  repeated Window window = 1;
}

// A Tag: key-value pair.
message Tag {
  string key = 1;
  string value = 2;
}

// A View specifies an Aggregation and a set of tag keys. The Aggregation will
// be broken down by the unique set of matching tag values for each measurement.
message View {
  // Name of view. Must be unique.
  string name = 1;

  // More detailed description, for documentation purposes.
  string description = 2;

  // Name of Resource to be broken down for this view.
  string resource_name = 3;

  // Aggregation type to associate with this View.
  AggregationDescriptor aggregation = 4;

  // Tag keys to match with a given Resource measurement. If no keys are
  // specified, then all stats are recorded. Keys must be unique.
  repeated string tag_key = 5;
}

// An Aggregation summarizes a series of individual Resource measurements.
message Aggregation {
  // Name of this aggregation.
  string name = 1;

  // More detailed description, for documentation purposes.
  string description = 2;

  // The data for this Aggregation.
  oneof data {
    uint64 count = 3;
    Distribution distribution = 4;
    IntervalStats interval_stats = 5;
  }

  // Tags associated with this Aggregation.
  repeated Tag tag = 6;
}

// A Metric represents all the Aggregations for a particular view.
message Metric {
  // View associated with this Metric.
  string view_name = 1;

  // Aggregations - each will have a unique set of tag values for the tag_keys
  // associated with the corresponding View.
  repeated Aggregation aggregation = 2;

  // Start and end timestamps over which the metric was accumulated. These
  // values are not relevant/defined for IntervalStats aggregations, which are
  // always accumulated over a fixed time period.
  Timestamp start = 3;
  Timestamp end = 4;
}
