// Copyright 2018, OpenCensus Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// This package describes the Metrics data model. It is currently experimental
// but may eventually become the wire format for metrics. Please see
// https://github.com/census-instrumentation/opencensus-specs/blob/master/stats/Metrics.md
// for more details.

syntax = "proto3";

package opencensus.proto.metrics.v1;

import "google/protobuf/timestamp.proto";
import "google/protobuf/wrappers.proto";
import "opencensus/proto/resource/v1/resource.proto";

option go_package = "github.com/census-instrumentation/opencensus-proto/gen-go/metrics/v1";

option java_multiple_files = true;
option java_package = "io.opencensus.proto.metrics.v1";
option java_outer_classname = "MetricsProto";

option ruby_package = "OpenCensus.Proto.Metrics.V1";

// Defines a Metric which has one or more timeseries.
message Metric {
  // The descriptor of the Metric.
  // TODO(issue #152): consider only sending the name of descriptor for
  // optimization.
  MetricDescriptor metric_descriptor = 1;

  // One or more timeseries for a single metric, where each timeseries has
  // one or more points.
  repeated TimeSeries timeseries = 2;

  // The resource for the metric. If unset, it may be set to a default value
  // provided for a sequence of messages in an RPC stream.
  opencensus.proto.resource.v1.Resource resource = 3;
}

// Defines a metric type and its schema.
message MetricDescriptor {
  // The metric type, including its DNS name prefix. It must be unique.
  string name = 1;

  // A detailed description of the metric, which can be used in documentation.
  string description = 2;

  // The unit in which the metric value is reported. Follows the format
  // described by http://unitsofmeasure.org/ucum.html.
  string unit = 3;

  // The kind of metric. It describes how the data is reported.
  //
  // A gauge is an instantaneous measurement of a value.
  //
  // A cumulative measurement is a value accumulated over a time interval. In
  // a time series, cumulative measurements should have the same start time,
  // increasing values and increasing end times, until an event resets the
  // cumulative value to zero and sets a new start time for the following
  // points.
  enum Type {
    // Do not use this default value.
    UNSPECIFIED = 0;

    // Integer gauge. The value can go both up and down.
    GAUGE_INT64 = 1;

    // Floating point gauge. The value can go both up and down.
    GAUGE_DOUBLE = 2;

    // Distribution gauge measurement. The count and sum can go both up and
    // down. Recorded values are always >= 0.
    // Used in scenarios like a snapshot of time the current items in a queue
    // have spent there.
    GAUGE_DISTRIBUTION = 3;

    // Integer cumulative measurement. The value cannot decrease, if resets
    // then the start_time should also be reset.
    CUMULATIVE_INT64 = 4;

    // Floating point cumulative measurement. The value cannot decrease, if
    // resets then the start_time should also be reset. Recorded values are
    // always >= 0.
    CUMULATIVE_DOUBLE = 5;

    // Distribution cumulative measurement. The count and sum cannot decrease,
    // if resets then the start_time should also be reset.
    CUMULATIVE_DISTRIBUTION = 6;

    // Some frameworks implemented Histograms as a summary of observations
    // (usually things like request durations and response sizes). While it
    // also provides a total count of observations and a sum of all observed
    // values, it calculates configurable percentiles over a sliding time
    // window. This is not recommended, since it cannot be aggregated.
    SUMMARY = 7;
  }
  Type type = 4;

  // The label keys associated with the metric descriptor.
  repeated LabelKey label_keys = 5;
}

// Defines a label key associated with a metric descriptor.
message LabelKey {
  // The key for the label.
  string key = 1;

  // A human-readable description of what this label key represents.
  string description = 2;
}

// A collection of data points that describes the time-varying values
// of a metric.
message TimeSeries {
  // Must be present for cumulative metrics. The time when the cumulative value
  // was reset to zero. Exclusive. The cumulative value is over the time interval
  // (start_timestamp, timestamp]. If not specified, the backend can use the
  // previous recorded value.
  google.protobuf.Timestamp start_timestamp = 1;

  // The set of label values that uniquely identify this timeseries. Applies to
  // all points. The order of label values must match that of label keys in the
  // metric descriptor.
  repeated LabelValue label_values = 2;

  // The data points of this timeseries. Point.value type MUST match the
  // MetricDescriptor.type.
  repeated Point points = 3;
}

message LabelValue {
  // The value for the label.
  string value = 1;
  // If false the value field is ignored and considered not set.
  // This is used to differentiate a missing label from an empty string.
  bool has_value = 2;
}

// A timestamped measurement.
message Point {
  // The moment when this point was recorded. Inclusive.
  // If not specified, the timestamp will be decided by the backend.
  google.protobuf.Timestamp timestamp = 1;

  // The actual point value.
  oneof value {
    // A 64-bit integer.
    int64 int64_value = 2;

    // A 64-bit double-precision floating-point number.
    double double_value = 3;

    // A distribution value.
    DistributionValue distribution_value = 4;

    // A summary value. This is not recommended, since it cannot be aggregated.
    SummaryValue summary_value = 5;
  }
}

// Distribution contains summary statistics for a population of values. It
// optionally contains a histogram representing the distribution of those
// values across a set of buckets.
message DistributionValue {
  // The number of values in the population. Must be non-negative. This value
  // must equal the sum of the values in bucket_counts if a histogram is
  // provided.
  int64 count = 1;

  // The sum of the values in the population. If count is zero then this field
  // must be zero.
  double sum = 2;

  // The sum of squared deviations from the mean of the values in the
  // population. For values x_i this is:
  //
  //     Sum[i=1..n]((x_i - mean)^2)
  //
  // Knuth, "The Art of Computer Programming", Vol. 2, page 323, 3rd edition
  // describes Welford's method for accumulating this sum in one pass.
  //
  // If count is zero then this field must be zero.
  double sum_of_squared_deviation = 3;

  // A Distribution may optionally contain a histogram of the values in the
  // population. The bucket boundaries for that histogram are described by
  // BucketOptions.
  //
  // If bucket_options has no type, then there is no histogram associated with
  // the Distribution.
  message BucketOptions {
    oneof type {
      // Bucket with explicit bounds.
      Explicit explicit = 1;
    }

    // Specifies a set of buckets with arbitrary upper-bounds.
    // This defines size(bounds) + 1 (= N) buckets. The boundaries for bucket
    // index i are:
    //
    // [0, bucket_bounds[i]) for i == 0
    // [bucket_bounds[i-1], bucket_bounds[i]) for 0 < i < N-1
    // [bucket_bounds[i], +infinity) for i == N-1
    message Explicit {
      // The values must be strictly increasing and > 0.
      repeated double bounds = 1;
    }

    // TODO: If OpenMetrics decides to support (a, b] intervals we should add
    // support for these by defining a boolean value here which decides what
    // type of intervals to use.
  }

  // Don't change bucket boundaries within a TimeSeries if your backend doesn't
  // support this.
  // TODO(issue #152): consider not required to send bucket options for
  // optimization.
  BucketOptions bucket_options = 4;

  message Bucket {
    // The number of values in each bucket of the histogram, as described in
    // bucket_bounds.
    int64 count = 1;

    // If the distribution does not have a histogram, then omit this field.
    Exemplar exemplar = 2;
  }

  // If the distribution does not have a histogram, then omit this field.
  // If there is a histogram, then the sum of the values in the Bucket counts
  // must equal the value in the count field of the distribution.
  repeated Bucket buckets = 5;

  // Exemplars are example points that may be used to annotate aggregated
  // Distribution values. They are metadata that gives information about a
  // particular value added to a Distribution bucket.
  message Exemplar {
    // Value of the exemplar point. It determines which bucket the exemplar
    // belongs to.
    double value = 1;

    // The observation (sampling) time of the above value.
    google.protobuf.Timestamp timestamp = 2;

    // Contextual information about the example value.
    map<string, string> attachments = 3;
  }
}

// The start_timestamp only applies to the count and sum in the SummaryValue.
message SummaryValue {
  // The total number of recorded values since start_time. Optional since
  // some systems don't expose this.
  google.protobuf.Int64Value count = 1;

  // The total sum of recorded values since start_time. Optional since some
  // systems don't expose this. If count is zero then this field must be zero.
  // This field must be unset if the sum is not available.
  google.protobuf.DoubleValue sum = 2;

  // The values in this message can be reset at arbitrary unknown times, with
  // the requirement that all of them are reset at the same time.
  message Snapshot {
    // The number of values in the snapshot. Optional since some systems don't
    // expose this.
    google.protobuf.Int64Value count = 1;

    // The sum of values in the snapshot. Optional since some systems don't
    // expose this. If count is zero then this field must be zero or not set
    // (if not supported).
    google.protobuf.DoubleValue sum = 2;

    // Represents the value at a given percentile of a distribution.
    message ValueAtPercentile {
      // The percentile of a distribution. Must be in the interval
      // (0.0, 100.0].
      double percentile = 1;

      // The value at the given percentile of a distribution.
      double value = 2;
    }

    // A list of values at different percentiles of the distribution calculated
    // from the current snapshot. The percentiles must be strictly increasing.
    repeated ValueAtPercentile percentile_values = 3;
  }

  // Values calculated over an arbitrary time window.
  Snapshot snapshot = 3;
}

