This document provides a detailed reference for the gRPC API exposed by the Cloud-Query service.
Note: the CloudQuery service requires PostgreSQL-backed features to be enabled. If you start the server with --database-enabled=false, the CloudQuery service will not be registered.
Cloud-Query exposes a gRPC service with endpoints for querying, schema retrieval, table listing, and data extraction across different cloud providers.
service CloudQuery {
rpc Query(QueryInput) returns (QueryResult) {}
rpc Schema(SchemaInput) returns (SchemaOutput) {}
rpc Tables(TablesInput) returns (TablesOutput) {}
rpc Extract(ExtractInput) returns (stream ExtractOutput) {}
}The service supports the following cloud providers:
- AWS
- Azure
- GCP
- vSphere
The Connection message represents a connection to a cloud provider with appropriate credentials.
message Connection {
string provider = 1;
oneof credentials {
AwsCredentials aws = 2;
AzureCredentials azure = 3;
GcpCredentials gcp = 4;
VSphereCredentials vsphere = 5;
}
}message AwsCredentials {
optional string access_key_id = 1;
optional string secret_access_key = 2;
optional string region = 3;
repeated string regions = 4;
optional string assume_role_arn = 5;
}AWS can also use the default AWS credential chain when static credentials are omitted.
#### Azure Credentials
```protobuf
message AzureCredentials {
string subscription_id = 1;
string tenant_id = 2;
string client_id = 3;
string client_secret = 4;
}
message GcpCredentials {
string service_account_json_b64 = 1;
string project = 2;
}message VSphereCredentials {
string server = 1;
string user = 2;
string password = 3;
optional string allow_unverified_ssl = 4;
}allow_unverified_ssl accepts boolean string values such as "true" or "false". When omitted, the vSphere plugin default is used.
The Query method allows you to execute SQL queries against cloud resources.
message QueryInput {
Connection connection = 1;
string query = 2;
}message QueryResult {
string result = 2;
}result is a JSON array of row objects.
Using curl to make a gRPC request (with grpcurl):
# Using grpcurl to make a Query request
grpcurl -d '{
"connection": {
"provider": "aws",
"aws": {
"access_key_id": "YOUR_ACCESS_KEY",
"secret_access_key": "YOUR_SECRET_KEY"
}
},
"query": "SELECT * FROM aws_ec2_instances"
}' -plaintext localhost:9192 cloudquery.CloudQuery/QueryUsing Postman:
- Create a new gRPC request
- Set the server URL to
localhost:9192 - Set the service to
cloudquery.CloudQuery - Select the
Querymethod - Set the request body:
{
"connection": {
"provider": "aws",
"aws": {
"access_key_id": "YOUR_ACCESS_KEY",
"secret_access_key": "YOUR_SECRET_KEY"
}
},
"query": "SELECT * FROM aws_ec2_instances"
}vSphere example:
grpcurl -d '{
"connection": {
"provider": "vsphere",
"vsphere": {
"server": "https://vcenter.example.com/sdk",
"user": "administrator@vsphere.local",
"password": "YOUR_PASSWORD",
"allow_unverified_ssl": "true"
}
},
"query": "SELECT name, power, guest_full_name FROM vsphere_vm"
}' -plaintext localhost:9192 cloudquery.CloudQuery/QueryThe Schema method retrieves the schema information for cloud resources.
message SchemaInput {
Connection connection = 1;
optional string table = 2;
}message SchemaColumn {
string column = 1;
string type = 2;
}
message SchemaOutput {
repeated SchemaResult result = 1;
}
message SchemaResult {
string table = 1;
repeated SchemaColumn columns = 2;
}Using curl (with grpcurl):
# Using grpcurl to make a Schema request
grpcurl -d '{
"connection": {
"provider": "aws",
"aws": {
"access_key_id": "YOUR_ACCESS_KEY",
"secret_access_key": "YOUR_SECRET_KEY"
}
},
"table": "aws_ec2_%"
}' -plaintext localhost:9192 cloudquery.CloudQuery/SchemaUsing Postman:
- Create a new gRPC request
- Set the server URL to
localhost:9192 - Set the service to
cloudquery.CloudQuery - Select the
Schemamethod - Set the request body:
{
"connection": {
"provider": "aws",
"aws": {
"access_key_id": "YOUR_ACCESS_KEY",
"secret_access_key": "YOUR_SECRET_KEY"
}
},
"table": "aws_ec2_%"
}The Tables method lists imported foreign tables for a provider connection.
message TablesInput {
Connection connection = 1;
optional string table = 2;
}message TablesOutput {
repeated string result = 1;
}The Extract method extracts raw data from cloud resources.
message ExtractInput {
Connection connection = 1;
}message ExtractOutput {
string type = 1;
string result = 2;
string id = 3;
repeated string links = 4;
}Extract is currently implemented for AWS resources. Other providers, including vSphere, return UNIMPLEMENTED for this endpoint.
Using curl (with grpcurl):
# Using grpcurl to make an Extract request
grpcurl -d '{
"connection": {
"provider": "aws",
"aws": {
"access_key_id": "YOUR_ACCESS_KEY",
"secret_access_key": "YOUR_SECRET_KEY"
}
}
}' -plaintext localhost:9192 cloudquery.CloudQuery/ExtractUsing Postman:
- Create a new gRPC request
- Set the server URL to
localhost:9192 - Set the service to
cloudquery.CloudQuery - Select the
Extractmethod - Set the request body:
{
"connection": {
"provider": "aws",
"aws": {
"access_key_id": "YOUR_ACCESS_KEY",
"secret_access_key": "YOUR_SECRET_KEY"
}
}
}The Cloud-Query API uses standard gRPC error codes. Based on the server implementation, the following error codes may be returned:
-
INVALID_ARGUMENT: Returned when the request contains invalid parameters, such as:- Invalid or missing provider information
- Invalid connection configuration
- Unsupported provider specified
-
INTERNAL: Returned when there's an internal server error, such as:- Failure to connect to the specified provider
- Database connection issues
- Execution errors during query processing
-
UNAVAILABLE: The service is temporarily unavailable (e.g., during server startup or shutdown)
Each error response includes a descriptive message explaining the specific issue encountered.
ToolQuery exposes a gRPC service for querying external observability tools (metrics, logs, traces) and invoking cloud functions across AWS/GCP/Azure.
ToolQuery support varies by operation:
| Tool | Metrics | Metric Label Search | Logs | Traces | Notes |
|---|---|---|---|---|---|
| Prometheus | Yes | Yes | No | No | Prometheus HTTP API range queries, metric name search, and metric-scoped label search |
| Datadog | Yes | Yes | Yes | Yes | Datadog Metrics (v1), Logs (v2), Spans (v2) APIs, metric search, and metric tag search |
| Elasticsearch | No | No | Yes | No | Elasticsearch typed Search API with query string |
| Loki | No | No | Yes | No | Loki HTTP query_range API |
| Tempo | No | No | No | Yes | Tempo HTTP search + trace fetch |
| Jaeger | No | No | No | Yes | Jaeger Query v3 REST API with structured filters |
| Dynatrace | Yes | No | Yes | Yes | Dynatrace Grail Query API (DQL via /platform/storage/query/v1/query:*) |
| CloudWatch | Yes | Yes | Yes | No | AWS SDK v2 CloudWatch + Logs Insights |
| Azure | Yes | Yes | Yes | No | Azure Monitor azmetrics + azlogs; Managed Prometheus delegates to Prometheus |
ToolQuery uses the following clients/SDKs and endpoints for each integration:
- Prometheus:
prometheus/client_golangHTTP API client,QueryRange(Prometheus/api/v1/query_range). Supports bearer token or basic auth. - Datadog:
datadog-api-client-gov2 SDK.- Metrics: v1
QueryMetrics. - Metrics label search: v2
ListTagsByMetricName. - Logs: v2
ListLogs. - Traces: v2
ListSpans.
- Metrics: v1
- Elasticsearch:
elastic/go-elasticsearchv9 typed client,Search(Elasticsearch/_search) with aquery_stringquery and@timestamprange filter. Requires API key. - Loki: REST client to
/loki/api/v1/query_range, bearer token auth, optionalX-Scope-OrgIDheader for tenancy. - Tempo: REST client to
/api/searchand/api/traces/{traceID}, bearer token auth, optionalX-Scope-OrgIDheader for tenancy. - Jaeger: REST client to stable Query v3 endpoint
POST /api/v3/traceswith structured trace filters. - Dynatrace: REST client to
/platform/storage/query/v1/query:executeand/platform/storage/query/v1/query:poll(Grail DQL), bearer token auth. - CloudWatch: AWS SDK for Go v2.
- Logs: CloudWatch Logs Insights
StartQuery+GetQueryResults. - Metrics: CloudWatch
GetMetricData(query expression). - Metrics search: CloudWatch
ListMetricswith bounded pagination and local filtering. - Metrics label search: CloudWatch
ListMetricsdimensions with bounded pagination.
- Logs: CloudWatch Logs Insights
- Azure: Azure Monitor Go SDK via Azure AD client credentials.
- Metrics:
monitor/query/azmetricsQueryResources. - Metrics search:
resourcemanager/monitor/armmonitormetric definitions pager. - Metrics label search: metric definitions dimensions for label names;
azmetrics.QueryResourcestime-series metadata for native Azure label values; Managed Prometheus delegates to Prometheus label APIs. - Logs:
monitor/query/azlogsQueryResource.
- Metrics:
service ToolQuery {
rpc Metrics(MetricsQueryInput) returns (MetricsQueryOutput) {}
rpc MetricsSearch(MetricsSearchInput) returns (MetricsSearchOutput) {}
rpc MetricsLabelSearch(MetricsLabelSearchInput) returns (MetricsLabelSearchOutput) {}
rpc Logs(LogsQueryInput) returns (LogsQueryOutput) {}
rpc Traces(TracesQueryInput) returns (TracesQueryOutput) {}
rpc InvokeLambda(InvokeLambdaInput) returns (InvokeLambdaOutput) {}
rpc RunLua(RunLuaInput) returns (RunLuaOutput) {}
rpc RunPython(RunPythonInput) returns (RunPythonOutput) {}
}message ElasticConnection {
string url = 1;
string username = 2;
string password = 3;
string index = 4;
}
message DatadogConnection {
optional string site = 1;
string apiKey = 2;
optional string appKey = 3;
}
message PrometheusConnection {
string url = 1;
optional string token = 2;
optional string username = 3;
optional string password = 4;
optional string tenant_id = 5;
}
message LokiConnection {
string url = 1;
optional string token = 2;
optional string tenant_id = 3;
optional string username = 4;
optional string password = 5;
}
message TempoConnection {
string url = 1;
optional string token = 2;
optional string tenant_id = 3;
optional string username = 4;
optional string password = 5;
}
message SplunkConnection {
string url = 1;
optional string token = 2;
optional string username = 3;
optional string password = 4;
}
message DynatraceConnection {
string url = 1;
string platformToken = 2;
}
message CloudwatchConnection {
string region = 1;
repeated string log_group_names = 2;
optional string access_key_id = 3;
optional string secret_access_key = 4;
optional string role_arn = 5;
optional string external_id = 6;
optional string role_session_name = 7;
}
message AzureConnection {
string subscription_id = 1;
string tenant_id = 2;
string client_id = 3;
string client_secret = 4;
}
message JaegerConnection {
string url = 1;
optional string token = 2;
optional string username = 3;
optional string password = 4;
}Implementation notes:
- Datadog requires
apiKey.appKeyandsiteare optional. - Elasticsearch requires
url,username,password, andindex. - Prometheus requires
url; bearer token or basic auth are optional. - Loki and Tempo pass the token as a bearer token when set, and include
tenant_idasX-Scope-OrgIDwhen provided. - Splunk requires
urlplus either bearer token or basic auth username/password. - CloudWatch requires
region. Optional auth fields support static credentials and/or assume-role.- If static credentials are omitted, default AWS credential chain is used (including pod identity).
- For logs queries, either
log_group_namesmust be configured or the query must include aSOURCEcommand.
- Azure requires
subscription_id,tenant_id,client_id, andclient_secret.- Metrics and logs operations are resource-scoped and use per-request Azure options for
resource_id.
- Metrics and logs operations are resource-scoped and use per-request Azure options for
message ToolConnection {
oneof connection {
ElasticConnection elastic = 1;
DatadogConnection datadog = 2;
PrometheusConnection prometheus = 3;
LokiConnection loki = 4;
TempoConnection tempo = 5;
SplunkConnection splunk = 6;
DynatraceConnection dynatrace = 7;
CloudwatchConnection cloudwatch = 8;
AzureConnection azure = 9;
JaegerConnection jaeger = 10;
}
}
message TimeRange {
google.protobuf.Timestamp start = 1;
google.protobuf.Timestamp end = 2;
}message MetricsQueryInput {
ToolConnection connection = 1;
string query = 2;
TimeRange range = 3;
optional string step = 4;
optional MetricsOptions options = 5;
}
message MetricsOptions {
optional AzureMetricsOptions azure = 1;
}
message AzureMetricsOptions {
string resource_id = 1;
string metrics_namespace = 2;
optional string aggregation = 3;
optional string filter = 4;
optional string order_by = 5;
optional string roll_up_by = 6;
optional string metrics_endpoint = 7;
optional string prometheus_url = 8;
}message MetricPoint {
google.protobuf.Timestamp timestamp = 1;
string name = 2;
double value = 3;
map<string, string> labels = 4;
}
message MetricsQueryOutput {
repeated MetricPoint metrics = 1;
}
message MetricsSearchInput {
ToolConnection connection = 1;
string query = 2;
optional int64 limit = 3;
optional MetricsSearchOptions options = 4;
}
message MetricsSearchOptions {
optional AzureMetricsSearchOptions azure = 1;
}
message AzureMetricsSearchOptions {
string resource_id = 1;
optional string prometheus_url = 2;
}
message MetricsSearchResult {
string name = 1;
}
message MetricsSearchOutput {
repeated MetricsSearchResult metrics = 1;
}
message MetricsLabelSearchInput {
ToolConnection connection = 1;
string metric = 2;
optional string query = 3;
optional string label = 4;
optional int64 limit = 5;
optional MetricsLabelSearchOptions options = 6;
}
message MetricsLabelSearchOptions {
optional AzureMetricsLabelSearchOptions azure = 1;
}
message AzureMetricsLabelSearchOptions {
string resource_id = 1;
optional string metrics_namespace = 2;
optional string prometheus_url = 3;
optional string metrics_endpoint = 4;
}
message MetricsLabelSearchResult {
string name = 1;
}
message MetricsLabelSearchOutput {
repeated MetricsLabelSearchResult results = 1;
}Request:
grpcurl -d '{
"connection": {
"prometheus": {
"url": "http://vmauth-vm-auth.monitoring:8427/select/0/prometheus",
"username": "<USERNAME>",
"password": "<PASSWORD>"
}
},
"query": "container_memory_working_set_bytes{pod=\"deployment-operator-5f4c46cb48-mfpdv\", container=\"deployment-operator\"}",
"range": {
"start": "2026-02-20T11:00:00.000Z",
"end": "2026-02-20T11:30:00.000Z"
},
"step": "10m"
}' -plaintext localhost:9192 toolquery.ToolQuery/MetricsOutput:
{
"metrics": [
{
"labels": {
"metrics_path": "/metrics/cadvisor",
"node": "ip-10-0-21-32.eu-central-1.compute.internal",
"job": "kubelet",
"instance": "ip-10-0-21-32.eu-central-1.compute.internal",
"prometheus": "monitoring/vmetrics-agent-victoria-metrics-k8s-stack"
},
"timestamp": "2026-02-20T11:00:00.000Z",
"name": "container_memory_working_set_bytes",
"value": 267456512
},
{
"labels": {
"metrics_path": "/metrics/cadvisor",
"node": "ip-10-0-21-32.eu-central-1.compute.internal",
"job": "kubelet",
"instance": "ip-10-0-21-32.eu-central-1.compute.internal",
"prometheus": "monitoring/vmetrics-agent-victoria-metrics-k8s-stack"
},
"timestamp": "2026-02-20T11:10:00.000Z",
"name": "container_memory_working_set_bytes",
"value": 224718848
}
]
}Datadog's metrics API does not accept a step parameter. Any step value provided to ToolQuery is ignored for Datadog requests.
grpcurl -d '{
"connection": {
"datadog": {
"apiKey": "<API_KEY>",
"appKey": "<APP_KEY>"
}
},
"query": "system.cpu.idle{*}",
"range": {
"start": "2026-02-18T12:07:29.892Z",
"end": "2026-02-19T12:07:29.892Z"
}
}' -plaintext localhost:9192 toolquery.ToolQuery/Metrics{
"metrics": [
{
"labels": {
"*": ""
},
"timestamp": "2026-02-18T12:10:00.000Z",
"name": "system.cpu.idle",
"value": 90.63973502591523
},
{
"labels": {
"*": ""
},
"timestamp": "2026-02-18T12:15:00.000Z",
"name": "system.cpu.idle",
"value": 90.97757912691068
},
{
"labels": {
"*": ""
},
"timestamp": "2026-02-18T12:20:00.000Z",
"name": "system.cpu.idle",
"value": 90.36301732455806
},
{
"labels": {
"*": ""
},
"timestamp": "2026-02-18T12:25:00.000Z",
"name": "system.cpu.idle",
"value": 90.98991169580084
},
{
"labels": {
"*": ""
},
"timestamp": "2026-02-18T12:30:00.000Z",
"name": "system.cpu.idle",
"value": 90.1358372557344
}
]
}Dynatrace metrics query uses the Grail (DQL) API.
The query must start with timeseries.
range and step request fields are not supported for Dynatrace and must be expressed in DQL (from:, to:, interval:).
grpcurl -d '{
"connection": {
"dynatrace": {
"url": "https://abc12345.live.dynatrace.com",
"platformToken": "<PLATFORM_TOKEN>"
}
},
"query": "timeseries from:now()-10m, avg(dt.host.cpu.usage), by:{host.name} | limit 1"
}' -plaintext localhost:9192 toolquery.ToolQuery/Metrics{
"metrics": [
{
"labels": {
"dt.entity.host": "HOST-12345678"
},
"timestamp": "2024-03-20T10:00:00Z",
"name": "builtin:host.cpu.usage",
"value": 15.5
}
]
}CloudWatch metrics query uses GetMetricData and expects query to be a CloudWatch metric math expression.
range defines the time window and step maps to CloudWatch period seconds.
log_group_names is only used by CloudWatch logs queries. It is ignored for metrics and metrics search.
grpcurl -d '{
"connection": {
"cloudwatch": {
"region": "us-east-1"
}
},
"query": "SEARCH(\"{AWS/EC2,InstanceId} MetricName=\\\"CPUUtilization\\\"\", \"Average\", 300)",
"range": {
"start": "2026-02-20T10:00:00Z",
"end": "2026-02-20T11:00:00Z"
},
"step": "300s"
}' -plaintext localhost:9192 toolquery.ToolQuery/MetricsAzure metrics query uses Azure Monitor azmetrics.QueryResources.
queryis a comma-separated metric names list.options.azure.resource_idandoptions.azure.metrics_namespaceare required.- Optional
options.azurefields:aggregation,filter,order_by,roll_up_by,metrics_endpoint. - If
options.azure.metrics_endpointis omitted, Cloud Query useshttps://global.metrics.monitor.azure.com.
range maps to Azure Monitor start_time + end_time. For Azure metrics, step is required and must be an ISO 8601 duration string (for example PT5M or PT1H).
grpcurl -d '{
"connection": {
"azure": {
"subscription_id": "<SUBSCRIPTION_ID>",
"tenant_id": "<TENANT_ID>",
"client_id": "<CLIENT_ID>",
"client_secret": "<CLIENT_SECRET>"
}
},
"query": "apiserver_cpu_usage_percentage",
"options": {
"azure": {
"resource_id": "/subscriptions/<SUBSCRIPTION_ID>/resourceGroups/rg-prod/providers/Microsoft.ContainerService/managedClusters/my-cluster",
"metrics_endpoint": "https://eastus.metrics.monitor.azure.com",
"metrics_namespace": "Microsoft.ContainerService/managedClusters"
}
},
"range": {
"start": "2026-04-13T00:00:00Z",
"end": "2026-04-13T21:00:00Z"
},
"step": "PT1H"
}' -plaintext localhost:9192 toolquery.ToolQuery/Metrics{
"metrics": [
{
"labels": {
"aggregation": "average",
"metric_namespace": "Microsoft.Compute/virtualMachines",
"resource_uri": "/subscriptions/<SUBSCRIPTION_ID>/resourceGroups/rg-prod/providers/Microsoft.Compute/virtualMachines/vm-prod-1",
"VMName": "vm-prod-1"
},
"timestamp": "2026-04-10T10:00:00Z",
"name": "Percentage CPU",
"value": 42.5
},
{
"labels": {
"aggregation": "maximum",
"metric_namespace": "Microsoft.Compute/virtualMachines",
"resource_uri": "/subscriptions/<SUBSCRIPTION_ID>/resourceGroups/rg-prod/providers/Microsoft.Compute/virtualMachines/vm-prod-1",
"VMName": "vm-prod-1"
},
"timestamp": "2026-04-10T10:00:00Z",
"name": "Percentage CPU",
"value": 65
}
]
}grpcurl -d '{
"connection": {
"cloudwatch": {
"region": "us-east-1",
"role_arn": "arn:aws:iam::123456789012:role/observability-readonly"
}
},
"query": "SEARCH(\"{AWS/EKS,ClusterName} MetricName=\\\"cluster_failed_request_count\\\"\", \"Sum\", 60)",
"range": {
"start": "2026-02-20T10:00:00Z",
"end": "2026-02-20T11:00:00Z"
},
"step": "60s"
}' -plaintext localhost:9192 toolquery.ToolQuery/MetricsMetricsSearch returns metric names or provider-specific metric identifiers that can be passed to MetricsLabelSearch.
queryis the search term. Most providers apply it as a plain substring or provider-native search filter.limitdefaults to a provider-specific conservative value.- Results contain only metric names. Use
Metricsto query time-series data andMetricsLabelSearchto inspect available labels for a specific metric.
grpcurl -d '{
"connection": {
"prometheus": {
"url": "http://vmauth-vm-auth.monitoring:8427/select/0/prometheus",
"username": "<USERNAME>",
"password": "<PASSWORD>"
}
},
"query": "container_cpu_usage_seconds_total",
"limit": 5
}' -plaintext localhost:9192 toolquery.ToolQuery/MetricsSearch{
"metrics": [
{ "name": "container_cpu_usage_seconds_total" },
{ "name": "container_cpu_user_seconds_total" }
]
}grpcurl -d '{
"connection": {
"datadog": {
"apiKey": "<API_KEY>",
"appKey": "<APP_KEY>"
}
},
"query": "system.cpu",
"limit": 3
}' -plaintext localhost:9192 toolquery.ToolQuery/MetricsSearch{
"metrics": [
{ "name": "system.cpu.idle" },
{ "name": "system.cpu.iowait" },
{ "name": "system.cpu.system" }
]
}Dynatrace metrics search uses a plain search term and Cloud Query composes DQL internally:
metrics | filter contains(metric.key, "<query>", caseSensitive: false) [| limit N].
grpcurl -d '{
"connection": {
"dynatrace": {
"url": "https://abc12345.live.dynatrace.com",
"platformToken": "<PLATFORM_TOKEN>"
}
},
"query": "builtin:host.cpu.usage",
"limit": 3
}' -plaintext localhost:9192 toolquery.ToolQuery/MetricsSearch{
"metrics": [
{ "name": "builtin:host.cpu.usage" },
{ "name": "builtin:host.mem.usage" },
{ "name": "fallback.metric.name" }
]
}CloudWatch metrics search is implemented via ListMetrics, with conservative API usage:
- scans at most 6 pages per request,
- applies
RecentlyActive=PT3Hto reduce API load, - stops early as soon as requested
limitis reached, - deduplicates results and returns names as
<namespace>/<metric>.
grpcurl -d '{
"connection": {
"cloudwatch": {
"region": "us-east-1"
}
},
"query": "cpu",
"limit": 10
}' -plaintext localhost:9192 toolquery.ToolQuery/MetricsSearch{
"metrics": [
{ "name": "AWS/EC2/CPUUtilization" },
{ "name": "AWS/EKS/cluster_failed_request_count" }
]
}Azure metrics search is resource-scoped and lists metric definitions from Azure Monitor for options.azure.resource_id.
query is a plain substring filter applied locally to metric definition names.
grpcurl -d '{
"connection": {
"azure": {
"subscription_id": "<SUBSCRIPTION_ID>",
"tenant_id": "<TENANT_ID>",
"client_id": "<CLIENT_ID>",
"client_secret": "<CLIENT_SECRET>"
}
},
"query": "node",
"options": {
"azure": {
"resource_id": "/subscriptions/<SUBSCRIPTION_ID>/resourceGroups/rg-prod/providers/Microsoft.ContainerService/managedClusters/aks-prod"
}
},
"limit": 5
}' -plaintext localhost:9192 toolquery.ToolQuery/MetricsSearch{
"metrics": [
{ "name": "node_cpu_usage_percentage" },
{ "name": "node_memory_working_set" }
]
}MetricsLabelSearch returns metric-scoped label metadata.
metricis required. Use a metric name or provider-specific metric identifier, typically fromMetricsSearch.- If
labelis omitted, results are label names formetric. - If
labelis provided, results are values for that label onmetric. queryis an optional case-insensitive substring filter applied locally to returned names or values.limitdefaults to a provider-specific conservative value.
Provider notes:
- Prometheus and Azure Managed Prometheus use Prometheus label APIs over a recent 24-hour window.
- Datadog uses indexed metric tags from
ListTagsByMetricName. - CloudWatch uses
ListMetricsdimensions for recently active metrics. CloudWatch metric identifiers should matchMetricsSearchoutput, for exampleAWS/EC2/CPUUtilization. - Native Azure Monitor uses metric-definition dimensions for label names. Label values require
options.azure.metrics_namespaceand are inferred from recent metric time-series metadata. - Dynatrace metric label search is currently unsupported.
grpcurl -d '{
"connection": {
"prometheus": {
"url": "http://vmauth-vm-auth.monitoring:8427/select/0/prometheus",
"username": "<USERNAME>",
"password": "<PASSWORD>"
}
},
"metric": "container_cpu_usage_seconds_total",
"query": "pod",
"limit": 10
}' -plaintext localhost:9192 toolquery.ToolQuery/MetricsLabelSearch{
"results": [
{ "name": "pod" },
{ "name": "pod_name" }
]
}grpcurl -d '{
"connection": {
"prometheus": {
"url": "http://vmauth-vm-auth.monitoring:8427/select/0/prometheus",
"username": "<USERNAME>",
"password": "<PASSWORD>"
}
},
"metric": "container_cpu_usage_seconds_total",
"label": "namespace",
"query": "prod",
"limit": 10
}' -plaintext localhost:9192 toolquery.ToolQuery/MetricsLabelSearch{
"results": [
{ "name": "prod" },
{ "name": "prod-system" }
]
}grpcurl -d '{
"connection": {
"azure": {
"subscription_id": "<SUBSCRIPTION_ID>",
"tenant_id": "<TENANT_ID>",
"client_id": "<CLIENT_ID>",
"client_secret": "<CLIENT_SECRET>"
}
},
"metric": "node_cpu_usage_percentage",
"options": {
"azure": {
"resource_id": "/subscriptions/<SUBSCRIPTION_ID>/resourceGroups/rg-prod/providers/Microsoft.ContainerService/managedClusters/aks-prod",
"metrics_namespace": "Microsoft.ContainerService/managedClusters"
}
},
"limit": 10
}' -plaintext localhost:9192 toolquery.ToolQuery/MetricsLabelSearch{
"results": [
{ "name": "node" }
]
}message LogsQueryInput {
ToolConnection connection = 1;
string query = 2;
TimeRange range = 3;
optional int32 limit = 4;
repeated LogsQueryFacet facets = 5;
optional LogsOptions options = 6;
}
message LogsOptions {
optional AzureLogsOptions azure = 1;
}
message AzureLogsOptions {
string resource_id = 1;
}message LogEntry {
google.protobuf.Timestamp timestamp = 1;
string message = 2;
map<string, string> labels = 3;
}
message LogsQueryOutput {
repeated LogEntry logs = 1;
}Azure logs query uses Azure Monitor azlogs.QueryResource.
options.azure.resource_idis required and used as the resource target.querymust be Azure Log Analytics syntax (KQL).rangeis passed astimespan.
grpcurl -d '{
"connection": {
"azure": {
"subscription_id": "<SUBSCRIPTION_ID>",
"tenant_id": "<TENANT_ID>",
"client_id": "<CLIENT_ID>",
"client_secret": "<CLIENT_SECRET>"
}
},
"query": "ContainerLog | where LogEntrySource == \"stderr\" | take 50",
"options": {
"azure": {
"resource_id": "/subscriptions/<SUBSCRIPTION_ID>/resourceGroups/<RESOURCE_GROUP>/providers/Microsoft.OperationalInsights/workspaces/azure-cluster-workspace"
}
},
"range": {
"start": "2026-04-10T09:00:00Z",
"end": "2026-04-10T11:00:00Z"
}
}' -plaintext localhost:9192 toolquery.ToolQuery/Logsgrpcurl -d '{
"connection": {
"loki": {
"url": "http://loki.loki:3100",
"tenant_id": "default"
}
},
"query": "{namespace=\"plrl-deploy-operator\"}",
"range": {
"start": "2026-02-20T00:00:00Z",
"end": "2026-02-20T21:00:00Z"
},
"limit": 1
}' -plaintext localhost:9192 toolquery.ToolQuery/Logs{
"logs": [
{
"labels": {
"app_kubernetes_io_name": "deployment-operator",
"container": "deployment-operator",
"filename": "/var/log/pods/plrl-deploy-operator_deployment-operator-ddb48bd67-88flh_be6de3a3-bd7a-4009-9df0-bee2a4ee8557/deployment-operator/0.log",
"namespace": "plrl-deploy-operator",
"node": "ip-10-0-21-32.eu-central-1.compute.internal",
"stream": "stderr",
"app_kubernetes_io_instance": "deploy-operator",
"detected_level": "unknown",
"pod": "deployment-operator-ddb48bd67-88flh",
"pod_template_hash": "ddb48bd67",
"service_name": "deployment-operator"
},
"timestamp": "2026-02-20T15:43:53.007Z",
"message": "I0220 15:43:53.007458 1 synchronizer.go:319] \"resync complete\" gvr=\"policy/v1, Resource=poddisruptionbudgets\" duration=\"10.02607ms\""
}
]
}CloudWatch logs use Logs Insights (StartQuery + GetQueryResults).
You can target log groups in two ways:
- set
connection.cloudwatch.log_group_namesand keepqueryfocused on filtering/projection, or - omit
log_group_namesand includeSOURCEin the query text.
grpcurl -d '{
"connection": {
"cloudwatch": {
"region": "us-east-1",
"log_group_names": ["/aws/eks/prod/app"]
}
},
"query": "fields @timestamp, @message | sort @timestamp desc",
"range": {
"start": "2026-02-20T00:00:00Z",
"end": "2026-02-20T21:00:00Z"
},
"limit": 20
}' -plaintext localhost:9192 toolquery.ToolQuery/Logsgrpcurl -d '{
"connection": {
"cloudwatch": {
"region": "us-east-1"
}
},
"query": "SOURCE logGroups(namePrefix: [\"/aws/eks/prod/app\"]) | fields @timestamp, @message | filter @message like /error|exception/ | sort @timestamp desc",
"range": {
"start": "2026-02-20T00:00:00Z",
"end": "2026-02-20T21:00:00Z"
},
"limit": 20
}' -plaintext localhost:9192 toolquery.ToolQuery/Logsgrpcurl -d '{
"connection": {
"elastic": {
"apiKey": "<API_KEY>",
"url": "http://elasticsearch-es-http.elastic:9200"
}
},
"query": "kubernetes.pod.name:deployment-operator-5f4c46cb48-mfpdv AND (error OR exception)",
"range": {
"start": "2026-02-20T10:00:00Z",
"end": "2026-02-20T12:30:00Z"
},
"limit": 100
}' -plaintext localhost:9192 toolquery.ToolQuery/Logs{
"logs": [
{
"labels": {
"agent.name": "ip-10-0-21-32.eu-central-1.compute.internal",
"agent.version": "8.16.0",
"kubernetes.pod.name": "deployment-operator-5f4c46cb48-mfpdv",
"cluster.name": "plrl-dev-aws",
"agent.type": "filebeat",
"kubernetes.namespace": "plrl-deploy-operator",
"kubernetes.container.name": "deployment-operator-agentk",
"kubernetes.node.name": "ip-10-0-21-32.eu-central-1.compute.internal",
"host.name": "ip-10-0-21-32.eu-central-1.compute.internal",
"host.hostname": "ip-10-0-21-32.eu-central-1.compute.internal",
"host.architecture": "x86_64",
"cluster.handle": "mgmt"
},
"timestamp": "2026-02-20T11:00:32.941Z",
"message": "{\"level\":\"error\",\"time\":\"2026-02-20T11:00:32.940Z\",\"msg\":\"Error handling a connection\",\"mod_name\":\"reverse_tunnel\",\"error\":\"rpc error: code = Unavailable desc = error reading from server: failed to get reader: failed to read frame header: EOF\"}"
}
]
}grpcurl -d '{
"connection": {
"datadog": {
"apiKey": "<API_KEY>",
"appKey": "<APP_KEY>"
}
},
"query": "service:plrl-deploy-operator",
"range": {
"start": "2026-02-19T12:07:29.892Z",
"end": "2026-02-19T19:07:29.892Z"
},
"limit": 1
}' -plaintext localhost:9192 toolquery.ToolQuery/Logs{
"logs": [
{
"labels": {
"datadog.submission_auth": "api_key",
"image_tag": "sha-c152379",
"kube_distribution": "eks",
"filename": "0.log",
"kube_container_name": "deployment-operator",
"kube_deployment": "deployment-operator",
"kube_ownerref_name": "deployment-operator-7c8c589d48",
"git.repository_url": "https://github.com/pluralsh/deployment-operator",
"kube_app_instance": "deploy-operator",
"pod_name": "deployment-operator-7c8c589d48-vr4dj",
"pod_phase": "running",
"image_id": "ghcr.io/pluralsh/deployment-operator@sha256:5b4a3e92e07d8703367218364d5ffd8718440ae2994ecb1c49af4921f30cd98a",
"image_name": "ghcr.io/pluralsh/deployment-operator",
"kube_namespace": "plrl-deploy-operator",
"kube_ownerref_kind": "replicaset",
"kube_replica_set": "deployment-operator-7c8c589d48",
"service": "plrl-deploy-operator",
"short_image": "deployment-operator",
"display_container_name": "deployment-operator_deployment-operator-7c8c589d48-vr4dj",
"env": "plrl-dev-aws",
"kube_qos": "burstable",
"status": "error",
"cluster_name": "plrl-dev-aws",
"container_name": "deployment-operator",
"dirname": "/var/log/pods/plrl-deploy-operator_deployment-operator-7c8c589d48-vr4dj_d302f6da-02ed-4d1f-8eea-4494b14558c6/deployment-operator",
"kube_app_name": "deployment-operator",
"kube_cluster_name": "plrl-dev-aws",
"source": "go"
},
"timestamp": "2026-02-19T19:07:28.388Z",
"message": "I0219 19:07:27.934241 1 synchronizer.go:319] \"resync complete\" gvr=\"platform.plural.sh/v1alpha1, Resource=licenses\" duration=\"7.451927ms\""
}
]
}Dynatrace logs query uses the Grail (DQL) API.
The query must start with fetch logs.
range and limit request fields are not supported for Dynatrace logs and must be expressed in DQL (from:, to:, | limit).
grpcurl -d '{
"connection": {
"dynatrace": {
"url": "https://abc12345.live.dynatrace.com",
"platformToken": "<PLATFORM_TOKEN>"
}
},
"query": "fetch logs | limit 1"
}' -plaintext localhost:9192 toolquery.ToolQuery/Logs{
"logs": [
{
"labels": {
"dt.process_name": "deployment-operator",
"dt.entity.host": "HOST-12345678"
},
"timestamp": "2024-03-20T10:15:00Z",
"message": "Starting deployment sync..."
}
]
}message TracesQueryInput {
ToolConnection connection = 1;
string query = 2;
TimeRange range = 3;
optional int32 limit = 4;
optional TracesOptions options = 5;
}
message TracesOptions {
optional JaegerTracesOptions jaeger = 1;
}
message JaegerTraceQueryAttribute {
string name = 1;
string value = 2;
}
message JaegerTracesOptions {
optional string operation_name = 1;
repeated JaegerTraceQueryAttribute attributes = 2;
optional string duration_min = 3;
optional string duration_max = 4;
}RunPython synchronously executes Monty's limited Python subset in a fresh logical sandbox session. input_json is optional but, when present, must encode an object. It is exposed as the global input; scripts write their structured response to the global output dictionary. Printed text is returned separately.
message RunPythonInput {
string script = 1;
string input_json = 2;
}
message RunPythonOutput {
string result_json = 1;
string stdout = 2;
}The runtime exposes no host filesystem, environment, network, subprocess, shell, package installation, third-party package, or host-tool callback. It limits source to 64 KiB, input and result JSON to 1 MiB, stdout to 64 KiB, execution to 60 seconds, memory to 100 MiB, recursion to 100 frames, wall time to 65 seconds, and concurrency to two runs per process. Up to 16 additional requests wait in a bounded FIFO queue. It uses gomonty v0.0.14, built against official Monty commit c9802b5f30d11fecf9f153feb1dfdab3abda070e; it is not CPython.
InvokeLambda invokes serverless functions using canonical provider identifiers only.
message InvokeLambdaInput {
cloudquery.Connection connection = 1;
string identifier = 2;
string payload_json = 3;
}
message InvokeLambdaOutput {
string result = 1;
string error = 2;
}Identifier formats:
- AWS: Lambda ARN (for example
arn:aws:lambda:us-east-1:123456789012:function:my-func) - GCP Cloud Run services:
projects/{project}/locations/{location}/services/{name}(or direct service URL limited tohttps://*.run.app, for examplehttps://{service}-{hash}-{region}.a.run.app) - Azure:
/subscriptions/{sub}/resourceGroups/{rg}/providers/Microsoft.Web/sites/{app}/functions/{func}(must include leading/)
Provider identifier examples:
- AWS:
arn:aws:lambda:us-east-1:123456789012:function:my-func - GCP:
projects/my-project/locations/us-central1/services/my-service - Azure:
/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/my-rg/providers/Microsoft.Web/sites/my-func-app/functions/my-handler
Required permissions:
- AWS:
lambda:InvokeFunctionon the target function. - GCP:
run.routes.invokerun.services.get
- Azure: permissions to read function metadata/secrets and invoke the function endpoint.
Response behavior:
- Successful invocation:
resultpopulated,errorempty. - Function/provider invocation failure:
errorpopulated (with optionalresultpayload when available).
message TraceSpan {
string trace_id = 1;
string span_id = 2;
string parent_id = 3;
string name = 4;
string service = 5;
google.protobuf.Timestamp start = 6;
google.protobuf.Timestamp end = 7;
map<string, string> tags = 8;
}
message TracesQueryOutput {
repeated TraceSpan spans = 1;
}limitapplies to the Tempo search results (number of trace IDs returned), not to the number of spans.- Cloud-Query fetches each trace by ID and returns all spans for each trace, so a single trace can produce many spans even when
limit=1. - There is no minimum enforced beyond
limit > 0.
grpcurl -d '{
"connection": {
"tempo": {
"url": "http://tempo.tempo:3200",
"token": "<OPTIONAL_TOKEN>",
"tenant_id": "<OPTIONAL_TENANT_ID>"
}
},
"query": "{resource.service.name=\"frontend\"}",
"range": {
"start": "2026-02-20T00:00:00Z",
"end": "2026-02-20T21:00:00Z"
},
"limit": 20
}' -plaintext localhost:9192 toolquery.ToolQuery/Traces{
"spans": [
{
"tags": {
"rpc.method": "GetProduct",
"rpc.service": "oteldemo.ProductCatalogService",
"net.peer.name": "product-catalog",
"net.peer.port": "8080",
"rpc.grpc.status_code": "0",
"rpc.system": "grpc"
},
"trace_id": "ABai2V4/Y9GC2/kagvFfow==",
"span_id": "xMZYf8yrA1o=",
"parent_id": "XkHiraV2nDk=",
"name": "grpc.oteldemo.ProductCatalogService/GetProduct",
"service": "frontend",
"start": "2026-02-20T14:43:28.095Z",
"end": "2026-02-20T14:43:28.099Z"
}
]
}Jaeger traces query uses the stable Query v3 API.
querymaps to Jaegerservice_name.time_rangemaps tostart_time_min/start_time_max.limitmaps tonum_traces.options.jaeger.operation_namemaps tooperation_name.options.jaeger.attributesmaps toattributes.options.jaeger.duration_minmaps tomin_duration.options.jaeger.duration_maxmaps tomax_duration.- Structured filters are passed through
options.jaeger:operation_nameduration_min/duration_max- dynamic
attributes[](key/value)
grpcurl -d '{
"connection": {
"jaeger": {
"url": "http://jaeger-query.monitoring.svc:16686",
"token": "<OPTIONAL_TOKEN>"
}
},
"query": "frontend",
"range": {
"start": "2026-04-10T00:00:00Z",
"end": "2026-04-10T01:00:00Z"
},
"limit": 20,
"options": {
"jaeger": {
"operation_name": "GET /api/products",
"duration_min": "10ms",
"attributes": [
{ "name": "http.status_code", "value": "500" }
]
}
}
}' -plaintext localhost:9192 toolquery.ToolQuery/Tracesgrpcurl -d '{
"connection": {
"datadog": {
"apiKey": "<API_KEY>",
"appKey": "<APP_KEY>"
}
},
"query": "*",
"range": {
"start": "2026-02-18T12:07:29.892Z",
"end": "2026-02-19T12:07:29.892Z"
},
"limit": 1
}' -plaintext localhost:9192 toolquery.ToolQuery/Traces{
"spans": [
{
"tags": {
"kube_namespace": "plrl-deploy-operator",
"kube_ownerref_name": "deployment-operator-d8cbc89b5",
"ingestion_reason": "auto",
"pod_name": "deployment-operator-d8cbc89b5-lfd25",
"kube_container_name": "deployment-operator",
"kube_app_instance": "deploy-operator",
"cluster_name": "plrl-dev-aws",
"kube_node": "ip-10-0-21-32.eu-central-1.compute.internal",
"kube_replica_set": "deployment-operator-d8cbc89b5",
"kube_qos": "burstable",
"kube_distribution": "eks",
"container_name": "deployment-operator",
"kube_deployment": "deployment-operator",
"pod_phase": "running",
"kube_app_name": "deployment-operator",
"short_image": "deployment-operator",
"env": "plrl-dev-aws",
"kube_cluster_name": "plrl-dev-aws",
"display_container_name": "deployment-operator_deployment-operator-d8cbc89b5-lfd25",
"image_tag": "sha-b50f5db",
"container_id": "0d3ea8788d1797977a8d916607084aebdae3b3c149819fcf969016b557be9a79",
"resource": "ExpireOlderThan",
"git.repository_url": "https://github.com/pluralsh/deployment-operator",
"source": "apm",
"image_name": "ghcr.io/pluralsh/deployment-operator",
"git.commit.sha": "b50f5db02ca728311d5786d10d708fdd9440f313",
"kube_ownerref_kind": "replicaset",
"image_id": "ghcr.io/pluralsh/deployment-operator@sha256:f6c360c6e92a222f6491b0c2ef12ea73ba2c5cfa052435b512c028f6998116f6",
"orch_cluster_id": "82218f51-ac4f-40cf-9de0-7d93abad98b7"
},
"trace_id": "6995ab82000000005195073c70013f33",
"span_id": "5878612844760612659",
"parent_id": "0",
"name": "ExpireOlderThan",
"service": "deployment-operator",
"start": "2026-02-18T12:07:30.533Z",
"end": "2026-02-18T12:07:30.533Z"
}
]
}Dynatrace traces query uses the Grail (DQL) API and maps span records to TraceSpan.
Expected DQL fields for mapping are: trace.id, span.id, span.name, start_time, end_time, duration.
The query must start with fetch spans.
range and limit request fields are not supported for Dynatrace traces and must be expressed in DQL (from:, to:, | limit).
grpcurl -d '{
"connection": {
"dynatrace": {
"url": "https://abc12345.live.dynatrace.com",
"platformToken": "<PLATFORM_TOKEN>"
}
},
"query": "fetch spans | limit 1"
}' -plaintext localhost:9192 toolquery.ToolQuery/Traces{
"spans": [
{
"tags": {
"dt.entity.service": "SERVICE-12345",
"k8s.namespace.name": "datadog",
"http.request.method": "GET"
},
"trace_id": "b7584e49925d0d6894c154f55c7d360e",
"span_id": "cc82a11e223cd2c6",
"name": "GET",
"start": "2026-04-03T12:57:20.556086Z",
"end": "2026-04-03T12:57:20.556304Z"
}
]
}