Skip to content

Commit

Permalink
Merge pull request #1 from freenetdigital/develop
Browse files Browse the repository at this point in the history
Develop
  • Loading branch information
floyd871 authored May 17, 2018
2 parents 495aa26 + 4f8ed5c commit 39064ba
Show file tree
Hide file tree
Showing 7 changed files with 970 additions and 441 deletions.
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
oracle.conf
access.conf
prometheus_oracle_exporter
*.log
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
MIT License

Copyright (c) 2017 Michael Neumann
Copyright (c) 2018 Freenet Digital GmbH

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
29 changes: 22 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Prometheus Oracle Exporter

A [Prometheus](https://prometheus.io/) exporter for Oracle. Insipred from (https://github.com/iamseth/oracledb_exporter). I'm a DBA , PRs welcomed.
A [Prometheus](https://prometheus.io/) exporter for Oracle.

The following metrics are exposed currently. Support for RAC (databasename and instancename added via lables)

Expand All @@ -9,31 +9,46 @@ The following metrics are exposed currently. Support for RAC (databasename and i
- oracledb_exporter_scrapes_total
- oracledb_uptime (days)
- oracledb_session (view v$session system/user active/passive)
- oracledb_sysmetric (view v$sysmetric (Physical Read Total IO Requests Per Sec / Physical Write Total IO Requests Per Sec
Physical Read Total Bytes Per Sec / Physical Write Total Bytes Per Sec)
- oracledb_sysmetric (view v$sysmetric
(Physical Read Total IO Requests Per Sec / Physical Write Total IO Requests Per Sec
Physical Read Total Bytes Per Sec / Physical Write Total Bytes Per Sec))
- oracledb_sysstat (view v$sysstat (parse count (total) / execute count / user commits / user rollbacks))
- oracledb_waitclass (view v$waitclass)
- oracledb_tablespace (tablespace total/free)
- oracledb_asmspace (Space in ASM (v$asm_disk/v$asm_diskgroup))
- oracledb_interconnect (view v$sysstat (gc cr blocks served / gc cr blocks flushed / gc cr blocks received))
- oracledb_recovery (percentage usage in FRA from V$RECOVERY_FILE_DEST)
- oracledb_redo (Redo log switches over last 5 min from v$log_history)
- oracledb_cachehitratio (Cache hit ratios (v$sysmetric)
...
- oracledb_up (Whether the Oracle server is up)
- oracledb_error (Errors parsed from the alert.log)
- oracledb_services (Active Oracle Services (v$active_services))
- oracledb_parameter (Configuration Parameters (v$parameter))
- oracledb_query (Self defined Queries in Configuration File)

The Oracle Alertlog file is scanned and the metrics are exposed as a gauge metric with a total occurence of the specific ORA.
Yo can define your own Queries and execute/scrape them

# Installation

Ensure that the environment variable DATA_SOURCE_NAME is set correctly before starting. You can add multiple instances, if you run more than one instance on a host. It is even possible to run one Exporter for all your Databases, but this is not recommended. We use it in our Company because on one host multiple Instances are running.
Ensure that the configfile (oracle.conf) is set correctly before starting. You can add multiple instances, e.g. the ASM instance. It is even possible to run one Exporter for all your Databases, but this is not recommended. We use it in our Company because on one host multiple Instances are running.


```bash
export DATA_SOURCE_NAME="system/oracle@myhost1;system/oracle@myhost2;system/oracle@myhost3"
export NLS_LANG=AMERICAN_AMERICA.UTF8
/path/to/binary -l log.level error -l web.listen-address 9161
/path/to/binary -configfile=/home/user/oracle.conf -web.listen-address :9161
```

## Usage

```bash
Usage of ./prometheus_oracle_exporter:
-accessfile string
Last access for parsed Oracle Alerts. (default "access.conf")
-configfile string
ConfigurationFile in YAML format. (default "oracle.conf")
-logfile string
Logfile for parsed Oracle Alerts. (default "exporter.log")
-web.listen-address string
Address to listen on for web interface and telemetry. (default ":9161")
-web.telemetry-path string
Expand Down
150 changes: 150 additions & 0 deletions alertlog.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
package main

import (
"bufio"
"os"
"time"
"regexp"
"strings"
"strconv"
"github.com/prometheus/common/log"
)


type Client struct {
Ip string `yaml:"ip"`
Date string `yaml:"date"`
}

type Lastlog struct {
Instance string `yaml:"instance"`
Clients []Client `yaml:"clients"`
}

type Lastlogs struct {
Cfgs []Lastlog `yaml:"lastlog"`
}

type oraerr struct {
ora string
text string
ignore string
count int
}

var (
Errors []oraerr
oralayout = "Mon Jan 02 15:04:05 2006"
lastlog Lastlogs
)


// Get individual ScrapeTime per Prometheus instance for alertlog
func (e *Exporter) GetLastScrapeTime(conf int) time.Time {
for i, _ := range lastlog.Cfgs {
if lastlog.Cfgs[i].Instance == config.Cfgs[conf].Instance {
for n, _ := range lastlog.Cfgs[i].Clients {
if lastlog.Cfgs[i].Clients[n].Ip == e.lastIp {
t, _ := time.Parse("2006-01-02 15:04:05 -0700 MST",string(lastlog.Cfgs[i].Clients[n].Date))
return t
}
}
}
}
return time.Now()
}

// Set individual ScrapeTime per Prometheus instance for alertlog
func (e *Exporter) SetLastScrapeTime(conf int,t time.Time) {
var indInst int = -1
var indIp int = -1
for i, _ := range lastlog.Cfgs {
if lastlog.Cfgs[i].Instance == config.Cfgs[conf].Instance {
indInst = i
for n, _ := range lastlog.Cfgs[i].Clients {
if lastlog.Cfgs[i].Clients[n].Ip == e.lastIp {
indIp = n
}
}
}
}
if indInst == -1 {
cln := Client{Ip: e.lastIp, Date: t.String()}
lastlog.Cfgs = append(lastlog.Cfgs, Lastlog{Instance: config.Cfgs[conf].Instance,
Clients: []Client{ cln } } )
}else{
if indIp == -1 {
cln := Client{Ip: e.lastIp, Date: t.String()}
lastlog.Cfgs[indInst].Clients = append(lastlog.Cfgs[indInst].Clients, cln)
}else{
lastlog.Cfgs[indInst].Clients[indIp].Date = t.String()
}
}
}

func addError(conf int, ora string, text string){
var found bool = false
for i, _ := range Errors {
if Errors[i].ora == ora {
Errors[i].count ++
found = true
}
}
if ! found {
ignore := "0"
for _ , e := range config.Cfgs[conf].Alertlog[0].Ignoreora {
if e == ora {; ignore = "1"; }
}
is := strings.Index(text, " ")
ip := strings.Index(text, ". ")
if is < 0 {; is = 0; }
if ip < 0 {; ip = len(text); }
ora := oraerr{ora: ora, text: text[is+1:ip-is], ignore: ignore, count: 1}
Errors = append (Errors, ora)
}
}

func (e *Exporter) ScrapeAlertlog() {
loc := time.Now().Location()
re := regexp.MustCompile(`ORA-[0-9]+`)

ReadAccess()
for conf, _ := range config.Cfgs {
var lastTime time.Time
Errors = nil
lastScrapeTime := e.GetLastScrapeTime(conf).Add(time.Second)

file, err := os.Open(config.Cfgs[conf].Alertlog[0].File)
if err != nil {
log.Infoln(err)
} else{
scanner := bufio.NewScanner(file)
for scanner.Scan() {
t, err := time.ParseInLocation(oralayout, scanner.Text(),loc)
if err == nil {
lastTime = t
} else {
if lastTime.After(lastScrapeTime) {
if re.MatchString(scanner.Text()) {
ora := re.FindString(scanner.Text())
addError(conf,ora, scanner.Text())
}
}
}
}
file.Close()
e.SetLastScrapeTime(conf,lastTime)
for i, _ := range Errors {
e.alertlog.WithLabelValues(config.Cfgs[conf].Database,
config.Cfgs[conf].Instance,
Errors[i].ora,
Errors[i].text,
Errors[i].ignore).Set(float64(Errors[i].count))
WriteLog(config.Cfgs[conf].Instance +
"(" + Errors[i].ignore + "/" + strconv.Itoa(Errors[i].count) + "): " +
Errors[i].ora + " - " + Errors[i].text)
}
}
}
WriteAccess()
}
Loading

0 comments on commit 39064ba

Please sign in to comment.