Creating Custom Metrics

To define a custom metric, use the metricDescriptors.create API method to create a custom metric descriptor. While it is possible to simply write time series data for your metric and let Stackdriver Monitoring auto-create the metric descriptor, you should know what information goes into a metric definition. After creating your new custom metric descriptor, you can use the metric with the metric descriptor API methods and the time series API methods. You can also use the metric for charting and alerting in Stackdriver Monitoring.

Custom metric names

When you create a custom metric, you assign it a custom metric type name that is unique among the custom metrics in your GCP project. The type name must begin with custom.googleapis.com/, followed by either a simple name or a path name. Path names can help you to organize your custom metrics, but they are handled just like simple names. See Metric names for more details. Here are examples of the two kinds of type names:

custom.googleapis.com/cpu_utilization
custom.googleapis.com/instance/cpu/utilization

Your custom metrics also have resource names that are unique among all GCP resources in all projects. The format of a resource name for a metric is:

projects/[PROJECT_ID]/metricDescriptors/[METRIC_TYPE]

If the previous custom metric examples were created in project my-project-id, then their resource names would be the following:

projects/my-project-id/metricDescriptors/custom.googleapis.com/cpu_utilization
projects/my-project-id/metricDescriptors/custom.googleapis.com/instance/cpu/utilization

When a resource name is used in a REST API URL, such as metricDescriptors.get, you have to URL-encode the metric type name, changing / to %2F:

projects/my-project-id/metricDescriptors/custom.googleapis.com%2Fcpu_utilization
projects/my-project-id/metricDescriptors/custom.googleapis.com%2Finstance%2Fcpu%2Futilization

Name or type? In the Stackdriver Monitoring API, metric type name fields are usually labeled type and resource name fields are usually labeled name. However, in conversation and some documentation, the type name is often called the "metric name."

Defining your custom metric

To create your custom metric, you must provide a MetricDescriptor object that specifies various information about the metric. Here is the information you'll need:

  • Choose a type name for the custom metric, as described in the previous section.

  • Choose a project in which to define your custom metric and write its time series data. Custom metrics for AWS should be created in the AWS connector project for your AWS account. The project you choose must also be a member of the Stackdriver account where you monitor the metric. If you need the same metric in several projects, then make identical definitions of the metric in each project.

  • Choose a display name and description of the metric. The display name is used in Stackdriver Monitoring's user interface.

  • Choose the metric's kind, value type, and (optionally) units. Not all value types and metric kinds are supported for custom metrics. See Metric kind.

  • Choose the metric's labels—their names, value types, and descriptions.

  • Choose which monitored resource objects you'll include with your metric's time series data points. You should think about this before creating your metric descriptor. The monitored resource type you use affects which metric labels you need. For example, if you include a VM instance object with your data, then you won't need a metric label to specify the instance. For more information, see Choosing a monitored resource type.

For more help in making these choices, you can browse the built-in metrics and look at their time series data.

Choosing a monitored resource type

Each of your metric's data points must include a monitored resource object. Points from different monitored resource objects are held in different time series.

You can use only the following monitored resource types in your custom metrics:

  • gce_instance Google Compute Engine instance.
  • gke_container Google Container Engine container.
  • dataflow_job Dataflow job.
  • aws_ec2_instance Amazon EC2 instance.
  • global Anything else.

A common practice is to use the monitored resource object that represents the physical resource where your application code is running. This has several advantages:

  • You get better performance.
  • You avoid out-of-order data caused by multiple instances writing to the same time series.
  • Your custom metric data can be grouped with other metric data from the same instance.

If none of the instance-related resource types are appropriate, use global. For example, Google App Engine users should use global because the resource type gae_app is not permitted in custom metrics.

Call the create method

With your choices made, call the metricDescriptors.create method, passing in a MetricDescriptor object. Alternatively, see Auto-creation of custom metrics. After Stackdriver Monitoring creates the new metric descriptor, you cannot change the descriptor except by deleting and recreating it, in which case you lose all the time series data previously collected for the old metric descriptor.

It is usually an error to call metricDescriptors.create using the same type name as an existing custom metric descriptor. However, if all the fields of the new MetricDescriptor object match the fields of the existing descriptor exactly, then it is not an error but it has no effect.

Example

In the following example, you create a gauge custom metric, custom.googleapis.com/stores/daily_sales. The metric has a single dimension label, store_id.

Protocol

The following are the sample parameters to metricDescriptors.create:

  • name (URL): projects/[PROJECT_ID]
  • Request body: supply a MetricDescriptor object such as the following:

    {
      "name": "",
      "description": "Daily sales records from all branch stores.",
      "displayName": "daily sales",
      "type": "custom.googleapis.com/stores/daily_sales",
      "metricKind": "GAUGE",
      "valueType": "DOUBLE",
      "unit": "{USD}",
      "labels": [
        {
          "key": "store_id",
          "valueType": "STRING",
          "description": "The ID of the store."
        },
      ],
    }
    

When creating a new custom metric, the name field in the MetricDescriptor is ignored and can be omitted. The create method returns the new metric descriptor with the name field filled in, which in this example would be the following:

"name": "projects/[PROJECT_ID]/metricDescriptors/custom.googleapis.com/stores/daily_sales"

You use this name, for example, if you want to get a metric's descriptor.

Try It! (You might have to type ENTER in the request body field to see the contents.)

C#

For more on installing and creating a Stackdriver Monitoring client, refer to Stackdriver Monitoring Client Libraries.

        public static object CreateMetric(string projectId,
            string metricType = "custom.googleapis.com/stores/daily_sales")
        {
            // Create client.
            MetricServiceClient metricServiceClient = MetricServiceClient.Create();

            // Prepare custom metric descriptor.      
            MetricDescriptor metricDescriptor = new MetricDescriptor();
            metricDescriptor.Name = "Daily Sales";
            metricDescriptor.DisplayName = "Daily Sales";
            metricDescriptor.Description = "Daily sales records from all branch stores.";
            metricDescriptor.MetricKind = MetricKind.Gauge;
            metricDescriptor.ValueType = MetricDescriptor.Types.ValueType.Double;
            metricDescriptor.Type = metricType;
            metricDescriptor.Unit = "{USD}";
            LabelDescriptor labels = new LabelDescriptor();
            labels.Key = "store_id";
            labels.ValueType = LabelDescriptor.Types.ValueType.String;
            labels.Description = "The ID of the store.";
            metricDescriptor.Labels.Add(labels);
            CreateMetricDescriptorRequest request = new CreateMetricDescriptorRequest
            {
                ProjectName = new ProjectName(projectId),
            };
            request.MetricDescriptor = metricDescriptor;
            // Make the request.
            MetricDescriptor response = metricServiceClient.CreateMetricDescriptor(request);
            Console.WriteLine("Done creating metric descriptor:");
            Console.WriteLine(JObject.Parse($"{response}").ToString());
            return 0;
        }

Node.js

For more on installing and creating a Stackdriver Monitoring client, refer to Stackdriver Monitoring Client Libraries.

// Imports the Google Cloud client library
const Monitoring = require('@google-cloud/monitoring');

// Creates a client
const client = Monitoring.v3.metric();

// The Google Cloud Platform project on which to execute the request
// const projectId = 'YOUR_PROJECT_ID';

const request = {
  name: client.projectPath(projectId),
  metricDescriptor: {
    description: 'Daily sales records from all branch stores.',
    displayName: 'Daily Sales',
    type: 'custom.googleapis.com/stores/daily_sales',
    metricKind: 'GAUGE',
    valueType: 'DOUBLE',
    unit: '{USD}',
    labels: [
      {
        key: 'store_id',
        valueType: 'STRING',
        description: 'The ID of the store.'
      }
    ]
  }
};

// Creates a custom metric descriptor
client.createMetricDescriptor(request)
  .then((results) => {
    const descriptor = results[0];

    console.log('Created custom Metric:\n');
    console.log(`Name: ${descriptor.displayName}`);
    console.log(`Description: ${descriptor.description}`);
    console.log(`Type: ${descriptor.type}`);
    console.log(`Kind: ${descriptor.metricKind}`);
    console.log(`Value Type: ${descriptor.valueType}`);
    console.log(`Unit: ${descriptor.unit}`);
    console.log('Labels:');
    descriptor.labels.forEach((label) => {
      console.log(`  ${label.key} (${label.valueType}) - ${label.description}`);
    });
  })
  .catch((err) => {
    console.error('ERROR:', err);
  });

PHP

For more on installing and creating a Stackdriver Monitoring client, refer to Stackdriver Monitoring Client Libraries.

use Google\Cloud\Monitoring\V3\MetricServiceClient;
use Google\Api\LabelDescriptor;
use Google\Api\LabelDescriptor_ValueType;
use Google\Api\MetricDescriptor;
use Google\Api\MetricDescriptor_MetricKind;
use Google\Api\MetricDescriptor_ValueType;

/**
 * Adds a new column to the Albums table in the example database.
 * Example:
 * ```
 * create_metric($projectId);
 * ```
 *
 * @param string $projectId Your project ID
 */
function create_metric($projectId)
{
    $metrics = new MetricServiceClient([
        'projectId' => $projectId,
    ]);

    $projectName = $metrics->formatProjectName($projectId);

    $descriptor = new MetricDescriptor();
    $descriptor->setDescription('Daily sales records from all branch stores.');
    $descriptor->setDisplayName('Daily Sales');
    $descriptor->setType('custom.googleapis.com/stores/daily_sales');
    $descriptor->setMetricKind(MetricDescriptor_MetricKind::GAUGE);
    $descriptor->setValueType(MetricDescriptor_ValueType::DOUBLE);
    $descriptor->setUnit('{USD}');
    $label = new LabelDescriptor();
    $label->setKey('store_id');
    $label->setValueType(LabelDescriptor_ValueType::STRING);
    $label->setDescription('The ID of the store.');
    $labels = [$label];
    $descriptor->setLabels($labels);

    $descriptor = $metrics->createMetricDescriptor($projectName, $descriptor);
    printf('Created a metric: ' . $descriptor->getName() . PHP_EOL);
}

Python

For more on installing and creating a Stackdriver Monitoring client, refer to Stackdriver Monitoring Client Libraries.

client = monitoring.Client()
descriptor = client.metric_descriptor(
    'custom.googleapis.com/my_metric',
    metric_kind=monitoring.MetricKind.GAUGE,
    value_type=monitoring.ValueType.DOUBLE,
    description='This is a simple example of a custom metric.')
descriptor.create()

Java

For more on installing and creating a Stackdriver Monitoring client, refer to Stackdriver Monitoring Client Libraries.

// Your Google Cloud Platform project ID
String projectId = System.getProperty("projectId");
String metricType = CUSTOM_METRIC_DOMAIN + "/" + type;

final MetricServiceClient client = MetricServiceClient.create();
ProjectName name = ProjectName.create(projectId);

MetricDescriptor descriptor = MetricDescriptor.newBuilder()
    .setType(metricType)
    .setDescription("This is a simple example of a custom metric.")
    .setMetricKind(MetricDescriptor.MetricKind.GAUGE)
    .setValueType(MetricDescriptor.ValueType.DOUBLE)
    .build();

CreateMetricDescriptorRequest request = CreateMetricDescriptorRequest.newBuilder()
    .setNameWithProjectName(name)
    .setMetricDescriptor(descriptor)
    .build();

client.createMetricDescriptor(request);

See Troubleshooting API calls if you have difficulty.

Next step: See Writing metric data.

Deleting metrics

To delete a custom metric, call the metricDescriptors.delete method.

Protocol

You can only delete custom metric types. Here is an example of deleting your stores/daily_sales metric created in the previous section:

  • name: projects/[PROJECT_ID]/metricDescriptors/custom.googleapis.com/stores/daily_sales

Try It!

C#

For more on installing and creating a Stackdriver Monitoring client, refer to Stackdriver Monitoring Client Libraries.

public static object DeleteMetric(string projectId, string metricType)
{
    // Create client.
    MetricServiceClient metricServiceClient = MetricServiceClient.Create();
    // Initialize request argument(s).
    MetricDescriptorName name = new MetricDescriptorName(projectId, metricType);
    // Make the request.
    metricServiceClient.DeleteMetricDescriptor(name);
    Console.WriteLine($"Done deleting metric descriptor: {name}");
    return 0;
}

Node.js

For more on installing and creating a Stackdriver Monitoring client, refer to Stackdriver Monitoring Client Libraries.

// Imports the Google Cloud client library
const Monitoring = require('@google-cloud/monitoring');

// Creates a client
const client = Monitoring.v3.metric();

// The Google Cloud Platform project on which to execute the request
// const projectId = 'YOUR_PROJECT_ID';

// The ID of the Metric Descriptor to delete, e.g.
// const metricId = 'custom.googleapis.com/stores/daily_sales';

const request = {
  name: client.metricDescriptorPath(projectId, metricId)
};

// Deletes a metric descriptor
client.deleteMetricDescriptor(request)
  .then((results) => {
    console.log(`Deleted ${metricId}`);
  })
  .catch((err) => {
    console.error('ERROR:', err);
  });

PHP

For more on installing and creating a Stackdriver Monitoring client, refer to Stackdriver Monitoring Client Libraries.

use Google\Cloud\Monitoring\V3\MetricServiceClient;

/**
 * Adds a new column to the Albums table in the example database.
 * Example:
 * ```
 * delete_metric($projectId, $databaseId);
 * ```
 *
 * @param string $projectId Your project ID
 * @param string $metricId  The ID of the Metric Descriptor to delete
 */
function delete_metric($projectId, $metricId)
{
    $metrics = new MetricServiceClient([
        'projectId' => $projectId,
    ]);

    $metricPath = $metrics->formatMetricDescriptorName($projectId, $metricId);
    $ret = $metrics->deleteMetricDescriptor($metricPath);

    printf('Deleted a metric: ' . $metricPath . PHP_EOL);
}

Python

For more on installing and creating a Stackdriver Monitoring client, refer to Stackdriver Monitoring Client Libraries.

client = monitoring.Client()

descriptor = client.metric_descriptor(descriptor_name)
descriptor.delete()

print('Deleted metric descriptor {}.'.format(descriptor_name))

Java

For more on installing and creating a Stackdriver Monitoring client, refer to Stackdriver Monitoring Client Libraries.

String projectId = System.getProperty("projectId");
final MetricServiceClient client = MetricServiceClient.create();
MetricDescriptorName metricName = MetricDescriptorName.create(projectId, name);
client.deleteMetricDescriptor(metricName);
System.out.println("Deleted descriptor " + name);

See Troubleshooting API calls if you have difficulty.

You cannot delete the time series data from your custom metric, but the data expires and is deleted according to the data retention policy.

Writing metric data

You can write data only to custom metric types. Use the timeSeries.create method to write the data points. The method appends a new data point to an existing time series, creating the time series if needed.

You write data points by passing a list of TimeSeries objects to timeSeries.create. Each object must specify a different time series, either in different metric types or in the same metric type:

  • Each time series is identified by the contents of the metric and resource fields of the TimeSeries object.
  • Omit the fields metricKind and valueType; they are ignored when writing data points.
  • Each TimeSeries object must contain only a single Point object:

    • The point's value and time interval must be consistent with the metric type's definition. For information on time intervals for different metric kinds, see Metric kinds.
    • The point's time interval must be later than any point already in the time series.
    • The end time of the interval must not be more than 24 hours in the past or more than five minutes in the future.
  • If you want to write more than one point to the same time series, then use a separate call to the timeSeries.create method for each point.

Best practice: When adding multiple data points to the same time series, do not add them faster than once per minute. If you are adding data points to different time series, then there is no rate limitation.

Protocol

To write a point to the stores/daily_sales custom metric used in the Creating metrics section, pass the following sample parameters to timeSeries.create method:

  • name: projects/[PROJECT_ID]
  • request body: include a list of TimeSeries objects. The following sample has only one time series in the list.

    {
     "timeSeries": [
      {
       "metric": {
        "type": "custom.googleapis.com/stores/daily_sales",
        "labels": {
         "store_id": "Pittsburgh"
        }
       },
       "resource": {
        "type": "global",
        "labels": {
         "project_id": "[PROJECT_ID]"
        }
       },
       "points": [
        {
         "interval": {
          "endTime": "2016-06-01T10:00:00-04:00"
         },
         "value": {
          "doubleValue": 123.45
         }
        }
       ]
      }
     ]
    }
    

Try It!

C#

For more on installing and creating a Stackdriver Monitoring client, refer to Stackdriver Monitoring Client Libraries.

        public static object WriteTimeSeriesData(string projectId)
        {
            // Create client.
            MetricServiceClient metricServiceClient = MetricServiceClient.Create();
            // Initialize request argument(s).
            ProjectName name = new ProjectName(projectId);
            // Prepare a data point. 
            Point dataPoint = new Point();
            TypedValue salesTotal = new TypedValue();
            salesTotal.DoubleValue = 123.45;
            dataPoint.Value = salesTotal;
            Timestamp timeStamp = new Timestamp();
            timeStamp.Seconds = (long)(DateTime.UtcNow - s_unixEpoch).TotalSeconds;
            TimeInterval interval = new TimeInterval();
            interval.EndTime = timeStamp;
            dataPoint.Interval = interval;

            // Prepare custom metric.
            Metric metric = new Metric();
            metric.Type = "custom.googleapis.com/stores/daily_sales";
            metric.Labels.Add("store_id", "Pittsburgh");

            // Prepare monitored resource.
            MonitoredResource resource = new MonitoredResource();
            resource.Type = "global";
            resource.Labels.Add("project_id", projectId);

            // Create a new time series using inputs.
            TimeSeries timeSeriesData = new TimeSeries();
            timeSeriesData.Metric = metric;
            timeSeriesData.Resource = resource;
            timeSeriesData.Points.Add(dataPoint);

            // Add newly created time series to list of time series to be written.
            IEnumerable<TimeSeries> timeSeries = new List<TimeSeries> { timeSeriesData };
            // Write time series data.
            metricServiceClient.CreateTimeSeries(name, timeSeries);
            Console.WriteLine("Done writing time series data:");
            Console.WriteLine(JObject.Parse($"{timeSeriesData}").ToString());
            return 0;
        }

Node.js

For more on installing and creating a Stackdriver Monitoring client, refer to Stackdriver Monitoring Client Libraries.

// Imports the Google Cloud client library
const Monitoring = require('@google-cloud/monitoring');

// Creates a client
const client = Monitoring.v3.metric();

// The Google Cloud Platform project on which to execute the request
// const projectId = 'YOUR_PROJECT_ID';

const dataPoint = {
  interval: {
    endTime: {
      seconds: Date.now() / 1000
    }
  },
  value: {
    doubleValue: 123.45
  }
};

const timeSeriesData = {
  metric: {
    type: 'custom.googleapis.com/stores/daily_sales',
    labels: {
      store_id: 'Pittsburgh'
    }
  },
  resource: {
    type: 'global',
    labels: {
      project_id: projectId
    }
  },
  points: [
    dataPoint
  ]
};

const request = {
  name: client.projectPath(projectId),
  timeSeries: [
    timeSeriesData
  ]
};

// Writes time series data
client.createTimeSeries(request)
  .then((results) => {
    console.log(`Done writing time series data.`);
  })
  .catch((err) => {
    console.error('ERROR:', err);
  });

Python

For more on installing and creating a Stackdriver Monitoring client, refer to Stackdriver Monitoring Client Libraries.

client = monitoring.Client()
resource = client.resource(
    'gce_instance',
    labels={
        'instance_id': '1234567890123456789',
        'zone': 'us-central1-f',
    }
)

metric = client.metric(
    type_='custom.googleapis.com/my_metric',
    labels={
    }
)
client.write_point(metric, resource, 3.14)

Java

For more on installing and creating a Stackdriver Monitoring client, refer to Stackdriver Monitoring Client Libraries.

String projectId = System.getProperty("projectId");
// Instantiates a client
MetricServiceClient metricServiceClient = MetricServiceClient.create();

// Prepares an individual data point
TimeInterval interval = TimeInterval.newBuilder()
    .setEndTime(Timestamps.fromMillis(System.currentTimeMillis()))
    .build();
TypedValue value = TypedValue.newBuilder()
    .setDoubleValue(123.45)
    .build();
Point point = Point.newBuilder()
    .setInterval(interval)
    .setValue(value)
    .build();

List<Point> pointList = new ArrayList<>();
pointList.add(point);

ProjectName name = ProjectName.create(projectId);

// Prepares the metric descriptor
Map<String, String> metricLabels = new HashMap<String, String>();
Metric metric = Metric.newBuilder()
    .setType("custom.googleapis.com/my_metric")
    .putAllLabels(metricLabels)
    .build();

// Prepares the monitored resource descriptor
Map<String, String> resourceLabels = new HashMap<String, String>();
resourceLabels.put("instance_id", "1234567890123456789");
resourceLabels.put("zone", "us-central1-f");

MonitoredResource resource = MonitoredResource.newBuilder()
    .setType("gce_instance")
    .putAllLabels(resourceLabels)
    .build();

// Prepares the time series request
TimeSeries timeSeries = TimeSeries.newBuilder()
    .setMetric(metric)
    .setResource(resource)
    .addAllPoints(pointList)
    .build();

List<TimeSeries> timeSeriesList = new ArrayList<>();
timeSeriesList.add(timeSeries);

CreateTimeSeriesRequest request = CreateTimeSeriesRequest.newBuilder()
    .setNameWithProjectName(name)
    .addAllTimeSeries(timeSeriesList)
    .build();

// Writes time series data
metricServiceClient.createTimeSeries(request);
System.out.println("Done writing time series value.");

See Troubleshooting API calls if you have difficulty.

Auto-creation of custom metrics

If you write metric data to a custom metric type that does not yet exist, then a new custom metric type is created for you automatically. Auto-creation is limited in the types of metrics it can create.

Specifically, during a call to timeSeries.create, if a TimeSeries object includes a Metric object for a nonexistent custom metric type, then Stackdriver Monitoring creates a new custom metric type whose MetricDescriptor has the following fields:

  • type: The type is copied from the Metric object's type field.
  • name: The name is created from the project ID in the method call and the custom metric type.
  • labels: The labels are those that appear in the Metric object. Each label descriptor in the new metric descriptor has the following fields:
    • key: the label key in the Metric object.
    • valueType: STRING
    • description: not set
  • metricKind: The default metric kind is GAUGE, but if you specify the metricKind parameter of the TimeSeries object, then the new metric has that kind. You can specify only GAUGE and CUMULATIVE kinds.
  • valueType: The value type is taken from the typed value of the Point being written. which must be BOOL, INT64, DOUBLE, or DISTRIBUTION. If you specify a value type in the valueType field of the TimeSeries, then that type must match the type of the Point.
  • unit: not set
  • description: "Auto created custom metric.".
  • displayName: not set

In a single timeSeries.create call you can include multiple TimeSeries objects that reference the same nonexistent custom metric type. In that case, the new type's fields are set in the same way, except that the labels in the new metric descriptor are the union of all the labels appearing in the Metric objects in the multiple time series.

After the custom metric type is created, you cannot change the new descriptor. For example, you cannot add or remove labels from the descriptor. You can delete the descriptor and start over.

Monitor your resources on the go

Get the Google Cloud Console app to help you manage your projects.

Send feedback about...

Stackdriver Monitoring