利用 OpenCensus 获取自定义指标

Cloud Monitoring 会自动从众多受监控资源列表中收集超过一千种内置指标。但这些指标无法捕获应用特有数据或客户端系统数据。通过这些指标,您可以了解有关后端延迟时间或磁盘使用情况的信息,但无法获知您的应用衍生了多少个后台例程。

应用特有指标是指您为捕获那些无法通过内置 Cloud Monitoring 指标采集的信息而定义和收集的指标。您可以利用一个由库提供的 API 来检测代码并由此捕获此类指标,然后将捕获到的指标发送到 Cloud Monitoring 等后端应用。

在 Cloud Monitoring 中,应用专用指标通常称为“自定义指标”。这些条款可以相互替换。它们也被称为“用户定义的指标”。

就 Cloud Monitoring 而言,自定义指标可以像内置指标一样使用。您可以为自定义指标绘制图表、设置提醒,也可以监控它们。但不同的是,自定义指标由您定义和写入数据,并且您可以删除这些指标。而对于内置指标,您不能执行这些操作。

您可以通过多种方式捕获自定义指标,包括直接使用 Cloud Monitoring API。Cloud Monitoring 建议您使用 OpenCensus 向代码插桩,由此收集自定义指标。

什么是 OpenCensus?

OpenCensus 是一个免费的开源项目,该项目的库具有以下特点:

  • 可为收集各种语言的指标和跟踪数据提供不受制于供应商的支持。
  • 您可以将收集到的数据导出到各种后端应用,包括 Cloud Monitoring。

如需受支持语言的最新列表,请参阅语言支持。 如需可使用导出器的后端应用的最新列表,请参阅导出器

为什么选择 OpenCensus?

虽然 Cloud Monitoring 会提供一个支持定义和收集自定义指标的 API,但这是一个低层级的专有 API。OpenCensus 不但会提供一个更加符合惯例的 API,而且还会提供一个导出器,该导出器可通过 Monitoring API 为您将指标数据发送到 Cloud Monitoring。

此外,OpenCensus 是一个开源项目。您可以使用一个不受制于供应商的库来导出收集的数据,而无需专有库。

OpenCensus 还对应用跟踪提供了良好的支持。如需大致了解这项支持,请参阅 OpenCensus 跟踪。Cloud Trace 建议使用 OpenCensus 对跟踪记录进行插桩检测。您可以利用只有单一发行版的库从各项服务中收集指标和跟踪数据。如需了解如何将 OpenCensus 与 Cloud Trace 配合使用,请参阅 Trace 的客户端库

准备工作

若要使用 Cloud Monitoring,您必须有启用了结算功能的 Google Cloud 项目。该项目还必须与工作区关联。Cloud Monitoring 使用工作区来组织受监控的 Google Cloud 项目。

如果您没有 Google Cloud 项目,请执行以下操作:

  1. 登录您的 Google 帐号。

    如果您还没有 Google 帐号,请注册一个新帐号

  2. 在 Cloud Console 的项目选择器页面上,选择或创建 Cloud 项目。

    转到项目选择器页面

  3. 确保您的 Google Cloud 项目已启用结算功能。 了解如何确认您的项目已启用结算功能

  4. 启用 Monitoring API。如需了解详情,请参阅启用 Monitoring API
  5. 如果您的应用在 Google Cloud 外部运行,则您的应用需要通过 Google Cloud 项目的身份验证。如需了解详情,请参阅身份验证使用入门

如需了解如何将项目与工作区关联,请参阅工作区

安装 OpenCensus

要使用 OpenCensus,您必须准备好指标库和 Stackdriver 导出器。

Go

要使用 OpenCensus,需要安装 Go 1.11 版本或更高版本。 系统会自动为您处理依赖项。

Java

对于 Maven,请将以下代码添加到 pom.xml 文件中的 dependencies 元素中:
<dependency>
  <groupId>io.opencensus</groupId>
  <artifactId>opencensus-api</artifactId>
  <version>${opencensus.version}</version>
</dependency>
<dependency>
  <groupId>io.opencensus</groupId>
  <artifactId>opencensus-impl</artifactId>
  <version>${opencensus.version}</version>
</dependency>
<dependency>
  <groupId>io.opencensus</groupId>
  <artifactId>opencensus-exporter-stats-stackdriver</artifactId>
  <version>${opencensus.version}</version>
</dependency>

Node.js

  1. 在安装 OpenCensus 核心和导出器库之前,请确保已准备好 Node.js 开发环境
  2. 安装 OpenCensus 的最简单方法是使用 npm
    npm install @opencensus/core
    npm install @opencensus/exporter-stackdriver
  3. 将如下所示的 require 语句添加到应用主脚本的顶部或任何其他代码开头的入口点处:
const {globalStats, MeasureUnit, AggregationType} = require('@opencensus/core');
const {StackdriverStatsExporter} = require('@opencensus/exporter-stackdriver');

Python

使用以下命令安装 OpenCensus 核心和 Stackdriver 导出器库:

pip install -r opencensus/requirements.txt

requirements.txt 文件位于这些示例的 GitHub 代码库 python-docs-samples 中。

使用 OpenCensus 获取指标

要使用 OpenCensus 获取指标,您大致需要执行以下三个步骤来检测代码:

  1. 导入 OpenCensus 统计信息和 OpenCensus Stackdriver 导出器包。
  2. 初始化 Stackdriver 导出器。
  3. 使用 OpenCensus API 来检测代码。

基本示例

以下是能够演示上述步骤的最精简程序。该程序运行一个循环并收集延迟时间指标;当循环结束时,它会将统计信息导出到 Cloud Monitoring 并退出运行:

Go


// metrics_quickstart is an example of exporting a custom metric from
// OpenCensus to Stackdriver.
package main

import (
	"context"
	"fmt"
	"log"
	"time"

	"contrib.go.opencensus.io/exporter/stackdriver"
	"go.opencensus.io/stats"
	"go.opencensus.io/stats/view"
	"golang.org/x/exp/rand"
)

var (
	// The task latency in milliseconds.
	latencyMs = stats.Float64("task_latency", "The task latency in milliseconds", "ms")
)

func main() {
	ctx := context.Background()

	// Register the view. It is imperative that this step exists,
	// otherwise recorded metrics will be dropped and never exported.
	v := &view.View{
		Name:        "task_latency_distribution",
		Measure:     latencyMs,
		Description: "The distribution of the task latencies",

		// Latency in buckets:
		// [>=0ms, >=100ms, >=200ms, >=400ms, >=1s, >=2s, >=4s]
		Aggregation: view.Distribution(0, 100, 200, 400, 1000, 2000, 4000),
	}
	if err := view.Register(v); err != nil {
		log.Fatalf("Failed to register the view: %v", err)
	}

	// Enable OpenCensus exporters to export metrics
	// to Stackdriver Monitoring.
	// Exporters use Application Default Credentials to authenticate.
	// See https://developers.google.com/identity/protocols/application-default-credentials
	// for more details.
	exporter, err := stackdriver.NewExporter(stackdriver.Options{})
	if err != nil {
		log.Fatal(err)
	}
	// Flush must be called before main() exits to ensure metrics are recorded.
	defer exporter.Flush()

	if err := exporter.StartMetricsExporter(); err != nil {
		log.Fatalf("Error starting metric exporter: %v", err)
	}
	defer exporter.StopMetricsExporter()

	// Record 100 fake latency values between 0 and 5 seconds.
	for i := 0; i < 100; i++ {
		ms := float64(5*time.Second/time.Millisecond) * rand.Float64()
		fmt.Printf("Latency %d: %f\n", i, ms)
		stats.Record(ctx, latencyMs.M(ms))
		time.Sleep(1 * time.Second)
	}

	fmt.Println("Done recording metrics")
}

Java


import com.google.common.collect.Lists;
import io.opencensus.exporter.stats.stackdriver.StackdriverStatsExporter;
import io.opencensus.stats.Aggregation;
import io.opencensus.stats.BucketBoundaries;
import io.opencensus.stats.Measure.MeasureLong;
import io.opencensus.stats.Stats;
import io.opencensus.stats.StatsRecorder;
import io.opencensus.stats.View;
import io.opencensus.stats.View.Name;
import io.opencensus.stats.ViewManager;
import java.io.IOException;
import java.util.Collections;
import java.util.Random;
import java.util.concurrent.TimeUnit;

public class Quickstart {
  private static final int EXPORT_INTERVAL = 70;
  private static final MeasureLong LATENCY_MS =
      MeasureLong.create("task_latency", "The task latency in milliseconds", "ms");
  // Latency in buckets:
  // [>=0ms, >=100ms, >=200ms, >=400ms, >=1s, >=2s, >=4s]
  private static final BucketBoundaries LATENCY_BOUNDARIES =
      BucketBoundaries.create(Lists.newArrayList(0d, 100d, 200d, 400d, 1000d, 2000d, 4000d));
  private static final StatsRecorder STATS_RECORDER = Stats.getStatsRecorder();

  public static void main(String[] args) throws IOException, InterruptedException {
    // Register the view. It is imperative that this step exists,
    // otherwise recorded metrics will be dropped and never exported.
    View view =
        View.create(
            Name.create("task_latency_distribution"),
            "The distribution of the task latencies.",
            LATENCY_MS,
            Aggregation.Distribution.create(LATENCY_BOUNDARIES),
            Collections.emptyList());

    ViewManager viewManager = Stats.getViewManager();
    viewManager.registerView(view);

    // Enable OpenCensus exporters to export metrics to Stackdriver Monitoring.
    // Exporters use Application Default Credentials to authenticate.
    // See https://developers.google.com/identity/protocols/application-default-credentials
    // for more details.
    StackdriverStatsExporter.createAndRegister();

    // Record 100 fake latency values between 0 and 5 seconds.
    Random rand = new Random();
    for (int i = 0; i < 100; i++) {
      long ms = (long) (TimeUnit.MILLISECONDS.convert(5, TimeUnit.SECONDS) * rand.nextDouble());
      System.out.println(String.format("Latency %d: %d", i, ms));
      STATS_RECORDER.newMeasureMap().put(LATENCY_MS, ms).record();
    }

    // The default export interval is 60 seconds. The thread with the StackdriverStatsExporter must
    // live for at least the interval past any metrics that must be collected, or some risk being
    // lost if they are recorded after the last export.

    System.out.println(
        String.format(
            "Sleeping %d seconds before shutdown to ensure all records are flushed.",
            EXPORT_INTERVAL));
    Thread.sleep(TimeUnit.MILLISECONDS.convert(EXPORT_INTERVAL, TimeUnit.SECONDS));
  }
}

Node.js

'use strict';

const {globalStats, MeasureUnit, AggregationType} = require('@opencensus/core');
const {StackdriverStatsExporter} = require('@opencensus/exporter-stackdriver');

const EXPORT_INTERVAL = process.env.EXPORT_INTERVAL || 60;
const LATENCY_MS = globalStats.createMeasureInt64(
  'task_latency',
  MeasureUnit.MS,
  'The task latency in milliseconds'
);

// Register the view. It is imperative that this step exists,
// otherwise recorded metrics will be dropped and never exported.
const view = globalStats.createView(
  'task_latency_distribution',
  LATENCY_MS,
  AggregationType.DISTRIBUTION,
  [],
  'The distribution of the task latencies.',
  // Latency in buckets:
  // [>=0ms, >=100ms, >=200ms, >=400ms, >=1s, >=2s, >=4s]
  [0, 100, 200, 400, 1000, 2000, 4000]
);

// Then finally register the views
globalStats.registerView(view);

// Enable OpenCensus exporters to export metrics to Stackdriver Monitoring.
// Exporters use Application Default Credentials (ADCs) to authenticate.
// See https://developers.google.com/identity/protocols/application-default-credentials
// for more details.
// Expects ADCs to be provided through the environment as ${GOOGLE_APPLICATION_CREDENTIALS}
// A Stackdriver workspace is required and provided through the environment as ${GOOGLE_PROJECT_ID}
const projectId = process.env.GOOGLE_PROJECT_ID;

// GOOGLE_APPLICATION_CREDENTIALS are expected by a dependency of this code
// Not this code itself. Checking for existence here but not retaining (as not needed)
if (!projectId || !process.env.GOOGLE_APPLICATION_CREDENTIALS) {
  throw Error('Unable to proceed without a Project ID');
}

// The minimum reporting period for Stackdriver is 1 minute.
const exporter = new StackdriverStatsExporter({
  projectId: projectId,
  period: EXPORT_INTERVAL * 1000,
});

// Pass the created exporter to Stats
globalStats.registerExporter(exporter);

// Record 100 fake latency values between 0 and 5 seconds.
for (let i = 0; i < 100; i++) {
  const ms = Math.floor(Math.random() * 5);
  console.log(`Latency ${i}: ${ms}`);
  globalStats.record([
    {
      measure: LATENCY_MS,
      value: ms,
    },
  ]);
}

/**
 * The default export interval is 60 seconds. The thread with the
 * StackdriverStatsExporter must live for at least the interval past any
 * metrics that must be collected, or some risk being lost if they are recorded
 * after the last export.
 */
setTimeout(() => {
  console.log('Done recording metrics.');
  globalStats.unregisterExporter(exporter);
}, EXPORT_INTERVAL * 1000);

Python


from random import random
import time

from opencensus.ext.stackdriver import stats_exporter
from opencensus.stats import aggregation
from opencensus.stats import measure
from opencensus.stats import stats
from opencensus.stats import view

# A measure that represents task latency in ms.
LATENCY_MS = measure.MeasureFloat(
    "task_latency",
    "The task latency in milliseconds",
    "ms")

# A view of the task latency measure that aggregates measurements according to
# a histogram with predefined bucket boundaries. This aggregate is periodically
# exported to Stackdriver Monitoring.
LATENCY_VIEW = view.View(
    "task_latency_distribution",
    "The distribution of the task latencies",
    [],
    LATENCY_MS,
    # Latency in buckets: [>=0ms, >=100ms, >=200ms, >=400ms, >=1s, >=2s, >=4s]
    aggregation.DistributionAggregation(
        [100.0, 200.0, 400.0, 1000.0, 2000.0, 4000.0]))

def main():
    # Register the view. Measurements are only aggregated and exported if
    # they're associated with a registered view.
    stats.stats.view_manager.register_view(LATENCY_VIEW)

    # Create the Stackdriver stats exporter and start exporting metrics in the
    # background, once every 60 seconds by default.
    exporter = stats_exporter.new_stats_exporter()
    print('Exporting stats to project "{}"'
          .format(exporter.options.project_id))

    # Register exporter to the view manager.
    stats.stats.view_manager.register_exporter(exporter)

    # Record 100 fake latency values between 0 and 5 seconds.
    for num in range(100):
        ms = random() * 5 * 1000

        mmap = stats.stats.stats_recorder.new_measurement_map()
        mmap.measure_float_put(LATENCY_MS, ms)
        mmap.record()

        print("Fake latency recorded ({}: {})".format(num, ms))

    # Keep the thread alive long enough for the exporter to export at least
    # once.
    time.sleep(65)

if __name__ == '__main__':
    main()
若将此指标数据导出到 Cloud Monitoring,您可以像使用其他任何数据一样使用它。

该程序会创建一个名为 task_latency_distribution 的 OpenCensus 视图。将该字符串导出到 Cloud Monitoring 时,该字符串会成为指标名称的一部分。如需了解如何以 Cloud Monitoring 指标描述符形式实现 OpenCensus 视图,请参阅检索指标描述符

因此,在选择要绘制成图表的指标时,您可以使用视图名称作为搜索字符串。例如,您可以在 Metrics Explorer 的查找资源类型和指标 (Find resource type and metric) 字段中输入该名称。以下屏幕截图显示了该结果:

Cloud Monitoring 中 OpenCensus 的指标。

热图中的每个条形代表程序的一次运行,而每个条形上的彩色部分代表延迟分布中的各个范围。 如需了解有关图表背后数据的更多详情,请参阅 Cloud Monitoring 中的 OpenCensus 指标

OpenCensus 文档

OpenCensus 提供了有关其 Metrics API 和 Stackdriver 导出器的权威参考文档。下表提供了这些参考文档的链接:

语言 API 参考文档 导出器文档 快速入门
Go Go API 统计信息和 Trace 导出器 指标
Java Java API 统计信息导出器 指标
NodeJS NodeJS API 统计信息导出器 指标
Python Python API 统计信息导出器 指标

映射模型

系统支持直接使用 Cloud Monitoring API 来收集自定义指标;如需查看此 API 的用法,请参阅使用自定义指标。实际上,OpenCensus Cloud Monitoring 导出器会为您使用此 API。

即便您不需要了解 Cloud Monitoring API 的具体细节,熟悉其构造和术语也有助于您了解 Cloud Monitoring 如何表示指标。本部分会介绍一些背景信息。

被提取到 Cloud Monitoring 的指标存储在 Cloud Monitoring 构造中。例如,您可以检索自定义指标的指标描述符(Monitoring API 中的一种类型)。如需了解详情,请参阅 MetricDescriptor。 例如,您会在为数据创建图表时遇到这些指标描述符。

术语和概念

OpenCensus API 使用的构造与 Cloud Monitoring 使用的构造不同,一些术语的使用也是如此。对于同一内容,在 Cloud Monitoring 中使用“指标”,而在 OpenCensus 中有时会使用“统计信息”。例如,将指标数据发送到 Cloud Monitoring 的 OpenCursus 组件称为“Stackdriver 统计信息导出器”。

如需简要了解 OpenCensus 指标模型,请参阅 OpenCensus 指标。

OpenCensus 统计信息数据模型与 Cloud Monitoring 指标数据模型之间并非整齐的一一映射。两种数据模型之间存在着许多相同概念,但这些概念不能直接相互换用。

  • OpenCensus 视图通常类似于 Monitoring API 中的 MetricDescriptor。该视图描述了如何收集和聚合各个测量结果。 所有记录的测量结果会按标记细分。

  • OpenCensus 标记是一个键值对。标记通常与 Monitoring API 中的 LabelDescriptor 相对应。您可以使用标记来捕获相关上下文信息,以便对指标进行过滤和分组。

  • OpenCensus 度量描述了要记录的指标数据。 OpenCensus 聚合是对数据应用的函数,用于汇总数据。系统在导出数据时会使用这两项内容来确定 Cloud Monitoring 指标描述符中报告的 MetricKindValueType 和单位。

  • OpenCensus 测量结果是为度量而收集的数据点。 测量结果必须聚合到视图中。 否则,独立的测量结果将会被丢弃。此构造与 Monitoring API 中的 Point 类似。测量结果聚合到视图中后,聚合数据将存储为视图数据,这类似于 Monitoring API 中的 TimeSeries

Cloud Monitoring 中的 OpenCensus 指标

您可以在 Cloud Monitoring 中检查导出的指标。基本示例中的屏幕截图来自于 Metrics Explorer。 如果您已运行该示例程序,则可以使用 Metrics Explorer 查看您的数据。

要使用 Metrics Explorer 查看受监控资源的指标,请执行以下操作:

  1. 在 Google Cloud Console 中,转到 Monitoring 或使用下面的按钮:
    转到 Monitoring
  2. 在 Monitoring 导航窗格中,点击 Metrics Explorer
  3. 查找资源类型和指标文本框中输入受监控资源的名称。

在指定指标时,您可以通过提供 OpenCensus 视图的名称来限制搜索范围。如需了解详情,请参阅选择指标

检索指标描述符

您可以使用 Monitoring API 直接检索指标数据。 若要检索指标数据,您需要知道 OpenCensus 指标所导出的 Cloud Monitoring 名称。

获取此信息的一种方法是检索导出器创建的指标描述符,并找到 type 字段的值。此值包含从中导出相应指标的 OpenCensus 视图的名称。如需详细了解指标描述符,请参阅 MetricDescriptor

您可以使用 metricDescriptors.list 方法参考页面上的 API Explorer(试用此 API)微件,查看为导出指标创建的指标描述符。要使用此工具检索 OpenCensus 指标的指标描述符,请执行以下操作:

  1. name 字段中输入项目名称:projects/[PROJECT_ID]。此文档使用 ID 为 a-gcp-project 的项目。

  2. filter 字段中输入过滤条件。OpenCensus 视图的名称将成为指标名称的一部分,因此您可以提供如下过滤条件,以便使用该名称来限制列表内容:

    metric.type=has_substring("task_latency_distribution")

    任何项目中都有很多指标描述符。使用 OpenCensus 视图名称中的子字符串进行过滤可以排除其中的大多数描述符。

  3. 点击执行按钮。

返回的指标描述符如下所示:

    {
      "metricDescriptors": [
        {
          "name": "projects/a-gcp-project/metricDescriptors/custom.googleapis.com/opencensus/task_latency_distribution",
          "labels": [
            {
              "key": "opencensus_task",
              "description": "Opencensus task identifier"
            }
          ],
          "metricKind": "CUMULATIVE",
          "valueType": "DISTRIBUTION",
          "unit": "ms",
          "description": "The distribution of the task latencies",
          "displayName": "OpenCensus/task_latency_distribution",
          "type": "custom.googleapis.com/opencensus/task_latency_distribution"
        }
      ]
    }

指标描述符中的这一行代码告诉您 Cloud Monitoring 中指标类型的名称:

    "type": "custom.googleapis.com/opencensus/task_latency_distribution"

利用此信息,您可以随后手动检索与此指标类型关联的数据。该数据也会显示在此指标的图表上。

检索指标数据

要手动检索某一指标类型的时间序列数据,您可以使用 timeSeries.list 方法参考页面上的试用此 API 工具:

  1. name 字段中输入您的项目名称:projects/[PROJECT_ID]
  2. filter 字段中输入所需指标类型的过滤条件:metric.type="custom.googleapis.com/opencensus/task_latency_distribution"
    • metric.type 键是时间序列中嵌入的一个类型字段。有关详细信息,请参阅TimeSeries
    • 该值是从指标描述符中提取的 type 值(请参阅检索指标描述符)。
  3. 指定以下字段的值,以输入检索的时间范围:
    • interval.endTime 为时间戳,例如:2018-10-11T15:48:38-04:00
    • interval.startTime(必须早于 interval.endTime
  4. 点击执行按钮。

此类检索的结果如下所示:

    {
      "timeSeries": [
        {
          "metric": {
            "labels": {
              "opencensus_task": "java-3424@docbuild"
            },
            "type": "custom.googleapis.com/opencensus/task_latency_distribution"
          },
          "resource": {
            "type": "gce_instance",
            "labels": {
              "instance_id": "2455918024984027105",
              "zone": "us-east1-b",
              "project_id": "a-gcp-project"
            }
          },
          "metricKind": "CUMULATIVE",
          "valueType": "DISTRIBUTION",
          "points": [
            {
              "interval": {
                "startTime": "2019-04-04T17:49:34.163Z",
                "endTime": "2019-04-04T17:50:42.917Z"
              },
              "value": {
                "distributionValue": {
                  "count": "100",
                  "mean": 2610.11,
                  "sumOfSquaredDeviation": 206029821.78999996,
                  "bucketOptions": {
                    "explicitBuckets": {
                      "bounds": [
                        0,
                        100,
                        200,
                        400,
                        1000,
                        2000,
                        4000
                      ]
                    }
                  },
                  "bucketCounts": [
                    "0",
                    "0",
                    "1",
                    "6",
                    "13",
                    "15",
                    "44",
                    "21"
                  ]
                }
              }
            }
          ]
        },
        [ ... data from additional program runs deleted ...]
      ]
    }

此处返回的数据包括以下内容:

  • 所收集数据涉及的受监控资源的信息。 OpenCensus 可以自动检测 gce_instancek8s_containeraws_ec2_instance 这些受监控的资源。此数据来自于 Compute Engine 实例上运行的程序。如需了解如何使用其他受监控的资源,请参阅为导出程序设置受监控的资源
  • 有关指标种类和值类型的说明。
  • 在请求的时间间隔内收集的实际数据点。