diff --git a/apps/dashboard/go.mod b/apps/dashboard/go.mod index 30fd80214d5..ed72fa1aa90 100644 --- a/apps/dashboard/go.mod +++ b/apps/dashboard/go.mod @@ -7,6 +7,7 @@ require ( github.com/grafana/grafana-app-sdk v0.40.3 github.com/grafana/grafana-plugin-sdk-go v0.278.0 github.com/grafana/grafana/pkg/apimachinery v0.0.0-20250514132646-acbc7b54ed9e + github.com/prometheus/client_golang v1.23.0 github.com/stretchr/testify v1.10.0 k8s.io/apimachinery v0.33.3 k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff @@ -80,7 +81,6 @@ require ( github.com/perimeterx/marshmallow v1.1.5 // indirect github.com/pierrec/lz4/v4 v4.1.22 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect - github.com/prometheus/client_golang v1.23.0 // indirect github.com/prometheus/client_model v0.6.2 // indirect github.com/prometheus/common v0.65.0 // indirect github.com/prometheus/procfs v0.16.1 // indirect diff --git a/apps/dashboard/pkg/migration/conversion/conversion.go b/apps/dashboard/pkg/migration/conversion/conversion.go index fb78859fc3c..ca93badb127 100644 --- a/apps/dashboard/pkg/migration/conversion/conversion.go +++ b/apps/dashboard/pkg/migration/conversion/conversion.go @@ -8,8 +8,11 @@ import ( dashv1 "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v1beta1" dashv2alpha1 "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1" dashv2beta1 "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1" + "github.com/grafana/grafana/pkg/infra/log" ) +var logger = log.New("dashboard.conversion") + func RegisterConversions(s *runtime.Scheme) error { // v0 conversions if err := s.AddConversionFunc((*dashv0.Dashboard)(nil), (*dashv1.Dashboard)(nil), func(a, b interface{}, scope conversion.Scope) error { diff --git a/apps/dashboard/pkg/migration/conversion/v0.go b/apps/dashboard/pkg/migration/conversion/v0.go index a63fe00f611..6ea720a3fb5 100644 --- a/apps/dashboard/pkg/migration/conversion/v0.go +++ b/apps/dashboard/pkg/migration/conversion/v0.go @@ -1,6 +1,9 @@ package conversion import ( + "errors" + "fmt" + "k8s.io/apimachinery/pkg/conversion" dashv0 "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v0alpha1" @@ -25,8 +28,45 @@ func Convert_V0_to_V1(in *dashv0.Dashboard, out *dashv1.Dashboard, scope convers if err := migration.Migrate(out.Spec.Object, schemaversion.LATEST_VERSION); err != nil { out.Status.Conversion.Failed = true out.Status.Conversion.Error = err.Error() + + // Classify error type for metrics + errorType := "conversion_error" + var migrationErr *schemaversion.MigrationError + var minVersionErr *schemaversion.MinimumVersionError + if errors.As(err, &migrationErr) { + errorType = "schema_version_migration_error" + } else if errors.As(err, &minVersionErr) { + errorType = "schema_minimum_version_error" + } + + // Record failure metrics + migration.MDashboardConversionFailureTotal.WithLabelValues( + dashv0.APIVERSION, + dashv1.APIVERSION, + fmt.Sprintf("%v", in.Spec.Object["schemaVersion"]), + fmt.Sprintf("%d", schemaversion.LATEST_VERSION), + errorType, + ).Inc() + + logger.Error("Dashboard conversion failed", + "sourceVersionAPI", dashv0.APIVERSION, + "targetVersionAPI", dashv1.APIVERSION, + "dashboardUID", in.UID, + "sourceSchemaVersion", in.Spec.Object["schemaVersion"], + "targetSchemaVersion", schemaversion.LATEST_VERSION, + "errorType", errorType, + "error", err) + + return nil } + migration.MDashboardConversionSuccessTotal.WithLabelValues( + dashv0.APIVERSION, + dashv1.APIVERSION, + fmt.Sprintf("%v", in.Spec.Object["schemaVersion"]), + fmt.Sprintf("%d", schemaversion.LATEST_VERSION), + ).Inc() + return nil } diff --git a/apps/dashboard/pkg/migration/metrics.go b/apps/dashboard/pkg/migration/metrics.go new file mode 100644 index 00000000000..04e88370e74 --- /dev/null +++ b/apps/dashboard/pkg/migration/metrics.go @@ -0,0 +1,44 @@ +package migration + +import ( + "github.com/prometheus/client_golang/prometheus" +) + +const ( + metricsNamespace = "grafana" + metricsSubSystem = "dashboard_migration" +) + +var ( + // MDashboardConversionSuccessTotal is a metric counter for successful dashboard conversions + MDashboardConversionSuccessTotal *prometheus.CounterVec + + // MDashboardConversionFailureTotal is a metric counter for failed dashboard conversions + MDashboardConversionFailureTotal *prometheus.CounterVec +) + +func init() { + MDashboardConversionSuccessTotal = prometheus.NewCounterVec(prometheus.CounterOpts{ + Namespace: metricsNamespace, + Subsystem: metricsSubSystem, + Name: "conversion_success_total", + Help: "Total number of successful dashboard conversions", + }, []string{"source_version_api", "target_version_api", "source_schema_version", "target_schema_version"}) + + MDashboardConversionFailureTotal = prometheus.NewCounterVec(prometheus.CounterOpts{ + Namespace: metricsNamespace, + Subsystem: metricsSubSystem, + Name: "conversion_failure_total", + Help: "Total number of failed dashboard conversions", + }, []string{"source_version_api", "target_version_api", "source_schema_version", "target_schema_version", "error_type"}) +} + +// RegisterMetrics registers all migration metrics with the provided Prometheus registerer +func RegisterMetrics(reg prometheus.Registerer) { + if reg != nil { + reg.MustRegister( + MDashboardConversionSuccessTotal, + MDashboardConversionFailureTotal, + ) + } +}