From 621677713c453b3d2e0c77f195972292d75e9353 Mon Sep 17 00:00:00 2001 From: kongfei Date: Wed, 24 Jan 2024 11:33:53 +0800 Subject: [PATCH] delete cgo dependency for oracle plugin --- .github/workflows/release.yaml | 4 +- .goreleaser.yaml | 1 - Makefile | 4 - go.mod | 4 +- go.sum | 13 +- inputs/oracle/oracle.go | 324 ++++++++++++++++++++++++++++ inputs/oracle/oracle_linux_amd64.go | 294 ------------------------- 7 files changed, 329 insertions(+), 315 deletions(-) delete mode 100644 inputs/oracle/oracle_linux_amd64.go diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 7c627965..cfe92a2f 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -11,8 +11,8 @@ jobs: goreleaser: runs-on: ubuntu-20.04 steps: - - name: Install libpcap - run: sudo apt-get install -y libpcap-dev + - name: Install libpcap and snmp + run: sudo apt-get install -y libpcap-dev snmp - name: Checkout Source Code uses: actions/checkout@v3 with: diff --git a/.goreleaser.yaml b/.goreleaser.yaml index fa6da957..275a5127 100644 --- a/.goreleaser.yaml +++ b/.goreleaser.yaml @@ -45,7 +45,6 @@ builds: goarch: - amd64 tags: - - enterprise - arppacket ldflags: - -s -w diff --git a/Makefile b/Makefile index 6f9362e9..4bc8fe73 100644 --- a/Makefile +++ b/Makefile @@ -19,10 +19,6 @@ build: echo "Building version $(GIT_VERSION)" go build -ldflags $(LDFLAGS) -o $(APP) -build-enterprise: - echo "Building version $(GIT_VERSION)" - go build --tags "enterprise" -ldflags $(LDFLAGS) -o $(APP) - build-pure: echo "Building version $(GIT_VERSION)" go build --tags "no_prometheus no_traces" -ldflags $(LDFLAGS) -o $(APP) diff --git a/go.mod b/go.mod index 1d45a240..64e90634 100644 --- a/go.mod +++ b/go.mod @@ -14,7 +14,6 @@ require ( github.com/go-redis/redis/v8 v8.11.5 github.com/go-sql-driver/mysql v1.6.0 github.com/gobwas/glob v0.2.3 - github.com/godror/godror v0.33.0 github.com/gogo/protobuf v1.3.2 github.com/golang/protobuf v1.5.3 github.com/golang/snappy v0.0.4 @@ -22,7 +21,6 @@ require ( github.com/hashicorp/consul/api v1.15.3 github.com/influxdata/line-protocol/v2 v2.2.1 github.com/jackc/pgx/v4 v4.17.2 - github.com/jmoiron/sqlx v1.3.5 github.com/json-iterator/go v1.1.12 github.com/koding/multiconfig v0.0.0-20171124222453-69c27309b2d7 github.com/krallistic/kazoo-go v0.0.0-20170526135507-a15279744f4e @@ -150,6 +148,7 @@ require ( github.com/open-telemetry/opentelemetry-collector-contrib/receiver/prometheusreceiver v0.54.0 github.com/percona/percona-toolkit v0.0.0-20211210121818-b2860eee3152 github.com/prometheus-community/pro-bing v0.1.0 + github.com/sijms/go-ora/v2 v2.8.6 github.com/sleepinggenius2/gosmi v0.4.4 github.com/tidwall/gjson v1.14.4 github.com/vmware/govmomi v0.29.0 @@ -230,7 +229,6 @@ require ( github.com/go-resty/resty/v2 v2.1.1-0.20191201195748-d7b97669fe48 // indirect github.com/go-stack/stack v1.8.1 // indirect github.com/go-zookeeper/zk v1.0.2 // indirect - github.com/godror/knownpb v0.1.0 // indirect github.com/gogo/googleapis v1.4.1 // indirect github.com/golang-jwt/jwt/v4 v4.2.0 // indirect github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe // indirect diff --git a/go.sum b/go.sum index e6362abf..0c9401ba 100644 --- a/go.sum +++ b/go.sum @@ -522,10 +522,6 @@ github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MG github.com/goccy/go-yaml v1.9.5/go.mod h1:U/jl18uSupI5rdI2jmuCswEA2htH9eXfferR3KfscvA= github.com/godbus/dbus/v5 v5.0.4 h1:9349emZab16e7zQvpmsbtjc18ykshndd8y2PG3sgJbA= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= -github.com/godror/godror v0.33.0 h1:ZK1W7GohHVDPoLp/37U9QCSHARnYB4vVxNJya+CyWQ4= -github.com/godror/godror v0.33.0/go.mod h1:qHYnDISFm/h0vM+HDwg0LpyoLvxRKFRSwvhYF7ufjZ8= -github.com/godror/knownpb v0.1.0 h1:dJPK8s/I3PQzGGaGcUStL2zIaaICNzKKAK8BzP1uLio= -github.com/godror/knownpb v0.1.0/go.mod h1:4nRFbQo1dDuwKnblRXDxrfCFYeT4hjg3GjMqef58eRE= github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/gofrs/uuid v4.2.0+incompatible h1:yyYWMnhkhrKwwr8gAOcOCYxOOscHgDS9yZgBrnJfGa0= github.com/gofrs/uuid v4.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= @@ -842,8 +838,6 @@ github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9Y github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= -github.com/jmoiron/sqlx v1.3.5 h1:vFFPA71p1o5gAeqtEAwLU4dnX2napprKtHr7PYIcN3g= -github.com/jmoiron/sqlx v1.3.5/go.mod h1:nRVWtLre0KfCLJvgxzCsLVMogSvQ1zNJtpYr2Ccp0mQ= github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc= github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= @@ -959,8 +953,6 @@ github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzp github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-runewidth v0.0.8/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= -github.com/mattn/go-sqlite3 v1.14.6 h1:dNPt6NO46WmLVt2DLNpwczCmdV5boIZ6g/tlDrlRUbg= -github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= @@ -1052,8 +1044,6 @@ github.com/oklog/run v1.1.0 h1:GEenZ1cK0+q0+wsJew9qUg/DyD8k3JzYsZAi5gYi2mA= github.com/oklog/run v1.1.0/go.mod h1:sVPdnTZT1zYwAJeCMu2Th4T21pA3FPOQRfWjQlk7DVU= github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= -github.com/oklog/ulid/v2 v2.0.2 h1:r4fFzBm+bv0wNKNh5eXTwU7i85y5x+uwkxCUTNVQqLc= -github.com/oklog/ulid/v2 v2.0.2/go.mod h1:mtBL0Qe/0HAx6/a4Z30qxVIAL1eQDweXq5lxOEiwQ68= github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= @@ -1144,7 +1134,6 @@ github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0Mw github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc= github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= -github.com/pborman/getopt v0.0.0-20170112200414-7148bc3a4c30/go.mod h1:85jBQOZwpVEaDAr341tbn15RS4fCAsIst0qp7i8ex1o= github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= github.com/pelletier/go-toml v1.7.0/go.mod h1:vwGMzjaWMwyfHwgIBhI2YUM4fB6nL6lVAvS1LBMMhTE= github.com/pelletier/go-toml v1.9.4 h1:tjENF6MfZAg8e4ZmZTeWaWiT2vXtsoO6+iuOjFhECwM= @@ -1262,6 +1251,8 @@ github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749 h1:bUGsEnyNbVPw06B github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/shurcooL/vfsgen v0.0.0-20200824052919-0d455de96546/go.mod h1:TrYk7fJVaAttu97ZZKrO9UbRa8izdowaMIZcxYMbVaw= +github.com/sijms/go-ora/v2 v2.8.6 h1:sq907Z5cno0YIXidOYM85tiQFFVhhgssw09IJPG0WG4= +github.com/sijms/go-ora/v2 v2.8.6/go.mod h1:EHxlY6x7y9HAsdfumurRfTd+v8NrEOTR3Xl4FWlH6xk= github.com/sirupsen/logrus v1.4.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= diff --git a/inputs/oracle/oracle.go b/inputs/oracle/oracle.go index 5a6ef42d..0b77ca82 100644 --- a/inputs/oracle/oracle.go +++ b/inputs/oracle/oracle.go @@ -1 +1,325 @@ package oracle + +import ( + "context" + "database/sql" + "fmt" + "log" + "strconv" + "strings" + "sync" + "time" + + go_ora "github.com/sijms/go-ora/v2" + + "flashcat.cloud/categraf/config" + "flashcat.cloud/categraf/inputs" + "flashcat.cloud/categraf/pkg/conv" + "flashcat.cloud/categraf/types" +) + +const inputName = "oracle" + +type Instance struct { + config.InstanceConfig + + Address string `toml:"address"` + Username string `toml:"username"` + Password string `toml:"password"` + IsSysDBA bool `toml:"is_sys_dba"` + IsSysOper bool `toml:"is_sys_oper"` + DisableConnectionPool bool `toml:"disable_connection_pool"` + MaxOpenConnections int `toml:"max_open_connections"` + Metrics []MetricConfig `toml:"metrics"` + GlobalMetrics []MetricConfig `toml:"-"` + client *sql.DB +} + +type MetricConfig struct { + Mesurement string `toml:"mesurement"` + LabelFields []string `toml:"label_fields"` + MetricFields []string `toml:"metric_fields"` + FieldToAppend string `toml:"field_to_append"` + Timeout config.Duration `toml:"timeout"` + Request string `toml:"request"` + IgnoreZeroResult bool `toml:"ignore_zero_result"` +} + +type Oracle struct { + config.PluginConfig + Instances []*Instance `toml:"instances"` + Metrics []MetricConfig `toml:"metrics"` +} + +func init() { + inputs.Add(inputName, func() inputs.Input { + return &Oracle{} + }) +} + +func (o *Oracle) Clone() inputs.Input { + return &Oracle{} +} + +func (o *Oracle) Name() string { + return inputName +} + +func (o *Oracle) Drop() { + for i := 0; i < len(o.Instances); i++ { + o.Instances[i].Drop() + } +} + +func (o *Oracle) GetInstances() []inputs.Instance { + ret := make([]inputs.Instance, len(o.Instances)) + for i := 0; i < len(o.Instances); i++ { + o.Instances[i].GlobalMetrics = o.Metrics + ret[i] = o.Instances[i] + } + return ret +} + +func (ins *Instance) Init() error { + if ins.Address == "" { + return types.ErrInstancesEmpty + } + + connString := ins.getConnectionString() + conn, err := sql.Open("oracle", connString) + if err != nil { + return fmt.Errorf("failed to open oracle database %s: %v", ins.Address, err) + } + if conn == nil { + return fmt.Errorf("failed to open oracle database: %s", ins.Address) + } + ins.client = conn + + if ins.MaxOpenConnections == 0 { + ins.MaxOpenConnections = 2 + } + + ins.client.SetMaxOpenConns(ins.MaxOpenConnections) + ins.client.SetMaxIdleConns(ins.MaxOpenConnections) + ins.client.SetConnMaxIdleTime(time.Duration(0)) + ins.client.SetConnMaxLifetime(time.Duration(0)) + + return nil +} + +func (ins *Instance) Drop() error { + if config.Config.DebugMode { + log.Println("D! dropping oracle connection:", ins.Address) + } + + if len(ins.Address) == 0 || ins.client == nil { + if config.Config.DebugMode { + log.Println("D! oracle address is empty or client is nil, so there is no need to close") + } + return nil + } + + if err := ins.client.Close(); err != nil { + log.Println("E! failed to close oracle connection:", ins.Address, "error:", err) + } + + return nil +} + +func (ins *Instance) Gather(slist *types.SampleList) { + if len(ins.Address) == 0 { + if config.Config.DebugMode { + log.Println("D! oracle address is empty") + } + return + } + tags := map[string]string{"address": ins.Address} + + defer func(begun time.Time) { + use := time.Since(begun).Seconds() + slist.PushFront(types.NewSample(inputName, "scrape_use_seconds", use, tags)) + }(time.Now()) + + if err := ins.client.Ping(); err != nil { + slist.PushFront(types.NewSample(inputName, "up", 0, tags)) + log.Println("E! failed to ping oracle:", ins.Address, "error:", err) + return + } else { + slist.PushFront(types.NewSample(inputName, "up", 1, tags)) + } + + waitMetrics := new(sync.WaitGroup) + + for i := 0; i < len(ins.Metrics); i++ { + m := ins.Metrics[i] + waitMetrics.Add(1) + go ins.scrapeMetric(waitMetrics, slist, m, tags) + } + + for i := 0; i < len(ins.GlobalMetrics); i++ { + m := ins.GlobalMetrics[i] + waitMetrics.Add(1) + go ins.scrapeMetric(waitMetrics, slist, m, tags) + } + + waitMetrics.Wait() +} + +func (ins *Instance) scrapeMetric(waitMetrics *sync.WaitGroup, slist *types.SampleList, metricConf MetricConfig, tags map[string]string) { + defer waitMetrics.Done() + + timeout := time.Duration(metricConf.Timeout) + ctx, cancel := context.WithTimeout(context.Background(), timeout) + defer cancel() + + rows, err := ins.client.QueryContext(ctx, metricConf.Request) + + if ctx.Err() == context.DeadlineExceeded { + log.Println("E! oracle query timeout, request:", metricConf.Request) + return + } + + if err != nil { + log.Println("E! failed to query:", err) + return + } + + defer rows.Close() + + cols, err := rows.Columns() + if err != nil { + log.Println("E! failed to get columns:", err) + return + } + + if config.Config.DebugMode { + log.Println("D! columns:", cols) + } + + for rows.Next() { + columns := make([]interface{}, len(cols)) + columnPointers := make([]interface{}, len(cols)) + for i := range columns { + columnPointers[i] = &columns[i] + } + + // Scan the result into the column pointers... + if err := rows.Scan(columnPointers...); err != nil { + log.Println("E! failed to scan:", err) + return + } + + // Create our map, and retrieve the value for each column from the pointers slice, + // storing it in the map with the name of the column as the key. + m := make(map[string]string) + for i, colName := range cols { + val := columnPointers[i].(*interface{}) + m[strings.ToLower(colName)] = fmt.Sprint(*val) + } + + count := 0 + if err = ins.parseRow(m, metricConf, slist, tags); err != nil { + log.Println("E! failed to parse row:", err) + continue + } else { + count++ + } + + if !metricConf.IgnoreZeroResult && count == 0 { + log.Println("E! no metrics found while parsing") + } + } +} + +func (ins *Instance) parseRow(row map[string]string, metricConf MetricConfig, slist *types.SampleList, tags map[string]string) error { + labels := make(map[string]string) + for k, v := range tags { + labels[k] = v + } + + for _, label := range metricConf.LabelFields { + labelValue, has := row[label] + if has { + labels[label] = strings.Replace(labelValue, " ", "_", -1) + } + } + + for _, column := range metricConf.MetricFields { + value, err := conv.ToFloat64(row[column]) + if err != nil { + log.Println("E! failed to convert field:", column, "value:", value, "error:", err) + return err + } + + if metricConf.FieldToAppend == "" { + slist.PushFront(types.NewSample(inputName, metricConf.Mesurement+"_"+column, value, labels)) + } else { + suffix := cleanName(row[metricConf.FieldToAppend]) + slist.PushFront(types.NewSample(inputName, metricConf.Mesurement+"_"+suffix+"_"+column, value, labels)) + } + } + + return nil +} + +// Oracle gives us some ugly names back. This function cleans things up for Prometheus. +func cleanName(s string) string { + s = strings.Replace(s, " ", "_", -1) // Remove spaces + s = strings.Replace(s, "(", "", -1) // Remove open parenthesis + s = strings.Replace(s, ")", "", -1) // Remove close parenthesis + s = strings.Replace(s, "/", "", -1) // Remove forward slashes + s = strings.Replace(s, "*", "", -1) // Remove asterisks + s = strings.Replace(s, "%", "percent", -1) + s = strings.ToLower(s) + return s +} + +func (ins *Instance) getConnectionString() string { + ip, port, service, err := explode(ins.Address) + if err != nil { + panic(fmt.Errorf("oracle address format error: %s", err)) + } + opts := make(map[string]string) + if ins.IsSysOper { + opts["dba privilege"] = "sysoper" + } + if ins.IsSysDBA { + opts["dba privilege"] = "sysdba" + } + if ins.DisableConnectionPool { + opts["mode"] = "standalone" + } + return go_ora.BuildUrl(ip, port, service, ins.Username, ins.Password, opts) + +} + +func explode(target string) (ip string, port int, service string, err error) { + ErrInvalidTarget := fmt.Errorf("invalid target: %s", target) + + parts := strings.Split(target, "/") + if len(parts) != 2 { + return "", 0, "", ErrInvalidTarget + } + + ipPort := strings.Split(parts[0], ":") + if len(ipPort) != 2 { + return "", 0, "", ErrInvalidTarget + } + + port, err = strconv.Atoi(ipPort[1]) + if err != nil { + return "", 0, "", ErrInvalidTarget + } + + ip = strings.TrimSpace(ipPort[0]) + if ip == "" { + return "", 0, "", ErrInvalidTarget + } + + service = strings.TrimSpace(parts[1]) + if service == "" { + return "", 0, "", ErrInvalidTarget + } + + return +} diff --git a/inputs/oracle/oracle_linux_amd64.go b/inputs/oracle/oracle_linux_amd64.go deleted file mode 100644 index c48636ab..00000000 --- a/inputs/oracle/oracle_linux_amd64.go +++ /dev/null @@ -1,294 +0,0 @@ -//go:build enterprise -// +build enterprise - -package oracle - -import ( - "context" - "fmt" - "log" - "strings" - "sync" - "time" - - "flashcat.cloud/categraf/config" - "flashcat.cloud/categraf/inputs" - "flashcat.cloud/categraf/pkg/conv" - "flashcat.cloud/categraf/types" - "github.com/godror/godror" - "github.com/godror/godror/dsn" - "github.com/jmoiron/sqlx" -) - -const inputName = "oracle" - -type Instance struct { - config.InstanceConfig - - Address string `toml:"address"` - Username string `toml:"username"` - Password string `toml:"password"` - IsSysDBA bool `toml:"is_sys_dba"` - IsSysOper bool `toml:"is_sys_oper"` - DisableConnectionPool bool `toml:"disable_connection_pool"` - MaxOpenConnections int `toml:"max_open_connections"` - Metrics []MetricConfig `toml:"metrics"` - GlobalMetrics []MetricConfig `toml:"-"` - client *sqlx.DB -} - -type MetricConfig struct { - Mesurement string `toml:"mesurement"` - LabelFields []string `toml:"label_fields"` - MetricFields []string `toml:"metric_fields"` - FieldToAppend string `toml:"field_to_append"` - Timeout config.Duration `toml:"timeout"` - Request string `toml:"request"` - IgnoreZeroResult bool `toml:"ignore_zero_result"` -} - -type Oracle struct { - config.PluginConfig - Instances []*Instance `toml:"instances"` - Metrics []MetricConfig `toml:"metrics"` -} - -func init() { - inputs.Add(inputName, func() inputs.Input { - return &Oracle{} - }) -} - -func (o *Oracle) Clone() inputs.Input { - return &Oracle{} -} - -func (o *Oracle) Name() string { - return inputName -} - -func (o *Oracle) Drop() { - for i := 0; i < len(o.Instances); i++ { - o.Instances[i].Drop() - } -} - -func (o *Oracle) GetInstances() []inputs.Instance { - ret := make([]inputs.Instance, len(o.Instances)) - for i := 0; i < len(o.Instances); i++ { - o.Instances[i].GlobalMetrics = o.Metrics - ret[i] = o.Instances[i] - } - return ret -} - -func (ins *Instance) Init() error { - if ins.Address == "" { - return types.ErrInstancesEmpty - } - - connString := ins.getConnectionString() - var err error - ins.client, err = sqlx.Open("godror", connString) - if err != nil { - return fmt.Errorf("failed to open oracle connection: %v", err) - } - - if ins.MaxOpenConnections == 0 { - ins.MaxOpenConnections = 2 - } - - ins.client.SetMaxOpenConns(ins.MaxOpenConnections) - ins.client.SetMaxIdleConns(ins.MaxOpenConnections) - ins.client.SetConnMaxIdleTime(time.Duration(0)) - ins.client.SetConnMaxLifetime(time.Duration(0)) - - return nil -} - -func (ins *Instance) Drop() error { - if config.Config.DebugMode { - log.Println("D! dropping oracle connection:", ins.Address) - } - - if len(ins.Address) == 0 || ins.client == nil { - if config.Config.DebugMode { - log.Println("D! oracle address is empty or client is nil, so there is no need to close") - } - return nil - } - - if err := ins.client.Close(); err != nil { - log.Println("E! failed to close oracle connection:", ins.Address, "error:", err) - } - - return nil -} - -func (ins *Instance) Gather(slist *types.SampleList) { - if len(ins.Address) == 0 { - if config.Config.DebugMode { - log.Println("D! oracle address is empty") - } - return - } - tags := map[string]string{"address": ins.Address} - - defer func(begun time.Time) { - use := time.Since(begun).Seconds() - slist.PushFront(types.NewSample(inputName, "scrape_use_seconds", use, tags)) - }(time.Now()) - - if err := ins.client.Ping(); err != nil { - slist.PushFront(types.NewSample(inputName, "up", 0, tags)) - log.Println("E! failed to ping oracle:", ins.Address, "error:", err) - return - } else { - slist.PushFront(types.NewSample(inputName, "up", 1, tags)) - } - - waitMetrics := new(sync.WaitGroup) - - for i := 0; i < len(ins.Metrics); i++ { - m := ins.Metrics[i] - waitMetrics.Add(1) - go ins.scrapeMetric(waitMetrics, slist, m, tags) - } - - for i := 0; i < len(ins.GlobalMetrics); i++ { - m := ins.GlobalMetrics[i] - waitMetrics.Add(1) - go ins.scrapeMetric(waitMetrics, slist, m, tags) - } - - waitMetrics.Wait() -} - -func (ins *Instance) scrapeMetric(waitMetrics *sync.WaitGroup, slist *types.SampleList, metricConf MetricConfig, tags map[string]string) { - defer waitMetrics.Done() - - timeout := time.Duration(metricConf.Timeout) - ctx, cancel := context.WithTimeout(context.Background(), timeout) - defer cancel() - - rows, err := ins.client.QueryContext(ctx, metricConf.Request) - - if ctx.Err() == context.DeadlineExceeded { - log.Println("E! oracle query timeout, request:", metricConf.Request) - return - } - - if err != nil { - log.Println("E! failed to query:", err) - return - } - - defer rows.Close() - - cols, err := rows.Columns() - if err != nil { - log.Println("E! failed to get columns:", err) - return - } - - if config.Config.DebugMode { - log.Println("D! columns:", cols) - } - - for rows.Next() { - columns := make([]interface{}, len(cols)) - columnPointers := make([]interface{}, len(cols)) - for i := range columns { - columnPointers[i] = &columns[i] - } - - // Scan the result into the column pointers... - if err := rows.Scan(columnPointers...); err != nil { - log.Println("E! failed to scan:", err) - return - } - - // Create our map, and retrieve the value for each column from the pointers slice, - // storing it in the map with the name of the column as the key. - m := make(map[string]string) - for i, colName := range cols { - val := columnPointers[i].(*interface{}) - m[strings.ToLower(colName)] = fmt.Sprint(*val) - } - - count := 0 - if err = ins.parseRow(m, metricConf, slist, tags); err != nil { - log.Println("E! failed to parse row:", err) - continue - } else { - count++ - } - - if !metricConf.IgnoreZeroResult && count == 0 { - log.Println("E! no metrics found while parsing") - } - } -} - -func (ins *Instance) parseRow(row map[string]string, metricConf MetricConfig, slist *types.SampleList, tags map[string]string) error { - labels := make(map[string]string) - for k, v := range tags { - labels[k] = v - } - - for _, label := range metricConf.LabelFields { - labelValue, has := row[label] - if has { - labels[label] = strings.Replace(labelValue, " ", "_", -1) - } - } - - for _, column := range metricConf.MetricFields { - value, err := conv.ToFloat64(row[column]) - if err != nil { - log.Println("E! failed to convert field:", column, "value:", value, "error:", err) - return err - } - - if metricConf.FieldToAppend == "" { - slist.PushFront(types.NewSample(inputName, metricConf.Mesurement+"_"+column, value, labels)) - } else { - suffix := cleanName(row[metricConf.FieldToAppend]) - slist.PushFront(types.NewSample(inputName, metricConf.Mesurement+"_"+suffix+"_"+column, value, labels)) - } - } - - return nil -} - -// Oracle gives us some ugly names back. This function cleans things up for Prometheus. -func cleanName(s string) string { - s = strings.Replace(s, " ", "_", -1) // Remove spaces - s = strings.Replace(s, "(", "", -1) // Remove open parenthesis - s = strings.Replace(s, ")", "", -1) // Remove close parenthesis - s = strings.Replace(s, "/", "", -1) // Remove forward slashes - s = strings.Replace(s, "*", "", -1) // Remove asterisks - s = strings.Replace(s, "%", "percent", -1) - s = strings.ToLower(s) - return s -} - -func (ins *Instance) getConnectionString() string { - return godror.ConnectionParams{ - StandaloneConnection: ins.DisableConnectionPool, - CommonParams: dsn.CommonParams{ - Username: ins.Username, - Password: dsn.NewPassword(ins.Password), - ConnectString: ins.Address, - }, - PoolParams: dsn.PoolParams{ - MinSessions: 0, - MaxSessions: ins.MaxOpenConnections, - SessionIncrement: 1, - }, - ConnParams: dsn.ConnParams{ - IsSysDBA: ins.IsSysDBA, - IsSysOper: ins.IsSysOper, - }, - }.StringWithPassword() -}