Skip to content

Commit a67a6b2

Browse files
Multi-database support (#238)
Multi-database support --------- Signed-off-by: Anders Swanson <anders.swanson@oracle.com>
1 parent 1d8025f commit a67a6b2

File tree

11 files changed

+607
-255
lines changed

11 files changed

+607
-255
lines changed

‎README.md

Lines changed: 134 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -603,12 +603,15 @@ You may provide the connection details using these variables:
603603

604604
The following example puts the logfile in the current location with the filename `alert.log` and loads the default matrics file (`default-metrics,toml`) from the current location.
605605

606-
If you prefer to provide configuration via a [config file](./example-config.yaml), you may do so with the `--config.file` argument. The use of a config file over command line arguments is preferred. If a config file is not provided, the default database connection is managed by command line arguments.
606+
If you prefer to provide configuration via a [config file](./example-config.yaml), you may do so with the `--config.file` argument. The use of a config file over command line arguments is preferred. If a config file is not provided, the "default" database connection is managed by command line arguments.
607607

608608
```yaml
609609
# Example Oracle Database Metrics Exporter Configuration file.
610610
# Environment variables of the form ${VAR_NAME} will be expanded.
611611
612+
# Example Oracle Database Metrics Exporter Configuration file.
613+
# Environment variables of the form ${VAR_NAME} will be expanded.
614+
612615
databases:
613616
## Path on which metrics will be served
614617
# metricsPath: /metrics
@@ -620,8 +623,7 @@ databases:
620623
password: ${DB_PASSWORD}
621624
## Database connection url
622625
url: localhost:1521/freepdb1
623-
## Metrics scrape interval for this database
624-
scrapeInterval: 15s
626+
625627
## Metrics query timeout for this database, in seconds
626628
queryTimeout: 5
627629
@@ -652,8 +654,11 @@ databases:
652654
# poolMinConnections: 15
653655
654656
metrics:
657+
## How often to scrape metrics. If not provided, metrics will be scraped on request.
658+
# scrapeInterval: 15s
659+
## Path to default metrics file.
655660
default: default-metrics.toml
656-
#
661+
## Paths to any custom metrics files
657662
custom:
658663
- custom-metrics-example/custom-metrics.toml
659664
@@ -666,10 +671,135 @@ log:
666671
# disable: 0
667672
```
668673

674+
### Scraping multiple databases
675+
676+
You may scrape as many databases as needed by defining named database configurations in the config file. The following configuration defines two databases, "db1", and "db2" for the metrics exporter.
677+
678+
```yaml
679+
# Example Oracle Database Metrics Exporter Configuration file.
680+
# Environment variables of the form ${VAR_NAME} will be expanded.
681+
682+
databases:
683+
## Path on which metrics will be served
684+
# metricsPath: /metrics
685+
686+
## As many named database configurations may be defined as needed.
687+
## It is recommended to define your database config in the config file, rather than using CLI arguments.
688+
689+
## Database connection information for the "db1" database.
690+
db1:
691+
## Database username
692+
username: ${DB1_USERNAME}
693+
## Database password
694+
password: ${DB1_PASSWORD}
695+
## Database connection url
696+
url: localhost:1521/freepdb1
697+
698+
## Metrics query timeout for this database, in seconds
699+
queryTimeout: 5
700+
701+
## Rely on Oracle Database External Authentication by network or OS
702+
# externalAuth: false
703+
## Database role
704+
# role: SYSDBA
705+
## Path to Oracle Database wallet, if using wallet
706+
# tnsAdmin: /path/to/database/wallet
707+
708+
### Connection settings:
709+
### Either the go-sql or Oracle Database connection pool may be used.
710+
### To use the Oracle Database connection pool over the go-sql connection pool,
711+
### set maxIdleConns to zero and configure the pool* settings.
712+
713+
### Connection pooling settings for the go-sql connection pool
714+
## Max open connections for this database using go-sql connection pool
715+
maxOpenConns: 10
716+
## Max idle connections for this database using go-sql connection pool
717+
maxIdleConns: 10
718+
719+
### Connection pooling settings for the Oracle Database connection pool
720+
## Oracle Database connection pool increment.
721+
# poolIncrement: 1
722+
## Oracle Database Connection pool maximum size
723+
# poolMaxConnections: 15
724+
## Oracle Database Connection pool minimum size
725+
# poolMinConnections: 15
726+
db2:
727+
## Database username
728+
username: ${DB2_USERNAME}
729+
## Database password
730+
password: ${DB2_PASSWORD}
731+
## Database connection url
732+
url: localhost:1522/freepdb1
733+
734+
## Metrics query timeout for this database, in seconds
735+
queryTimeout: 5
736+
737+
## Rely on Oracle Database External Authentication by network or OS
738+
# externalAuth: false
739+
## Database role
740+
# role: SYSDBA
741+
## Path to Oracle Database wallet, if using wallet
742+
# tnsAdmin: /path/to/database/wallet
743+
744+
### Connection settings:
745+
### Either the go-sql or Oracle Database connection pool may be used.
746+
### To use the Oracle Database connection pool over the go-sql connection pool,
747+
### set maxIdleConns to zero and configure the pool* settings.
748+
749+
### Connection pooling settings for the go-sql connection pool
750+
## Max open connections for this database using go-sql connection pool
751+
maxOpenConns: 10
752+
## Max idle connections for this database using go-sql connection pool
753+
maxIdleConns: 10
754+
755+
### Connection pooling settings for the Oracle Database connection pool
756+
## Oracle Database connection pool increment.
757+
# poolIncrement: 1
758+
## Oracle Database Connection pool maximum size
759+
# poolMaxConnections: 15
760+
## Oracle Database Connection pool minimum size
761+
# poolMinConnections: 15
762+
763+
metrics:
764+
## How often to scrape metrics. If not provided, metrics will be scraped on request.
765+
# scrapeInterval: 15s
766+
## Path to default metrics file.
767+
default: default-metrics.toml
768+
## Paths to any custom metrics files
769+
custom:
770+
- custom-metrics-example/custom-metrics.toml
771+
772+
log:
773+
# Path of log file
774+
destination: /opt/alert.log
775+
# Interval of log updates
776+
interval: 15s
777+
## Set disable to 1 to disable logging
778+
# disable: 0
779+
```
780+
781+
669782
```shell
670783
./oracledb_exporter --log.destination="./alert.log" --default.metrics="./default-metrics.toml"
671784
```
672785

786+
#### Scraping metrics from specific databases
787+
788+
By default, metrics are scraped from every connected database. To expose only certain metrics on specific databases, configure the `databases` property of a metric. The following metric definition will only be scraped from databases "db2" and "db3":
789+
790+
```toml
791+
[[metric]]
792+
context = "db_platform"
793+
labels = [ "platform_name" ]
794+
metricsdesc = { value = "Database platform" }
795+
request = '''
796+
SELECT platform_name, 1 as value FROM v$database
797+
'''
798+
databases = [ "db2", "db3" ]
799+
```
800+
801+
If the `databases` array is empty or not provided for a metric, that metric will be scraped from all connected databases.
802+
673803
### Using OCI Vault
674804
675805
The exporter will read the password from a secret stored in OCI Vault if you set these two environment variables:

‎alertlog/alertlog.go

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,10 @@
44
package alertlog
55

66
import (
7-
"database/sql"
87
"encoding/json"
98
"errors"
109
"fmt"
10+
"github.com/oracle/oracle-db-appdev-monitoring/collector"
1111
"io"
1212
"log/slog"
1313
"os"
@@ -21,13 +21,14 @@ type LogRecord struct {
2121
Message string `json:"message"`
2222
}
2323

24-
var queryFailures int = 0
24+
var databaseFailures map[string]int = map[string]int{}
2525

26-
func UpdateLog(logDestination string, logger *slog.Logger, db *sql.DB) {
26+
func UpdateLog(logDestination string, logger *slog.Logger, d *collector.Database) {
2727

28+
queryFailures := databaseFailures[d.Name]
2829
if queryFailures == 3 {
29-
logger.Info("Failed to query the alert log three consecutive times, so will not try any more")
30-
queryFailures++
30+
logger.Info("Failed to query the alert log three consecutive times, so will not try any more", "database", d.Name)
31+
databaseFailures[d.Name]++
3132
return
3233
}
3334

@@ -37,10 +38,10 @@ func UpdateLog(logDestination string, logger *slog.Logger, db *sql.DB) {
3738

3839
// check if the log file exists, and if not, create it
3940
if _, err := os.Stat(logDestination); errors.Is(err, os.ErrNotExist) {
40-
logger.Info("Log destination file does not exist, will try to create it: " + logDestination)
41+
logger.Info("Log destination file does not exist, will try to create it: "+logDestination, "database", d.Name)
4142
f, e := os.Create(logDestination)
4243
if e != nil {
43-
logger.Error("Failed to create the log file: " + logDestination)
44+
logger.Error("Failed to create the log file: "+logDestination, "database", d.Name)
4445
return
4546
}
4647
f.Close()
@@ -50,7 +51,7 @@ func UpdateLog(logDestination string, logger *slog.Logger, db *sql.DB) {
5051
file, err := os.Open(logDestination)
5152

5253
if err != nil {
53-
logger.Error("Could not open the alert log destination file: " + logDestination)
54+
logger.Error("Could not open the alert log destination file: "+logDestination, "database", d.Name)
5455
return
5556
}
5657

@@ -108,9 +109,9 @@ func UpdateLog(logDestination string, logger *slog.Logger, db *sql.DB) {
108109
from v$diag_alert_ext
109110
where originating_timestamp > to_utc_timestamp_tz('%s')`, lastLogRecord.Timestamp)
110111

111-
rows, err := db.Query(stmt)
112+
rows, err := d.Session.Query(stmt)
112113
if err != nil {
113-
logger.Error("Error querying the alert logs")
114+
logger.Error("Error querying the alert logs", "database", d.Name)
114115
queryFailures++
115116
return
116117
}
@@ -119,7 +120,7 @@ func UpdateLog(logDestination string, logger *slog.Logger, db *sql.DB) {
119120
// write them to the file
120121
outfile, err := os.OpenFile(logDestination, os.O_APPEND|os.O_WRONLY, 0600)
121122
if err != nil {
122-
logger.Error("Could not open log file for writing: " + logDestination)
123+
logger.Error("Could not open log file for writing: "+logDestination, "database", d.Name)
123124
return
124125
}
125126
defer outfile.Close()
@@ -128,7 +129,7 @@ func UpdateLog(logDestination string, logger *slog.Logger, db *sql.DB) {
128129
for rows.Next() {
129130
var newRecord LogRecord
130131
if err := rows.Scan(&newRecord.Timestamp, &newRecord.ModuleId, &newRecord.ECID, &newRecord.Message); err != nil {
131-
logger.Error("Error reading a row from the alert logs")
132+
logger.Error("Error reading a row from the alert logs", "database", d.Name)
132133
return
133134
}
134135

@@ -137,18 +138,18 @@ func UpdateLog(logDestination string, logger *slog.Logger, db *sql.DB) {
137138

138139
jsonLogRecord, err := json.Marshal(newRecord)
139140
if err != nil {
140-
logger.Error("Error marshalling alert log record")
141+
logger.Error("Error marshalling alert log record", "database", d.Name)
141142
return
142143
}
143144

144145
if _, err = outfile.WriteString(string(jsonLogRecord) + "\n"); err != nil {
145-
logger.Error("Could not write to log file: " + logDestination)
146+
logger.Error("Could not write to log file: "+logDestination, "database", d.Name)
146147
return
147148
}
148149
}
149150

150151
if err = rows.Err(); err != nil {
151-
logger.Error("Error querying the alert logs")
152+
logger.Error("Error querying the alert logs", "database", d.Name)
152153
queryFailures++
153154
}
154155
}

0 commit comments

Comments
 (0)