Skip to content

Commit 7204a64

Browse files
authored
LibraryElements: Creates usage stats for panels and variables (grafana#34476)
* LibraryPanels: Adds usage collection * Refactor: renames Panel and Variable consts * Chore: initialize stats * Refactor: moves library element migrations to migration namespace
1 parent abe5c06 commit 7204a64

22 files changed

+174
-126
lines changed

pkg/infra/metrics/metrics.go

+20
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,12 @@ var (
185185
grafanaBuildVersion *prometheus.GaugeVec
186186

187187
grafanaPluginBuildInfoDesc *prometheus.GaugeVec
188+
189+
// StatsTotalLibraryPanels is a metric of total number of library panels stored in Grafana.
190+
StatsTotalLibraryPanels prometheus.Gauge
191+
192+
// StatsTotalLibraryVariables is a metric of total number of library variables stored in Grafana.
193+
StatsTotalLibraryVariables prometheus.Gauge
188194
)
189195

190196
func init() {
@@ -547,6 +553,18 @@ func init() {
547553
Help: "number of evaluation calls",
548554
Namespace: ExporterName,
549555
})
556+
557+
StatsTotalLibraryPanels = prometheus.NewGauge(prometheus.GaugeOpts{
558+
Name: "stat_totals_library_panels",
559+
Help: "total amount of library panels in the database",
560+
Namespace: ExporterName,
561+
})
562+
563+
StatsTotalLibraryVariables = prometheus.NewGauge(prometheus.GaugeOpts{
564+
Name: "stat_totals_library_variables",
565+
Help: "total amount of library variables in the database",
566+
Namespace: ExporterName,
567+
})
550568
}
551569

552570
// SetBuildInformation sets the build information for this binary
@@ -640,6 +658,8 @@ func initMetricVars() {
640658
StatsTotalDashboardVersions,
641659
StatsTotalAnnotations,
642660
MAccessEvaluationCount,
661+
StatsTotalLibraryPanels,
662+
StatsTotalLibraryVariables,
643663
)
644664
}
645665

pkg/infra/usagestats/usage_stats.go

+4
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,8 @@ func (uss *UsageStatsService) GetUsageReport(ctx context.Context) (UsageReport,
7272
metrics["stats.dashboard_versions.count"] = statsQuery.Result.DashboardVersions
7373
metrics["stats.annotations.count"] = statsQuery.Result.Annotations
7474
metrics["stats.alert_rules.count"] = statsQuery.Result.AlertRules
75+
metrics["stats.library_panels.count"] = statsQuery.Result.LibraryPanels
76+
metrics["stats.library_variables.count"] = statsQuery.Result.LibraryVariables
7577
validLicCount := 0
7678
if uss.License.HasValidLicense() {
7779
validLicCount = 1
@@ -317,6 +319,8 @@ func (uss *UsageStatsService) updateTotalStats() {
317319
metrics.StatsTotalDashboardVersions.Set(float64(statsQuery.Result.DashboardVersions))
318320
metrics.StatsTotalAnnotations.Set(float64(statsQuery.Result.Annotations))
319321
metrics.StatsTotalAlertRules.Set(float64(statsQuery.Result.AlertRules))
322+
metrics.StatsTotalLibraryPanels.Set(float64(statsQuery.Result.LibraryPanels))
323+
metrics.StatsTotalLibraryVariables.Set(float64(statsQuery.Result.LibraryVariables))
320324

321325
dsStats := models.GetDataSourceStatsQuery{}
322326
if err := uss.Bus.Dispatch(&dsStats); err != nil {

pkg/infra/usagestats/usage_stats_test.go

+4
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,8 @@ func TestMetrics(t *testing.T) {
6363
DashboardVersions: 16,
6464
Annotations: 17,
6565
AlertRules: 18,
66+
LibraryPanels: 19,
67+
LibraryVariables: 20,
6668
}
6769
getSystemStatsQuery = query
6870
return nil
@@ -313,6 +315,8 @@ func TestMetrics(t *testing.T) {
313315
assert.Equal(t, 16, metrics.Get("stats.dashboard_versions.count").MustInt())
314316
assert.Equal(t, 17, metrics.Get("stats.annotations.count").MustInt())
315317
assert.Equal(t, 18, metrics.Get("stats.alert_rules.count").MustInt())
318+
assert.Equal(t, 19, metrics.Get("stats.library_panels.count").MustInt())
319+
assert.Equal(t, 20, metrics.Get("stats.library_variables.count").MustInt())
316320

317321
assert.Equal(t, 9, metrics.Get("stats.ds."+models.DS_ES+".count").MustInt())
318322
assert.Equal(t, 10, metrics.Get("stats.ds."+models.DS_PROMETHEUS+".count").MustInt())

pkg/models/libraryelements.go

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package models
2+
3+
// LibraryElementKind is used for the kind of library element
4+
type LibraryElementKind int
5+
6+
const (
7+
// PanelElement is used for library elements that are of the Panel kind
8+
PanelElement LibraryElementKind = iota + 1
9+
// VariableElement is used for library elements that are of the Variable kind
10+
VariableElement
11+
)
12+
13+
const LibraryElementConnectionTableName = "library_element_connection"

pkg/models/stats.go

+2
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ type SystemStats struct {
1919
DashboardVersions int64
2020
Annotations int64
2121
AlertRules int64
22+
LibraryPanels int64
23+
LibraryVariables int64
2224

2325
Admins int
2426
Editors int

pkg/services/libraryelements/database.go

+9-9
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ SELECT DISTINCT
2222
, u1.email AS created_by_email
2323
, u2.login AS updated_by_name
2424
, u2.email AS updated_by_email
25-
, (SELECT COUNT(connection_id) FROM ` + connectionTableName + ` WHERE element_id = le.id AND kind=1) AS connected_dashboards`
25+
, (SELECT COUNT(connection_id) FROM ` + models.LibraryElementConnectionTableName + ` WHERE element_id = le.id AND kind=1) AS connected_dashboards`
2626
)
2727

2828
func getFromLibraryElementDTOWithMeta(dialect migrator.Dialect) string {
@@ -41,9 +41,9 @@ func syncFieldsWithModel(libraryElement *LibraryElement) error {
4141
return err
4242
}
4343

44-
if LibraryElementKind(libraryElement.Kind) == Panel {
44+
if models.LibraryElementKind(libraryElement.Kind) == models.PanelElement {
4545
model["title"] = libraryElement.Name
46-
} else if LibraryElementKind(libraryElement.Kind) == Variable {
46+
} else if models.LibraryElementKind(libraryElement.Kind) == models.VariableElement {
4747
model["name"] = libraryElement.Name
4848
}
4949
if model["type"] != nil {
@@ -520,7 +520,7 @@ func (l *LibraryElementService) getConnections(c *models.ReqContext, uid string)
520520
var libraryElementConnections []libraryElementConnectionWithMeta
521521
builder := sqlstore.SQLBuilder{}
522522
builder.Write("SELECT lec.*, u1.login AS created_by_name, u1.email AS created_by_email")
523-
builder.Write(" FROM " + connectionTableName + " AS lec")
523+
builder.Write(" FROM " + models.LibraryElementConnectionTableName + " AS lec")
524524
builder.Write(" LEFT JOIN " + l.SQLStore.Dialect.Quote("user") + " AS u1 ON lec.created_by = u1.id")
525525
builder.Write(" INNER JOIN dashboard AS dashboard on lec.connection_id = dashboard.id")
526526
builder.Write(` WHERE lec.element_id=?`, element.ID)
@@ -562,7 +562,7 @@ func (l *LibraryElementService) getElementsForDashboardID(c *models.ReqContext,
562562
", coalesce(dashboard.uid, '') AS folder_uid" +
563563
getFromLibraryElementDTOWithMeta(l.SQLStore.Dialect) +
564564
" LEFT JOIN dashboard AS dashboard ON dashboard.id = le.folder_id" +
565-
" INNER JOIN " + connectionTableName + " AS lce ON lce.element_id = le.id AND lce.kind=1 AND lce.connection_id=?"
565+
" INNER JOIN " + models.LibraryElementConnectionTableName + " AS lce ON lce.element_id = le.id AND lce.kind=1 AND lce.connection_id=?"
566566
sess := session.SQL(sql, dashboardID)
567567
err := sess.Find(&libraryElements)
568568
if err != nil {
@@ -610,7 +610,7 @@ func (l *LibraryElementService) getElementsForDashboardID(c *models.ReqContext,
610610
// connectElementsToDashboardID adds connections for all elements Library Elements in a Dashboard.
611611
func (l *LibraryElementService) connectElementsToDashboardID(c *models.ReqContext, elementUIDs []string, dashboardID int64) error {
612612
err := l.SQLStore.WithTransactionalDbSession(c.Context.Req.Context(), func(session *sqlstore.DBSession) error {
613-
_, err := session.Exec("DELETE FROM "+connectionTableName+" WHERE kind=1 AND connection_id=?", dashboardID)
613+
_, err := session.Exec("DELETE FROM "+models.LibraryElementConnectionTableName+" WHERE kind=1 AND connection_id=?", dashboardID)
614614
if err != nil {
615615
return err
616616
}
@@ -646,7 +646,7 @@ func (l *LibraryElementService) connectElementsToDashboardID(c *models.ReqContex
646646
// disconnectElementsFromDashboardID deletes connections for all Library Elements in a Dashboard.
647647
func (l *LibraryElementService) disconnectElementsFromDashboardID(c *models.ReqContext, dashboardID int64) error {
648648
return l.SQLStore.WithTransactionalDbSession(c.Context.Req.Context(), func(session *sqlstore.DBSession) error {
649-
_, err := session.Exec("DELETE FROM "+connectionTableName+" WHERE kind=1 AND connection_id=?", dashboardID)
649+
_, err := session.Exec("DELETE FROM "+models.LibraryElementConnectionTableName+" WHERE kind=1 AND connection_id=?", dashboardID)
650650
if err != nil {
651651
return err
652652
}
@@ -676,7 +676,7 @@ func (l *LibraryElementService) deleteLibraryElementsInFolderUID(c *models.ReqCo
676676
ConnectionID int64 `xorm:"connection_id"`
677677
}
678678
sql := "SELECT lec.connection_id FROM library_element AS le"
679-
sql += " INNER JOIN " + connectionTableName + " AS lec on le.id = lec.element_id"
679+
sql += " INNER JOIN " + models.LibraryElementConnectionTableName + " AS lec on le.id = lec.element_id"
680680
sql += " WHERE le.folder_id=? AND le.org_id=?"
681681
err = session.SQL(sql, folderID, c.SignedInUser.OrgId).Find(&connectionIDs)
682682
if err != nil {
@@ -694,7 +694,7 @@ func (l *LibraryElementService) deleteLibraryElementsInFolderUID(c *models.ReqCo
694694
return err
695695
}
696696
for _, elementID := range elementIDs {
697-
_, err := session.Exec("DELETE FROM "+connectionTableName+" WHERE element_id=?", elementID.ID)
697+
_, err := session.Exec("DELETE FROM "+models.LibraryElementConnectionTableName+" WHERE element_id=?", elementID.ID)
698698
if err != nil {
699699
return err
700700
}

pkg/services/libraryelements/guard.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,11 @@ func isGeneralFolder(folderID int64) bool {
1111
}
1212

1313
func (l *LibraryElementService) requireSupportedElementKind(kindAsInt int64) error {
14-
kind := LibraryElementKind(kindAsInt)
14+
kind := models.LibraryElementKind(kindAsInt)
1515
switch kind {
16-
case Panel:
16+
case models.PanelElement:
1717
return nil
18-
case Variable:
18+
case models.VariableElement:
1919
return nil
2020
default:
2121
return errLibraryElementUnSupportedElementKind

pkg/services/libraryelements/libraryelements.go

-51
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import (
66
"github.com/grafana/grafana/pkg/models"
77
"github.com/grafana/grafana/pkg/registry"
88
"github.com/grafana/grafana/pkg/services/sqlstore"
9-
"github.com/grafana/grafana/pkg/services/sqlstore/migrator"
109
"github.com/grafana/grafana/pkg/setting"
1110
)
1211

@@ -27,8 +26,6 @@ type LibraryElementService struct {
2726
log log.Logger
2827
}
2928

30-
const connectionTableName = "library_element_connection"
31-
3229
func init() {
3330
registry.RegisterService(&LibraryElementService{})
3431
}
@@ -66,51 +63,3 @@ func (l *LibraryElementService) DisconnectElementsFromDashboard(c *models.ReqCon
6663
func (l *LibraryElementService) DeleteLibraryElementsInFolder(c *models.ReqContext, folderUID string) error {
6764
return l.deleteLibraryElementsInFolderUID(c, folderUID)
6865
}
69-
70-
// AddMigration defines database migrations.
71-
// If Panel Library is not enabled does nothing.
72-
func (l *LibraryElementService) AddMigration(mg *migrator.Migrator) {
73-
libraryElementsV1 := migrator.Table{
74-
Name: "library_element",
75-
Columns: []*migrator.Column{
76-
{Name: "id", Type: migrator.DB_BigInt, IsPrimaryKey: true, IsAutoIncrement: true},
77-
{Name: "org_id", Type: migrator.DB_BigInt, Nullable: false},
78-
{Name: "folder_id", Type: migrator.DB_BigInt, Nullable: false},
79-
{Name: "uid", Type: migrator.DB_NVarchar, Length: 40, Nullable: false},
80-
{Name: "name", Type: migrator.DB_NVarchar, Length: 150, Nullable: false},
81-
{Name: "kind", Type: migrator.DB_BigInt, Nullable: false},
82-
{Name: "type", Type: migrator.DB_NVarchar, Length: 40, Nullable: false},
83-
{Name: "description", Type: migrator.DB_NVarchar, Length: 255, Nullable: false},
84-
{Name: "model", Type: migrator.DB_Text, Nullable: false},
85-
{Name: "created", Type: migrator.DB_DateTime, Nullable: false},
86-
{Name: "created_by", Type: migrator.DB_BigInt, Nullable: false},
87-
{Name: "updated", Type: migrator.DB_DateTime, Nullable: false},
88-
{Name: "updated_by", Type: migrator.DB_BigInt, Nullable: false},
89-
{Name: "version", Type: migrator.DB_BigInt, Nullable: false},
90-
},
91-
Indices: []*migrator.Index{
92-
{Cols: []string{"org_id", "folder_id", "name", "kind"}, Type: migrator.UniqueIndex},
93-
},
94-
}
95-
96-
mg.AddMigration("create library_element table v1", migrator.NewAddTableMigration(libraryElementsV1))
97-
mg.AddMigration("add index library_element org_id-folder_id-name-kind", migrator.NewAddIndexMigration(libraryElementsV1, libraryElementsV1.Indices[0]))
98-
99-
libraryElementConnectionV1 := migrator.Table{
100-
Name: connectionTableName,
101-
Columns: []*migrator.Column{
102-
{Name: "id", Type: migrator.DB_BigInt, IsPrimaryKey: true, IsAutoIncrement: true},
103-
{Name: "element_id", Type: migrator.DB_BigInt, Nullable: false},
104-
{Name: "kind", Type: migrator.DB_BigInt, Nullable: false},
105-
{Name: "connection_id", Type: migrator.DB_BigInt, Nullable: false},
106-
{Name: "created", Type: migrator.DB_DateTime, Nullable: false},
107-
{Name: "created_by", Type: migrator.DB_BigInt, Nullable: false},
108-
},
109-
Indices: []*migrator.Index{
110-
{Cols: []string{"element_id", "kind", "connection_id"}, Type: migrator.UniqueIndex},
111-
},
112-
}
113-
114-
mg.AddMigration("create "+connectionTableName+" table v1", migrator.NewAddTableMigration(libraryElementConnectionV1))
115-
mg.AddMigration("add index "+connectionTableName+" element_id-kind-connection_id", migrator.NewAddIndexMigration(libraryElementConnectionV1, libraryElementConnectionV1.Indices[0]))
116-
}

pkg/services/libraryelements/libraryelements_create_test.go

+4-2
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ import (
55

66
"github.com/google/go-cmp/cmp"
77
"github.com/stretchr/testify/require"
8+
9+
"github.com/grafana/grafana/pkg/models"
810
)
911

1012
func TestCreateLibraryElement(t *testing.T) {
@@ -24,7 +26,7 @@ func TestCreateLibraryElement(t *testing.T) {
2426
FolderID: 1,
2527
UID: sc.initialResult.Result.UID,
2628
Name: "Text - Library Panel",
27-
Kind: int64(Panel),
29+
Kind: int64(models.PanelElement),
2830
Type: "text",
2931
Description: "A description",
3032
Model: map[string]interface{}{
@@ -69,7 +71,7 @@ func TestCreateLibraryElement(t *testing.T) {
6971
FolderID: 1,
7072
UID: result.Result.UID,
7173
Name: "Library Panel Name",
72-
Kind: int64(Panel),
74+
Kind: int64(models.PanelElement),
7375
Type: "text",
7476
Description: "A description",
7577
Model: map[string]interface{}{

0 commit comments

Comments
 (0)