diff --git a/coderd/prometheusmetrics/insights/metricscollector.go b/coderd/prometheusmetrics/insights/metricscollector.go index dc1d3e5363..207541fc09 100644 --- a/coderd/prometheusmetrics/insights/metricscollector.go +++ b/coderd/prometheusmetrics/insights/metricscollector.go @@ -19,9 +19,9 @@ import ( ) var ( - templatesActiveUsersDesc = prometheus.NewDesc("coderd_insights_templates_active_users", "The number of active users of the template.", []string{"template_name"}, nil) - applicationsUsageSecondsDesc = prometheus.NewDesc("coderd_insights_applications_usage_seconds", "The application usage per template.", []string{"template_name", "application_name", "slug"}, nil) - parametersDesc = prometheus.NewDesc("coderd_insights_parameters", "The parameter usage per template.", []string{"template_name", "parameter_name", "parameter_type", "parameter_value"}, nil) + templatesActiveUsersDesc = prometheus.NewDesc("coderd_insights_templates_active_users", "The number of active users of the template.", []string{"template_name", "organization_name"}, nil) + applicationsUsageSecondsDesc = prometheus.NewDesc("coderd_insights_applications_usage_seconds", "The application usage per template.", []string{"template_name", "application_name", "slug", "organization_name"}, nil) + parametersDesc = prometheus.NewDesc("coderd_insights_parameters", "The parameter usage per template.", []string{"template_name", "parameter_name", "parameter_type", "parameter_value", "organization_name"}, nil) ) type MetricsCollector struct { @@ -38,7 +38,8 @@ type insightsData struct { apps []database.GetTemplateAppInsightsByTemplateRow params []parameterRow - templateNames map[uuid.UUID]string + templateNames map[uuid.UUID]string + organizationNames map[uuid.UUID]string // template ID → org name } type parameterRow struct { @@ -137,6 +138,7 @@ func (mc *MetricsCollector) Run(ctx context.Context) (func(), error) { templateIDs := uniqueTemplateIDs(templateInsights, appInsights, paramInsights) templateNames := make(map[uuid.UUID]string, len(templateIDs)) + organizationNames := make(map[uuid.UUID]string, len(templateIDs)) if len(templateIDs) > 0 { templates, err := mc.database.GetTemplatesWithFilter(ctx, database.GetTemplatesWithFilterParams{ IDs: templateIDs, @@ -146,6 +148,31 @@ func (mc *MetricsCollector) Run(ctx context.Context) (func(), error) { return } templateNames = onlyTemplateNames(templates) + + // Build org name lookup so that metrics can + // distinguish templates with the same name across + // different organizations. + orgIDs := make([]uuid.UUID, 0, len(templates)) + for _, t := range templates { + orgIDs = append(orgIDs, t.OrganizationID) + } + orgIDs = slice.Unique(orgIDs) + + orgs, err := mc.database.GetOrganizations(ctx, database.GetOrganizationsParams{ + IDs: orgIDs, + }) + if err != nil { + mc.logger.Error(ctx, "unable to fetch organizations from database", slog.Error(err)) + return + } + orgNameByID := make(map[uuid.UUID]string, len(orgs)) + for _, o := range orgs { + orgNameByID[o.ID] = o.Name + } + organizationNames = make(map[uuid.UUID]string, len(templates)) + for _, t := range templates { + organizationNames[t.ID] = orgNameByID[t.OrganizationID] + } } // Refresh the collector state @@ -154,7 +181,8 @@ func (mc *MetricsCollector) Run(ctx context.Context) (func(), error) { apps: appInsights, params: paramInsights, - templateNames: templateNames, + templateNames: templateNames, + organizationNames: organizationNames, }) } @@ -194,44 +222,46 @@ func (mc *MetricsCollector) Collect(metricsCh chan<- prometheus.Metric) { // Custom apps for _, appRow := range data.apps { metricsCh <- prometheus.MustNewConstMetric(applicationsUsageSecondsDesc, prometheus.GaugeValue, float64(appRow.UsageSeconds), data.templateNames[appRow.TemplateID], - appRow.DisplayName, appRow.SlugOrPort) + appRow.DisplayName, appRow.SlugOrPort, data.organizationNames[appRow.TemplateID]) } // Built-in apps for _, templateRow := range data.templates { + orgName := data.organizationNames[templateRow.TemplateID] + metricsCh <- prometheus.MustNewConstMetric(applicationsUsageSecondsDesc, prometheus.GaugeValue, float64(templateRow.UsageVscodeSeconds), data.templateNames[templateRow.TemplateID], codersdk.TemplateBuiltinAppDisplayNameVSCode, - "") + "", orgName) metricsCh <- prometheus.MustNewConstMetric(applicationsUsageSecondsDesc, prometheus.GaugeValue, float64(templateRow.UsageJetbrainsSeconds), data.templateNames[templateRow.TemplateID], codersdk.TemplateBuiltinAppDisplayNameJetBrains, - "") + "", orgName) metricsCh <- prometheus.MustNewConstMetric(applicationsUsageSecondsDesc, prometheus.GaugeValue, float64(templateRow.UsageReconnectingPtySeconds), data.templateNames[templateRow.TemplateID], codersdk.TemplateBuiltinAppDisplayNameWebTerminal, - "") + "", orgName) metricsCh <- prometheus.MustNewConstMetric(applicationsUsageSecondsDesc, prometheus.GaugeValue, float64(templateRow.UsageSshSeconds), data.templateNames[templateRow.TemplateID], codersdk.TemplateBuiltinAppDisplayNameSSH, - "") + "", orgName) } // Templates for _, templateRow := range data.templates { - metricsCh <- prometheus.MustNewConstMetric(templatesActiveUsersDesc, prometheus.GaugeValue, float64(templateRow.ActiveUsers), data.templateNames[templateRow.TemplateID]) + metricsCh <- prometheus.MustNewConstMetric(templatesActiveUsersDesc, prometheus.GaugeValue, float64(templateRow.ActiveUsers), data.templateNames[templateRow.TemplateID], data.organizationNames[templateRow.TemplateID]) } // Parameters for _, parameterRow := range data.params { - metricsCh <- prometheus.MustNewConstMetric(parametersDesc, prometheus.GaugeValue, float64(parameterRow.count), data.templateNames[parameterRow.templateID], parameterRow.name, parameterRow.aType, parameterRow.value) + metricsCh <- prometheus.MustNewConstMetric(parametersDesc, prometheus.GaugeValue, float64(parameterRow.count), data.templateNames[parameterRow.templateID], parameterRow.name, parameterRow.aType, parameterRow.value, data.organizationNames[parameterRow.templateID]) } } diff --git a/coderd/prometheusmetrics/insights/testdata/insights-metrics.json b/coderd/prometheusmetrics/insights/testdata/insights-metrics.json index e672ed304a..6acfb61dd0 100644 --- a/coderd/prometheusmetrics/insights/testdata/insights-metrics.json +++ b/coderd/prometheusmetrics/insights/testdata/insights-metrics.json @@ -1,13 +1,13 @@ { - "coderd_insights_applications_usage_seconds[application_name=JetBrains,slug=,template_name=golden-template]": 60, - "coderd_insights_applications_usage_seconds[application_name=Visual Studio Code,slug=,template_name=golden-template]": 60, - "coderd_insights_applications_usage_seconds[application_name=Web Terminal,slug=,template_name=golden-template]": 0, - "coderd_insights_applications_usage_seconds[application_name=SSH,slug=,template_name=golden-template]": 60, - "coderd_insights_applications_usage_seconds[application_name=Golden Slug,slug=golden-slug,template_name=golden-template]": 180, - "coderd_insights_parameters[parameter_name=first_parameter,parameter_type=string,parameter_value=Foobar,template_name=golden-template]": 1, - "coderd_insights_parameters[parameter_name=first_parameter,parameter_type=string,parameter_value=Baz,template_name=golden-template]": 1, - "coderd_insights_parameters[parameter_name=second_parameter,parameter_type=bool,parameter_value=true,template_name=golden-template]": 2, - "coderd_insights_parameters[parameter_name=third_parameter,parameter_type=number,parameter_value=789,template_name=golden-template]": 1, - "coderd_insights_parameters[parameter_name=third_parameter,parameter_type=number,parameter_value=999,template_name=golden-template]": 1, - "coderd_insights_templates_active_users[template_name=golden-template]": 1 + "coderd_insights_applications_usage_seconds[application_name=JetBrains,organization_name=coder,slug=,template_name=golden-template]": 60, + "coderd_insights_applications_usage_seconds[application_name=Visual Studio Code,organization_name=coder,slug=,template_name=golden-template]": 60, + "coderd_insights_applications_usage_seconds[application_name=Web Terminal,organization_name=coder,slug=,template_name=golden-template]": 0, + "coderd_insights_applications_usage_seconds[application_name=SSH,organization_name=coder,slug=,template_name=golden-template]": 60, + "coderd_insights_applications_usage_seconds[application_name=Golden Slug,organization_name=coder,slug=golden-slug,template_name=golden-template]": 180, + "coderd_insights_parameters[organization_name=coder,parameter_name=first_parameter,parameter_type=string,parameter_value=Foobar,template_name=golden-template]": 1, + "coderd_insights_parameters[organization_name=coder,parameter_name=first_parameter,parameter_type=string,parameter_value=Baz,template_name=golden-template]": 1, + "coderd_insights_parameters[organization_name=coder,parameter_name=second_parameter,parameter_type=bool,parameter_value=true,template_name=golden-template]": 2, + "coderd_insights_parameters[organization_name=coder,parameter_name=third_parameter,parameter_type=number,parameter_value=789,template_name=golden-template]": 1, + "coderd_insights_parameters[organization_name=coder,parameter_name=third_parameter,parameter_type=number,parameter_value=999,template_name=golden-template]": 1, + "coderd_insights_templates_active_users[organization_name=coder,template_name=golden-template]": 1 } diff --git a/docs/admin/integrations/prometheus.md b/docs/admin/integrations/prometheus.md index 3779f961f7..ace326019f 100644 --- a/docs/admin/integrations/prometheus.md +++ b/docs/admin/integrations/prometheus.md @@ -175,9 +175,9 @@ deployment. They will always be available from the agent. | `coderd_dbpurge_iteration_duration_seconds` | histogram | Duration of each dbpurge iteration in seconds. | `success` | | `coderd_dbpurge_records_purged_total` | counter | Total number of records purged by type. | `record_type` | | `coderd_experiments` | gauge | Indicates whether each experiment is enabled (1) or not (0) | `experiment` | -| `coderd_insights_applications_usage_seconds` | gauge | The application usage per template. | `application_name` `slug` `template_name` | -| `coderd_insights_parameters` | gauge | The parameter usage per template. | `parameter_name` `parameter_type` `parameter_value` `template_name` | -| `coderd_insights_templates_active_users` | gauge | The number of active users of the template. | `template_name` | +| `coderd_insights_applications_usage_seconds` | gauge | The application usage per template. | `application_name` `organization_name` `slug` `template_name` | +| `coderd_insights_parameters` | gauge | The parameter usage per template. | `organization_name` `parameter_name` `parameter_type` `parameter_value` `template_name` | +| `coderd_insights_templates_active_users` | gauge | The number of active users of the template. | `organization_name` `template_name` | | `coderd_license_active_users` | gauge | The number of active users. | | | `coderd_license_errors` | gauge | The number of active license errors. | | | `coderd_license_limit_users` | gauge | The user seats limit based on the active Coder license. | | diff --git a/scripts/metricsdocgen/generated_metrics b/scripts/metricsdocgen/generated_metrics index c3a38fc620..887f8f469c 100644 --- a/scripts/metricsdocgen/generated_metrics +++ b/scripts/metricsdocgen/generated_metrics @@ -159,13 +159,13 @@ coderd_dbpurge_records_purged_total{record_type=""} 0 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 +coderd_insights_applications_usage_seconds{template_name="",application_name="",slug="",organization_name=""} 0 # HELP coderd_insights_parameters The parameter usage per template. # TYPE coderd_insights_parameters gauge -coderd_insights_parameters{template_name="",parameter_name="",parameter_type="",parameter_value=""} 0 +coderd_insights_parameters{template_name="",parameter_name="",parameter_type="",parameter_value="",organization_name=""} 0 # HELP coderd_insights_templates_active_users The number of active users of the template. # TYPE coderd_insights_templates_active_users gauge -coderd_insights_templates_active_users{template_name=""} 0 +coderd_insights_templates_active_users{template_name="",organization_name=""} 0 # HELP coderd_license_active_users The number of active users. # TYPE coderd_license_active_users gauge coderd_license_active_users 0