feat(scripts/metricsdocgen): add prometheus.New*() and New*Vec() patterns to metrics scanner (#21462)

## Description

This PR implements extraction of metrics defined using `prometheus.New*()` and `prometheus.New*Vec()` patterns with `*Opts{}` structs.

## Changes

* Add `extractOptsMetric()` to handle:
  * `prometheus.NewGauge(prometheus.GaugeOpts{...})`
  * `prometheus.NewCounter(prometheus.CounterOpts{...})`
  * `prometheus.NewHistogram(prometheus.HistogramOpts{...})`
  * `prometheus.NewSummary(prometheus.SummaryOpts{...})`
  * `prometheus.New*Vec(prometheus.*Opts{...}, labels)`
* Script generates an updated `scripts/metricsdocgen/generated_metrics` file

Related to: https://github.com/coder/coder/issues/13223

**Disclosure:** This PR was mainly developed with Claude Sonnet 4, with iterative review and refinement by @ssncferreira
This commit is contained in:
Susana Ferreira
2026-02-13 11:13:55 +00:00
committed by GitHub
parent 45280d5516
commit bcb437d281
2 changed files with 407 additions and 2 deletions
+183
View File
@@ -1,21 +1,168 @@
# HELP agent_reconnecting_pty_connections_total
# TYPE agent_reconnecting_pty_connections_total counter
agent_reconnecting_pty_connections_total 0
# HELP agent_reconnecting_pty_errors_total
# TYPE agent_reconnecting_pty_errors_total counter
agent_reconnecting_pty_errors_total{error_type=""} 0
# HELP agent_scripts_executed_total
# TYPE agent_scripts_executed_total counter
agent_scripts_executed_total{success=""} 0
# HELP agent_sessions_errors_total
# TYPE agent_sessions_errors_total counter
agent_sessions_errors_total{magic_type="",pty="",error_type=""} 0
# HELP agent_sessions_total
# TYPE agent_sessions_total counter
agent_sessions_total{magic_type="",pty=""} 0
# HELP agent_ssh_server_failed_connections_total
# TYPE agent_ssh_server_failed_connections_total counter
agent_ssh_server_failed_connections_total 0
# HELP agent_ssh_server_sftp_connections_total
# TYPE agent_ssh_server_sftp_connections_total counter
agent_ssh_server_sftp_connections_total 0
# HELP agent_ssh_server_sftp_server_errors_total
# TYPE agent_ssh_server_sftp_server_errors_total counter
agent_ssh_server_sftp_server_errors_total 0
# HELP agent_x11_handler_errors_total
# TYPE agent_x11_handler_errors_total counter
agent_x11_handler_errors_total{error_type=""} 0
# HELP coder_pubsub_connected Whether we are connected (1) or not connected (0) to postgres
# TYPE coder_pubsub_connected gauge
coder_pubsub_connected 0
# HELP coder_pubsub_current_events The current number of pubsub event channels listened for
# TYPE coder_pubsub_current_events gauge
coder_pubsub_current_events 0
# HELP coder_pubsub_current_subscribers The current number of active pubsub subscribers
# TYPE coder_pubsub_current_subscribers gauge
coder_pubsub_current_subscribers 0
# HELP coder_pubsub_disconnections_total Total number of times we disconnected unexpectedly from postgres
# TYPE coder_pubsub_disconnections_total counter
coder_pubsub_disconnections_total 0
# HELP coder_pubsub_latency_measure_errs_total The number of pubsub latency measurement failures
# TYPE coder_pubsub_latency_measure_errs_total counter
coder_pubsub_latency_measure_errs_total 0
# HELP coder_pubsub_latency_measures_total The number of pubsub latency measurements
# TYPE coder_pubsub_latency_measures_total counter
coder_pubsub_latency_measures_total 0
# HELP coder_pubsub_messages_total Total number of messages received from postgres
# TYPE coder_pubsub_messages_total counter
coder_pubsub_messages_total{size=""} 0
# HELP coder_pubsub_published_bytes_total Total number of bytes successfully published across all publishes
# TYPE coder_pubsub_published_bytes_total counter
coder_pubsub_published_bytes_total 0
# HELP coder_pubsub_publishes_total Total number of calls to Publish
# TYPE coder_pubsub_publishes_total counter
coder_pubsub_publishes_total{success=""} 0
# HELP coder_pubsub_receive_latency_seconds The time taken to receive a message from a pubsub event channel
# TYPE coder_pubsub_receive_latency_seconds gauge
coder_pubsub_receive_latency_seconds 0
# HELP coder_pubsub_received_bytes_total Total number of bytes received across all messages
# TYPE coder_pubsub_received_bytes_total counter
coder_pubsub_received_bytes_total 0
# HELP coder_pubsub_send_latency_seconds The time taken to send a message into a pubsub event channel
# TYPE coder_pubsub_send_latency_seconds gauge
coder_pubsub_send_latency_seconds 0
# HELP coder_pubsub_subscribes_total Total number of calls to Subscribe/SubscribeWithErr
# TYPE coder_pubsub_subscribes_total counter
coder_pubsub_subscribes_total{success=""} 0
# HELP coder_servertailnet_connections_total Total number of TCP connections made to workspace agents.
# TYPE coder_servertailnet_connections_total counter
coder_servertailnet_connections_total{network=""} 0
# HELP coder_servertailnet_open_connections Total number of TCP connections currently open to workspace agents.
# TYPE coder_servertailnet_open_connections gauge
coder_servertailnet_open_connections{network=""} 0
# HELP coderd_agentapi_metadata_batch_size Total number of metadata entries in each batch, updated before flushes.
# TYPE coderd_agentapi_metadata_batch_size histogram
coderd_agentapi_metadata_batch_size 0
# HELP coderd_agentapi_metadata_batch_utilization Number of metadata keys per agent in each batch, updated before flushes.
# TYPE coderd_agentapi_metadata_batch_utilization histogram
coderd_agentapi_metadata_batch_utilization 0
# HELP coderd_agentapi_metadata_batches_total Total number of metadata batches flushed.
# TYPE coderd_agentapi_metadata_batches_total counter
coderd_agentapi_metadata_batches_total{reason=""} 0
# HELP coderd_agentapi_metadata_dropped_keys_total Total number of metadata keys dropped due to capacity limits.
# TYPE coderd_agentapi_metadata_dropped_keys_total counter
coderd_agentapi_metadata_dropped_keys_total 0
# HELP coderd_agentapi_metadata_flush_duration_seconds Time taken to flush metadata batch to database and pubsub.
# TYPE coderd_agentapi_metadata_flush_duration_seconds histogram
coderd_agentapi_metadata_flush_duration_seconds{reason=""} 0
# HELP coderd_agentapi_metadata_flushed_total Total number of unique metadatas flushed.
# TYPE coderd_agentapi_metadata_flushed_total counter
coderd_agentapi_metadata_flushed_total 0
# HELP coderd_agentapi_metadata_publish_errors_total Total number of metadata batch pubsub publish calls that have resulted in an error.
# TYPE coderd_agentapi_metadata_publish_errors_total counter
coderd_agentapi_metadata_publish_errors_total 0
# HELP coderd_agents_apps Agent applications with statuses.
# TYPE coderd_agents_apps gauge
coderd_agents_apps{agent_name="",username="",workspace_name="",app_name="",health=""} 0
# HELP coderd_agents_connection_latencies_seconds Agent connection latencies in seconds.
# TYPE coderd_agents_connection_latencies_seconds gauge
coderd_agents_connection_latencies_seconds{agent_name="",username="",workspace_name="",derp_region="",preferred=""} 0
# HELP coderd_agents_connections Agent connections with statuses.
# TYPE coderd_agents_connections gauge
coderd_agents_connections{agent_name="",username="",workspace_name="",status="",lifecycle_state="",tailnet_node=""} 0
# HELP coderd_agents_up The number of active agents per workspace.
# TYPE coderd_agents_up gauge
coderd_agents_up{username="",workspace_name="",template_name="",template_version=""} 0
# HELP coderd_agentstats_connection_count The number of established connections by agent
# TYPE coderd_agentstats_connection_count gauge
coderd_agentstats_connection_count 0
# HELP coderd_agentstats_connection_median_latency_seconds The median agent connection latency in seconds
# TYPE coderd_agentstats_connection_median_latency_seconds gauge
coderd_agentstats_connection_median_latency_seconds 0
# HELP coderd_agentstats_currently_reachable_peers The number of peers (e.g. clients) that are currently reachable over the encrypted network.
# TYPE coderd_agentstats_currently_reachable_peers gauge
coderd_agentstats_currently_reachable_peers{connection_type=""} 0
# HELP coderd_agentstats_rx_bytes Agent Rx bytes
# TYPE coderd_agentstats_rx_bytes gauge
coderd_agentstats_rx_bytes 0
# HELP coderd_agentstats_session_count_jetbrains The number of session established by JetBrains
# TYPE coderd_agentstats_session_count_jetbrains gauge
coderd_agentstats_session_count_jetbrains 0
# HELP coderd_agentstats_session_count_reconnecting_pty The number of session established by reconnecting PTY
# TYPE coderd_agentstats_session_count_reconnecting_pty gauge
coderd_agentstats_session_count_reconnecting_pty 0
# HELP coderd_agentstats_session_count_ssh The number of session established by SSH
# TYPE coderd_agentstats_session_count_ssh gauge
coderd_agentstats_session_count_ssh 0
# HELP coderd_agentstats_session_count_vscode The number of session established by VSCode
# TYPE coderd_agentstats_session_count_vscode gauge
coderd_agentstats_session_count_vscode 0
# HELP coderd_agentstats_startup_script_seconds Amount of time taken to run the startup script in seconds.
# TYPE coderd_agentstats_startup_script_seconds gauge
coderd_agentstats_startup_script_seconds{success=""} 0
# HELP coderd_agentstats_tx_bytes Agent Tx bytes
# TYPE coderd_agentstats_tx_bytes gauge
coderd_agentstats_tx_bytes 0
# HELP coderd_api_active_users_duration_hour The number of users that have been active within the last hour.
# TYPE coderd_api_active_users_duration_hour gauge
coderd_api_active_users_duration_hour 0
# HELP coderd_api_total_user_count The total number of registered users, partitioned by status.
# TYPE coderd_api_total_user_count gauge
coderd_api_total_user_count{status=""} 0
# HELP coderd_api_workspace_latest_build The current number of workspace builds by status for all non-deleted workspaces.
# TYPE coderd_api_workspace_latest_build gauge
coderd_api_workspace_latest_build{status=""} 0
# HELP coderd_db_query_counts_total Total number of queries labelled by HTTP route, method, and query name.
# TYPE coderd_db_query_counts_total counter
coderd_db_query_counts_total{route="",method="",query=""} 0
# HELP coderd_db_query_latencies_seconds Latency distribution of queries in seconds.
# TYPE coderd_db_query_latencies_seconds histogram
coderd_db_query_latencies_seconds{query=""} 0
# HELP coderd_db_tx_duration_seconds Duration of transactions in seconds.
# TYPE coderd_db_tx_duration_seconds histogram
coderd_db_tx_duration_seconds{success="",tx_id=""} 0
# HELP coderd_db_tx_executions_count Total count of transactions executed. 'retries' is expected to be 0 for a successful transaction.
# TYPE coderd_db_tx_executions_count counter
coderd_db_tx_executions_count{success="",retries="",tx_id=""} 0
# HELP coderd_dbpurge_iteration_duration_seconds Duration of each dbpurge iteration in seconds.
# TYPE coderd_dbpurge_iteration_duration_seconds histogram
coderd_dbpurge_iteration_duration_seconds{success=""} 0
# HELP coderd_dbpurge_records_purged_total Total number of records purged by type.
# TYPE coderd_dbpurge_records_purged_total counter
coderd_dbpurge_records_purged_total{record_type=""} 0
# HELP coderd_experiments Indicates whether each experiment is enabled (1) or not (0)
# TYPE coderd_experiments gauge
coderd_experiments{experiment=""} 0
# HELP coderd_insights_applications_usage_seconds The application usage per template.
# TYPE coderd_insights_applications_usage_seconds gauge
coderd_insights_applications_usage_seconds{template_name="",application_name="",slug=""} 0
@@ -40,6 +187,9 @@ coderd_license_user_limit_enabled 0
# HELP coderd_license_warnings The number of active license warnings.
# TYPE coderd_license_warnings gauge
coderd_license_warnings 0
# HELP coderd_prebuilt_workspace_claim_duration_seconds Time to claim a prebuilt workspace by organization, template, and preset.
# TYPE coderd_prebuilt_workspace_claim_duration_seconds histogram
coderd_prebuilt_workspace_claim_duration_seconds{organization_name="",template_name="",preset_name=""} 0
# HELP coderd_prebuilt_workspaces_claimed_total Total number of prebuilt workspaces which were claimed by users. Claiming refers to creating a workspace with a preset selected for which eligible prebuilt workspaces are available and one is reassigned to a user.
# TYPE coderd_prebuilt_workspaces_claimed_total counter
coderd_prebuilt_workspaces_claimed_total{template_name="",preset_name="",organization_name=""} 0
@@ -70,3 +220,36 @@ coderd_prebuilt_workspaces_resource_replacements_total{template_name="",preset_n
# HELP coderd_prebuilt_workspaces_running Current number of prebuilt workspaces that are in a running state. These workspaces have started successfully but may not yet be claimable by users (see coderd_prebuilt_workspaces_eligible).
# TYPE coderd_prebuilt_workspaces_running gauge
coderd_prebuilt_workspaces_running{template_name="",preset_name="",organization_name=""} 0
# HELP coderd_prometheusmetrics_agents_execution_seconds Histogram for duration of agents metrics collection in seconds.
# TYPE coderd_prometheusmetrics_agents_execution_seconds histogram
coderd_prometheusmetrics_agents_execution_seconds 0
# HELP coderd_prometheusmetrics_agentstats_execution_seconds Histogram for duration of agent stats metrics collection in seconds.
# TYPE coderd_prometheusmetrics_agentstats_execution_seconds histogram
coderd_prometheusmetrics_agentstats_execution_seconds 0
# HELP coderd_prometheusmetrics_metrics_aggregator_execution_cleanup_seconds Histogram for duration of metrics aggregator cleanup in seconds.
# TYPE coderd_prometheusmetrics_metrics_aggregator_execution_cleanup_seconds histogram
coderd_prometheusmetrics_metrics_aggregator_execution_cleanup_seconds 0
# HELP coderd_prometheusmetrics_metrics_aggregator_execution_update_seconds Histogram for duration of metrics aggregator update in seconds.
# TYPE coderd_prometheusmetrics_metrics_aggregator_execution_update_seconds histogram
coderd_prometheusmetrics_metrics_aggregator_execution_update_seconds 0
# HELP coderd_prometheusmetrics_metrics_aggregator_store_size The number of metrics stored in the aggregator
# TYPE coderd_prometheusmetrics_metrics_aggregator_store_size gauge
coderd_prometheusmetrics_metrics_aggregator_store_size 0
# HELP coderd_proxyhealth_health_check_duration_seconds Histogram for duration of proxy health collection in seconds.
# TYPE coderd_proxyhealth_health_check_duration_seconds histogram
coderd_proxyhealth_health_check_duration_seconds 0
# HELP coderd_proxyhealth_health_check_results This endpoint returns a number to indicate the health status. -3 (unknown), -2 (Unreachable), -1 (Unhealthy), 0 (Unregistered), 1 (Healthy)
# TYPE coderd_proxyhealth_health_check_results gauge
coderd_proxyhealth_health_check_results{proxy_id=""} 0
# HELP coderd_template_workspace_build_duration_seconds Duration from workspace build creation to agent ready, by template.
# TYPE coderd_template_workspace_build_duration_seconds histogram
coderd_template_workspace_build_duration_seconds{template_name="",organization_name="",transition="",status="",is_prebuild=""} 0
# HELP coderd_workspace_creation_duration_seconds Time to create a workspace by organization, template, preset, and type (regular or prebuild).
# TYPE coderd_workspace_creation_duration_seconds histogram
coderd_workspace_creation_duration_seconds{organization_name="",template_name="",preset_name="",type=""} 0
# HELP coderd_workspace_creation_total Total regular (non-prebuilt) workspace creations by organization, template, and preset.
# TYPE coderd_workspace_creation_total counter
coderd_workspace_creation_total{organization_name="",template_name="",preset_name=""} 0
# HELP coderd_workspace_latest_build_status The current workspace statuses by template, transition, and owner for all non-deleted workspaces.
# TYPE coderd_workspace_latest_build_status gauge
coderd_workspace_latest_build_status{status="",template_name="",template_version="",workspace_owner="",workspace_transition=""} 0
+224 -2
View File
@@ -50,12 +50,26 @@ type Metric struct {
Labels []string // Label names for this metric
}
// metricOpts holds the fields extracted from a prometheus.*Opts struct.
type metricOpts struct {
Namespace string
Subsystem string
Name string
Help string
}
// declarations holds const/var values collected from a file for resolving references.
type declarations struct {
strings map[string]string // string constants/variables
stringSlices map[string][]string // []string variables
}
// packageDeclarations holds exported string constants collected from all scanned files,
// keyed by package name. This allows resolving cross-file references.
// Note: resolution depends on directory scan order in scanDirs, i.e.,
// constants from later directories won't be available when scanning earlier ones.
var packageDeclarations = make(map[string]map[string]string)
func main() {
metrics, err := scanAllDirs()
if err != nil {
@@ -142,7 +156,10 @@ func scanFile(path string) ([]Metric, error) {
return nil, xerrors.Errorf("parsing file: %w", err)
}
// First pass: collect const and var declarations for resolving references.
// Collect exported constants into the global package declarations map.
collectPackageConsts(file)
// Collect file-local const and var declarations for resolving references.
decls := collectDecls(file)
var metrics []Metric
@@ -156,6 +173,11 @@ func scanFile(path string) ([]Metric, error) {
metric, ok := extractMetricFromCall(call, decls)
if ok {
// TODO(ssncferreira): Consider filtering out metrics with empty Help descriptions.
// These indicate missing documentation in the source code.
if metric.Help == "" {
log.Printf("WARNING: metric %q has no HELP description, consider updating the source code", metric.Name)
}
metrics = append(metrics, metric)
}
@@ -165,10 +187,51 @@ func scanFile(path string) ([]Metric, error) {
return metrics, nil
}
// collectPackageConsts collects exported string constants from a file into
// the global packageDeclarations map, keyed by package name.
func collectPackageConsts(file *ast.File) {
pkgName := file.Name.Name
if packageDeclarations[pkgName] == nil {
packageDeclarations[pkgName] = make(map[string]string)
}
for _, decl := range file.Decls {
genDecl, ok := decl.(*ast.GenDecl)
if !ok || genDecl.Tok != token.CONST {
continue
}
for _, spec := range genDecl.Specs {
valueSpec, ok := spec.(*ast.ValueSpec)
if !ok {
continue
}
for i, name := range valueSpec.Names {
if !ast.IsExported(name.Name) {
continue
}
if i >= len(valueSpec.Values) {
continue
}
if lit, ok := valueSpec.Values[i].(*ast.BasicLit); ok {
if lit.Kind == token.STRING {
packageDeclarations[pkgName][name.Name] = strings.Trim(lit.Value, `"`)
}
}
}
}
}
}
// resolveStringExpr attempts to resolve an expression to a string value.
// Examples:
// - "my_metric": "my_metric" (string literal)
// - metricName: resolved value of metricName constant (identifier)
// - agentmetrics.LabelUsername: resolved from package constants (selector)
func resolveStringExpr(expr ast.Expr, decls declarations) string {
switch e := expr.(type) {
case *ast.BasicLit:
@@ -177,7 +240,15 @@ func resolveStringExpr(expr ast.Expr, decls declarations) string {
return decls.strings[e.Name]
case *ast.BinaryExpr:
return resolveBinaryExpr(e, decls)
case *ast.SelectorExpr:
// Handle pkg.Const syntax.
if ident, ok := e.X.(*ast.Ident); ok {
if pkgConsts, ok := packageDeclarations[ident.Name]; ok {
return pkgConsts[e.Sel.Name]
}
}
}
return ""
}
@@ -331,6 +402,153 @@ func extractNewDescMetric(call *ast.CallExpr, decls declarations) (Metric, bool)
}, true
}
// parseMetricFuncName parses a prometheus function name and returns the metric type
// and whether it's a Vec type. Returns empty string if not a recognized metric function.
func parseMetricFuncName(funcName string) (MetricType, bool) {
isVec := strings.HasSuffix(funcName, "Vec")
baseName := strings.TrimSuffix(funcName, "Vec")
switch baseName {
case "NewGauge":
return MetricTypeGauge, isVec
case "NewCounter":
return MetricTypeCounter, isVec
case "NewHistogram":
return MetricTypeHistogram, isVec
case "NewSummary":
return MetricTypeSummary, isVec
}
return "", false
}
// extractOpts extracts fields from a prometheus.*Opts composite literal.
func extractOpts(expr ast.Expr, decls declarations) (metricOpts, bool) {
// Handle both direct composite literals and calls that return opts.
var lit *ast.CompositeLit
switch e := expr.(type) {
case *ast.CompositeLit:
lit = e
case *ast.UnaryExpr:
// Handle &prometheus.GaugeOpts{...}
if l, ok := e.X.(*ast.CompositeLit); ok {
lit = l
}
}
if lit == nil {
return metricOpts{}, false
}
var opts metricOpts
for _, elt := range lit.Elts {
kv, ok := elt.(*ast.KeyValueExpr)
if !ok {
continue
}
key, ok := kv.Key.(*ast.Ident)
if !ok {
continue
}
value := resolveStringExpr(kv.Value, decls)
switch key.Name {
case "Namespace":
opts.Namespace = value
case "Subsystem":
opts.Subsystem = value
case "Name":
opts.Name = value
case "Help":
opts.Help = value
}
}
return opts, opts.Name != ""
}
// buildMetricName constructs the full metric name from namespace, subsystem, and name.
func buildMetricName(namespace, subsystem, name string) string {
metricNameParts := make([]string, 0, 3)
if namespace != "" {
metricNameParts = append(metricNameParts, namespace)
}
if subsystem != "" {
metricNameParts = append(metricNameParts, subsystem)
}
if name != "" {
metricNameParts = append(metricNameParts, name)
}
// Join non-empty parts with "_" to handle optional namespace/subsystem.
// e.g., ("coderd", "", "agents_up"): "coderd_agents_up"
return strings.Join(metricNameParts, "_")
}
// extractOptsMetric extracts a metric from prometheus.New*() or prometheus.New*Vec() calls.
// Supported patterns:
// - prometheus.NewGauge(prometheus.GaugeOpts{...})
// - prometheus.NewCounter(prometheus.CounterOpts{...})
// - prometheus.NewHistogram(prometheus.HistogramOpts{...})
// - prometheus.NewSummary(prometheus.SummaryOpts{...})
// - prometheus.NewGaugeVec(prometheus.GaugeOpts{...}, labels)
// - prometheus.NewCounterVec(prometheus.CounterOpts{...}, labels)
// - prometheus.NewHistogramVec(prometheus.HistogramOpts{...}, labels)
// - prometheus.NewSummaryVec(prometheus.SummaryOpts{...}, labels)
func extractOptsMetric(call *ast.CallExpr, decls declarations) (Metric, bool) {
sel, ok := call.Fun.(*ast.SelectorExpr)
if !ok {
return Metric{}, false
}
// Match calls that are exactly "prometheus.New*(...)". This checks the local
// package identifier, not the resolved import path. If the prometheus package
// is imported with an alias, this will not match.
ident, ok := sel.X.(*ast.Ident)
if !ok || ident.Name != "prometheus" {
return Metric{}, false
}
funcName := sel.Sel.Name
metricType, isVec := parseMetricFuncName(funcName)
if metricType == "" {
return Metric{}, false
}
// Need at least one argument (the Opts struct).
if len(call.Args) < 1 {
return Metric{}, false
}
// Extract metric info from the Opts struct.
opts, ok := extractOpts(call.Args[0], decls)
if !ok {
log.Printf("extractOptsMetric: skipping prometheus.%s() call: could not extract opts", funcName)
return Metric{}, false
}
// Extract labels for Vec types.
var labels []string
if isVec && len(call.Args) >= 2 {
labels = extractLabels(call.Args[1], decls)
}
// Build the full metric name.
name := buildMetricName(opts.Namespace, opts.Subsystem, opts.Name)
if name == "" {
log.Printf("extractOptsMetric: skipping prometheus.%s() call: could not build metric name", funcName)
return Metric{}, false
}
return Metric{
Name: name,
Type: metricType,
Help: opts.Help,
Labels: labels,
}, true
}
// extractMetricFromCall attempts to extract a Metric from a function call expression.
// It returns the metric and true if successful, or an empty metric and false if
// the call is not a metric registration.
@@ -345,8 +563,12 @@ func extractMetricFromCall(call *ast.CallExpr, decls declarations) (Metric, bool
return metric, true
}
// Check for prometheus.New*() and prometheus.New*Vec() patterns.
if metric, ok := extractOptsMetric(call, decls); ok {
return metric, true
}
// TODO(ssncferreira): Implement upstack.
// Handle prometheus.New*Vec() and prometheus.New*() with *Opts{}
// Handle promauto.With(reg).New*() pattern
return Metric{}, false