Skip to content

Commit b509205

Browse files
Add Query Timeout to metrics definition (#118)
* Query Timeout --------- Signed-off-by: Anders Swanson <anders.swanson@oracle.com>
1 parent ef006ed commit b509205

File tree

3 files changed

+56
-16
lines changed

3 files changed

+56
-16
lines changed

‎README.md

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -621,7 +621,7 @@ Usage of oracledb_exporter:
621621
--log.level value
622622
Only log messages with the given severity or above. Valid levels: [debug, info, warn, error, fatal].
623623
--custom.metrics string
624-
File that may contain various custom metrics in a TOML file.
624+
Comma separated list of file(s) that contain various custom metrics in a TOML format.
625625
--default.metrics string
626626
Default TOML file metrics.
627627
--web.systemd-socket
@@ -634,6 +634,8 @@ Usage of oracledb_exporter:
634634
Number of maximum idle connections in the connection pool. (default "0")
635635
--database.maxOpenConns string
636636
Number of maximum open connections in the connection pool. (default "10")
637+
--query.timeout int
638+
Query timeout (in seconds).
637639
--web.config.file
638640
Path to configuration file that can enable TLS or authentication.
639641
```
@@ -654,12 +656,21 @@ exporter, you can:
654656
- Use `--custom.metrics` flag followed by a comma separated list of TOML files, or
655657
- Export `CUSTOM_METRICS` variable environment (`export CUSTOM_METRICS=my-custom-metrics.toml,my-other-custom-metrics.toml`)
656658

657-
This file must contain the following elements:
658-
659-
- One or several metric sections (`[[metric]]`)
660-
- For each section: a context, a request and a map between the field(s) in the request and comment(s).
661-
662-
Here's a simple example:
659+
Custom metrics file must contain a series of `[[metric]]` definitions, in TOML. Each metric definition must follow the custom metric schema:
660+
661+
| Field Name | Description | Type | Required | Default |
662+
|------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------|----------|------------------------------|
663+
| context | Metric context, used to build metric FQN | String | Yes | |
664+
| labels | Metric labels, which must match column names in the query. Any column that is not a label will be parsed as a metric | Array of Strings | No | |
665+
| metricsdesc | Mapping between field(s) in the request and comment(s) | Dictionary of Strings | Yes | |
666+
| metricstype | Mapping between field(s) in the request and [Prometheus metric types](https://prometheus.io/docs/concepts/metric_types/) | Dictionary of Strings | No | |
667+
| metricsbuckets | Split [histogram](https://prometheus.io/docs/concepts/metric_types/#histogram) metric types into buckets based on value ([example](./custom-metrics-example/metric-histogram-example.toml)) | Dictionary of String dictionaries | No | |
668+
| fieldtoappend | Field from the request to append to the metric FQN | String | No | |
669+
| request | Oracle database query to run for metrics scraping | String | Yes | |
670+
| ignorezeroresult | Whether or not an error will be printed if the request does not return any results | Boolean | No | false |
671+
| querytimeout | Oracle Database query timeout, in seconds | Integer | No | 5, or value of query.timeout |
672+
673+
Here's a simple example of a metric definition:
663674
664675
```
665676
[[metric]]

‎collector/collector.go

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ type Metric struct {
8181
FieldToAppend string
8282
Request string
8383
IgnoreZeroResult bool
84+
QueryTimeout int
8485
}
8586

8687
// Metrics is a container structure for prometheus metrics
@@ -469,17 +470,17 @@ func (e *Exporter) reloadMetrics() {
469470
}
470471

471472
// ScrapeMetric is an interface method to call scrapeGenericValues using Metric struct values
472-
func (e *Exporter) ScrapeMetric(db *sql.DB, ch chan<- prometheus.Metric, metricDefinition Metric) error {
473+
func (e *Exporter) ScrapeMetric(db *sql.DB, ch chan<- prometheus.Metric, m Metric) error {
473474
level.Debug(e.logger).Log("msg", "Calling function ScrapeGenericValues()")
474-
return e.scrapeGenericValues(db, ch, metricDefinition.Context, metricDefinition.Labels,
475-
metricDefinition.MetricsDesc, metricDefinition.MetricsType, metricDefinition.MetricsBuckets,
476-
metricDefinition.FieldToAppend, metricDefinition.IgnoreZeroResult,
477-
metricDefinition.Request)
475+
return e.scrapeGenericValues(db, ch, m.Context, m.Labels, m.MetricsDesc,
476+
m.MetricsType, m.MetricsBuckets, m.FieldToAppend, m.IgnoreZeroResult,
477+
m.Request, m.QueryTimeout)
478478
}
479479

480480
// generic method for retrieving metrics.
481481
func (e *Exporter) scrapeGenericValues(db *sql.DB, ch chan<- prometheus.Metric, context string, labels []string,
482-
metricsDesc map[string]string, metricsType map[string]string, metricsBuckets map[string]map[string]string, fieldToAppend string, ignoreZeroResult bool, request string) error {
482+
metricsDesc map[string]string, metricsType map[string]string, metricsBuckets map[string]map[string]string,
483+
fieldToAppend string, ignoreZeroResult bool, request string, queryTimeout int) error {
483484
metricsCount := 0
484485
genericParser := func(row map[string]string) error {
485486
// Construct labels value
@@ -572,7 +573,7 @@ func (e *Exporter) scrapeGenericValues(db *sql.DB, ch chan<- prometheus.Metric,
572573
return nil
573574
}
574575
level.Debug(e.logger).Log("msg", "Calling function GeneratePrometheusMetrics()")
575-
err := e.generatePrometheusMetrics(db, genericParser, request)
576+
err := e.generatePrometheusMetrics(db, genericParser, request, queryTimeout)
576577
level.Debug(e.logger).Log("msg", "ScrapeGenericValues() - metricsCount: "+strconv.Itoa(metricsCount))
577578
if err != nil {
578579
return err
@@ -585,8 +586,13 @@ func (e *Exporter) scrapeGenericValues(db *sql.DB, ch chan<- prometheus.Metric,
585586

586587
// inspired by https://kylewbanks.com/blog/query-result-to-map-in-golang
587588
// Parse SQL result and call parsing function to each row
588-
func (e *Exporter) generatePrometheusMetrics(db *sql.DB, parse func(row map[string]string) error, query string) error {
589-
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(e.config.QueryTimeout)*time.Second)
589+
func (e *Exporter) generatePrometheusMetrics(db *sql.DB, parse func(row map[string]string) error, query string, queryTimeout int) error {
590+
timeout := e.config.QueryTimeout
591+
if queryTimeout > 0 {
592+
timeout = queryTimeout
593+
}
594+
timeoutDuration := time.Duration(timeout) * time.Second
595+
ctx, cancel := context.WithTimeout(context.Background(), timeoutDuration)
590596
defer cancel()
591597
rows, err := db.QueryContext(ctx, query)
592598

‎collector/metrics.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// Copyright (c) 2024, Oracle and/or its affiliates.
2+
// Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl
3+
4+
package collector
5+
6+
import (
7+
"github.com/go-kit/log/level"
8+
"strconv"
9+
)
10+
11+
func (e *Exporter) parseFloat(metric, metricHelp string, row map[string]string) (float64, bool) {
12+
value, ok := row[metric]
13+
if !ok {
14+
return -1, ok
15+
}
16+
valueFloat, err := strconv.ParseFloat(value, 64)
17+
if err != nil {
18+
level.Error(e.logger).Log("msg", "Unable to convert current value to float (metric="+metric+
19+
",metricHelp="+metricHelp+",value=<"+row[metric]+">)")
20+
return -1, false
21+
}
22+
return valueFloat, true
23+
}

0 commit comments

Comments
 (0)