diff --git a/CHANGELOG.md b/CHANGELOG.md index b49a104..c3b1500 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,10 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/). +## 1.1.0 - 2018-11-20 +### Added +- DBID and Global Name to all entities + ## 1.0.0 - 2018-11-16 ### Changed - Updated to version 1.0.0 diff --git a/src/metric_definitions.go b/src/metric_definitions.go index 3ae1816..90fdef8 100644 --- a/src/metric_definitions.go +++ b/src/metric_definitions.go @@ -185,6 +185,234 @@ var oracleTablespaceMetrics = oracleMetricGroup{ }, } +var globalNameInstanceMetric = oracleMetricGroup{ + sqlQuery: func() string { + query := `SELECT + t1.INST_ID, + t2.GLOBAL_NAME + FROM (SELECT INST_ID FROM gv$instance) t1, + (SELECT GLOBAL_NAME FROM global_name) t2` + return query + }, + + metrics: []*oracleMetric{ + { + name: "globalName", + identifier: "GLOBAL_NAME", + metricType: metric.ATTRIBUTE, + defaultMetric: true, + }, + }, + + metricsGenerator: func(rows *sql.Rows, metrics []*oracleMetric, metricChan chan<- newrelicMetricSender) error { + + type pgaRow struct { + instID int + value string + } + for rows.Next() { + + // Scan the row into a struct + var tempPgaRow pgaRow + err := rows.Scan(&tempPgaRow.instID, &tempPgaRow.value) + if err != nil { + return err + } + + // Match the metric to one of the metrics we want to collect + for _, metric := range metrics { + if metric.defaultMetric || args.ExtendedMetrics { + newMetric := &newrelicMetric{ + name: metric.name, + value: tempPgaRow.value, + metricType: metric.metricType, + } + + metadata := map[string]string{"instanceID": strconv.Itoa(tempPgaRow.instID)} + + // Send the new metric down the channel + metricChan <- newrelicMetricSender{metric: newMetric, metadata: metadata} + break + + } + } + } + + return nil + }, +} + +var globalNameTablespaceMetric = oracleMetricGroup{ + sqlQuery: func() string { + query := `SELECT + t1.TABLESPACE_NAME, + t2.GLOBAL_NAME + FROM (SELECT TABLESPACE_NAME FROM DBA_TABLESPACES) t1, + (SELECT GLOBAL_NAME FROM global_name) t2` + return query + }, + + metrics: []*oracleMetric{ + { + name: "globalName", + identifier: "GLOBAL_NAME", + metricType: metric.ATTRIBUTE, + defaultMetric: true, + }, + }, + + metricsGenerator: func(rows *sql.Rows, metrics []*oracleMetric, metricChan chan<- newrelicMetricSender) error { + + type pgaRow struct { + tableName string + value string + } + for rows.Next() { + + // Scan the row into a struct + var tempPgaRow pgaRow + err := rows.Scan(&tempPgaRow.tableName, &tempPgaRow.value) + if err != nil { + return err + } + + // Match the metric to one of the metrics we want to collect + for _, metric := range metrics { + if metric.defaultMetric || args.ExtendedMetrics { + newMetric := &newrelicMetric{ + name: metric.name, + value: tempPgaRow.value, + metricType: metric.metricType, + } + + metadata := map[string]string{"tablespace": tempPgaRow.tableName} + + // Send the new metric down the channel + metricChan <- newrelicMetricSender{metric: newMetric, metadata: metadata} + break + + } + } + } + + return nil + }, +} + +var dbIDInstanceMetric = oracleMetricGroup{ + sqlQuery: func() string { + query := `SELECT + t1.INST_ID, + t2.DBID + FROM (SELECT INST_ID FROM gv$instance) t1, + (SELECT DBID FROM v$database) t2` + return query + }, + + metrics: []*oracleMetric{ + { + name: "dbID", + identifier: "DBID", + metricType: metric.ATTRIBUTE, + defaultMetric: true, + }, + }, + + metricsGenerator: func(rows *sql.Rows, metrics []*oracleMetric, metricChan chan<- newrelicMetricSender) error { + + type pgaRow struct { + instID int + value string + } + for rows.Next() { + + // Scan the row into a struct + var tempPgaRow pgaRow + err := rows.Scan(&tempPgaRow.instID, &tempPgaRow.value) + if err != nil { + return err + } + + // Match the metric to one of the metrics we want to collect + for _, metric := range metrics { + if metric.defaultMetric || args.ExtendedMetrics { + newMetric := &newrelicMetric{ + name: metric.name, + value: tempPgaRow.value, + metricType: metric.metricType, + } + + metadata := map[string]string{"instanceID": strconv.Itoa(tempPgaRow.instID)} + + // Send the new metric down the channel + metricChan <- newrelicMetricSender{metric: newMetric, metadata: metadata} + break + + } + } + } + + return nil + }, +} + +var dbIDTablespaceMetric = oracleMetricGroup{ + sqlQuery: func() string { + query := `SELECT + t1.TABLESPACE_NAME, + t2.DBID + FROM (SELECT TABLESPACE_NAME FROM DBA_TABLESPACES) t1, + (SELECT DBID FROM v$database) t2` + return query + }, + + metrics: []*oracleMetric{ + { + name: "dbID", + identifier: "DBID", + metricType: metric.ATTRIBUTE, + defaultMetric: true, + }, + }, + + metricsGenerator: func(rows *sql.Rows, metrics []*oracleMetric, metricChan chan<- newrelicMetricSender) error { + + type pgaRow struct { + tableName string + value string + } + for rows.Next() { + + // Scan the row into a struct + var tempPgaRow pgaRow + err := rows.Scan(&tempPgaRow.tableName, &tempPgaRow.value) + if err != nil { + return err + } + + // Match the metric to one of the metrics we want to collect + for _, metric := range metrics { + if metric.defaultMetric || args.ExtendedMetrics { + newMetric := &newrelicMetric{ + name: metric.name, + value: tempPgaRow.value, + metricType: metric.metricType, + } + + metadata := map[string]string{"tablespace": tempPgaRow.tableName} + + // Send the new metric down the channel + metricChan <- newrelicMetricSender{metric: newMetric, metadata: metadata} + break + + } + } + } + + return nil + }, +} + var oracleReadWriteMetrics = oracleMetricGroup{ sqlQuery: func() string { return ` diff --git a/src/metrics.go b/src/metrics.go index 6837f6f..4284e54 100644 --- a/src/metrics.go +++ b/src/metrics.go @@ -19,10 +19,12 @@ func collectMetrics(db *sql.DB, populaterWg *sync.WaitGroup, i *integration.Inte metricChan := make(chan newrelicMetricSender, 100) // large buffer for speed // Create a goroutine for each of the metric groups to collect - collectorWg.Add(3) + collectorWg.Add(5) go oracleReadWriteMetrics.Collect(db, &collectorWg, metricChan) go oraclePgaMetrics.Collect(db, &collectorWg, metricChan) go oracleSysMetrics.Collect(db, &collectorWg, metricChan) + go globalNameInstanceMetric.Collect(db, &collectorWg, metricChan) + go dbIDInstanceMetric.Collect(db, &collectorWg, metricChan) // Separate logic is needed to see if we should even collect tablespaces collectTableSpaces(db, &collectorWg, metricChan) @@ -131,8 +133,11 @@ func collectTableSpaces(db *sql.DB, wg *sync.WaitGroup, metricChan chan<- newrel return } - wg.Add(1) + wg.Add(3) go oracleTablespaceMetrics.Collect(db, wg, metricChan) + go globalNameTablespaceMetric.Collect(db, wg, metricChan) + go dbIDTablespaceMetric.Collect(db, wg, metricChan) + } func queryNumTablespaces(db *sql.DB) (int, error) { diff --git a/src/metrics_definitions_test.go b/src/metrics_definitions_test.go index a49a46e..194f85f 100644 --- a/src/metrics_definitions_test.go +++ b/src/metrics_definitions_test.go @@ -17,7 +17,7 @@ func TestOracleTablespaceMetrics(t *testing.T) { t.Error(err) } - mock.ExpectQuery(".*").WillReturnRows( + mock.ExpectQuery(`SELECT TABLESPACE_NAME, SUM\(bytes\) AS "USED",.*`).WillReturnRows( sqlmock.NewRows([]string{"TABLESPACE_NAME", "USED", "OFFLINE", "SIZE", "USED_PERCENT"}). AddRow("testtablespace", 1234, 0, 4321, 12), ) @@ -68,6 +68,239 @@ func TestOracleTablespaceMetrics(t *testing.T) { } } +func Test_dbIDTablespaceMetric(t *testing.T) { + db, mock, err := sqlmock.New() + if err != nil { + t.Error(err) + } + + mock.ExpectQuery(`SELECT t1.TABLESPACE_NAME, t2.DBID.*`).WillReturnRows( + sqlmock.NewRows([]string{"TABLESPACE_NAME", "DBID"}). + AddRow("testtablespace", 12345), + ) + + var wg sync.WaitGroup + metricChan := make(chan newrelicMetricSender, 10) + + wg.Add(1) + go dbIDTablespaceMetric.Collect(db, &wg, metricChan) + go func() { + wg.Wait() + close(metricChan) + }() + var generatedMetrics []newrelicMetricSender + for { + newMetric, ok := <-metricChan + if !ok { + break + } + generatedMetrics = append(generatedMetrics, newMetric) + } + + expectedMetrics := []newrelicMetricSender{ + { + metric: &newrelicMetric{ + name: "dbID", + value: "12345", + metricType: metric.ATTRIBUTE, + }, + metadata: map[string]string{ + "tablespace": "testtablespace", + }, + }, + } + + if !reflect.DeepEqual(expectedMetrics, generatedMetrics) { + t.Errorf("failed to get expected metric: %s", pretty.Diff(expectedMetrics, generatedMetrics)) + } +} + +func Test_globalNameTablespaceMetric(t *testing.T) { + db, mock, err := sqlmock.New() + if err != nil { + t.Error(err) + } + mock.ExpectQuery(`SELECT t1.TABLESPACE_NAME, t2.GLOBAL_NAME.*`).WillReturnRows( + sqlmock.NewRows([]string{"TABLESPACE_NAME", "GLOBAL_NAME"}). + AddRow("testtablespace", "global_name"), + ) + + var wg sync.WaitGroup + metricChan := make(chan newrelicMetricSender, 10) + + wg.Add(1) + go globalNameTablespaceMetric.Collect(db, &wg, metricChan) + go func() { + wg.Wait() + close(metricChan) + }() + var generatedMetrics []newrelicMetricSender + for { + newMetric, ok := <-metricChan + if !ok { + break + } + generatedMetrics = append(generatedMetrics, newMetric) + } + + expectedMetrics := []newrelicMetricSender{ + { + metric: &newrelicMetric{ + name: "globalName", + value: "global_name", + metricType: metric.ATTRIBUTE, + }, + metadata: map[string]string{ + "tablespace": "testtablespace", + }, + }, + } + + if !reflect.DeepEqual(expectedMetrics, generatedMetrics) { + t.Errorf("failed to get expected metric: %s", pretty.Diff(expectedMetrics, generatedMetrics)) + } +} + +func Test_dbIDInstanceMetric(t *testing.T) { + db, mock, err := sqlmock.New() + if err != nil { + t.Error(err) + } + + mock.ExpectQuery(`SELECT t1.INST_ID, t2.DBID.*`).WillReturnRows( + sqlmock.NewRows([]string{"INST_ID", "DBID"}). + AddRow(1, 12345), + ) + + var wg sync.WaitGroup + metricChan := make(chan newrelicMetricSender, 10) + + wg.Add(1) + go dbIDInstanceMetric.Collect(db, &wg, metricChan) + go func() { + wg.Wait() + close(metricChan) + }() + var generatedMetrics []newrelicMetricSender + for { + newMetric, ok := <-metricChan + if !ok { + break + } + generatedMetrics = append(generatedMetrics, newMetric) + } + + expectedMetrics := []newrelicMetricSender{ + { + metric: &newrelicMetric{ + name: "dbID", + value: "12345", + metricType: metric.ATTRIBUTE, + }, + metadata: map[string]string{ + "instanceID": "1", + }, + }, + } + + if !reflect.DeepEqual(expectedMetrics, generatedMetrics) { + t.Errorf("failed to get expected metric: %s", pretty.Diff(expectedMetrics, generatedMetrics)) + } +} + +func Test_globalNameInstanceMetric(t *testing.T) { + db, mock, err := sqlmock.New() + if err != nil { + t.Error(err) + } + + mock.ExpectQuery(`SELECT t1.INST_ID, t2.GLOBAL_NAME.*`).WillReturnRows( + sqlmock.NewRows([]string{"INST_ID", "GLOBAL_NAME"}). + AddRow(1, "global_name"), + ) + + var wg sync.WaitGroup + metricChan := make(chan newrelicMetricSender, 10) + + wg.Add(1) + go globalNameInstanceMetric.Collect(db, &wg, metricChan) + go func() { + wg.Wait() + close(metricChan) + }() + var generatedMetrics []newrelicMetricSender + for { + newMetric, ok := <-metricChan + if !ok { + break + } + generatedMetrics = append(generatedMetrics, newMetric) + } + + expectedMetrics := []newrelicMetricSender{ + { + metric: &newrelicMetric{ + name: "globalName", + value: "global_name", + metricType: metric.ATTRIBUTE, + }, + metadata: map[string]string{ + "instanceID": "1", + }, + }, + } + + if !reflect.DeepEqual(expectedMetrics, generatedMetrics) { + t.Errorf("failed to get expected metric: %s", pretty.Diff(expectedMetrics, generatedMetrics)) + } +} + +func TestOracleTablespaceGlobalNameMetrics(t *testing.T) { + db, mock, err := sqlmock.New() + if err != nil { + t.Error(err) + } + mock.ExpectQuery(`SELECT t1.TABLESPACE_NAME, t2.GLOBAL_NAME.*`).WillReturnRows( + sqlmock.NewRows([]string{"TABLESPACE_NAME", "GLOBAL_NAME"}). + AddRow("testtablespace", "global_name"), + ) + + var wg sync.WaitGroup + metricChan := make(chan newrelicMetricSender, 10) + + wg.Add(1) + go globalNameTablespaceMetric.Collect(db, &wg, metricChan) + go func() { + wg.Wait() + close(metricChan) + }() + var generatedMetrics []newrelicMetricSender + for { + newMetric, ok := <-metricChan + if !ok { + break + } + generatedMetrics = append(generatedMetrics, newMetric) + } + + expectedMetrics := []newrelicMetricSender{ + { + metric: &newrelicMetric{ + name: "globalName", + value: "global_name", + metricType: metric.ATTRIBUTE, + }, + metadata: map[string]string{ + "tablespace": "testtablespace", + }, + }, + } + + if !reflect.DeepEqual(expectedMetrics, generatedMetrics) { + t.Errorf("failed to get expected metric: %s", pretty.Diff(expectedMetrics, generatedMetrics)) + } +} + func TestOracleTablespaceMetrics_Whitlist(t *testing.T) { tablespaceWhiteList = []string{"testtablespace", "othertablespace"} diff --git a/src/oracledb.go b/src/oracledb.go index f0c0bb8..ebc835c 100644 --- a/src/oracledb.go +++ b/src/oracledb.go @@ -28,7 +28,7 @@ type argumentList struct { const ( integrationName = "com.newrelic.oracledb" - integrationVersion = "1.0.0" + integrationVersion = "1.1.0" ) var (