From d2ae94213dac913cd1e05f8d970cc178368b9aa7 Mon Sep 17 00:00:00 2001 From: Mark Nelson Date: Tue, 25 Jul 2023 11:35:49 -0400 Subject: [PATCH 01/17] initial code drop from iamseth, swapped out oracle driver, added teq metrics, build on oraclelinux, produce only oraclelinux container images, updated config to split out user/pwd Signed-off-by: Mark Nelson --- .dockerignore | 9 + .github/config/lint/errcheck.exclude.txt | 0 .github/dependabot.yml | 7 + .github/workflow-config.json | 20 + .github/workflows/pull-request.yml | 329 ++ .github/workflows/release.yml | 228 ++ .../.gitignore => .gitignore | 3 + .golangci.yml | 177 + Dockerfile | 41 + LICENSE.txt | 93 +- Makefile | 177 +- README.md | 946 ++--- THIRD_PARTY_LICENSE.txt | 581 ---- THIRD_PARTY_LICENSES_DEV.txt | 24 - collector/collector.go | 603 ++++ collector/default_metrics.go | 91 + custom-metrics-example/custom-metrics.toml | 45 + .../metric-dual-example.toml | 6 + .../metric-histogram-example.toml | 18 + .../multi-metric-dual-example-labels.toml | 10 + ...m-metrics.toml => default-asm-metrics.toml | 0 ... => default-metrics.legacy-tablespace.toml | 0 ...fault-metrics.toml => default-metrics.toml | 5 +- docker-compose.yml | 76 - .../graf_app_vol/dashboard_concurrency.json | 1997 ----------- docker_vol/graf_app_vol/dashboard_io.json | 493 --- docker_vol/graf_app_vol/dashboard_query.json | 1664 --------- docker_vol/graf_app_vol/dashboard_sys.json | 3098 ----------------- docker_vol/prom_app_vol/config.yml | 25 - docker_vol/prom_app_vol/myrules.yml | 14 - docker_vol/prom_app_vol/web.yml | 12 - exporter/Dockerfile | 16 - exporter/auth_config.yml | 9 - exporter/default-metrics.toml | 733 ---- go.mod | 40 + go.sum | 94 + main.go | 98 + ...g-principles.md => operating-principles.md | 6 +- oracle-db-monitoring-exporter/Makefile | 68 - oracle-db-monitoring-exporter/go.mod | 13 - oracle-db-monitoring-exporter/go.sum | 81 - oracle-db-monitoring-exporter/main.go | 710 ---- .../oci8.pc.template | 7 - .../oraclelinux/Dockerfile | 52 - oracledb/Dockerfile | 10 - oracledb/oracledb_entrypoint.sh | 26 - prometheus/Dockerfile | 16 - prometheus/prom_entrypoint.sh | 22 - systemd-example/oracleasm_exporter.service | 24 + systemd-example/oracledb_exporter.service | 30 + teq-default-metrics.toml | 255 ++ tests/1000-integers.toml | 12 + 52 files changed, 2632 insertions(+), 10482 deletions(-) create mode 100644 .dockerignore create mode 100644 .github/config/lint/errcheck.exclude.txt create mode 100644 .github/dependabot.yml create mode 100644 .github/workflow-config.json create mode 100644 .github/workflows/pull-request.yml create mode 100644 .github/workflows/release.yml rename oracle-db-monitoring-exporter/.gitignore => .gitignore (65%) create mode 100644 .golangci.yml create mode 100644 Dockerfile delete mode 100644 THIRD_PARTY_LICENSE.txt delete mode 100644 THIRD_PARTY_LICENSES_DEV.txt create mode 100644 collector/collector.go create mode 100644 collector/default_metrics.go create mode 100644 custom-metrics-example/custom-metrics.toml create mode 100644 custom-metrics-example/metric-dual-example.toml create mode 100644 custom-metrics-example/metric-histogram-example.toml create mode 100644 custom-metrics-example/multi-metric-dual-example-labels.toml rename oracle-db-monitoring-exporter/default-asm-metrics.toml => default-asm-metrics.toml (100%) rename oracle-db-monitoring-exporter/default-metrics.legacy-tablespace.toml => default-metrics.legacy-tablespace.toml (100%) rename oracle-db-monitoring-exporter/default-metrics.toml => default-metrics.toml (93%) delete mode 100644 docker-compose.yml delete mode 100644 docker_vol/graf_app_vol/dashboard_concurrency.json delete mode 100644 docker_vol/graf_app_vol/dashboard_io.json delete mode 100644 docker_vol/graf_app_vol/dashboard_query.json delete mode 100644 docker_vol/graf_app_vol/dashboard_sys.json delete mode 100644 docker_vol/prom_app_vol/config.yml delete mode 100644 docker_vol/prom_app_vol/myrules.yml delete mode 100644 docker_vol/prom_app_vol/web.yml delete mode 100644 exporter/Dockerfile delete mode 100644 exporter/auth_config.yml delete mode 100644 exporter/default-metrics.toml create mode 100644 go.mod create mode 100644 go.sum create mode 100644 main.go rename oracle-db-monitoring-exporter/operating-principles.md => operating-principles.md (88%) delete mode 100644 oracle-db-monitoring-exporter/Makefile delete mode 100644 oracle-db-monitoring-exporter/go.mod delete mode 100644 oracle-db-monitoring-exporter/go.sum delete mode 100644 oracle-db-monitoring-exporter/main.go delete mode 100644 oracle-db-monitoring-exporter/oci8.pc.template delete mode 100644 oracle-db-monitoring-exporter/oraclelinux/Dockerfile delete mode 100644 oracledb/Dockerfile delete mode 100644 oracledb/oracledb_entrypoint.sh delete mode 100644 prometheus/Dockerfile delete mode 100644 prometheus/prom_entrypoint.sh create mode 100644 systemd-example/oracleasm_exporter.service create mode 100644 systemd-example/oracledb_exporter.service create mode 100755 teq-default-metrics.toml create mode 100644 tests/1000-integers.toml diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..5f34aa7 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,9 @@ +oraclelinux +alpline +.* +tests +*-example.toml +.golangci.yml +*.md +*.pc +dist \ No newline at end of file diff --git a/.github/config/lint/errcheck.exclude.txt b/.github/config/lint/errcheck.exclude.txt new file mode 100644 index 0000000..e69de29 diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..ffb65e4 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,7 @@ +version: 2 +updates: + - package-ecosystem: "gomod" + directory: "/" + schedule: + # Check for updates to GitHub Actions every weekday + interval: "daily" diff --git a/.github/workflow-config.json b/.github/workflow-config.json new file mode 100644 index 0000000..d419d6c --- /dev/null +++ b/.github/workflow-config.json @@ -0,0 +1,20 @@ +{ + "go-version": "1.19.4", + "deploy": { + "pull-request": true, + "sign-docker-image": false, + "pull-request-images": [ + { + "name": "ubuntu" + } + ], + "release-images": [ + { + "name": "ubuntu" + }, + { + "name": "oraclelinux" + } + ] + } +} diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml new file mode 100644 index 0000000..739a37b --- /dev/null +++ b/.github/workflows/pull-request.yml @@ -0,0 +1,329 @@ +name: Pull request +run-name: "${{ github.event.pull_request.title }} / [${{ github.actor }}] is testing${{ github.event.pull_request.head.repo.full_name != github.event.pull_request.base.repo.full_name && format(' from fork [{0}]', github.event.pull_request.head.repo.full_name) || format(' from current repo') }} πŸš€" + +on: + pull_request: + branches: + - main + - master + paths-ignore: + - ".github/**/*.yml" + - "**/*.md" + - "README.md" + +permissions: + contents: write + packages: write + security-events: write + pull-requests: write + +concurrency: + group: ${{ github.event.pull_request.head.repo.full_name }}-${{ github.head_ref }}/${{ github.ref }} + cancel-in-progress: true + +env: + PULL_REQUEST_NUMBER: ${{ github.event.pull_request.number }} + PULL_REQUEST_BRANCH: ${{ github.head_ref }} + PULL_REQUEST_HEAD: ${{ github.event.pull_request.head.sha }} + CONCURRENCY_GROUP: ${{ github.event.pull_request.head.repo.full_name }}-${{ github.head_ref }}/${{ github.ref }} + BRANCH: ${{ github.event.pull_request.head.ref }} + REPOSITORY: ${{ github.event.pull_request.head.repo.full_name }} + REGISTRY: ghcr.io + IMAGE_NAME: "ghcr.io/${{ github.repository }}" + RELEASE: false + FORKED: ${{ github.event.pull_request.head.repo.full_name != github.event.pull_request.base.repo.full_name }} + +jobs: + config: + if: github.triggering_actor != 'dependabot[bot]' + runs-on: ubuntu-latest + outputs: + go-version: ${{ fromJson(steps.config.outputs.config).go-version }} + deploy-pull-request: ${{ fromJson(steps.config.outputs.config).deploy.pull-request }} + deploy-sign-docker-image: ${{ fromJson(steps.config.outputs.config).deploy.sign-docker-image }} + deploy-pre-release-matrix: ${{ steps.pre-release-matrix.outputs.matrix }} + deploy-release-matrix: ${{ steps.release-matrix.outputs.matrix }} + is-forked: ${{ github.event.pull_request.head.repo.full_name != github.event.pull_request.base.repo.full_name }} + steps: + - name: Checkout + uses: actions/checkout@v3 + with: + ref: ${{ env.PULL_REQUEST_HEAD }} + + - name: Read config + id: config + run: echo "config=$(jq -M -c '.' ./.github/workflow-config.json)" >> $GITHUB_OUTPUT + + - name: List Pre-release profiles + id: deploy-pr-images-profiles + run: echo "profiles=$(jq -c -M '.deploy."pull-request-images"' .github/workflow-config.json)" >> $GITHUB_OUTPUT + + - name: Deploy pre-release matrix + id: pre-release-matrix + run: echo 'matrix={"include":${{ steps.deploy-pr-images-profiles.outputs.profiles }} }' >> $GITHUB_OUTPUT + + - name: List release profiles + id: deploy-images-profiles + run: echo "profiles=$(jq -c -M '.deploy."release-images"' .github/workflow-config.json)" >> $GITHUB_OUTPUT + + - name: Deploy release matrix + id: release-matrix + run: echo 'matrix={"include":${{ steps.deploy-images-profiles.outputs.profiles }} }' >> $GITHUB_OUTPUT + + build: + runs-on: ubuntu-latest + needs: + - config + outputs: + version: ${{ steps.version.outputs.version }} + steps: + - name: Checkout + uses: actions/checkout@v3 + with: + ref: ${{ env.PULL_REQUEST_HEAD }} + + - name: Setup Golang + uses: actions/setup-go@v3 + with: + go-version: ${{ needs.config.outputs.go-version }} + + - name: Get makefile versions + id: version + run: | + version=$(make version) + echo "version=$version" >> $GITHUB_OUTPUT + + - name: Setup Golang cache + uses: actions/cache@v3 + with: + path: | + ~/go/pkg/mod + ~/.cache/go-build + key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} + restore-keys: | + ${{ runner.os }}-go- + + - name: Build artifact + run: make go-build + + - name: Run test + run: make go-test + + - name: Go lint + continue-on-error: true + uses: golangci/golangci-lint-action@v3 + with: + args: --issues-exit-code=0 + skip-pkg-cache: true + skip-build-cache: true + + - name: Package artifact + run: | + mv dist/*.tar.gz application.tar.gz + + - name: Upload build output + uses: actions/upload-artifact@v3 + with: + name: application + path: "./application.tar.gz" + + - name: Upload test coverage + uses: actions/upload-artifact@v3 + with: + name: test-coverage + path: "./test-coverage.out" + + release: + runs-on: ubuntu-latest + needs: + - config + - build + outputs: + version: ${{ steps.version.outputs.version }} + steps: + - name: Checkout + uses: actions/checkout@v3 + with: + ref: ${{ env.PULL_REQUEST_HEAD }} + + - name: Set alpha version + id: version + run: echo "version=${{ needs.build.outputs.version }}-rc.pr-${{ env.PULL_REQUEST_NUMBER }}-${{ github.run_number }}" >> $GITHUB_OUTPUT + + - name: Download artifact + uses: actions/download-artifact@v3 + with: + name: application + path: "." + + - name: Rename artifact + run: mv application.tar.gz ${{ github.event.repository.name }}.tar.gz + + - name: Create release + if: ${{ env.FORKED == 'false' }} + uses: softprops/action-gh-release@v1 + id: create-release + with: + name: Pre-release ${{ steps.version.outputs.version }} + tag_name: ${{ steps.version.outputs.version }} + token: ${{ secrets.GITHUB_TOKEN }} + body: ${{ steps.changelog.outputs.changelog }} + files: | + ./${{ github.event.repository.name }}.tar.gz + draft: false + prerelease: true + generate_release_notes: true + + deploy: + name: deploy-[${{ matrix.name }}] + runs-on: ubuntu-latest + strategy: + max-parallel: 1 + matrix: ${{ fromJson(needs.config.outputs.deploy-pre-release-matrix) }} + if: ${{ !failure() && needs.release.result == 'success' && needs.config.outputs.deploy-pull-request == 'true' }} + needs: + - config + - build + - release + steps: + - name: Checkout + uses: actions/checkout@v3 + with: + ref: ${{ env.PULL_REQUEST_HEAD }} + + - name: Build image + id: docker-meta + env: + VERSION: "${{ needs.release.outputs.version }}" + run: | + make ${{ matrix.name }}-image + TAG_SUFFIX=$(echo "-${{ matrix.name }}" | sed s/-ubuntu//) + echo "image-id=$IMAGE_NAME" >> $GITHUB_OUTPUT + echo "image-version=${VERSION}${TAG_SUFFIX}" >> $GITHUB_OUTPUT + + - name: Log in to registry + if: ${{ env.FORKED == 'false' }} + run: echo "${{ secrets.GITHUB_TOKEN }}" | docker login ${{ env.REGISTRY }} -u $ --password-stdin + + - name: Push image + if: ${{ env.FORKED == 'false' }} + env: + VERSION: "${{ needs.release.outputs.version }}" + run: | + make push-${{ matrix.name }}-image + + - name: Format current time + if: ${{ env.FORKED == 'false' }} + id: time_now + run: echo "time_now_formatted=$(date +'%Y-%m-%d %H:%M:%S')" >> "$GITHUB_OUTPUT" + + - name: Find releases comment + if: ${{ env.FORKED == 'false' }} + uses: peter-evans/find-comment@v2 + id: find_comment + with: + issue-number: ${{ github.event.pull_request.number }} + comment-author: "github-actions[bot]" + body-includes: "Following docker image(s) have been created for this PR." + + - name: Create releases comment + uses: peter-evans/create-or-update-comment@v2 + if: ${{ steps.find_comment.outputs.comment-id == '' && env.FORKED == 'false' }} + with: + comment-id: ${{ steps.find_comment.outputs.comment-id }} + issue-number: ${{ github.event.pull_request.number }} + edit-mode: replace + body: | + ## Pull Request information + Following docker image(s) have been created for this PR. + | Time | Tag | + | --- | --- | + | ${{ steps.time_now.outputs.time_now_formatted }} | **${{ steps.docker-meta.outputs.image-version }}** | + + - name: Append releases comment + uses: peter-evans/create-or-update-comment@v2 + if: ${{ steps.find_comment.outputs.comment-id != '' && env.FORKED == 'false' }} + with: + comment-id: ${{ steps.find_comment.outputs.comment-id }} + issue-number: ${{ github.event.pull_request.number }} + edit-mode: append + body: | + | ${{ steps.time_now.outputs.time_now_formatted }} | **${{ steps.docker-meta.outputs.image-version }}** | + + - name: Setup cosign + if: ${{ needs.config.outputs.deploy-sign-docker-image == 'true' && env.FORKED == 'false' }} + uses: sigstore/cosign-installer@main + + - name: Write signing key to disk (only needed for `cosign sign --key`) + if: ${{ needs.config.outputs.deploy-sign-docker-image == 'true' && env.FORKED == 'false' }} + continue-on-error: true + run: echo "${{ secrets.SIGNING_SECRET }}" > cosign.key + + - name: Sign the published Docker image + if: ${{ needs.config.outputs.deploy-sign-docker-image == 'true' && env.FORKED == 'false' }} + continue-on-error: true + env: + COSIGN_PASSWORD: "" + VERSION: "${{ needs.release.outputs.version }}" + run: make sign-${{ matrix.name }}-image + + - name: Container scan + uses: aquasecurity/trivy-action@0.8.0 + env: + image-ref: "${{ steps.docker-meta.outputs.image-id }}:${{ steps.docker-meta.outputs.image-version }}" + with: + image-ref: "${{ env.image-ref }}" + ignore-unfixed: true + vuln-type: "os,library" + severity: "CRITICAL,HIGH" + format: "sarif" + output: "trivy-results.sarif" + + - name: Upload Trivy scan results to GitHub Security tab + uses: github/codeql-action/upload-sarif@v2 + with: + sarif_file: "trivy-results.sarif" + category: trivy-${{ matrix.name }} + + comments: + runs-on: ubuntu-latest + if: ${{ needs.config.outputs.is-forked == 'false' }} + needs: + - config + - release + steps: + - name: Format current time + id: time_now + run: echo "time_now_formatted=$(date +'%Y-%m-%d %H:%M:%S')" >> "$GITHUB_OUTPUT" + + - name: Find releases comment + uses: peter-evans/find-comment@v2 + id: find_comment + with: + issue-number: ${{ github.event.pull_request.number }} + comment-author: "github-actions[bot]" + body-includes: "Following release(s) have been created for this PR." + + - name: Create releases comment + uses: peter-evans/create-or-update-comment@v2 + if: ${{ steps.find_comment.outputs.comment-id == '' }} + with: + comment-id: ${{ steps.find_comment.outputs.comment-id }} + issue-number: ${{ github.event.pull_request.number }} + edit-mode: replace + body: | + ## Pull Request information + Following release(s) have been created for this PR. + | Time | Release | + | --- | --- | + | ${{ steps.time_now.outputs.time_now_formatted }} | **${{ needs.release.outputs.version }}** | + + - name: Append releases comment + uses: peter-evans/create-or-update-comment@v2 + if: ${{ steps.find_comment.outputs.comment-id != '' }} + with: + comment-id: ${{ steps.find_comment.outputs.comment-id }} + issue-number: ${{ github.event.pull_request.number }} + edit-mode: append + body: | + | ${{ steps.time_now.outputs.time_now_formatted }} | **${{ needs.release.outputs.version }}** | diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..761a89d --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,228 @@ +name: Release + +on: + pull_request_target: + types: + - closed + branches: + - master + - main + +concurrency: + group: ${{ github.workflow }} + cancel-in-progress: false + +permissions: + contents: write + packages: write + security-events: write + +env: + PULL_REQUEST_NUMBER: ${{ github.event.pull_request.number }} + PULL_REQUEST_BRANCH: ${{ github.head_ref }} + BRANCH: ${{ github.event.pull_request.head.ref }} + REGISTRY: ghcr.io + IMAGE_NAME: "ghcr.io/${{ github.repository }}" + RELEASE: true + +jobs: + config: + if: github.triggering_actor != 'dependabot[bot]' && github.event.pull_request.merged == true + runs-on: ubuntu-latest + outputs: + go-version: ${{ fromJson(steps.config.outputs.config).go-version }} + deploy-pull-request: ${{ fromJson(steps.config.outputs.config).deploy.pull-request }} + deploy-sign-docker-image: ${{ fromJson(steps.config.outputs.config).deploy.sign-docker-image }} + deploy-pre-release-matrix: ${{ steps.pre-release-matrix.outputs.matrix }} + deploy-release-matrix: ${{ steps.release-matrix.outputs.matrix }} + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Read config + id: config + run: echo "config=$(jq -M -c '.' ./.github/workflow-config.json)" >> $GITHUB_OUTPUT + + - name: List Pre-release profiles + id: deploy-pr-images-profiles + run: echo "profiles=$(jq -c -M '.deploy."pull-request-images"' .github/workflow-config.json)" >> $GITHUB_OUTPUT + + - name: Deploy pre-release matrix + id: pre-release-matrix + run: echo 'matrix={"include":${{ steps.deploy-pr-images-profiles.outputs.profiles }} }' >> $GITHUB_OUTPUT + + - name: List release profiles + id: deploy-images-profiles + run: echo "profiles=$(jq -c -M '.deploy."release-images"' .github/workflow-config.json)" >> $GITHUB_OUTPUT + + - name: Deploy release matrix + id: release-matrix + run: echo 'matrix={"include":${{ steps.deploy-images-profiles.outputs.profiles }} }' >> $GITHUB_OUTPUT + + build: + runs-on: ubuntu-latest + needs: + - config + outputs: + version: ${{ steps.version.outputs.version }} + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Setup Golang + uses: actions/setup-go@v3 + with: + go-version: ${{ needs.config.outputs.go-version }} + + - name: Get makefile versions + id: version + run: | + version=$(make version) + echo "version=$version" >> $GITHUB_OUTPUT + + - name: Setup Golang cache + uses: actions/cache@v3 + with: + path: | + ~/go/pkg/mod + ~/.cache/go-build + key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} + restore-keys: | + ${{ runner.os }}-go- + + - name: Build artifact + run: make go-build + + - name: Run test + run: make go-test + + - name: Go lint + continue-on-error: true + uses: golangci/golangci-lint-action@v3 + with: + args: --issues-exit-code=0 + skip-pkg-cache: true + skip-build-cache: true + + - name: Package artifact + run: | + mv dist/*.tar.gz application.tar.gz + + - name: Upload build output + uses: actions/upload-artifact@v3 + with: + name: application + path: "./application.tar.gz" + + - name: Upload test coverage + uses: actions/upload-artifact@v3 + with: + name: test-coverage + path: "./test-coverage.out" + + release: + runs-on: ubuntu-latest + needs: + - config + - build + outputs: + version: ${{ steps.version.outputs.version }} + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Set releasw version + id: version + run: echo "version=${{ needs.build.outputs.version }}" >> $GITHUB_OUTPUT + + - name: Download artifact + uses: actions/download-artifact@v3 + with: + name: application + path: "." + + - name: Rename artifact + run: mv application.tar.gz ${{ github.event.repository.name }}.tar.gz + + - name: Create release + uses: softprops/action-gh-release@v1 + id: create-release + with: + name: Release ${{ steps.version.outputs.version }} + tag_name: ${{ steps.version.outputs.version }} + token: ${{ secrets.GITHUB_TOKEN }} + body: ${{ steps.changelog.outputs.changelog }} + files: | + ./${{ github.event.repository.name }}.tar.gz + draft: false + prerelease: false + generate_release_notes: true + + deploy: + name: deploy-[${{ matrix.name }}] + runs-on: ubuntu-latest + strategy: + max-parallel: 1 + matrix: ${{ fromJson(needs.config.outputs.deploy-release-matrix) }} + if: ${{ !failure() && needs.release.result == 'success' && needs.config.outputs.deploy-pull-request == 'true' }} + needs: + - config + - build + - release + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Build image + id: docker-meta + env: + VERSION: "${{ needs.release.outputs.version }}" + run: | + make ${{ matrix.name }}-image + TAG_SUFFIX=$(echo "-${{ matrix.name }}" | sed s/-ubuntu//) + echo "image-id=$IMAGE_NAME" >> $GITHUB_OUTPUT + echo "image-version=${VERSION}${TAG_SUFFIX}" >> $GITHUB_OUTPUT + + - name: Log in to registry + run: echo "${{ secrets.GITHUB_TOKEN }}" | docker login ${{ env.REGISTRY }} -u $ --password-stdin + + - name: Push image + env: + VERSION: "${{ needs.release.outputs.version }}" + run: | + make push-${{ matrix.name }}-image + + - name: Setup cosign + if: ${{ needs.config.outputs.deploy-sign-docker-image == 'true' }} + uses: sigstore/cosign-installer@main + + - name: Write signing key to disk (only needed for `cosign sign --key`) + if: ${{ needs.config.outputs.deploy-sign-docker-image == 'true' }} + continue-on-error: true + run: echo "${{ secrets.SIGNING_SECRET }}" > cosign.key + + - name: Sign the published Docker image + if: ${{ needs.config.outputs.deploy-sign-docker-image == 'true' }} + continue-on-error: true + env: + COSIGN_PASSWORD: "" + VERSION: "${{ needs.release.outputs.version }}" + run: make sign-${{ matrix.name }}-image + + - name: Container scan + uses: aquasecurity/trivy-action@0.8.0 + env: + image-ref: "${{ steps.docker-meta.outputs.image-id }}:${{ steps.docker-meta.outputs.image-version }}" + with: + image-ref: "${{ env.image-ref }}" + ignore-unfixed: true + vuln-type: "os,library" + severity: "CRITICAL,HIGH" + format: "sarif" + output: "trivy-results.sarif" + + - name: Upload Trivy scan results to GitHub Security tab + uses: github/codeql-action/upload-sarif@v2 + with: + sarif_file: "trivy-results.sarif" + category: trivy-${{ matrix.name }} diff --git a/oracle-db-monitoring-exporter/.gitignore b/.gitignore similarity index 65% rename from oracle-db-monitoring-exporter/.gitignore rename to .gitignore index 9190510..80f37da 100644 --- a/oracle-db-monitoring-exporter/.gitignore +++ b/.gitignore @@ -3,4 +3,7 @@ dist/ .idea *.apk *.pub +*.key oci8.pc +*.skip +.vscode \ No newline at end of file diff --git a/.golangci.yml b/.golangci.yml new file mode 100644 index 0000000..88aa0d6 --- /dev/null +++ b/.golangci.yml @@ -0,0 +1,177 @@ +run: + # timeout for analysis, e.g. 30s, 5m, default is 1m + deadline: 2m + +linters: + disable-all: true + enable: + # Default enabled + - deadcode + - errcheck + - gosimple + - govet + - ineffassign + - staticcheck + - structcheck + - typecheck + - unused + - varcheck + + # Ours + - gocritic + - gofumpt + - goimports + - lll + - misspell + - stylecheck + - unconvert + - unparam + - gocyclo + - gocognit + - dupl + - errorlint + - bodyclose + - exhaustive + - exhaustivestruct + - gochecknoinits + - goconst + - godox + - importas + - nestif + - nolintlint + - predeclared + - testpackage + # - wrapcheck (consider adding) + fast: false + +linters-settings: + errcheck: + # report about not checking of errors in type assertions: `a := b.(MyStruct)`; + check-type-assertions: false + + # report about assignment of errors to blank identifier: `num, _ := strconv.Atoi(numStr)`; + check-blank: true + + # path to a file containing a list of functions to exclude from checking + # see https://github.com/kisielk/errcheck#excluding-functions for details + exclude: .github/config/lint/errcheck.exclude.txt + govet: + # report about shadowed variables + check-shadowing: true + gofumpt: + # Choose whether or not to use the extra rules that are disabled by default + extra-rules: true + goimports: + maligned: + # print struct with more effective memory layout or not, false by default + suggest-new: true + misspell: + # Correct spellings using locale preferences for US or UK. + # Default is to use a neutral variety of English. + # Setting locale to US will correct the British spelling of 'colour' to 'color'. + locale: US + ignore-words: + - cancelled + lll: + # max line length, lines longer will be reported. Default is 120. + # '\t' is counted as 1 character by default, and can be changed with the tab-width option + line-length: 160 + # tab width in spaces. Default to 1. + tab-width: 1 + unused: + # treat code as a program (not a library) and report unused exported identifiers; default is false. + # XXX: if you enable this setting, unused will report a lot of false-positives in text editors: + # if it's called for subdir of a project it can't find funcs usages. All text editor integrations + # with golangci-lint call it on a directory with the changed file. + check-exported: false + unparam: + gocritic: + enabled-checks: + - appendCombine + - argOrder + - assignOp + - badCond + - boolExprSimplify + - builtinShadow + - captLocal + - caseOrder + - codegenComment + - commentedOutCode + - commentedOutImport + - defaultCaseOrder + - deprecatedComment + - docStub + - dupArg + - dupBranchBody + - dupCase + - dupSubExpr + - elseif + - emptyFallthrough + - equalFold + - flagDeref + - flagName + - hexLiteral + - indexAlloc + - initClause + - methodExprCall + - nilValReturn + - octalLiteral + - offBy1 + - rangeExprCopy + - regexpMust + - sloppyLen + - stringXbytes + - switchTrue + - typeAssertChain + - typeSwitchVar + - typeUnparen + - underef + - unlambda + - unslice + - valSwap + - weakCond + + # Unused + # - unnecessaryBlock + # - yodaStyleExpr + # - appendAssign + # - commentFormatting + # - emptyStringTest + # - exitAfterDefer + # - ifElseChain + # - hugeParam + # - importShadow + # - nestingReduce + # - paramTypeCombine + # - ptrToRefParam + # - rangeValCopy + # - singleCaseSwitch + # - sloppyReassign + # - unlabelStmt + # - unnamedResult + # - wrapperFunc + staticcheck: + # Select the Go version to target. The default is '1.13'. + go: "1.19" + stylecheck: + # Select the Go version to target. The default is '1.13'. + go: "1.19" + godox: + # report any comments starting with keywords, this is useful for TODO or FIXME comments that + # might be left in the code accidentally and should be resolved before merging + keywords: + - TODO + - HACK + - FIX + - FIXME + - TEMP + - TMP + nolintlint: + # Disable to ensure that nolint directives don't have a leading space. Default is true. + allow-leading-space: false + # Exclude following linters from requiring an explanation. Default is []. + allow-no-explanation: [errcheck] + # Enable to require an explanation of nonzero length after each nolint directive. Default is false. + require-explanation: true + # Enable to require nolint directives to mention the specific linter being suppressed. Default is false. + require-specific: true diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..bb44d52 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,41 @@ +ARG BASE_IMAGE +# Build is starting here +#FROM docker.io/library/golang:1.19 AS build +FROM ${BASE_IMAGE} AS build + +RUN microdnf install golang-1.19.10 + +WORKDIR /go/src/oracledb_exporter +COPY . . +RUN go get -d -v + +ARG VERSION +ENV VERSION ${VERSION:-0.1.0} + +RUN GOOS=linux GOARCH=amd64 go build -v -ldflags "-X main.Version=${VERSION} -s -w" + +FROM ${BASE_IMAGE} as exporter +LABEL org.opencontainers.image.authors="Oracle America, Inc." +LABEL org.opencontainers.image.description="Oracle Database Observability Exporter" + +ENV VERSION ${VERSION:-0.1.0} +ENV DEBIAN_FRONTEND=noninteractive + +RUN microdnf install -y oracle-instantclient-release-el8 && microdnf install -y oracle-instantclient-basic + +ENV LD_LIBRARY_PATH /usr/lib/oracle/21/client64/lib +ENV PATH $PATH:/usr/lib/oracle/21/client64/bin +ENV ORACLE_HOME /usr/lib/oracle/21/client64 + +#ARG LEGACY_TABLESPACE +#ENV LEGACY_TABLESPACE=${LEGACY_TABLESPACE} +COPY --from=build /go/src/oracledb_exporter/oracle-db-appdev-monitoring /oracledb_exporter +#ADD ./default-metrics${LEGACY_TABLESPACE}.toml /default-metrics.toml + +ENV DATA_SOURCE_NAME system/oracle@oracle/xe + +EXPOSE 9161 + +USER 1000 + +ENTRYPOINT ["/oracledb_exporter"] diff --git a/LICENSE.txt b/LICENSE.txt index 6207534..75a677a 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -1,35 +1,58 @@ -Copyright (c) 2021 Oracle and/or its affiliates. - - The Universal Permissive License (UPL), Version 1.0 - - Subject to the condition set forth below, permission is hereby granted to any - person obtaining a copy of this software, associated documentation and/or data - (collectively the "Software"), free of charge and under any and all copyright - rights in the Software, and any and all patent rights owned or freely - licensable by each licensor hereunder covering either (i) the unmodified - Software as contributed to or provided by such licensor, or (ii) the Larger - Works (as defined below), to deal in both - - (a) the Software, and - (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if - one is included with the Software (each a "Larger Work" to which the Software - is contributed by such licensors), - - without restriction, including without limitation the rights to copy, create - derivative works of, display, perform, and distribute the Software and make, - use, sell, offer for sale, import, export, have made, and have sold the - Software and the Larger Work(s), and to sublicense the foregoing rights on - either these or other terms. - - This license is subject to the following condition: - The above copyright notice and either this complete permission notice or at - a minimum a reference to the UPL must be included in all copies or - substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - SOFTWARE. +The MIT License (MIT) + +Copyright (c) 2016 Seth Miller + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +-- + +Copyright (c) 2023 Oracle and/or its affiliates. + +The Universal Permissive License (UPL), Version 1.0 + +Subject to the condition set forth below, permission is hereby granted to any +person obtaining a copy of this software, associated documentation and/or data +(collectively the "Software"), free of charge and under any and all copyright +rights in the Software, and any and all patent rights owned or freely +licensable by each licensor hereunder covering either (i) the unmodified +Software as contributed to or provided by such licensor, or (ii) the Larger +Works (as defined below), to deal in both + +(a) the Software, and +(b) any piece of software and/or hardware listed in the lrgrwrks.txt file if +one is included with the Software (each a "Larger Work" to which the Software +is contributed by such licensors), + +without restriction, including without limitation the rights to copy, create +derivative works of, display, perform, and distribute the Software and make, +use, sell, offer for sale, import, export, have made, and have sold the +Software and the Larger Work(s), and to sublicense the foregoing rights on +either these or other terms. + +This license is subject to the following condition: +The above copyright notice and either this complete permission notice or at +a minimum a reference to the UPL must be included in all copies or +substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/Makefile b/Makefile index ad1faef..46043ee 100644 --- a/Makefile +++ b/Makefile @@ -1,26 +1,163 @@ -# -# Makefile Version 1.0 -# -# Copyright (c) 2021 Oracle and/or its affiliates. -# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ -# -# +ARCH ?= $(shell uname -m) +OS_TYPE ?= $(shell uname -s | tr '[:upper:]' '[:lower:]') +ARCH_TYPE ?= $(subst x86_64,amd64,$(patsubst i%86,386,$(ARCH))) +GOOS ?= $(shell go env GOOS) +GOARCH ?= $(shell go env GOARCH) +VERSION ?= 0.99.6 +LDFLAGS := -X main.Version=$(VERSION) +GOFLAGS := -ldflags "$(LDFLAGS) -s -w" +BUILD_ARGS = --build-arg VERSION=$(VERSION) +LEGACY_TABLESPACE = --build-arg LEGACY_TABLESPACE=.legacy-tablespace +OUTDIR = ./dist +IMAGE_NAME ?= oracle/observability-exporter +IMAGE_ID ?= $(IMAGE_NAME):$(VERSION) +IMAGE_ID_LATEST?= $(IMAGE_NAME):latest +RELEASE ?= true -deploy: - docker stack deploy --compose-file docker-compose.yml oracledb-monitor +#UBUNTU_BASE_IMAGE ?= docker.io/library/ubuntu:23.04 +ORACLE_LINUX_BASE_IMAGE ?= ghcr.io/oracle/oraclelinux:8-slim +#ALPINE_BASE_IMAGE ?= docker.io/library/alpine:3.17 -down: - docker stack rm oracledb-monitor +ifeq ($(GOOS), windows) +EXT?=.exe +else +EXT?= +endif -log-oracledb: - docker service logs --follow oracledb-monitor_oracledb --raw +export LD_LIBRARY_PATH -log-exporter: - docker service logs --follow oracledb-monitor_exporter --raw +version: + @echo "$(VERSION)" -# pause: -# docker-compose pause -# -# unpause: -# docker-compose unpause +.PHONY: go-build +go-build: + @echo "Build $(OS_TYPE)" + mkdir -p $(OUTDIR)/oracledb_exporter-$(VERSION).$(GOOS)-$(GOARCH)/ + go build $(GOFLAGS) -o $(OUTDIR)/oracledb_exporter-$(VERSION).$(GOOS)-$(GOARCH)/oracledb_exporter$(EXT) + #cp default-metrics.toml $(OUTDIR)/$(DIST_DIR) + cp teq-default-metrics.toml $(OUTDIR)/$(DIST_DIR)/default-metrics.toml + (cd dist ; tar cfz oracledb_exporter-$(VERSION).$(GOOS)-$(GOARCH).tar.gz oracledb_exporter-$(VERSION).$(GOOS)-$(GOARCH)) + +.PHONY: go-build-linux-amd64 +go-build-linux-amd64: + CGO_ENABLED=1 GOOS=linux GOARCH=amd64 $(MAKE) go-build -j2 + +.PHONY: go-build-linux-arm64 +go-build-linux-arm64: + CGO_ENABLED=1 GOOS=linux GOARCH=arm64 $(MAKE) go-build -j2 + +.PHONY: go-build-darwin-amd64 +go-build-darwin-amd64: + CGO_ENABLED=1 GOOS=darwin GOARCH=amd64 $(MAKE) go-build -j2 + +.PHONY: go-build-darwin-arm64 +go-build-darwin-arm64: + CGO_ENABLED=1 GOOS=darwin GOARCH=arm64 $(MAKE) go-build -j2 + +.PHONY: go-build-windows-amd64 +go-build-windows-amd64: + CGO_ENABLED=1 GOOS=windows GOARCH=amd64 $(MAKE) go-build -j2 + +.PHONY: go-build-windows-x86 +go-build-windows-x86: + CGO_ENABLED=1 GOOS=windows GOARCH=386 $(MAKE) go-build -j2 + +go-lint: + @echo "Linting codebase" + docker run --rm -v $(shell pwd):/app -v ~/.cache/golangci-lint/v1.50.1:/root/.cache -w /app golangci/golangci-lint:v1.50.1 golangci-lint run -v + +local-build: go-build + @true + +build: docker + @true + +deps: + go get + +go-test: + @echo "Run tests" + GOOS=$(OS_TYPE) GOARCH=$(ARCH_TYPE) go test -coverprofile="test-coverage.out" $$(go list ./... | grep -v /vendor/) + +clean: + rm -rf ./dist sgerrand.rsa.pub glibc-*.apk oracle-*.rpm + +#docker: ubuntu-image alpine-image oraclelinux-image +docker: oraclelinux-image + +push-images: + @make --no-print-directory push-ubuntu-image + @make --no-print-directory push-oraclelinux-image + @make --no-print-directory push-alpine-image + +oraclelinux-image: + if DOCKER_CLI_EXPERIMENTAL=enabled docker manifest inspect "$(IMAGE_ID)-oraclelinux" > /dev/null; then \ + echo "Image \"$(IMAGE_ID)-oraclelinux\" already exists on ghcr.io"; \ + else \ + docker build --progress=plain $(BUILD_ARGS) -t "$(IMAGE_ID)-oraclelinux" --build-arg BASE_IMAGE=$(ORACLE_LINUX_BASE_IMAGE) . && \ + #docker build --progress=plain $(BUILD_ARGS) $(LEGACY_TABLESPACE) -t "$(IMAGE_ID)-oraclelinux_legacy-tablespace" --build-arg BASE_IMAGE=$(ORACLE_LINUX_BASE_IMAGE) . && \ + docker tag "$(IMAGE_ID)-oraclelinux" "$(IMAGE_NAME):oraclelinux"; \ + fi + +push-oraclelinux-image: + docker push $(IMAGE_ID)-oraclelinux +ifeq ("$(RELEASE)", "true") + docker push "$(IMAGE_NAME):oraclelinux" + docker push "$(IMAGE_ID)-oraclelinux_legacy-tablespace" +endif + +sign-oraclelinux-image: +ifneq ("$(wildcard cosign.key)","") + cosign sign --key cosign.key $(IMAGE_ID)-oraclelinux +else + @echo "Can't find cosign.key file" +endif + +ubuntu-image: + if DOCKER_CLI_EXPERIMENTAL=enabled docker manifest inspect "$(IMAGE_ID)" > /dev/null; then \ + echo "Image \"$(IMAGE_ID)\" already exists on ghcr.io"; \ + else \ + docker build --progress=plain $(BUILD_ARGS) --build-arg BASE_IMAGE=$(UBUNTU_BASE_IMAGE) -t "$(IMAGE_ID)" . && \ + docker build --progress=plain $(BUILD_ARGS) --build-arg BASE_IMAGE=$(UBUNTU_BASE_IMAGE) $(LEGACY_TABLESPACE) -t "$(IMAGE_ID)_legacy-tablespace" . && \ + docker tag "$(IMAGE_ID)" "$(IMAGE_ID_LATEST)"; \ + fi + +push-ubuntu-image: + docker push $(IMAGE_ID) +ifeq ("$(RELEASE)", "true") + docker push "$(IMAGE_ID_LATEST)" + docker push "$(IMAGE_ID)_legacy-tablespace" +endif + +sign-ubuntu-image: +ifneq ("$(wildcard cosign.key)","") + cosign sign --key cosign.key $(IMAGE_ID) + cosign sign --key cosign.key $(IMAGE_ID_LATEST) +else + @echo "Can't find cosign.key file" +endif + +alpine-image: + if DOCKER_CLI_EXPERIMENTAL=enabled docker manifest inspect "$(IMAGE_ID)-alpine" > /dev/null; then \ + echo "Image \"$(IMAGE_ID)-alpine\" already exists on ghcr.io"; \ + else \ + docker build --progress=plain $(BUILD_ARGS) -t "$(IMAGE_ID)-alpine" --build-arg BASE_IMAGE=$(ALPINE_BASE_IMAGE) . && \ + docker build --progress=plain $(BUILD_ARGS) $(LEGACY_TABLESPACE) --build-arg BASE_IMAGE=$(ALPINE_BASE_IMAGE) -t "$(IMAGE_ID)-alpine_legacy-tablespace" . && \ + docker tag "$(IMAGE_ID)-alpine" "$(IMAGE_NAME):alpine"; \ + fi + +push-alpine-image: + docker push $(IMAGE_ID)-alpine +ifeq ("$(RELEASE)", "true") + docker push "$(IMAGE_NAME):alpine" +endif + +sign-alpine-image: +ifneq ("$(wildcard cosign.key)","") + cosign sign --key cosign.key $(IMAGE_ID)-alpine +else + @echo "Can't find cosign.key file" +endif + +.PHONY: version build deps go-test clean docker diff --git a/README.md b/README.md index 7dd241f..71b44d2 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,23 @@ -# Unified App Dev Monitoring with Oracle Database +# Oracle DB Exporter -This distribution contains scripts and code for exporting metrics and logs from the Oracle Database, to provide converged app-dev monitoring for data-centric applications. Metrics from the application layer, Kubernetes, and Oracle Database will be combined to provide unified observability to developers. The project uses Prometheus for metrics and Loki for logs, and uses Grafana as the single pane-of-glass dashboard. +[![Build Status](https://travis-ci.org/iamseth/oracledb_exporter.svg)](https://travis-ci.org/iamseth/oracledb_exporter) +[![GoDoc](https://godoc.org/github.com/iamseth/oracledb_exporter?status.svg)](http://godoc.org/github.com/iamseth/oracledb_exporter) +[![Report card](https://goreportcard.com/badge/github.com/iamseth/oracledb_exporter)](https://goreportcard.com/badge/github.com/iamseth/oracledb_exporter) -v1 (preview) - contains export of key database metrics to Prometheus and suggested Grafana dashboard +##### Table of Contents -The following metrics are exposed currently by default. +[Description](#description) +[Installation](#installation) +[Running](#running) +[Grafana](#grafana) +[Troubleshooting](#troubleshooting) +[Operating principles](operating-principles.md) + +# Description + +A [Prometheus](https://prometheus.io/) exporter for Oracle modeled after the MySQL exporter. I'm not a DBA or seasoned Go developer so PRs definitely welcomed. + +The following metrics are exposed currently. - oracledb_exporter_last_scrape_duration_seconds - oracledb_exporter_last_scrape_error @@ -32,699 +45,193 @@ The following metrics are exposed currently by default. - oracledb_resource_current_utilization - oracledb_resource_limit_value -## Table of Contents - -- [Unified App Dev Monitoring with Oracle Database](#unified-app-dev-monitoring-with-oracle-database) - - [Table of Contents](#table-of-contents) - - [Directory Structure](#directory-structure) - - [Prerequisite Components Setup with Docker Swarm](#prerequisite-components-setup-with-docker-swarm) - - [Dockerfile and Images for Docker Swarm](#dockerfile-and-images-for-docker-swarm) - - [Oracle Database Docker Image Building](#oracle-database-docker-image-building) - - [Oracle Database Exporter Docker Image Building](#oracle-database-exporter-docker-image-building) - - [Prometheus Docker Image Building](#prometheus-docker-image-building) - - [Grafana Docker Image Building](#grafana-docker-image-building) - - [Docker Compose with Docker Swarm](#docker-compose-with-docker-swarm) - - [Oracle Database Part in Compose File](#oracle-database-part-in-compose-file) - - [Exporter Part in Compose File](#exporter-part-in-compose-file) - - [Docker Volume in Compose File](#docker-volume-in-compose-file) - - [Prometheus Part in Compose File](#prometheus-part-in-compose-file) - - [Grafana Part in Compose File](#grafana-part-in-compose-file) - - [Monitor Startup and Components Modification](#monitor-startup-and-components-modification) - - [Startup and Run](#startup-and-run) - - [i. Start the Monitor](#i-start-the-monitor) - - [ii. View Logging](#ii-view-logging) - - [iii. Stop/Remove the Monitor Program](#iii-stopremove-the-monitor-program) - - [Exporter Metrics modification and refresh](#exporter-metrics-modification-and-refresh) - - [Prometheus storage/alert rule modification and refresh](#prometheus-storagealert-rule-modification-and-refresh) - - [Grafana Setup and Refresh](#grafana-setup-and-refresh) - - [Oracle Database Monitoring Exporter](#oracle-database-monitoring-exporter) - - [Description](#description) - - [Installation](#installation) - - [Running](#running) - - [Usage](#usage) - - [Default metrics](#default-metrics) - - [Custom metrics](#custom-metrics) - - [Customize metrics in a docker image](#customize-metrics-in-a-docker-image) - - [Using a multiple host data source name](#using-a-multiple-host-data-source-name) - - [Files & Folder](#files--folder) - - [Environment Variables](#environment-variables) - - [TLS connection to database](#tls-connection-to-database) - - [FAQ/Troubleshooting](#faqtroubleshooting) - - [Unable to convert current value to float (metric=par,metri...in.go:285](#unable-to-convert-current-value-to-float-metricparmetriingo285) - - [Error scraping for wait_time](#error-scraping-for-wait_time) - - [An Oracle instance generates trace files](#an-oracle-instance-generates-trace-files) - - [Data Storage](#data-storage) - -## Directory Structure - -```text -. -β”œβ”€β”€ Makefile -β”œβ”€β”€ docker-compose.yml # aggregate all services -β”œβ”€β”€ README.md # this! -β”‚ -β”œβ”€β”€ docker_vol/ -β”‚ β”œβ”€β”€ graf_app_vol/ -β”‚ β”‚ β”œβ”€β”€ dashboard_concurrency.json -β”‚ β”‚ β”œβ”€β”€ dashboard_io.json -β”‚ β”‚ β”œβ”€β”€ dashboard_query.json -β”‚ β”‚ └── dashboard_sys.json -β”‚ β”‚ -β”‚ └── prom_app_vol/ -β”‚ β”œβ”€β”€ myrules.yml # rules of prometheus metrics and alerts -β”‚ β”œβ”€β”€ config.yml # connection configuration -β”‚ └── web.yml # authentication configuration -β”‚ -β”‚ -β”œβ”€β”€ oracledb/ # local Oracle Database(19c) container -β”‚ β”œβ”€β”€ Dockerfile -β”‚ └── oracledb_entrypoint.sh # docker secret setup scripts -β”‚ -β”‚ -β”œβ”€β”€ oracle-db-monitoring-exporter/ # customized basic exporter program -β”‚ -β”‚ -β”œβ”€β”€ exporter/ # query and format metrics -β”‚ β”œβ”€β”€ Dockerfile -β”‚ β”œβ”€β”€ auth_config.yml # http auth config of the exporter -β”‚ └── default-metrics.toml # queries to collect metrics -β”‚ -β”‚ -β”œβ”€β”€ prometheus/ # time-series metrics storage -β”‚ β”œβ”€β”€ Dockerfile -β”‚ └── prom_entrypoint.sh # docker secret setup scripts -β”‚ -β”‚ -└── grafana/ # monitor dashboard - β”œβ”€β”€ Dockerfile - β”œβ”€β”€ dashboards/ - β”‚ └── all.yml # config of dashboard - β”‚ - └── datasources - └── all.yml # specify prometheus as the datasource -``` - ---- - -## Prerequisite Components Setup with Docker Swarm - -### Dockerfile and Images for Docker Swarm - -In order to protect user's sensitive config info and data, a Docker Secret is used which requires Docker Swarm mode. - -```sh -docker swarm init -# use `docker info` to check status of swarm mode -``` - -> For more details about docker swarm, please visit [docker swarm init official documentation](https://docs.docker.com/engine/reference/commandline/swarm_init/). - -Each component in [Docker Swarm](https://docs.docker.com/engine/swarm/) mode is a [Docker Service](https://docs.docker.com/engine/reference/commandline/service/). A group of docker services is called [Docker Stack](https://docs.docker.com/engine/reference/commandline/stack/). - -Hence, we are going to use following command to start the monitor. - -``` sh -docker stack deploy --compose-file {yaml_compose_file} {stack_name} -``` - -This is different from `docker-compose` which can build the image during setup as Docker Swarm requires a pre-built image for each service(container). - -#### Oracle Database Docker Image Building - -- Files involved - - `./oracledb/Dockerfile` - - `./oracledb/oracledb_entrypoint.sh` - -```sh -cd exporter -docker build --tag {oracledb_image_name} . -# or -docker build --tag {oracledb_image_name}:{image_tag} . -# examples -docker build --tag oracledb_monitor_oracledb . -docker build --tag oracledb_monitor_oracledb:1.0 . -``` - -> For more details about docker build, please visit the [official documentation](https://docs.docker.com/engine/reference/commandline/build/) - -#### Oracle Database Exporter Docker Image Building - -An additional http authentication feature is provided to enhance connection security, therefore, it is necessary to build a basic exporter image from `oracle-db-monitoring-exporter` and then a customized image with configuration files. - -- Files involved: - - `./oracle-db-monitoring-exporter/*` - - `./exporter/Dockerfile` - - `./exporter/auth_config.yml` - - `./exporter/default-metrics.toml` - - `./exporter/localhost.cert` (you need to create your own version) - - `./exporter/localhost.key` (you need to create your own version) - -a. Base Image - -This is a required step. - -```sh -# Base Image - -cd oracle-db-monitoring-exporter -make oraclelinux-image -# This will build three base images and we are going to use either -# "oracle-db-monitoring-exporter:0.3.0-oraclelinux" or "oracle-db-monitoring-exporter:oraclelinux" -``` - -b. Before creating the customized image, it is necessary to setup the authentication username and password for the exporter and encrypt it with a docker secret. Then, specify the docker secret names in `exporter/auth_config.yml` - -```sh -echo "{exp_auth_username}" | docker secret create {secret_name} - -echo "{exp_auth_password}" | docker secret create {secret_name} - - -# examples -echo "mntmgr" | docker secret create auth.username - -echo "P@55w0rd" | docker secret create auth.password - -``` - -```yaml -# auth_config.yml -username: auth.username -password: auth.password -``` - -c. Ensure metric queries are finished and saved in `exporter/default-metrics.toml`. - -d. Generate ssl key and ssl certificate for https transportation. - -```sh -cd exporter -openssl req \ - -x509 \ - -newkey rsa:4096 \ - -nodes \ - -keyout localhost.key \ - -out localhost.crt -``` - -Make sure `localhost.key` and `localhost.crt` are under `./exporter/` - -*If you need to change file names of cert and key, don't forget to modify the `Dockerfile`.* - -e. Final Customized Image - -```sh -# Customized Image - -cd exporter -docker build --tag {image_name}:{tag_name} . -# example -docker build --tag oracledb_monitor_exporter:1.0 . -``` - -#### Prometheus Docker Image Building - -- Files involved: - - `./docker_vol/prom_app_vol/config.yml` - - `./docker_vol/prom_app_vol/myrules.yml` - - `./docker_vol/prom_app_vol/web.yml` - - `./prometheus/Dockerfile` - - `./prometheus/prom_entrypoint.sh` - - `./prometheus/localhost.cert` (you need to create your own version) - - `./prometheus/localhost.key` (you need to create your own version) - -a. Generate ssl key and ssl certificate for https transportation. - -```sh -cd exporter -openssl req \ - -x509 \ - -newkey rsa:4096 \ - -nodes \ - -keyout localhost.key \ - -out localhost.crt -``` - -Make sure your `localhost.key` and `localhost.crt` are under `./prometheus/` - -*If you need to change file names of cert and key, don't forget to modify the `Dockerfile` and `web.yml`.* - -b. Build Prometheus image - -```sh -cd prometheus -docker build --tag {image_name}:{tag_name} . -# example -docker build --tag oracledb_monitor_prometheus:1.0 . -``` - -c. Setup http authentication username and password for Prometheus in `./docker_vol/prom_app_vol/web.yml` and Docker Secret. - -The password required to connect to Prometheus should be hashed with [bcrypt](https://github.com/prometheus/exporter-toolkit/blob/master/docs/web-configuration.md#about-bcrypt). Use `htpasswd` command to hash the password with bcrypt. - -```sh -htpasswd -nBC 10 β€œβ€ | tr -d β€˜:\n’ -``` - -Copy and paste the result, and create such docker secret. For example, the bcrypt hashing of `test` is `$2b$12$hNf2lSsxfm0.i4a.1kVpSOVyBCfIB51VRjgBUyv6kdnyTlgWj81Ay`. - -```sh -echo "{prom_auth_password}" | docker secret create prom.auth.pwd - -# example -echo "\$2b\$12\$hNf2lSsxfm0.i4a.1kVpSOVyBCfIB51VRjgBUyv6kdnyTlgWj81Ay" | docker secret create prom.auth.pwd - -# don't forget add a '\' before each '$' -# you can also create a docker secret with a file. visit the documentation. -``` - -***Here the secret name is required to be 'prom.auth.pwd'.*** - -```yaml -# web.yml -basic_auth_users: - {prom_auth_username}: {docker_secret_name_of_auth_pwd} - # example: - mntmgr: prom.auth.pwd -``` - -#### Grafana Docker Image Building - -- Files involved: - - `./docker_vol/prom_app_vol/config.yml` - - `./docker_vol/prom_app_vol/myrules.yml` - - `./docker_vol/prom_app_vol/web.yml` - - `./prometheus/Dockerfile` - - `./prometheus/prom_entrypoint.sh` - - `./prometheus/localhost.cert` (you need to create your own version) - - `./prometheus/localhost.key` (you need to create your own version) - -a. Generate ssl key and ssl certificate for https transportation. - -```sh -cd exporter -openssl req \ - -x509 \ - -newkey rsa:4096 \ - -nodes \ - -keyout localhost.key \ - -out localhost.crt -``` - -Make sure your `localhost.key` and `localhost.crt` are under `./grafana/` - -*If you need to change file names of cert and key, don't forget to modify the `Dockerfile` and `grafana.ini`.* - -b. For Grafana, you can setup the connection to Prometheus in `./grafana/datasources/all.yml`, setup config of dashboards in `./grafana/dashboards/all.yml`, while all of the provision dashboards are in `./docker_vol/graf_app_vol/*.json`. - -You should setup connection and configuration of Grafana before building the image, but you can modify dashboard raw codes after startup. - -```sh -cd grafana -docker build --tag {image_name}:{tag_name} . -# example -docker build --tag oracledb_monitor_grafana:1.0 . -``` - -### Docker Compose with Docker Swarm - -To enable the usage of compose file in docker swarm command line, we need the version of `docker-compose.yml` to be at least 3.1. - -```yaml -# docker-compose.yml -version: 3.1 # We have it by default. Don't delete it in your customization. -``` - -There is a default `docker-compose.yml`, but it is still necessary to setup docker secret in your environment. - -#### Oracle Database Part in Compose File - -```yaml -services: - oracledb: - image: {db_image_name:image_tab} # oracledb_monitor_oracledb:1.0 - container_name: 'oracledb' - environment: - ORACLE_SID: ORCLCDB - ORACLE_PWD: DOCKER_SECRET@{pwd_secret_name} # DOCKER_SECRET@oracle.pwd - secrets: - - {pwd_secret_name} # oracle.pwd - ports: - - '1521:1521' - - '8080:8080' - tty: true - -secrets: - {pwd_secret_name}: # oracle.pwd: - external: true -``` - -You need to create your password to DBA with Docker Secret. +# Installation -```sh -echo "{sysdba_pwd}" | docker secret create {secret_name} - -# example -echo "P@55w0rd" | docker secret create oracle.pwd - -``` +## Docker -> For more details about docker secret in compose file, please visit the [official documentation](https://docs.docker.com/engine/swarm/secrets/#use-secrets-in-compose). +You can run via Docker using an existing image. Since version 0.4, the images are available on the github registry. -#### Exporter Part in Compose File +Here an example to retrieve the version 0.5.0: -```yaml -services: - exporter: - image: {exporter_image_name:image_tab} # oracledb_monitor_exporter:1.0 - container_name: 'exporter' - environment: - DATA_SOURCE_NAME: {dsn_secret_name} # data.source.name - secrets: - - {dsn_secret_name} # data.source.name - - {exp_auth_username} # auth.username - - {exp_auth_password} # auth.password - depends_on: - - oracledb - ports: - - '9161:9161' - -secrets: - {dsn_secret_name}: # data.source.name - external: true - {exp_auth_username}: # auth.username - external: true - {exp_auth_password}: # auth.password - external: true -``` - -`{exp_auth_username}` and `{exp_auth_password}` are the ones we've setup in the [previous step](#oracle-database-exporter-docker-image-building). - -You need to setup your database connection string, auth username and password of the exporter with Docker Secret. - -For the Data Connection String, we strongly recommend not using sysdba, and instead creating your own common cdb user. - -```sh -# After the creation and initialization of your Oracle Database -# in the shell of your database system -# for container, to login to shell -docker exec -it --user oracle {container_id} /bin/bash - -sqlplus sys/{sysdba_pwd} as sysdba -``` - -```sql -DROP USER c##mntmgr CASCADE; -- a prefix of c## is required -CREATE USER c##mntmgr IDENTIFIED BY test CONTAINER=ALL; -GRANT CREATE SESSION TO c##mntmgr; -GRANT select_catalog_role TO c##mntmgr; -GRANT select any dictionary TO c##mntmgr; -``` - -So the DSN of `c##mntmgr` to your Oracle Database is `c##mntmgr:test@oracledb/ORCLCDB`. Encrypt it with Docker Secret. - -```sh -echo "c##mntmgr:test@oracledb/ORCLCDB" | docker secret create data.source.name - +```bash +docker pull ghcr.io/iamseth/oracledb_exporter:0.5.0 ``` -> For more details about Oracle Easy Connect Naming, please visit [official documentation](https://docs.oracle.com/en/database/oracle/oracle-database/18/ntcli/specifying-a-connection-by-using-the-easy-connect-naming-method.html#GUID-1035ABB3-5ADE-4697-A5F8-28F9F79A7504)* - -#### Docker Volume in Compose File - -Before Prometheus and Grafana Part, we need to set the docker volume. - -To use configuration files and dashboard of Prometheus and Grafana in `./docker_vol`, please setup volumes to Prometheus and Grafana containers. +And here a command to run it and forward the port: -```yaml -services: - prometheus: - volumes: - - {directory_to_prom_app_vol}:/etc/prometheus/prometheus_vol - # for example - # ?/docker_vol/prom_app_vol:/etc/prometheus/prometheus_vol +```bash +docker run -it --rm -p 9161:9161 ghcr.io/iamseth/oracledb_exporter:0.5.0 ``` -#### Prometheus Part in Compose File - -```yaml -# docker-compose.yml -prometheus: - image: oracledb_monitor_prometheus:1.0 - container_name: 'prometheus' - secrets: - - prom.auth.pwd - - auth.username # exporter auth username - - auth.password # exporter auth password - depends_on: - - exporter - ports: - - '9090:9090' - - '9093:9093' - volumes: - - ./docker_vol/prom_app_vol:/etc/prometheus/prometheus_vol - tty: true +If you don't already have an Oracle server, you can run one locally in a container and then link the exporter to it. -secrets: - prom.auth.pwd: - external: true +```bash +docker run -d --name oracle -p 1521:1521 wnameless/oracle-xe-11g-r2:18.04-apex +docker run -d --name oracledb_exporter --link=oracle -p 9161:9161 -e DATA_SOURCE_NAME=oracle://system:oracle@oracle:1521/xe ghcr.io/iamseth/oracledb_exporter:0.5.0 ``` -```yaml -# config.yml in ./docker_vol/prom_app_vol/ -# this file is for exporter connection -global: - scrape_interval: 30s - scrape_timeout: 30s - evaluation_interval: 30s +Since 0.2.1, the exporter image exist with Alpine flavor. Watch out for their use. It is for the moment a test. -scrape_configs: - - job_name: 'TEQ Monitor' - static_configs: - - targets: ['exporter:9161'] - basic_auth: - username: auth.username # docker secret name of exporter auth username - password: auth.password # docker secret name of exporter auth password - -rule_files: - - "/etc/prometheus/prometheus_vol/myrules.yml" # your prom rules +```bash +docker run -d --name oracledb_exporter --link=oracle -p 9161:9161 -e DATA_SOURCE_NAME=oracle://system:oracle@oracle/xe iamseth/oracledb_exporter:alpine ``` -#### Grafana Part in Compose File - -```yaml -# docker-compose.yml -grafana: - image: oracledb-monitor_graf - container_name: 'grafana' - depends_on: - - prometheus - ports: - - '3000:3000' - volumes: - - ./docker_vol/graf_app_vol:/var/lib/grafana/grafana_vol -``` +### Different Docker Images ---- +Different Linux Distros: -## Monitor Startup and Components Modification +- `x.y.z` - Ubuntu Linux image +- `x.y.z-oraclelinux` - Oracle Enterprise Linux image +- `x.y.z-Alpine` - Alpine Linux image -Setup all [configurations prerequisites](#prerequisite-link). +Forked Version: +All the above docker images have a duplicate image tag ending in +`_legacy-tablespace`. These versions use the older/deprecated tablespace +utilization calculation based on the aggregate sum of file sizes in a given +tablespace. The newer mechanism takes into account block sizes, extents, and +fragmentation aligning with the same metrics reported from the Oracle Enterprise +Manager. See https://github.com/iamseth/oracledb_exporter/issues/153 for +details. The versions above should have a more useful tablespace utilization +calculation going forward. -### Startup and Run +## Binary Release -#### i. Start the Monitor +Pre-compiled versions for Linux 64 bit and Mac OSX 64 bit can be found under [releases](https://github.com/iamseth/oracledb_exporter/releases). -```sh -docker stack deploy --compose-file docker-compose.yml {stack_name} +In order to run, you'll need the [Oracle Instant Client Basic](http://www.oracle.com/technetwork/database/features/instant-client/index-097480.html) +for your operating system. Only the basic version is required for execution. -# for example -docker stack deploy --compose-file docker-compose.yml oracledb-monitor +# Running +Ensure that the environment variable DATA_SOURCE_NAME is set correctly before starting. +DATA_SOURCE_NAME should be in Oracle Database connection string format: -# or run `make deploy` -# check Makefile to edit your own commands +```conn + oracle://user:pass@server/service_name[?OPTION1=VALUE1[&OPTIONn=VALUEn]...] ``` -The first time you build and start the Oracle Database container, it will take about 15 to 20 minutes for Oracle Database to get ready. Create your general user when it done. - -Then, go to the [Grafana Dashboard](https://localhost:3000). By default, username: admin, password: admin - -> If using Chrome, it may show "Your connection is not private" and "NET::ERR_CERT_INVALID", and prevent you from visiting the Grafana board. Please use other browser. You will meet the same problem during visiting local [Prometheus Dashboard](https://localhost:9090) and [exporter metrics](https://localhost:9161/metrics) with https protocol. This problem is due to that we are using self-signed certificates which Chrome does not recognize. - -***To enable your Grafana to connect to your Prometheus database, when you login to Grafana dashboard, go to `Configuration` -> `Data Sources` -> `Prometheus`(data connection). Then A) enable the `Basic auth`, `Skip TLS Verify` and `With CA Cert` under `Auth` section, B) type in the Prometheus auth username and password you just setup, and then finally C) save and test.*** - -You can also visit the [Prometheus Dashboard](https://localhost:9090) and [exporter metrics](https://localhost:9161/metrics) to track. - -#### ii. View Logging - -```sh -# display all docker services -docker service ls -# show logs of one service -docker service logs --follow {docker_service_name} --raw -# for example -docker service logs --follow oracledb-monitor_oracledb --raw -docker service logs --follow oracledb-monitor_exporter --raw +For Example: -# or run `make log-oracledb` -# check Makefile to edit your own commands +```bash +# export Oracle location: +export DATA_SOURCE_NAME=oracle://system:password@oracle-sid +# or using a complete url: +export DATA_SOURCE_NAME=oracle://user:password@myhost:1521/service +# 19c client for primary/standby configuration +export DATA_SOURCE_NAME=oracle://user:password@primaryhost:1521,standbyhost:1521/service +# 19c client for primary/standby configuration with options +export DATA_SOURCE_NAME=oracle://user:password@primaryhost:1521,standbyhost:1521/service?connect_timeout=5&transport_connect_timeout=3&retry_count=3 +# 19c client for ASM instance connection (requires SYSDBA) +export DATA_SOURCE_NAME=oracle://user:password@primaryhost:1521,standbyhost:1521/+ASM?as=sysdba +# Then run the exporter +/path/to/binary/oracledb_exporter --log.level error --web.listen-address 0.0.0.0:9161 ``` - -> For more details about docker service logging, please visit the [official documentation](https://docs.docker.com/engine/reference/commandline/service_logs/) - -#### iii. Stop/Remove the Monitor Program - -```sh -docker stack rm {stack_name} -# example -docker stack rm oracledb-monitor - -# or run `make down` -# This will both stop and remove all four services in this docker stack. +## Default-metrics requirement +Make sure to grant `SYS` privilege on `SELECT` statement for the monitoring user, on the following tables. ``` - -*Be careful when you run this, since it will clean all local files in the containers (files in volumes won't be deleted), so make sure you've backup/relocated necessary files before running this.* - -### Exporter Metrics modification and refresh - -You can modify or add metrics by editing `exporter\default-metrics.toml` - -After updating it, it is necessary to rebuild the image and redeploy the exporter service. -*Be careful when you rebuild the image and redeploy the service, since it will remove the old container and start a new one, so make sure you've backup/relocated necessary files.* - -```sh -# if no updates were made to the image and only changes docker-compose of exporter service were made, -# there is no need to re-build the image, simply run: -docker stack deploy --compose-file docker-compose.yml oracledb-monitor - -# if metric files were modified -# rebuild and restart the exporter service with a new image -cd {Dockerfile_path} -docker build --tag {image_title_tag} . -docker service update {service_name} --image {image_title_tag} -# for example -docker build --tag oracledb_monitor_exporter:1.1 . -docker service update oracledb-monitor_exporter --image oracledb_monitor_exporter:1.1 - -# if you change your compose file, just re-run the deploy command -# it will restart the service you changed -docker stack deploy --compose-file docker-compose.yml oracledb-monitor - -# get service name -docker service ls +dba_tablespace_usage_metrics +dba_tablespaces +v$system_wait_class +v$asm_diskgroup_stat +v$datafile +v$sysstat +v$process +v$waitclassmetric +v$session +v$resource_limit ``` -> For more details about docker service, please visit the [Official Documentation](https://docs.docker.com/engine/reference/commandline/service/) -> For more details about exporter and metrics editing/configuring, please check [Oracle Database Monitoring Exporter](#db-mnt-exporter) part. - -### Prometheus storage/alert rule modification and refresh +# Integration with System D -Prometheus configuration can be modified in `docker_vol\prom_app_vol\config.yml`, and add recording and alerting rules can be modified in `docker_vol\prom_app_vol\myrules.yml`. +Create **oracledb_exporter** user with disabled login and **oracledb_exporter** group\ +mkdir /etc/oracledb_exporter\ +chown root:oracledb_exporter /etc/oracledb_exporter +chmod 775 /etc/oracledb_exporter +Put config files to **/etc/oracledb_exporter** +Put binary to **/usr/local/bin** -After updating either of them, it is necessary to enter the container and restart the Prometheus process. - -```sh -# get container name -docker ps +Create file **/etc/systemd/system/oracledb_exporter.service** with the following content: -# enter bash shell of the container -docker exec -it --user root {container_name/container_id} /bin/bash -# for example -docker exec -it --user root oracledb-monitor_prometheus /bin/bash - -# restart the prometheus process without killing it -kill -HUP 1 +```bash +[Unit] +Description=Service for oracle telemetry client +After=network.target +[Service] +Type=oneshot +#!!! Set your values and uncomment +#User=oracledb_exporter +#Group=oracledb_exporter +#Environment="DATA_SOURCE_NAME=dbsnmp/Bercut01@//primaryhost:1521,standbyhost:1521/myservice?transport_connect_timeout=5&retry_count=3" +#Environment="LD_LIBRARY_PATH=/u01/app/oracle/product/19.0.0/dbhome_1/lib" +#Environment="ORACLE_HOME=/u01/app/oracle/product/19.0.0/dbhome_1" +#Environment="CUSTOM_METRICS=/etc/oracledb_exporter/custom-metrics.toml" +ExecStart=/usr/local/bin/oracledb_exporter \ + --default.metrics "/etc/oracledb_exporter/default-metrics.toml" \ + --log.level error --web.listen-address 0.0.0.0:9161 +[Install] +WantedBy=multi-user.target ``` -> For more details about Prometheus config and rule files, please visit [Prometheus Configuration](https://prometheus.io/docs/prometheus/latest/configuration/configuration/) - -### Grafana Setup and Refresh - -You can add or modify Grafana panels and add dashboards on the [Grafana webpage](https://localhost:3000). However, although you can save cache, you can not save the dashboard to the source file. Instead, you can go to Setting of the dashboard and copy the JSON Model to replace the original json file(`docker_vol\graf_app_vol\{dashboard}.json`). +Then tell System D to read files: -> For more details about provision and config of Grafana, please visit [Grafana Lab](https://grafana.com/docs/grafana/latest/administration/provisioning/). + systemctl daemon-reload -## Oracle Database Monitoring Exporter +Start this new service: -### Description + systemctl start oracledb_exporter -A [Prometheus](https://prometheus.io/) exporter for Oracle Database. +Check service status: -### Installation - -We currently only support `oraclelinux` container version. - -```bash -cd oracle-db-monitoring-exporter -make oraclelinux-image -``` - -### Running - -Ensure the environment variable DATA_SOURCE_NAME is set correctly before starting. -DATA_SOURCE_NAME should be in Oracle EZCONNECT format: - -19c Oracle Client supports enhanced EZCONNECT, you are able to failover to standby DB or gather some heavy metrics from active standby DB and specify some additional parameters. Within 19c client you are able to connect 12c primary/standby DB too :) - -For Example: - -```bash -# export Oracle location: -export DATA_SOURCE_NAME=system/password@oracle-sid -# or using a complete url: -export DATA_SOURCE_NAME=user/password@//myhost:1521/service -# 19c client for primary/standby configuration -export DATA_SOURCE_NAME=user/password@//primaryhost:1521,standbyhost:1521/service -# 19c client for primary/standby configuration with options -export DATA_SOURCE_NAME=user/password@//primaryhost:1521,standbyhost:1521/service?connect_timeout=5&transport_connect_timeout=3&retry_count=3 -# 19c client for ASM instance connection (requires SYSDBA) -export DATA_SOURCE_NAME=user/password@//primaryhost:1521,standbyhost:1521/+ASM?as=sysdba -# Then run the exporter -/path/to/binary/oracle-db-monitoring-exporter --log.level error --web.listen-address 0.0.0.0:9161 -``` + systemctl status oracledb_exporter -### Usage +## Usage ```bash -Usage of oracle-db-monitoring-exporter: +Usage of oracledb_exporter: --log.format value - If set use a syslog logger or JSON logging. Example: logger:syslog?appname=bob&local=7 or logger:stdout?json=true. Defaults to stderr. + If set use a syslog logger or JSON logging. Example: logger:syslog?appname=bob&local=7 or logger:stdout?json=true. Defaults to stderr. --log.level value - Only log messages with the given severity or above. Valid levels: [debug, info, warn, error, fatal]. + Only log messages with the given severity or above. Valid levels: [debug, info, warn, error, fatal]. --custom.metrics string File that may contain various custom metrics in a TOML file. --default.metrics string Default TOML file metrics. + --web.systemd-socket + Use systemd socket activation listeners instead of port listeners (Linux only). --web.listen-address string - Address to listen on for web interface and telemetry. (default ":9161") + Address to listen on for web interface and telemetry. (default ":9161") --web.telemetry-path string - Path under which to expose metrics. (default "/metrics") + Path under which to expose metrics. (default "/metrics") --database.maxIdleConns string Number of maximum idle connections in the connection pool. (default "0") --database.maxOpenConns string Number of maximum open connections in the connection pool. (default "10") - --web.secured-metrics boolean - Expose metrics using https server. (default "false") - --web.ssl-server-cert string - Path to the PEM encoded certificate file. - --web.ssl-server-key string - Path to the PEM encoded key file. + --web.config.file + Path to configuration file that can enable TLS or authentication. ``` -#### Default metrics +# Default metrics This exporter comes with a set of default metrics defined in **default-metrics.toml**. You can modify this file or -provide a different one using ``default.metrics`` option. +provide a different one using `default.metrics` option. -#### Custom metrics +# Custom metrics > NOTE: Do not put a `;` at the end of your SQL queries as this will **NOT** work. This exporter does not have the metrics you want? You can provide new one using TOML file. To specify this file to the exporter, you can: -- Use ``--custom.metrics`` flag followed by the TOML file -- Export CUSTOM_METRICS variable environment (``export CUSTOM_METRICS=my-custom-metrics.toml``) +- Use `--custom.metrics` flag followed by the TOML file +- Export CUSTOM_METRICS variable environment (`export CUSTOM_METRICS=my-custom-metrics.toml`) This file must contain the following elements: -- One or several metric section (``[[metric]]``) +- One or several metric section (`[[metric]]`) - For each section a context, a request and a map between a field of your request and a comment. Here's a simple example: -```toml +``` [[metric]] context = "test" request = "SELECT 1 as value_1, 2 as value_2 FROM DUAL" @@ -733,7 +240,7 @@ metricsdesc = { value_1 = "Simple example returning always 1.", value_2 = "Same This file produce the following entries in the exporter: -```text +``` # HELP oracledb_test_value_1 Simple example returning always 1. # TYPE oracledb_test_value_1 gauge oracledb_test_value_1 1 @@ -744,7 +251,7 @@ oracledb_test_value_2 2 You can also provide labels using labels field. Here's an example providing two metrics, with and without labels: -```toml +``` [[metric]] context = "context_no_label" request = "SELECT 1 as value_1, 2 as value_2 FROM DUAL" @@ -759,7 +266,7 @@ metricsdesc = { value_1 = "Simple example returning always 1.", value_2 = "Same This TOML file produce the following result: -```text +``` # HELP oracledb_context_no_label_value_1 Simple example returning always 1. # TYPE oracledb_context_no_label_value_1 gauge oracledb_context_no_label_value_1 1 @@ -776,7 +283,7 @@ oracledb_context_with_labels_value_2{label_1="First label",label_2="Second label Last, you can set metric type using **metricstype** field. -```toml +``` [[metric]] context = "context_with_labels" labels = [ "label_1", "label_2" ] @@ -788,7 +295,7 @@ metricstype = { value_1 = "counter" } This TOML file will produce the following result: -```text +``` # HELP oracledb_test_value_1 Simple test example returning always 1 as counter. # TYPE oracledb_test_value_1 counter oracledb_test_value_1 1 @@ -799,22 +306,23 @@ oracledb_test_value_2 2 You can find [here](./custom-metrics-example/custom-metrics.toml) a working example of custom metrics for slow queries, big queries and top 100 tables. -#### Customize metrics in a docker image +# Customize metrics in a docker image If you run the exporter as a docker image and want to customize the metrics, you can use the following example: ```Dockerfile -FROM oracle-db-monitoring-exporter:oraclelinux +FROM iamseth/oracledb_exporter:latest COPY custom-metrics.toml / ENTRYPOINT ["/oracledb_exporter", "--custom.metrics", "/custom-metrics.toml"] ``` -#### Using a multiple host data source name +# Using a multiple host data source name > NOTE: This has been tested with v0.2.6a and will most probably work on versions above. -> NOTE: While `user/password@//database1.example.com:1521,database3.example.com:1521/DBPRIM` works with SQLPlus, it doesn't seem to work with `oracledb-exporter` v0.2.6a. + +> NOTE: While `user/password@//database1.example.com:1521,database3.example.com:1521/DBPRIM` works with SQLPlus, it doesn't seem to work with oracledb-exporter v0.2.6a. In some cases, one might want to scrape metrics from the currently available database when having a active-passive replication setup. @@ -822,14 +330,14 @@ This will try to connect to any available database to scrape for the metrics. Wi This example allows to achieve this: -#### Files & Folder +### Files & Folder: - tns_admin folder: `/path/to/tns_admin` - tnsnames.ora file: `/path/to/tns_admin/tnsnames.ora` Example of a tnsnames.ora file: -```ora +``` database = (DESCRIPTION = (ADDRESS_LIST = @@ -842,53 +350,118 @@ database = ) ``` -#### Environment Variables +### Environment Variables - `TNS_ENTRY`: Name of the entry to use (`database` in the example file above) - `TNS_ADMIN`: Path you choose for the tns admin folder (`/path/to/tns_admin` in the example file above) -- `DATA_SOURCE_NAME`: Datasource pointing to the `TNS_ENTRY` (`user/password@database` in the example file above) +- `DATA_SOURCE_NAME`: Datasource pointing to the `TNS_ENTRY` (`user:password@database` in the example file above) -#### TLS connection to database +# TLS connection to database First, set the following variables: -```bash -export WALLET_PATH=/wallet/path/to/use -export TNS_ENTRY=tns_entry -export DB_USERNAME=db_username -export TNS_ADMIN=/tns/admin/path/to/use -``` + export WALLET_PATH=/wallet/path/to/use + export TNS_ENTRY=tns_entry + export DB_USERNAME=db_username + export TNS_ADMIN=/tns/admin/path/to/use Create the wallet and set the credential: -```bash -mkstore -wrl $WALLET_PATH -create -mkstore -wrl $WALLET_PATH -createCredential $TNS_ENTRY $DB_USERNAME -``` + mkstore -wrl $WALLET_PATH -create + mkstore -wrl $WALLET_PATH -createCredential $TNS_ENTRY $DB_USERNAME Then, update sqlnet.ora: -```bash -echo " -WALLET_LOCATION = (SOURCE = (METHOD = FILE) (METHOD_DATA = (DIRECTORY = $WALLET_PATH ))) -SQLNET.WALLET_OVERRIDE = TRUE -SSL_CLIENT_AUTHENTICATION = FALSE -" >> $TNS_ADMIN/sqlnet.ora -``` + echo " + WALLET_LOCATION = (SOURCE = (METHOD = FILE) (METHOD_DATA = (DIRECTORY = $WALLET_PATH ))) + SQLNET.WALLET_OVERRIDE = TRUE + SSL_CLIENT_AUTHENTICATION = FALSE + " >> $TNS_ADMIN/sqlnet.ora To use the wallet, use the wallet_location parameter. You may need to disable ssl verification with the ssl_server_dn_match parameter. Here a complete example of string connection: -```text -DATA_SOURCE_NAME=username/password@tcps://dbhost:port/service? -ssl_server_dn_match=false&wallet_location=wallet_path + DATA_SOURCE_NAME=oracle://username:password@server:port/service?ssl_server_dn_match=false&wallet_location=wallet_path + +For more details, have a look at the following location: https://github.com/iamseth/oracledb_exporter/issues/84 + +# Integration with Grafana + +An example Grafana dashboard is available [here](https://grafana.com/grafana/dashboards/3333-oracledb/). + +# Build + +## Docker build + +To build Ubuntu and Alpine image, run the following command: + + make docker + +You can also build only Ubuntu image: + + make ubuntu-image + +Or Alpine: + + make alpine-image + +## Building Binaries + +Run build: + +```sh + make go-build +``` + +will output binaries and archive inside the `dist` folder for the building operating system. + +## Import into your Golang Application + +The `oracledb_exporter` can also be imported into your Go based applications. The [Grafana Agent](https://github.com/grafana/agent/) uses this pattern to implement the [OracleDB integration](https://grafana.com/docs/grafana-cloud/data-configuration/integrations/integration-reference/integration-oracledb/). Feel free to modify the code to fit your application's use case. + +Here is a small snippet of an example usage of the exporter in code: + +```go + promLogConfig := &promlog.Config{} + // create your own config + logger := promlog.New(promLogConfig) + + // replace with your connection string + connectionString := "oracle://username:password@localhost:1521/orcl.localnet" + oeExporter, err := oe.NewExporter(logger, &oe.Config{ + DSN: connectionString, + MaxIdleConns: 0, + MaxOpenConns: 10, + QueryTimeout: 5, + }) + + if err != nil { + panic(err) + } + + metricChan := make(chan prometheus.Metric, len(oeExporter.DefaultMetrics().Metric)) + oeExporter.Collect(metricChan) + + // alternatively its possible to run scrapes on an interval + // and Collect() calls will only return updated data once + // that intervaled scrape is run + // please note this is a blocking call so feel free to run + // in a separate goroutine + // oeExporter.RunScheduledScrapes(context.Background(), time.Minute) + + for r := range metricChan { + // Write to the client of your choice + // or spin up a promhttp.Server to serve these metrics + r.Write(&dto.Metric{}) + } + ``` -### FAQ/Troubleshooting +# FAQ/Troubleshooting -#### Unable to convert current value to float (metric=par,metri...in.go:285 +## Unable to convert current value to float (metric=par,metri...in.go:285 Oracle is trying to send a value that we cannot convert to float. This could be anything like 'UNLIMITED' or 'UNDEFINED' or 'WHATEVER'. @@ -902,27 +475,41 @@ metricsdesc = { current_utilization= "Generic counter metric from v$resource_lim request="SELECT resource_name,current_utilization,CASE WHEN TRIM(limit_value) LIKE 'UNLIMITED' THEN '-1' ELSE TRIM(limit_value) END as limit_value FROM v$resource_limit" ``` -If the value of limit_value is 'UNLIMITED', the request send back the value -1. +If the value of limite_value is 'UNLIMITED', the request send back the value -1. You can increase the log level (`--log.level debug`) in order to get the statement generating this error. -#### Error scraping for wait_time +## error while loading shared libraries: libclntsh.so.xx.x: cannot open shared object file: No such file or directory + +This exporter use libs from Oracle in order to connect to Oracle Database. If you are running the binary version, you +must install the Oracle binaries somewhere on your machine and **you must install the good version number**. If the +error talk about the version 18.3, you **must** install 18.3 binary version. If it's 12.2, you **must** install 12.2. + +An alternative is to run this exporter using a Docker container. This way, you don't have to worry about Oracle binaries +version as they are embedded in the container. + +Here an example to run this exporter (to scrap metrics from system/oracle@//host:1521/service-or-sid) and bind the exporter port (9161) to the global machine: + +`docker run -it --rm -p 9161:9161 -e DATA_SOURCE_NAME=oracle://system/oracle@//host:1521/service-or-sid iamseth/oracledb_exporter:0.2.6a` + +## Error scraping for wait_time If you experience an error `Error scraping for wait_time: sql: Scan error on column index 1: converting driver.Value type string (",01") to a float64: invalid syntax source="main.go:144"` you may need to set the NLS_LANG variable. ```bash + export NLS_LANG=AMERICAN_AMERICA.WE8ISO8859P1 export DATA_SOURCE_NAME=system/oracle@myhost -/path/to/binary --log.level error --web.listen-address 9161 +/path/to/binary --log.level error --web.listen-address :9161 ``` If using Docker, set the same variable using the -e flag. -#### An Oracle instance generates trace files +## An Oracle instance generates a lot of trace files being monitored by exporter -An Oracle instance will generally generate a number of trace files alongside its alert log file. One trace file per scraping event. The trace file contains the following lines +As being said, Oracle instance may (and probably does) generate a lot of trace files alongside its alert log file, one trace file per scraping event. The trace file contains the following lines -```text +``` ... *** MODULE NAME:(prometheus_oracle_exporter-amd64@hostname) ... @@ -930,18 +517,27 @@ kgxgncin: clsssinit: CLSS init failed with status 3 kgxgncin: clsssinit: return status 3 (0 SKGXN not av) from CLSS ``` -The root cause is Oracle's reaction of querying ASM-related views without ASM used. The current workaround proposed is to setup a regular task to cleanup these trace files from the filesystem, as example +The root cause is Oracle's reaction of quering ASM-related views without ASM used. The current workaround proposed is to setup a regular task to cleanup these trace files from the filesystem, as example -```bash -find $ORACLE_BASE/diag/rdbms -name '*.tr[cm]' -mtime +14 -delete +``` +$ find $ORACLE_BASE/diag/rdbms -name '*.tr[cm]' -mtime +14 -delete ``` -## Data Storage +## TLS and basic authentication -By default the retention of Prometheus is configured to 15 days. On average, Prometheus uses only around 1-2 bytes per sample. Thus, to plan the capacity of a Prometheus server, you can use the rough formula: +Apache Exporter supports TLS and basic authentication. This enables better +control of the various HTTP endpoints. -```text -needed_disk_space = retention_time_seconds * ingested_samples_per_second * bytes_per_sample -``` +To use TLS and/or basic authentication, you need to pass a configuration file +using the `--web.config` parameter. The format of the file is described +[in the exporter-toolkit repository](https://github.com/prometheus/exporter-toolkit/blob/master/docs/web-configuration.md). + +Note that the TLS and basic authentication settings affect all HTTP endpoints: +/metrics for scraping, /probe for probing, and the web UI. + + +## Multi-target support + +This exporter supports the multi-target pattern. This allows running a single instance of this exporter for multiple Oracle targets. -Roughly, Oracle Database Monitor System has 100 samples every 1 minute, meaning 1.67 samples per second on average. You could base on your retention time to determine the capacity of the server. +To use the multi-target functionality, send a http request to the endpoint `/scrape?target=foo:1521` where target is set to the DSN of the Oracle instance to scrape metrics from. diff --git a/THIRD_PARTY_LICENSE.txt b/THIRD_PARTY_LICENSE.txt deleted file mode 100644 index 23ece68..0000000 --- a/THIRD_PARTY_LICENSE.txt +++ /dev/null @@ -1,581 +0,0 @@ -github.com/oracle/oracle-db-appdev-monitoring/oracle-db-monitoring-exporter --------- Copyrights - - ------------------------ Dependencies Grouped by License ------------ --------- Dependency -github.com/matttproud/golang_protobuf_extensions --------- Copyrights -Copyright 2012 Matt T. Proud (matt.proud@gmail.com) -Copyright 2013 Matt T. Proud -Copyright 2016 Matt T. Proud --------- Notices -Copyright 2012 Matt T. Proud (matt.proud@gmail.com) - - --------- Dependency -github.com/oracle/oci-go-sdk/v49 --------- Copyrights -Copyright (c) 2016, 2018, 2020, Oracle and/or its affiliates. All rights reserved. -Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved. -Copyright (c) 2016, 2018, 2020, Oracle and/or its affiliates. -Copyright (c) 2016, 2018, 2021, Oracle and/or its affiliates. All rights reserved. -Copyright Β© 2012-2020 Mat Ryer, Tyler Bunnell and contributors. -Copyright Β© 2013 The Go Authors. All rights reserved. -Copyright (c) 2012-2020 Mat Ryer, Tyler Bunnell and contributors. -Copyright (c) 2013 The Go Authors. All rights reserved. --------- Notices -Copyright (c) 2016, 2018, 2020, Oracle and/or its affiliates. - --------- Dependency -github.com/prometheus/client_golang --------- Copyrights -Copyright 2018 The Prometheus Authors -Copyright 2012-2015 The Prometheus Authors -Copyright 2013-2015 Blake Mizerany, BjΓΆrn Rabenstein -Copyright 2010 The Go Authors -Copyright 2013 Matt T. Proud -Copyright 2015 The Prometheus Authors -Copyright 2017 The Prometheus Authors -Copyright 2019 The Prometheus Authors -Copyright 2014 The Prometheus Authors -Copyright 2016 The Prometheus Authors -Copyright (c) 2013, The Prometheus Authors --------- Notices -Prometheus instrumentation library for Go applications -Copyright 2012-2015 The Prometheus Authors - -This product includes software developed at -SoundCloud Ltd. (http://soundcloud.com/). - - -The following components are included in this product: - -perks - a fork of https://github.com/bmizerany/perks -https://github.com/beorn7/perks -Copyright 2013-2015 Blake Mizerany, BjΓΆrn Rabenstein -See https://github.com/beorn7/perks/blob/master/README.md for license details. - -Go support for Protocol Buffers - Google's data interchange format -http://github.com/golang/protobuf/ -Copyright 2010 The Go Authors -See source code for license details. - -Support for streaming Protocol Buffer messages for the Go language (golang). -https://github.com/matttproud/golang_protobuf_extensions -Copyright 2013 Matt T. Proud -Licensed under the Apache License, Version 2.0 - - --------- Dependency -github.com/prometheus/client_model --------- Copyrights -Copyright 2013 Prometheus Team -Copyright 2012-2015 The Prometheus Authors --------- Notices -Data model artifacts for Prometheus. -Copyright 2012-2015 The Prometheus Authors - -This product includes software developed at -SoundCloud Ltd. (http://soundcloud.com/). - - --------- Dependency -github.com/prometheus/common --------- Copyrights -Copyright 2018 The Prometheus Authors -Copyright 2015 The Prometheus Authors -Copyright 2016 The Prometheus Authors -Copyright 2014 The Prometheus Authors -Copyright (c) 2011, Open Knowledge Foundation Ltd. -Copyright 2013 The Prometheus Authors -Copyright 2019 The Prometheus Authors -Copyright 2017 The Prometheus Authors --------- Notices -Common libraries shared by Prometheus Go components. -Copyright 2015 The Prometheus Authors - -This product includes software developed at -SoundCloud Ltd. (http://soundcloud.com/). - - --------- Dependency -github.com/prometheus/procfs --------- Copyrights -Copyright 2018 The Prometheus Authors -Copyright 2014-2015 The Prometheus Authors -Copyright 2017 The Prometheus Authors -Copyright 2014 Prometheus Team -Copyright 2019 The Prometheus Authors -Copyright 2017 Prometheus Team --------- Notices -procfs provides functions to retrieve system, kernel and process -metrics from the pseudo-filesystem proc. - -Copyright 2014-2015 The Prometheus Authors - -This product includes software developed at -SoundCloud Ltd. (http://soundcloud.com/). - - --------- Dependency -gopkg.in/yaml.v2 --------- Copyrights -Copyright (c) 2006 Kirill Simonov -Copyright 2011-2016 Canonical Ltd. --------- Notices -Copyright 2011-2016 Canonical Ltd. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. - --------- Dependencies Summary -github.com/matttproud/golang_protobuf_extensions -github.com/prometheus/client_golang -github.com/prometheus/client_model -github.com/prometheus/common -github.com/prometheus/procfs -gopkg.in/yaml.v2 - --------- License used by Dependencies -SPDX:Apache-2.0 - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - --------- Dependencies Summary -github.com/oracle/oci-go-sdk/v49 --------- License used by Dependencies -Copyright (c) 2016, 2018, 2020, Oracle and/or its affiliates. -All rights reserved. -This software is dual-licensed to you under the Universal Permissive License (UPL) 1.0 -as shown at https://oss.oracle.com/licenses/upl -or Apache License 2.0 as shown at http://www.apache.org/licenses/LICENSE-2.0. -You may choose either license. - -SPDX:The Universal Permissive License (UPL), Version 1.0 - - Subject to the condition set forth below, permission is hereby granted to any - person obtaining a copy of this software, associated documentation and/or data - (collectively the "Software"), free of charge and under any and all copyright - rights in the Software, and any and all patent rights owned or freely - licensable by each licensor hereunder covering either (i) the unmodified - Software as contributed to or provided by such licensor, or (ii) the Larger - Works (as defined below), to deal in both - - (a) the Software, and - (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if - one is included with the Software (each a "Larger Work" to which the Software - is contributed by such licensors), - - without restriction, including without limitation the rights to copy, create - derivative works of, display, perform, and distribute the Software and make, - use, sell, offer for sale, import, export, have made, and have sold the - Software and the Larger Work(s), and to sublicense the foregoing rights on - either these or other terms. - - This license is subject to the following condition: - The above copyright notice and either this complete permission notice or at - a minimum a reference to the UPL must be included in all copies or - substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - SOFTWARE. - - SPDX:Apache-2.0 - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - -See above for text - ------------------------ Dependencies Grouped by License ------------ --------- Dependency -github.com/alecthomas/template --------- Copyrights -Copyright (c) 2012 The Go Authors. All rights reserved. -Copyright 2011 The Go Authors. All rights reserved. -Copyright 2012 The Go Authors. All rights reserved. - --------- Dependency -github.com/golang/protobuf --------- Copyrights -Copyright 2010 The Go Authors. All rights reserved. -Copyright 2010 The Go Authors. -Copyright 2016 The Go Authors. All rights reserved. -Copyright 2015 The Go Authors. All rights reserved. -Copyright 2011 The Go Authors. All rights reserved. -Copyright 2018 The Go Authors. All rights reserved. -Copyright 2017 The Go Authors. All rights reserved. -Copyright 2014 The Go Authors. All rights reserved. -Copyright 2012 The Go Authors. All rights reserved. -Copyright 2013 The Go Authors. All rights reserved. - --------- Dependency -golang.org/x/crypto --------- Copyrights -Copyright (c) 2009 The Go Authors. All rights reserved. -Copyright 2015 The Go Authors. All rights reserved. -Copyright 2016 The Go Authors. All rights reserved. -Copyright 2017 The Go Authors. All rights reserved. -Copyright 2018 The Go Authors. All rights reserved. -Copyright 2011 The Go Authors. All rights reserved. -Copyright 2010 The Go Authors. All rights reserved. -Copyright 2012 The Go Authors. All rights reserved. -Copyright 2013 The Go Authors. All rights reserved. -Copyright 2014 The Go Authors. All rights reserved. -Copyright 2009 The Go Authors. All rights reserved. --------- Patents -Additional IP Rights Grant (Patents) - -"This implementation" means the copyrightable works distributed by -Google as part of the Go project. - -Google hereby grants to You a perpetual, worldwide, non-exclusive, -no-charge, royalty-free, irrevocable (except as stated in this section) -patent license to make, have made, use, offer to sell, sell, import, -transfer and otherwise run, modify and propagate the contents of this -implementation of Go, where such license applies only to those patent -claims, both currently owned or controlled by Google and acquired in -the future, licensable by Google that are necessarily infringed by this -implementation of Go. This grant does not include claims that would be -infringed only as a consequence of further modification of this -implementation. If you or your agent or exclusive licensee institute or -order or agree to the institution of patent litigation against any -entity (including a cross-claim or counterclaim in a lawsuit) alleging -that this implementation of Go or any code incorporated within this -implementation of Go constitutes direct or contributory patent -infringement, or inducement of patent infringement, then any patent -rights granted to you under this License for this implementation of Go -shall terminate as of the date such litigation is filed. - - --------- Dependency -golang.org/x/sys --------- Copyrights -Copyright (c) 2009 The Go Authors. All rights reserved. -Copyright 2019 The Go Authors. All rights reserved. -Copyright 2018 The Go Authors. All rights reserved. -Copyright 2012 The Go Authors. All rights reserved. -Copyright 2011 The Go Authors. All rights reserved. -Copyright 2015 The Go Authors. All rights reserved. -Copyright 2009 The Go Authors. All rights reserved. -Copyright 2013 The Go Authors. All rights reserved. -Copyright 2016 The Go Authors. All rights reserved. -Copyright 2017 The Go Authors. All rights reserved. -Copyright 2010 The Go Authors. All rights reserved. -Copyright 2014 The Go Authors. All rights reserved. -Copyright 2009,2010 The Go Authors. All rights reserved. -Copyright 2017 The Go Authors. All right reserved. --------- Patents -Additional IP Rights Grant (Patents) - -"This implementation" means the copyrightable works distributed by -Google as part of the Go project. - -Google hereby grants to You a perpetual, worldwide, non-exclusive, -no-charge, royalty-free, irrevocable (except as stated in this section) -patent license to make, have made, use, offer to sell, sell, import, -transfer and otherwise run, modify and propagate the contents of this -implementation of Go, where such license applies only to those patent -claims, both currently owned or controlled by Google and acquired in -the future, licensable by Google that are necessarily infringed by this -implementation of Go. This grant does not include claims that would be -infringed only as a consequence of further modification of this -implementation. If you or your agent or exclusive licensee institute or -order or agree to the institution of patent litigation against any -entity (including a cross-claim or counterclaim in a lawsuit) alleging -that this implementation of Go or any code incorporated within this -implementation of Go constitutes direct or contributory patent -infringement, or inducement of patent infringement, then any patent -rights granted to you under this License for this implementation of Go -shall terminate as of the date such litigation is filed. - - --------- Dependencies Summary -github.com/alecthomas/template -github.com/golang/protobuf -golang.org/x/crypto -golang.org/x/sys - --------- License used by Dependencies -SPDX:BSD-3-Clause--modified-by-Google -Redistribution and use in source and binary forms, with -or without modification, are permitted provided that the following conditions -are met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ------------------------ Dependencies Grouped by License ------------ --------- Dependency -github.com/BurntSushi/toml --------- Copyrights -Copyright (c) 2013 TOML authors -Copyright 2010 The Go Authors. All rights reserved. - --------- Dependency -github.com/alecthomas/units --------- Copyrights -Copyright (C) 2014 Alec Thomas - --------- Dependency -github.com/beorn7/perks --------- Copyrights -Copyright (C) 2013 Blake Mizerany - --------- Dependency -github.com/mattn/go-oci8 --------- Copyrights -Copyright (c) 2014-2018 Yasuhiro Matsumoto, http://mattn.kaoriya.net - --------- Dependency -github.com/sirupsen/logrus --------- Copyrights -Copyright (c) 2014 Simon Eskildsen -Copyright (c) 2012 Miki Tebeka . - --------- Dependency -github.com/sony/gobreaker --------- Copyrights -Copyright 2015 Sony Corporation - --------- Dependency -gopkg.in/alecthomas/kingpin.v2 --------- Copyrights -Copyright (C) 2014 Alec Thomas - --------- Dependencies Summary -github.com/BurntSushi/toml -github.com/alecthomas/units -github.com/beorn7/perks -github.com/mattn/go-oci8 -github.com/sirupsen/logrus -github.com/sony/gobreaker -gopkg.in/alecthomas/kingpin.v2 - --------- License used by Dependencies -SPDX:MIT -Permission is hereby granted, free of charge, to any person -obtaining a copy of this software and associated documentation files -(the "Software"), to deal in the Software without restriction, including without -limitation the rights to use, copy, modify, merge, publish, distribute, -sublicense, and/or sell copies of the Software, and to permit persons to whom -the Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -ATTRIBUTION-HELPER-GENERATED: -License file based on go.mod with md5 sum: f1316bb9b092275023d2bad0df4ec426 diff --git a/THIRD_PARTY_LICENSES_DEV.txt b/THIRD_PARTY_LICENSES_DEV.txt deleted file mode 100644 index 4217f11..0000000 --- a/THIRD_PARTY_LICENSES_DEV.txt +++ /dev/null @@ -1,24 +0,0 @@ -The following modules are licensed by third parties solely under the identified -terms and conditions: -oracledb_exporter - -The MIT License (MIT) - -Copyright (c) 2016 Seth Miller - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, -INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A -PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/collector/collector.go b/collector/collector.go new file mode 100644 index 0000000..ae1dadd --- /dev/null +++ b/collector/collector.go @@ -0,0 +1,603 @@ +package collector + +import ( + "bytes" + "context" + "crypto/sha256" + "database/sql" + "errors" + "fmt" + "hash" + "io" + "os" + "strconv" + "strings" + "sync" + "time" + + "github.com/BurntSushi/toml" + "github.com/go-kit/log" + "github.com/go-kit/log/level" + "github.com/godror/godror" + "github.com/prometheus/client_golang/prometheus" +) + +// Exporter collects Oracle DB metrics. It implements prometheus.Collector. +type Exporter struct { + config *Config + mu *sync.Mutex + metricsToScrape Metrics + scrapeInterval *time.Duration + user string + password string + connectString string + duration, error prometheus.Gauge + totalScrapes prometheus.Counter + scrapeErrors *prometheus.CounterVec + scrapeResults []prometheus.Metric + up prometheus.Gauge + db *sql.DB + logger log.Logger +} + +// Config is the configuration of the exporter +type Config struct { + User string + Password string + ConnectString string + MaxIdleConns int + MaxOpenConns int + CustomMetrics string + QueryTimeout int + DefaultMetricsFile string +} + +// CreateDefaultConfig returns the default configuration of the Exporter +// it is to be of note that the DNS will be empty when +func CreateDefaultConfig() *Config { + return &Config{ + MaxIdleConns: 0, + MaxOpenConns: 10, + CustomMetrics: "", + QueryTimeout: 5, + DefaultMetricsFile: "", + } +} + +// Metric is an object description +type Metric struct { + Context string + Labels []string + MetricsDesc map[string]string + MetricsType map[string]string + MetricsBuckets map[string]map[string]string + FieldToAppend string + Request string + IgnoreZeroResult bool +} + +// Metrics is a container structure for prometheus metrics +type Metrics struct { + Metric []Metric +} + +var ( + additionalMetrics Metrics + hashMap = make(map[int][]byte) + namespace = "oracledb" + exporterName = "exporter" +) + +func maskDsn(dsn string) string { + parts := strings.Split(dsn, "@") + if len(parts) > 1 { + maskedURL := "***@" + parts[1] + return maskedURL + } + return dsn +} + +// NewExporter creates a new Exporter instance +func NewExporter(logger log.Logger, cfg *Config) (*Exporter, error) { + e := &Exporter{ + mu: &sync.Mutex{}, + user: cfg.User, + password: cfg.Password, + connectString: cfg.ConnectString, + duration: prometheus.NewGauge(prometheus.GaugeOpts{ + Namespace: namespace, + Subsystem: exporterName, + Name: "last_scrape_duration_seconds", + Help: "Duration of the last scrape of metrics from Oracle DB.", + }), + totalScrapes: prometheus.NewCounter(prometheus.CounterOpts{ + Namespace: namespace, + Subsystem: exporterName, + Name: "scrapes_total", + Help: "Total number of times Oracle DB was scraped for metrics.", + }), + scrapeErrors: prometheus.NewCounterVec(prometheus.CounterOpts{ + Namespace: namespace, + Subsystem: exporterName, + Name: "scrape_errors_total", + Help: "Total number of times an error occured scraping a Oracle database.", + }, []string{"collector"}), + error: prometheus.NewGauge(prometheus.GaugeOpts{ + Namespace: namespace, + Subsystem: exporterName, + Name: "last_scrape_error", + Help: "Whether the last scrape of metrics from Oracle DB resulted in an error (1 for error, 0 for success).", + }), + up: prometheus.NewGauge(prometheus.GaugeOpts{ + Namespace: namespace, + Name: "up", + Help: "Whether the Oracle database server is up.", + }), + logger: logger, + config: cfg, + } + e.metricsToScrape = e.DefaultMetrics() + err := e.connect() + return e, err +} + +// Describe describes all the metrics exported by the Oracle DB exporter. +func (e *Exporter) Describe(ch chan<- *prometheus.Desc) { + // We cannot know in advance what metrics the exporter will generate + // So we use the poor man's describe method: Run a collect + // and send the descriptors of all the collected metrics. The problem + // here is that we need to connect to the Oracle DB. If it is currently + // unavailable, the descriptors will be incomplete. Since this is a + // stand-alone exporter and not used as a library within other code + // implementing additional metrics, the worst that can happen is that we + // don't detect inconsistent metrics created by this exporter + // itself. Also, a change in the monitored Oracle instance may change the + // exported metrics during the runtime of the exporter. + + metricCh := make(chan prometheus.Metric) + doneCh := make(chan struct{}) + + go func() { + for m := range metricCh { + ch <- m.Desc() + } + close(doneCh) + }() + + e.Collect(metricCh) + close(metricCh) + <-doneCh +} + +// Collect implements prometheus.Collector. +func (e *Exporter) Collect(ch chan<- prometheus.Metric) { + // they are running scheduled scrapes we should only scrape new data + // on the interval + if e.scrapeInterval != nil && *e.scrapeInterval != 0 { + // read access must be checked + e.mu.Lock() + for _, r := range e.scrapeResults { + ch <- r + } + e.mu.Unlock() + return + } + + // otherwise do a normal scrape per request + e.mu.Lock() // ensure no simultaneous scrapes + defer e.mu.Unlock() + e.scrape(ch) + ch <- e.duration + ch <- e.totalScrapes + ch <- e.error + e.scrapeErrors.Collect(ch) + ch <- e.up +} + +// RunScheduledScrapes is only relevant for users of this package that want to set the scrape on a timer +// rather than letting it be per Collect call +func (e *Exporter) RunScheduledScrapes(ctx context.Context, si time.Duration) { + e.scrapeInterval = &si + ticker := time.NewTicker(si) + defer ticker.Stop() + + for { + select { + case <-ticker.C: + e.mu.Lock() // ensure no simultaneous scrapes + e.scheduledScrape() + e.mu.Unlock() + case <-ctx.Done(): + return + } + } +} + +func (e *Exporter) scheduledScrape() { + metricCh := make(chan prometheus.Metric, 5) + + wg := &sync.WaitGroup{} + wg.Add(1) + go func() { + defer wg.Done() + e.scrapeResults = []prometheus.Metric{} + for { + scrapeResult, more := <-metricCh + if more { + e.scrapeResults = append(e.scrapeResults, scrapeResult) + continue + } + return + } + }() + e.scrape(metricCh) + + // report metadata metrics + metricCh <- e.duration + metricCh <- e.totalScrapes + metricCh <- e.error + e.scrapeErrors.Collect(metricCh) + metricCh <- e.up + + close(metricCh) + wg.Wait() +} + +func (e *Exporter) scrape(ch chan<- prometheus.Metric) { + e.totalScrapes.Inc() + var err error + defer func(begun time.Time) { + e.duration.Set(time.Since(begun).Seconds()) + if err == nil { + e.error.Set(0) + } else { + e.error.Set(1) + } + }(time.Now()) + + if err = e.db.Ping(); err != nil { + if strings.Contains(err.Error(), "sql: database is closed") { + level.Info(e.logger).Log("Reconnecting to DB") + err = e.connect() + if err != nil { + level.Error(e.logger).Log("Error reconnecting to DB", err) + } + } + } + + if err = e.db.Ping(); err != nil { + level.Error(e.logger).Log("Error pinging oracle:", err) + e.up.Set(0) + return + } + + level.Debug(e.logger).Log("Successfully pinged Oracle database: ", maskDsn(e.connectString)) + e.up.Set(1) + + if e.checkIfMetricsChanged() { + e.reloadMetrics() + } + + wg := sync.WaitGroup{} + + for _, metric := range e.metricsToScrape.Metric { + wg.Add(1) + metric := metric //https://golang.org/doc/faq#closures_and_goroutines + + go func() { + defer wg.Done() + + level.Debug(e.logger).Log("About to scrape metric: ") + level.Debug(e.logger).Log("- Metric MetricsDesc: ", metric.MetricsDesc) + level.Debug(e.logger).Log("- Metric Context: ", metric.Context) + level.Debug(e.logger).Log("- Metric MetricsType: ", metric.MetricsType) + level.Debug(e.logger).Log("- Metric MetricsBuckets: ", metric.MetricsBuckets, "(Ignored unless Histogram type)") + level.Debug(e.logger).Log("- Metric Labels: ", metric.Labels) + level.Debug(e.logger).Log("- Metric FieldToAppend: ", metric.FieldToAppend) + level.Debug(e.logger).Log("- Metric IgnoreZeroResult: ", metric.IgnoreZeroResult) + level.Debug(e.logger).Log("- Metric Request: ", metric.Request) + + if len(metric.Request) == 0 { + level.Error(e.logger).Log("Error scraping for ", metric.MetricsDesc, ". Did you forget to define request in your toml file?") + return + } + + if len(metric.MetricsDesc) == 0 { + level.Error(e.logger).Log("Error scraping for query", metric.Request, ". Did you forget to define metricsdesc in your toml file?") + return + } + + for column, metricType := range metric.MetricsType { + if metricType == "histogram" { + _, ok := metric.MetricsBuckets[column] + if !ok { + level.Error(e.logger).Log("Unable to find MetricsBuckets configuration key for metric. (metric=" + column + ")") + return + } + } + } + + scrapeStart := time.Now() + if err = e.ScrapeMetric(e.db, ch, metric); err != nil { + level.Error(e.logger).Log("Error scraping for", metric.Context, "_", metric.MetricsDesc, time.Since(scrapeStart), ":", err) + e.scrapeErrors.WithLabelValues(metric.Context).Inc() + } else { + level.Debug(e.logger).Log("Successfully scraped metric: ", metric.Context, metric.MetricsDesc, time.Since(scrapeStart)) + } + }() + } + wg.Wait() +} + +func (e *Exporter) connect() error { + level.Debug(e.logger).Log("Launching connection: ", maskDsn(e.connectString)) + + var P godror.ConnectionParams + P.Username, P.Password, P.ConnectString = e.user, godror.NewPassword(e.password), e.connectString + + db := sql.OpenDB(godror.NewConnector(P)) + // if err != nil { + // level.Error(e.logger).Log("Error while connecting to", e.dsn) + // return err + // } + level.Debug(e.logger).Log("set max idle connections to ", e.config.MaxIdleConns) + db.SetMaxIdleConns(e.config.MaxIdleConns) + level.Debug(e.logger).Log("set max open connections to ", e.config.MaxOpenConns) + db.SetMaxOpenConns(e.config.MaxOpenConns) + level.Debug(e.logger).Log("Successfully connected to: ", maskDsn(e.connectString)) + e.db = db + return nil +} + +func (e *Exporter) checkIfMetricsChanged() bool { + for i, _customMetrics := range strings.Split(e.config.CustomMetrics, ",") { + if len(_customMetrics) == 0 { + continue + } + level.Debug(e.logger).Log("Checking modifications in following metrics definition file:", _customMetrics) + h := sha256.New() + if err := hashFile(h, _customMetrics); err != nil { + level.Error(e.logger).Log("Unable to get file hash", err) + return false + } + // If any of files has been changed reload metrics + if !bytes.Equal(hashMap[i], h.Sum(nil)) { + level.Info(e.logger).Log(_customMetrics, "has been changed. Reloading metrics...") + hashMap[i] = h.Sum(nil) + return true + } + } + return false +} + +func hashFile(h hash.Hash, fn string) error { + f, err := os.Open(fn) + if err != nil { + return err + } + defer f.Close() + if _, err := io.Copy(h, f); err != nil { + return err + } + return nil +} + +func (e *Exporter) reloadMetrics() { + // Truncate metricsToScrape + e.metricsToScrape.Metric = []Metric{} + + // Load default metrics + defaultMetrics := e.DefaultMetrics() + e.metricsToScrape.Metric = defaultMetrics.Metric + + // If custom metrics, load it + if strings.Compare(e.config.CustomMetrics, "") != 0 { + for _, _customMetrics := range strings.Split(e.config.CustomMetrics, ",") { + if _, err := toml.DecodeFile(_customMetrics, &additionalMetrics); err != nil { + level.Error(e.logger).Log(err) + panic(errors.New("Error while loading " + _customMetrics)) + } else { + level.Info(e.logger).Log("Successfully loaded custom metrics from: " + _customMetrics) + } + e.metricsToScrape.Metric = append(e.metricsToScrape.Metric, additionalMetrics.Metric...) + } + } else { + level.Debug(e.logger).Log("No custom metrics defined.") + } +} + +// ScrapeMetric is an interface method to call scrapeGenericValues using Metric struct values +func (e *Exporter) ScrapeMetric(db *sql.DB, ch chan<- prometheus.Metric, metricDefinition Metric) error { + level.Debug(e.logger).Log("Calling function ScrapeGenericValues()") + return e.scrapeGenericValues(db, ch, metricDefinition.Context, metricDefinition.Labels, + metricDefinition.MetricsDesc, metricDefinition.MetricsType, metricDefinition.MetricsBuckets, + metricDefinition.FieldToAppend, metricDefinition.IgnoreZeroResult, + metricDefinition.Request) +} + +// generic method for retrieving metrics. +func (e *Exporter) scrapeGenericValues(db *sql.DB, ch chan<- prometheus.Metric, context string, labels []string, + metricsDesc map[string]string, metricsType map[string]string, metricsBuckets map[string]map[string]string, fieldToAppend string, ignoreZeroResult bool, request string) error { + metricsCount := 0 + genericParser := func(row map[string]string) error { + // Construct labels value + labelsValues := []string{} + for _, label := range labels { + labelsValues = append(labelsValues, row[label]) + } + // Construct Prometheus values to sent back + for metric, metricHelp := range metricsDesc { + value, err := strconv.ParseFloat(strings.TrimSpace(row[metric]), 64) + // If not a float, skip current metric + if err != nil { + level.Error(e.logger).Log("Unable to convert current value to float (metric=" + metric + + ",metricHelp=" + metricHelp + ",value=<" + row[metric] + ">)") + continue + } + level.Debug(e.logger).Log("Query result looks like: ", value) + // If metric do not use a field content in metric's name + if strings.Compare(fieldToAppend, "") == 0 { + desc := prometheus.NewDesc( + prometheus.BuildFQName(namespace, context, metric), + metricHelp, + labels, nil, + ) + if metricsType[strings.ToLower(metric)] == "histogram" { + count, err := strconv.ParseUint(strings.TrimSpace(row["count"]), 10, 64) + if err != nil { + level.Error(e.logger).Log("Unable to convert count value to int (metric=" + metric + + ",metricHelp=" + metricHelp + ",value=<" + row["count"] + ">)") + continue + } + buckets := make(map[float64]uint64) + for field, le := range metricsBuckets[metric] { + lelimit, err := strconv.ParseFloat(strings.TrimSpace(le), 64) + if err != nil { + level.Error(e.logger).Log("Unable to convert bucket limit value to float (metric=" + metric + + ",metricHelp=" + metricHelp + ",bucketlimit=<" + le + ">)") + continue + } + counter, err := strconv.ParseUint(strings.TrimSpace(row[field]), 10, 64) + if err != nil { + level.Error(e.logger).Log("Unable to convert ", field, " value to int (metric="+metric+ + ",metricHelp="+metricHelp+",value=<"+row[field]+">)") + continue + } + buckets[lelimit] = counter + } + ch <- prometheus.MustNewConstHistogram(desc, count, value, buckets, labelsValues...) + } else { + ch <- prometheus.MustNewConstMetric(desc, getMetricType(metric, metricsType), value, labelsValues...) + } + // If no labels, use metric name + } else { + desc := prometheus.NewDesc( + prometheus.BuildFQName(namespace, context, cleanName(row[fieldToAppend])), + metricHelp, + nil, nil, + ) + if metricsType[strings.ToLower(metric)] == "histogram" { + count, err := strconv.ParseUint(strings.TrimSpace(row["count"]), 10, 64) + if err != nil { + level.Error(e.logger).Log("Unable to convert count value to int (metric=" + metric + + ",metricHelp=" + metricHelp + ",value=<" + row["count"] + ">)") + continue + } + buckets := make(map[float64]uint64) + for field, le := range metricsBuckets[metric] { + lelimit, err := strconv.ParseFloat(strings.TrimSpace(le), 64) + if err != nil { + level.Error(e.logger).Log("Unable to convert bucket limit value to float (metric=" + metric + + ",metricHelp=" + metricHelp + ",bucketlimit=<" + le + ">)") + continue + } + counter, err := strconv.ParseUint(strings.TrimSpace(row[field]), 10, 64) + if err != nil { + level.Error(e.logger).Log("Unable to convert ", field, " value to int (metric="+metric+ + ",metricHelp="+metricHelp+",value=<"+row[field]+">)") + continue + } + buckets[lelimit] = counter + } + ch <- prometheus.MustNewConstHistogram(desc, count, value, buckets) + } else { + ch <- prometheus.MustNewConstMetric(desc, getMetricType(metric, metricsType), value) + } + } + metricsCount++ + } + return nil + } + level.Debug(e.logger).Log("Calling function GeneratePrometheusMetrics()") + err := e.generatePrometheusMetrics(db, genericParser, request) + level.Debug(e.logger).Log("ScrapeGenericValues() - metricsCount: ", metricsCount) + if err != nil { + return err + } + if !ignoreZeroResult && metricsCount == 0 { + return errors.New("No metrics found while parsing") + } + return err +} + +// inspired by https://kylewbanks.com/blog/query-result-to-map-in-golang +// Parse SQL result and call parsing function to each row +func (e *Exporter) generatePrometheusMetrics(db *sql.DB, parse func(row map[string]string) error, query string) error { + ctx, cancel := context.WithTimeout(context.Background(), time.Duration(e.config.QueryTimeout)*time.Second) + defer cancel() + rows, err := db.QueryContext(ctx, query) + + if ctx.Err() == context.DeadlineExceeded { + return errors.New("Oracle query timed out") + } + + if err != nil { + return err + } + cols, err := rows.Columns() + defer rows.Close() + + for rows.Next() { + // Create a slice of interface{}'s to represent each column, + // and a second slice to contain pointers to each item in the columns slice. + 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 { + return err + } + + // 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.Sprintf("%v", *val) + } + // Call function to parse row + if err := parse(m); err != nil { + return err + } + } + return nil +} + +func getMetricType(metricType string, metricsType map[string]string) prometheus.ValueType { + var strToPromType = map[string]prometheus.ValueType{ + "gauge": prometheus.GaugeValue, + "counter": prometheus.CounterValue, + "histogram": prometheus.UntypedValue, + } + + strType, ok := metricsType[strings.ToLower(metricType)] + if !ok { + return prometheus.GaugeValue + } + valueType, ok := strToPromType[strings.ToLower(strType)] + if !ok { + panic(errors.New("Error while getting prometheus type " + strings.ToLower(strType))) + } + return valueType +} + +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.ToLower(s) + return s +} + +func (e *Exporter) logError(s string) { + _ = level.Error(e.logger).Log(s) +} + +func (e *Exporter) logDebug(s string) { + _ = level.Debug(e.logger).Log(s) +} diff --git a/collector/default_metrics.go b/collector/default_metrics.go new file mode 100644 index 0000000..237f5b9 --- /dev/null +++ b/collector/default_metrics.go @@ -0,0 +1,91 @@ +package collector + +import ( + "errors" + "fmt" + "path/filepath" + + "github.com/BurntSushi/toml" + "github.com/go-kit/log/level" +) + +// needs the const if imported, cannot os.ReadFile in this case +const defaultMetricsConst = ` +[[metric]] +context = "sessions" +labels = [ "status", "type" ] +metricsdesc = { value= "Gauge metric with count of sessions by status and type." } +request = "SELECT status, type, COUNT(*) as value FROM v$session GROUP BY status, type" + +[[metric]] +context = "resource" +labels = [ "resource_name" ] +metricsdesc = { current_utilization= "Generic counter metric from v$resource_limit view in Oracle (current value).", limit_value="Generic counter metric from v$resource_limit view in Oracle (UNLIMITED: -1)." } +request="SELECT resource_name,current_utilization,CASE WHEN TRIM(limit_value) LIKE 'UNLIMITED' THEN '-1' ELSE TRIM(limit_value) END as limit_value FROM v$resource_limit" + +[[metric]] +context = "asm_diskgroup" +labels = [ "name" ] +metricsdesc = { total = "Total size of ASM disk group.", free = "Free space available on ASM disk group." } +request = "SELECT name,total_mb*1024*1024 as total,free_mb*1024*1024 as free FROM v$asm_diskgroup_stat where exists (select 1 from v$datafile where name like '+%')" +ignorezeroresult = true + +[[metric]] +context = "activity" +metricsdesc = { value="Generic counter metric from v$sysstat view in Oracle." } +fieldtoappend = "name" +request = "SELECT name, value FROM v$sysstat WHERE name IN ('parse count (total)', 'execute count', 'user commits', 'user rollbacks')" + +[[metric]] +context = "process" +metricsdesc = { count="Gauge metric with count of processes." } +request = "SELECT COUNT(*) as count FROM v$process" + +[[metric]] +context = "wait_time" +metricsdesc = { value="Generic counter metric from v$waitclassmetric view in Oracle." } +fieldtoappend= "wait_class" +request = ''' +SELECT + n.wait_class as WAIT_CLASS, + round(m.time_waited/m.INTSIZE_CSEC,3) as VALUE +FROM + v$waitclassmetric m, v$system_wait_class n +WHERE + m.wait_class_id=n.wait_class_id AND n.wait_class != 'Idle' +''' + +[[metric]] +context = "tablespace" +labels = [ "tablespace", "type" ] +metricsdesc = { bytes = "Generic counter metric of tablespaces bytes in Oracle.", max_bytes = "Generic counter metric of tablespaces max bytes in Oracle.", free = "Generic counter metric of tablespaces free bytes in Oracle.", used_percent = "Gauge metric showing as a percentage of how much of the tablespace has been used." } +request = ''' +SELECT + dt.tablespace_name as tablespace, + dt.contents as type, + dt.block_size * dtum.used_space as bytes, + dt.block_size * dtum.tablespace_size as max_bytes, + dt.block_size * (dtum.tablespace_size - dtum.used_space) as free, + dtum.used_percent +FROM dba_tablespace_usage_metrics dtum, dba_tablespaces dt +WHERE dtum.tablespace_name = dt.tablespace_name +ORDER by tablespace +''' +` + +// DefaultMetrics is a somewhat hacky way to load the default metrics +func (e *Exporter) DefaultMetrics() Metrics { + var metricsToScrape Metrics + if e.config.DefaultMetricsFile != "" { + if _, err := toml.DecodeFile(filepath.Clean(e.config.DefaultMetricsFile), &metricsToScrape); err != nil { + level.Error(e.logger).Log(fmt.Sprintf("there was an issue while loading specified default metrics file at: "+e.config.DefaultMetricsFile+", proceeding to run with default metrics."), err) + } + return metricsToScrape + } + + if _, err := toml.Decode(defaultMetricsConst, &metricsToScrape); err != nil { + level.Error(e.logger).Log(err) + panic(errors.New("Error while loading " + defaultMetricsConst)) + } + return metricsToScrape +} diff --git a/custom-metrics-example/custom-metrics.toml b/custom-metrics-example/custom-metrics.toml new file mode 100644 index 0000000..bdc5e03 --- /dev/null +++ b/custom-metrics-example/custom-metrics.toml @@ -0,0 +1,45 @@ +[[metric]] +context = "slow_queries" +metricsdesc = { p95_time_usecs= "Gauge metric with percentile 95 of elapsed time.", p99_time_usecs= "Gauge metric with percentile 99 of elapsed time." } +request = "select percentile_disc(0.95) within group (order by elapsed_time) as p95_time_usecs, percentile_disc(0.99) within group (order by elapsed_time) as p99_time_usecs from v$sql where last_active_time >= sysdate - 5/(24*60)" + +[[metric]] +context = "big_queries" +metricsdesc = { p95_rows= "Gauge metric with percentile 95 of returned rows.", p99_rows= "Gauge metric with percentile 99 of returned rows." } +request = "select percentile_disc(0.95) within group (order by rownum) as p95_rows, percentile_disc(0.99) within group (order by rownum) as p99_rows from v$sql where last_active_time >= sysdate - 5/(24*60)" + +[[metric]] +context = "size_user_segments_top100" +metricsdesc = {table_bytes="Gauge metric with the size of the tables in user segments."} +labels = ["segment_name"] +request = "select * from (select segment_name,sum(bytes) as table_bytes from user_segments where segment_type='TABLE' group by segment_name) order by table_bytes DESC FETCH NEXT 100 ROWS ONLY" + +[[metric]] +context = "size_user_segments_top100" +metricsdesc = {table_partition_bytes="Gauge metric with the size of the table partition in user segments."} +labels = ["segment_name"] +request = "select * from (select segment_name,sum(bytes) as table_partition_bytes from user_segments where segment_type='TABLE PARTITION' group by segment_name) order by table_partition_bytes DESC FETCH NEXT 100 ROWS ONLY" + +[[metric]] +context = "size_user_segments_top100" +metricsdesc = {cluster_bytes="Gauge metric with the size of the cluster in user segments."} +labels = ["segment_name"] +request = "select * from (select segment_name,sum(bytes) as cluster_bytes from user_segments where segment_type='CLUSTER' group by segment_name) order by cluster_bytes DESC FETCH NEXT 100 ROWS ONLY" + +[[metric]] +context = "size_dba_segments_top100" +metricsdesc = {table_bytes="Gauge metric with the size of the tables in user segments."} +labels = ["segment_name"] +request = "select * from (select segment_name,sum(bytes) as table_bytes from dba_segments where segment_type='TABLE' group by segment_name) order by table_bytes DESC FETCH NEXT 100 ROWS ONLY" + +[[metric]] +context = "size_dba_segments_top100" +metricsdesc = {table_partition_bytes="Gauge metric with the size of the table partition in user segments."} +labels = ["segment_name"] +request = "select * from (select segment_name,sum(bytes) as table_partition_bytes from dba_segments where segment_type='TABLE PARTITION' group by segment_name) order by table_partition_bytes DESC FETCH NEXT 100 ROWS ONLY" + +[[metric]] +context = "size_dba_segments_top100" +metricsdesc = {cluster_bytes="Gauge metric with the size of the cluster in user segments."} +labels = ["segment_name"] +request = "select * from (select segment_name,sum(bytes) as cluster_bytes from dba_segments where segment_type='CLUSTER' group by segment_name) order by cluster_bytes DESC FETCH NEXT 100 ROWS ONLY" \ No newline at end of file diff --git a/custom-metrics-example/metric-dual-example.toml b/custom-metrics-example/metric-dual-example.toml new file mode 100644 index 0000000..f6dd444 --- /dev/null +++ b/custom-metrics-example/metric-dual-example.toml @@ -0,0 +1,6 @@ +[[metric]] +context = "test" +request = "SELECT 1 as value_1, 2 as value_2, 'First label' as label_1, 'Second label' as label_2 FROM DUAL" +metricsdesc = { value_1 = "Simple example returning always 1 as counter.", value_2 = "Same but returning always 2 as gauge." } +# Can be counter or gauge (default) +metricstype = { value_1 = "counter" } diff --git a/custom-metrics-example/metric-histogram-example.toml b/custom-metrics-example/metric-histogram-example.toml new file mode 100644 index 0000000..52fb1e5 --- /dev/null +++ b/custom-metrics-example/metric-histogram-example.toml @@ -0,0 +1,18 @@ +[[metric]] +context = "test_histo" +request = "SELECT 'firstlabel' as label1, 'secondlabel' as label2, 3 as le_20, 19 as le_40, 31 as le_60, 40 as le_80, 45 as count, 123.45 as data FROM DUAL" +metricsdesc = { data = "Histogram - sum total of all values in the data field." } +metricstype = { data = "histogram" } +labels = [ "label1", "label2" ] +metricsbuckets = { data = { le_20 = "20", le_40 = "40", le_60 = "60", le_80 = "80" } } + +# # Yields metrics as follows: +# # HELP oracledb_test_histo_data Histogram - sum total of all values in the data field. +# # TYPE oracledb_test_histo_data histogram +# oracledb_test_histo_data_bucket{label1="firstlabel",label2="secondlabel",le="20"} 3 +# oracledb_test_histo_data_bucket{label1="firstlabel",label2="secondlabel",le="40"} 19 +# oracledb_test_histo_data_bucket{label1="firstlabel",label2="secondlabel",le="60"} 31 +# oracledb_test_histo_data_bucket{label1="firstlabel",label2="secondlabel",le="80"} 40 +# oracledb_test_histo_data_bucket{label1="firstlabel",label2="secondlabel",le="+Inf"} 45 +# oracledb_test_histo_data_sum{label1="firstlabel",label2="secondlabel"} 123.45 +# oracledb_test_histo_data_count{label1="firstlabel",label2="secondlabel"} 45 diff --git a/custom-metrics-example/multi-metric-dual-example-labels.toml b/custom-metrics-example/multi-metric-dual-example-labels.toml new file mode 100644 index 0000000..eb32bdc --- /dev/null +++ b/custom-metrics-example/multi-metric-dual-example-labels.toml @@ -0,0 +1,10 @@ +[[metric]] +context = "context_no_label" +request = "SELECT 1 as value_1, 2 as value_2 FROM DUAL" +metricsdesc = { value_1 = "Simple example returning always 1.", value_2 = "Same but returning always 2." } + +[[metric]] +context = "context_with_labels" +labels = [ "label_1", "label_2" ] +request = "SELECT 1 as value_1, 2 as value_2, 'First label' as label_1, 'Second label' as label_2 FROM DUAL" +metricsdesc = { value_1 = "Simple example returning always 1.", value_2 = "Same but returning always 2." } diff --git a/oracle-db-monitoring-exporter/default-asm-metrics.toml b/default-asm-metrics.toml similarity index 100% rename from oracle-db-monitoring-exporter/default-asm-metrics.toml rename to default-asm-metrics.toml diff --git a/oracle-db-monitoring-exporter/default-metrics.legacy-tablespace.toml b/default-metrics.legacy-tablespace.toml similarity index 100% rename from oracle-db-monitoring-exporter/default-metrics.legacy-tablespace.toml rename to default-metrics.legacy-tablespace.toml diff --git a/oracle-db-monitoring-exporter/default-metrics.toml b/default-metrics.toml similarity index 93% rename from oracle-db-monitoring-exporter/default-metrics.toml rename to default-metrics.toml index 28c7e93..0a5a3d1 100644 --- a/oracle-db-monitoring-exporter/default-metrics.toml +++ b/default-metrics.toml @@ -45,14 +45,15 @@ WHERE [[metric]] context = "tablespace" labels = [ "tablespace", "type" ] -metricsdesc = { bytes = "Generic counter metric of tablespaces bytes in Oracle.", max_bytes = "Generic counter metric of tablespaces max bytes in Oracle.", free = "Generic counter metric of tablespaces free bytes in Oracle." } +metricsdesc = { bytes = "Generic counter metric of tablespaces bytes in Oracle.", max_bytes = "Generic counter metric of tablespaces max bytes in Oracle.", free = "Generic counter metric of tablespaces free bytes in Oracle.", used_percent = "Gauge metric showing as a percentage of how much of the tablespace has been used." } request = ''' SELECT dt.tablespace_name as tablespace, dt.contents as type, dt.block_size * dtum.used_space as bytes, dt.block_size * dtum.tablespace_size as max_bytes, - dt.block_size * (dtum.tablespace_size - dtum.used_space) as free + dt.block_size * (dtum.tablespace_size - dtum.used_space) as free, + dtum.used_percent FROM dba_tablespace_usage_metrics dtum, dba_tablespaces dt WHERE dtum.tablespace_name = dt.tablespace_name ORDER by tablespace diff --git a/docker-compose.yml b/docker-compose.yml deleted file mode 100644 index 9dfb04b..0000000 --- a/docker-compose.yml +++ /dev/null @@ -1,76 +0,0 @@ -# -# docker-compose.yml Version 1.0 -# -# Copyright (c) 2021 Oracle and/or its affiliates. -# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ -# -# - -version: '3.1' -services: - oracledb: - image: oracledb_monitor_oracledb:1.0 - container_name: 'oracledb' - environment: - ORACLE_SID: ORCLCDB - ORACLE_PWD: DOCKER_SECRET@oracle.pwd - secrets: - - oracle.pwd - ports: - - '1521:1521' - - '8080:8080' - tty: true - - exporter: - image: oracledb_monitor_exporter:1.0 - container_name: 'exporter' - environment: - DATA_SOURCE_NAME: auth.data.source.name - secrets: - - auth.data.source.name - - auth.username - - auth.password - depends_on: - - oracledb - ports: - - '9161:9161' - - - prometheus: - image: oracledb_monitor_prometheus:1.0 - container_name: 'prometheus' - secrets: - - prom.auth.pwd - - auth.username - - auth.password - depends_on: - - exporter - ports: - - '9090:9090' - - '9093:9093' - volumes: - - ./docker_vol/prom_app_vol:/etc/prometheus/prometheus_vol - tty: true - - - grafana: - image: oracledb_monitor_grafana:1.0 - container_name: 'grafana' - depends_on: - - prometheus - ports: - - '3000:3000' - volumes: - - ./docker_vol/graf_app_vol:/var/lib/grafana/grafana_vol - -secrets: - auth.data.source.name: - external: true - oracle.pwd: - external: true - auth.username: - external: true - auth.password: - external: true - prom.auth.pwd: - external: true diff --git a/docker_vol/graf_app_vol/dashboard_concurrency.json b/docker_vol/graf_app_vol/dashboard_concurrency.json deleted file mode 100644 index 57de2ca..0000000 --- a/docker_vol/graf_app_vol/dashboard_concurrency.json +++ /dev/null @@ -1,1997 +0,0 @@ -{ - "annotations": { - "list": [ - { - "builtIn": 1, - "datasource": "-- Grafana --", - "enable": true, - "hide": true, - "iconColor": "rgba(0, 211, 255, 1)", - "name": "Annotations & Alerts", - "type": "dashboard" - } - ] - }, - "editable": true, - "gnetId": null, - "graphTooltip": 0, - "id": 3, - "links": [], - "panels": [ - { - "datasource": null, - "description": "", - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "custom": { - "align": null, - "filterable": false - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [ - { - "matcher": { - "id": "byName", - "options": "%Time-out" - }, - "properties": [ - { - "id": "mappings", - "value": [ - { - "from": "0", - "id": 1, - "text": "0", - "to": "0.005", - "type": 2 - } - ] - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "%DB time" - }, - "properties": [ - { - "id": "unit", - "value": "percentunit" - } - ] - } - ] - }, - "gridPos": { - "h": 12, - "w": 24, - "x": 0, - "y": 0 - }, - "id": 2, - "interval": null, - "maxDataPoints": null, - "options": { - "frameIndex": 1, - "showHeader": true, - "sortBy": [ - { - "desc": true, - "displayName": "Total Wait Time (s)" - } - ] - }, - "pluginVersion": "7.5.5", - "targets": [ - { - "exemplar": false, - "expr": "oracledb_concurrency_foreground_wait_class_waits_fg", - "format": "table", - "instant": true, - "interval": "", - "legendFormat": "", - "refId": "waits" - }, - { - "exemplar": true, - "expr": "oracledb_concurrency_foreground_wait_class_total_wait_time_fg", - "format": "table", - "hide": false, - "instant": true, - "interval": "", - "intervalFactor": 1, - "legendFormat": "", - "refId": "total_wait_time" - }, - { - "exemplar": true, - "expr": "oracledb_concurrency_foreground_wait_class_avg_wait_time_fg", - "format": "table", - "hide": false, - "instant": true, - "interval": "", - "legendFormat": "", - "refId": "avg_wait_time" - }, - { - "exemplar": true, - "expr": "oracledb_concurrency_foreground_wait_class_total_wait_time_fg / on(con_id, inst_id, instance, job) group_left oracledb_concurrency_sysstat_db_time", - "format": "table", - "hide": false, - "instant": true, - "interval": "", - "legendFormat": "", - "refId": "%db_time" - }, - { - "exemplar": true, - "expr": "oracledb_concurrency_foreground_wait_class_timeouts_fg / on(con_id, inst_id, instance, job) group_left oracledb_concurrency_sysevent_total_timeout", - "format": "table", - "hide": false, - "instant": true, - "interval": "", - "legendFormat": "", - "refId": "%time-out" - } - ], - "title": "Foreground Wait Class", - "transformations": [ - { - "id": "filterFieldsByName", - "options": { - "include": { - "names": [ - "wait_class_name", - "Value #waits", - "Value #total_wait_time", - "Value #avg_wait_time", - "Value #%db_time", - "Value #%time-out" - ] - } - } - }, - { - "id": "merge", - "options": {} - }, - { - "id": "organize", - "options": { - "excludeByName": {}, - "indexByName": { - "Value #%db_time": 5, - "Value #%time-out": 2, - "Value #avg_wait_time": 4, - "Value #total_wait_time": 3, - "Value #waits": 1, - "wait_class_name": 0 - }, - "renameByName": { - "Value #%cpu": "%CPU", - "Value #%db_time": "%DB time", - "Value #%io": "%IO", - "Value #%time-out": "%Time-out", - "Value #%total": "%Total", - "Value #avg_wait_time": "Avg Wait Time (s)", - "Value #elapsed_time": "Elapsed Time(s)", - "Value #elapsed_time / Value #executions": "Elapsed Time per Exec (s)", - "Value #executions": "Executions", - "Value #total_wait_time": "Total Wait Time (s)", - "Value #waits": "Waits", - "module": "Module", - "sql_id": "SQL ID", - "wait_class_name": "Wait Class" - } - } - }, - { - "id": "sortBy", - "options": { - "fields": {}, - "sort": [ - { - "desc": true, - "field": "Total Wait Time (s)" - } - ] - } - } - ], - "type": "table" - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": null, - "description": "", - "fieldConfig": { - "defaults": {}, - "overrides": [] - }, - "fill": 1, - "fillGradient": 0, - "gridPos": { - "h": 12, - "w": 12, - "x": 0, - "y": 12 - }, - "hiddenSeries": false, - "id": 6, - "interval": null, - "legend": { - "alignAsTable": true, - "avg": false, - "current": false, - "max": false, - "min": false, - "rightSide": true, - "show": true, - "total": true, - "values": true - }, - "lines": true, - "linewidth": 1, - "maxDataPoints": null, - "nullPointMode": "null", - "options": { - "alertThreshold": true - }, - "percentage": false, - "pluginVersion": "7.5.5", - "pointradius": 2, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "exemplar": false, - "expr": "oracledb_concurrency_foreground_wait_class_total_wait_time_fg", - "format": "time_series", - "instant": false, - "interval": "", - "intervalFactor": 1, - "legendFormat": "{{wait_class_name}}", - "refId": "waits" - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Foreground Wait Class - Wait Time (s) [Accumulate]", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "transformations": [], - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "$$hashKey": "object:523", - "format": "none", - "label": "", - "logBase": 1, - "max": null, - "min": null, - "show": true - }, - { - "$$hashKey": "object:524", - "format": "none", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - }, - { - "datasource": null, - "description": "", - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 10, - "gradientMode": "none", - "hideFrom": { - "graph": false, - "legend": false, - "tooltip": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "never", - "spanNulls": true - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "none" - }, - "overrides": [] - }, - "gridPos": { - "h": 12, - "w": 12, - "x": 12, - "y": 12 - }, - "id": 13, - "interval": null, - "maxDataPoints": null, - "options": { - "graph": {}, - "legend": { - "calcs": [ - "last" - ], - "displayMode": "table", - "placement": "right" - }, - "tooltipOptions": { - "mode": "single" - } - }, - "pluginVersion": "7.5.5", - "targets": [ - { - "exemplar": false, - "expr": "delta(oracledb_concurrency_foreground_wait_class_total_wait_time_fg[2m])", - "format": "time_series", - "instant": false, - "interval": "", - "intervalFactor": 2, - "legendFormat": "{{wait_class_name}}", - "refId": "waits" - } - ], - "timeFrom": null, - "timeShift": null, - "title": "Foreground Wait Class - Wait Time (s) [2m Delta]", - "transformations": [], - "type": "timeseries" - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": null, - "description": "", - "fieldConfig": { - "defaults": { - "unit": "short" - }, - "overrides": [] - }, - "fill": 1, - "fillGradient": 0, - "gridPos": { - "h": 12, - "w": 12, - "x": 0, - "y": 24 - }, - "hiddenSeries": false, - "id": 5, - "interval": null, - "legend": { - "alignAsTable": true, - "avg": false, - "current": false, - "max": false, - "min": false, - "rightSide": true, - "show": true, - "total": true, - "values": true - }, - "lines": true, - "linewidth": 1, - "maxDataPoints": null, - "nullPointMode": "null", - "options": { - "alertThreshold": true - }, - "percentage": false, - "pluginVersion": "7.5.5", - "pointradius": 2, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "exemplar": false, - "expr": "oracledb_concurrency_foreground_wait_class_waits_fg", - "format": "time_series", - "instant": false, - "interval": "", - "intervalFactor": 1, - "legendFormat": "{{wait_class_name}}", - "refId": "waits" - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Foreground Wait Class - Wait Count [accumulte]", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "transformations": [], - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - }, - { - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - }, - { - "datasource": null, - "description": "", - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 10, - "gradientMode": "none", - "hideFrom": { - "graph": false, - "legend": false, - "tooltip": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "never", - "spanNulls": true - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "none" - }, - "overrides": [] - }, - "gridPos": { - "h": 12, - "w": 12, - "x": 12, - "y": 24 - }, - "id": 12, - "interval": null, - "maxDataPoints": null, - "options": { - "graph": {}, - "legend": { - "calcs": [ - "last" - ], - "displayMode": "table", - "placement": "right" - }, - "tooltipOptions": { - "mode": "single" - } - }, - "pluginVersion": "7.5.5", - "targets": [ - { - "exemplar": false, - "expr": "delta(oracledb_concurrency_foreground_wait_class_waits_fg[2m])", - "format": "time_series", - "instant": false, - "interval": "", - "intervalFactor": 2, - "legendFormat": "{{wait_class_name}}", - "refId": "waits" - } - ], - "timeFrom": null, - "timeShift": null, - "title": "Foreground Wait Class - Wait Count [2m Delta]", - "transformations": [], - "type": "timeseries" - }, - { - "datasource": null, - "description": "", - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "none" - }, - "overrides": [] - }, - "gridPos": { - "h": 14, - "w": 12, - "x": 0, - "y": 36 - }, - "id": 14, - "interval": null, - "maxDataPoints": null, - "options": { - "displayMode": "basic", - "orientation": "horizontal", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "showUnfilled": true, - "text": {} - }, - "pluginVersion": "7.5.5", - "targets": [ - { - "exemplar": false, - "expr": "oracledb_concurrency_foreground_wait_class_avg_wait_time_fg", - "format": "time_series", - "instant": false, - "interval": "", - "legendFormat": "{{wait_class_name}}", - "refId": "waits" - } - ], - "title": "Foreground Wait Class - Average Wait Time (s) [Accumulate]", - "transformations": [], - "type": "bargauge" - }, - { - "datasource": null, - "description": "", - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "custom": { - "align": null, - "filterable": false - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [ - { - "matcher": { - "id": "byName", - "options": "%Time-out" - }, - "properties": [ - { - "id": "mappings", - "value": [ - { - "from": "0", - "id": 1, - "text": "0%", - "to": "0.005", - "type": 2 - } - ] - }, - { - "id": "unit", - "value": "percentunit" - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "%DB time" - }, - "properties": [ - { - "id": "unit", - "value": "percentunit" - } - ] - } - ] - }, - "gridPos": { - "h": 21, - "w": 24, - "x": 0, - "y": 50 - }, - "id": 3, - "interval": null, - "maxDataPoints": null, - "options": { - "frameIndex": 1, - "showHeader": true, - "sortBy": [ - { - "desc": true, - "displayName": "Total Wait Time (s)" - } - ] - }, - "pluginVersion": "7.5.5", - "targets": [ - { - "exemplar": false, - "expr": "oracledb_concurrency_foreground_wait_event_waits_fg", - "format": "table", - "instant": true, - "interval": "", - "legendFormat": "", - "refId": "waits" - }, - { - "exemplar": true, - "expr": "oracledb_concurrency_foreground_wait_event_time_waited_fg", - "format": "table", - "hide": false, - "instant": true, - "interval": "", - "intervalFactor": 1, - "legendFormat": "", - "refId": "total_wait_time" - }, - { - "exemplar": true, - "expr": "oracledb_concurrency_foreground_wait_event_average_wait_fg", - "format": "table", - "hide": false, - "instant": true, - "interval": "", - "legendFormat": "", - "refId": "avg_wait_time" - }, - { - "exemplar": true, - "expr": "oracledb_concurrency_foreground_wait_event_time_waited_fg / on(con_id, inst_id, instance, job) group_left oracledb_concurrency_sysstat_db_time", - "format": "table", - "hide": false, - "instant": true, - "interval": "", - "legendFormat": "", - "refId": "%db_time" - }, - { - "exemplar": true, - "expr": "oracledb_concurrency_foreground_wait_event_timeouts_fg / on(con_id, inst_id, instance, job) group_left oracledb_concurrency_sysevent_total_timeout", - "format": "table", - "hide": false, - "instant": true, - "interval": "", - "legendFormat": "", - "refId": "%time-out" - } - ], - "title": "Foreground Wait Event", - "transformations": [ - { - "id": "filterFieldsByName", - "options": { - "include": { - "names": [ - "Value #waits", - "Value #total_wait_time", - "Value #avg_wait_time", - "Value #%db_time", - "Value #%time-out", - "event_name" - ] - } - } - }, - { - "id": "merge", - "options": {} - }, - { - "id": "organize", - "options": { - "excludeByName": {}, - "indexByName": { - "Value #%db_time": 5, - "Value #%time-out": 2, - "Value #avg_wait_time": 4, - "Value #total_wait_time": 3, - "Value #waits": 1, - "event_name": 0 - }, - "renameByName": { - "Value #%cpu": "%CPU", - "Value #%db_time": "%DB time", - "Value #%io": "%IO", - "Value #%time-out": "%Time-out", - "Value #%total": "%Total", - "Value #avg_wait_time": "Avg Wait Time (s)", - "Value #elapsed_time": "Elapsed Time(s)", - "Value #elapsed_time / Value #executions": "Elapsed Time per Exec (s)", - "Value #executions": "Executions", - "Value #total_wait_time": "Total Wait Time (s)", - "Value #waits": "Waits", - "event_name": "Event", - "module": "Module", - "sql_id": "SQL ID", - "wait_class_name": "Wait Class" - } - } - }, - { - "id": "sortBy", - "options": { - "fields": {}, - "sort": [ - { - "desc": true, - "field": "Total Wait Time (s)" - } - ] - } - } - ], - "type": "table" - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": null, - "description": "", - "fieldConfig": { - "defaults": {}, - "overrides": [] - }, - "fill": 1, - "fillGradient": 0, - "gridPos": { - "h": 12, - "w": 12, - "x": 0, - "y": 71 - }, - "hiddenSeries": false, - "id": 15, - "interval": null, - "legend": { - "alignAsTable": true, - "avg": false, - "current": false, - "max": false, - "min": false, - "rightSide": true, - "show": true, - "total": true, - "values": true - }, - "lines": true, - "linewidth": 1, - "maxDataPoints": null, - "nullPointMode": "null", - "options": { - "alertThreshold": true - }, - "percentage": false, - "pluginVersion": "7.5.5", - "pointradius": 2, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "exemplar": false, - "expr": "oracledb_concurrency_foreground_wait_event_time_waited_fg", - "format": "time_series", - "instant": false, - "interval": "", - "intervalFactor": 1, - "legendFormat": "{{event_name}}", - "refId": "waits" - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Foreground Wait Event - Wait Time (s) [Accumulate]", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "transformations": [], - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "$$hashKey": "object:523", - "format": "none", - "label": "", - "logBase": 1, - "max": null, - "min": null, - "show": true - }, - { - "$$hashKey": "object:524", - "format": "none", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - }, - { - "datasource": null, - "description": "", - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 10, - "gradientMode": "none", - "hideFrom": { - "graph": false, - "legend": false, - "tooltip": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "never", - "spanNulls": true - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "none" - }, - "overrides": [] - }, - "gridPos": { - "h": 12, - "w": 12, - "x": 12, - "y": 71 - }, - "id": 17, - "interval": null, - "maxDataPoints": null, - "options": { - "graph": {}, - "legend": { - "calcs": [ - "last" - ], - "displayMode": "table", - "placement": "right" - }, - "tooltipOptions": { - "mode": "single" - } - }, - "pluginVersion": "7.5.5", - "targets": [ - { - "exemplar": false, - "expr": "delta(oracledb_concurrency_foreground_wait_event_time_waited_fg[2m])", - "format": "time_series", - "instant": false, - "interval": "", - "intervalFactor": 1, - "legendFormat": "{{event_name}}", - "refId": "waits" - } - ], - "timeFrom": null, - "timeShift": null, - "title": "Foreground Wait Event - Wait Time (s) [2m Delta]", - "transformations": [], - "type": "timeseries" - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": null, - "description": "", - "fieldConfig": { - "defaults": {}, - "overrides": [] - }, - "fill": 1, - "fillGradient": 0, - "gridPos": { - "h": 12, - "w": 12, - "x": 0, - "y": 83 - }, - "hiddenSeries": false, - "id": 16, - "interval": null, - "legend": { - "alignAsTable": true, - "avg": false, - "current": false, - "max": false, - "min": false, - "rightSide": true, - "show": true, - "total": true, - "values": true - }, - "lines": true, - "linewidth": 1, - "maxDataPoints": null, - "nullPointMode": "null", - "options": { - "alertThreshold": true - }, - "percentage": false, - "pluginVersion": "7.5.5", - "pointradius": 2, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "exemplar": false, - "expr": "oracledb_concurrency_foreground_wait_event_waits_fg", - "format": "time_series", - "instant": false, - "interval": "", - "intervalFactor": 1, - "legendFormat": "{{event_name}}", - "refId": "waits" - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Foreground Wait Event - Wait Count [Accumulate]", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "transformations": [], - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "$$hashKey": "object:523", - "format": "none", - "label": "", - "logBase": 1, - "max": null, - "min": null, - "show": true - }, - { - "$$hashKey": "object:524", - "format": "none", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - }, - { - "datasource": null, - "description": "", - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 10, - "gradientMode": "none", - "hideFrom": { - "graph": false, - "legend": false, - "tooltip": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "never", - "spanNulls": true - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "none" - }, - "overrides": [] - }, - "gridPos": { - "h": 12, - "w": 12, - "x": 12, - "y": 83 - }, - "id": 18, - "interval": null, - "maxDataPoints": null, - "options": { - "graph": {}, - "legend": { - "calcs": [ - "last" - ], - "displayMode": "table", - "placement": "right" - }, - "tooltipOptions": { - "mode": "single" - } - }, - "pluginVersion": "7.5.5", - "targets": [ - { - "exemplar": false, - "expr": "delta(oracledb_concurrency_foreground_wait_event_waits_fg[2m])", - "format": "time_series", - "instant": false, - "interval": "", - "intervalFactor": 1, - "legendFormat": "{{event_name}}", - "refId": "waits" - } - ], - "timeFrom": null, - "timeShift": null, - "title": "Foreground Wait Event - Wait Count [2m Delta]", - "transformations": [], - "type": "timeseries" - }, - { - "datasource": null, - "description": "", - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "none" - }, - "overrides": [] - }, - "gridPos": { - "h": 14, - "w": 12, - "x": 0, - "y": 95 - }, - "id": 19, - "interval": null, - "maxDataPoints": null, - "options": { - "displayMode": "basic", - "orientation": "horizontal", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "showUnfilled": true, - "text": {} - }, - "pluginVersion": "7.5.5", - "targets": [ - { - "exemplar": false, - "expr": "oracledb_concurrency_foreground_wait_event_average_wait_fg", - "format": "time_series", - "instant": false, - "interval": "", - "legendFormat": "{{event_name}}", - "refId": "waits" - } - ], - "title": "Foreground Wait Event - Average Wait Time (s) [Accumulate]", - "transformations": [], - "type": "bargauge" - }, - { - "datasource": null, - "description": "", - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "custom": { - "align": null, - "filterable": false - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [ - { - "matcher": { - "id": "byName", - "options": "%Time-out" - }, - "properties": [ - { - "id": "mappings", - "value": [ - { - "from": "0", - "id": 1, - "text": "0%", - "to": "0.005", - "type": 2 - } - ] - }, - { - "id": "unit", - "value": "percentunit" - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "%DB time" - }, - "properties": [ - { - "id": "unit", - "value": "percentunit" - } - ] - } - ] - }, - "gridPos": { - "h": 21, - "w": 24, - "x": 0, - "y": 109 - }, - "id": 4, - "interval": null, - "maxDataPoints": null, - "options": { - "frameIndex": 1, - "showHeader": true, - "sortBy": [ - { - "desc": true, - "displayName": "Total Wait Time (s)" - } - ] - }, - "pluginVersion": "7.5.5", - "targets": [ - { - "exemplar": false, - "expr": "oracledb_concurrency_background_wait_event_waits_bg", - "format": "table", - "instant": true, - "interval": "", - "legendFormat": "", - "refId": "waits" - }, - { - "exemplar": true, - "expr": "oracledb_concurrency_background_wait_event_time_waited_bg", - "format": "table", - "hide": false, - "instant": true, - "interval": "", - "intervalFactor": 1, - "legendFormat": "", - "refId": "total_wait_time" - }, - { - "exemplar": true, - "expr": "oracledb_concurrency_background_wait_event_average_wait_bg", - "format": "table", - "hide": false, - "instant": true, - "interval": "", - "legendFormat": "", - "refId": "avg_wait_time" - }, - { - "exemplar": true, - "expr": "oracledb_concurrency_background_wait_event_time_waited_bg / on(con_id, inst_id, instance, job) group_left oracledb_concurrency_sysstat_total_time_waited_bg", - "format": "table", - "hide": false, - "instant": true, - "interval": "", - "legendFormat": "", - "refId": "%bg_time" - }, - { - "exemplar": true, - "expr": "oracledb_concurrency_background_wait_event_timeouts_bg / on(con_id, inst_id, instance, job) group_left oracledb_concurrency_sysevent_total_timeout", - "format": "table", - "hide": false, - "instant": true, - "interval": "", - "legendFormat": "", - "refId": "%time-out" - } - ], - "title": "Background Wait Event", - "transformations": [ - { - "id": "filterFieldsByName", - "options": { - "include": { - "names": [ - "event_name", - "Value #waits", - "Value #total_wait_time", - "Value #avg_wait_time", - "Value #%time-out", - "Value #%bg_time" - ] - } - } - }, - { - "id": "merge", - "options": {} - }, - { - "id": "organize", - "options": { - "excludeByName": {}, - "indexByName": { - "Value #%db_time": 5, - "Value #%time-out": 2, - "Value #avg_wait_time": 4, - "Value #total_wait_time": 3, - "Value #waits": 1, - "event_name": 0 - }, - "renameByName": { - "Value #%cpu": "%CPU", - "Value #%db_time": "%DB time", - "Value #%io": "%IO", - "Value #%time-out": "%Time-out", - "Value #%total": "%Total", - "Value #avg_wait_time": "Avg Wait Time (s)", - "Value #elapsed_time": "Elapsed Time(s)", - "Value #elapsed_time / Value #executions": "Elapsed Time per Exec (s)", - "Value #executions": "Executions", - "Value #total_wait_time": "Total Wait Time (s)", - "Value #waits": "Waits", - "event_name": "Event", - "module": "Module", - "sql_id": "SQL ID", - "wait_class_name": "Wait Class" - } - } - }, - { - "id": "sortBy", - "options": { - "fields": {}, - "sort": [ - { - "desc": true, - "field": "Total Wait Time (s)" - } - ] - } - } - ], - "type": "table" - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": null, - "description": "", - "fieldConfig": { - "defaults": {}, - "overrides": [] - }, - "fill": 1, - "fillGradient": 0, - "gridPos": { - "h": 12, - "w": 12, - "x": 0, - "y": 130 - }, - "hiddenSeries": false, - "id": 20, - "interval": null, - "legend": { - "alignAsTable": true, - "avg": false, - "current": false, - "max": false, - "min": false, - "rightSide": true, - "show": true, - "total": true, - "values": true - }, - "lines": true, - "linewidth": 1, - "maxDataPoints": null, - "nullPointMode": "null", - "options": { - "alertThreshold": true - }, - "percentage": false, - "pluginVersion": "7.5.5", - "pointradius": 2, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "exemplar": false, - "expr": "oracledb_concurrency_background_wait_event_time_waited_bg", - "format": "time_series", - "instant": false, - "interval": "", - "intervalFactor": 1, - "legendFormat": "{{event_name}}", - "refId": "waits" - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Background Wait Event - Wait Time (s) [Accumulate]", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "transformations": [], - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "$$hashKey": "object:523", - "format": "none", - "label": "", - "logBase": 1, - "max": null, - "min": null, - "show": true - }, - { - "$$hashKey": "object:524", - "format": "none", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - }, - { - "datasource": null, - "description": "", - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 10, - "gradientMode": "none", - "hideFrom": { - "graph": false, - "legend": false, - "tooltip": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "never", - "spanNulls": true - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "none" - }, - "overrides": [] - }, - "gridPos": { - "h": 12, - "w": 12, - "x": 12, - "y": 130 - }, - "id": 21, - "interval": null, - "maxDataPoints": null, - "options": { - "graph": {}, - "legend": { - "calcs": [ - "last" - ], - "displayMode": "table", - "placement": "right" - }, - "tooltipOptions": { - "mode": "single" - } - }, - "pluginVersion": "7.5.5", - "targets": [ - { - "exemplar": false, - "expr": "delta(oracledb_concurrency_background_wait_event_time_waited_bg[2m])", - "format": "time_series", - "instant": false, - "interval": "", - "intervalFactor": 1, - "legendFormat": "{{event_name}}", - "refId": "waits" - } - ], - "timeFrom": null, - "timeShift": null, - "title": "Background Wait Event - Wait Time (s) [2m Delta]", - "transformations": [], - "type": "timeseries" - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": null, - "description": "", - "fieldConfig": { - "defaults": {}, - "overrides": [] - }, - "fill": 1, - "fillGradient": 0, - "gridPos": { - "h": 12, - "w": 12, - "x": 0, - "y": 142 - }, - "hiddenSeries": false, - "id": 22, - "interval": null, - "legend": { - "alignAsTable": true, - "avg": false, - "current": false, - "max": false, - "min": false, - "rightSide": true, - "show": true, - "total": true, - "values": true - }, - "lines": true, - "linewidth": 1, - "maxDataPoints": null, - "nullPointMode": "null", - "options": { - "alertThreshold": true - }, - "percentage": false, - "pluginVersion": "7.5.5", - "pointradius": 2, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "exemplar": false, - "expr": "oracledb_concurrency_background_wait_event_waits_bg", - "format": "time_series", - "instant": false, - "interval": "", - "intervalFactor": 1, - "legendFormat": "{{event_name}}", - "refId": "waits" - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Background Wait Event - Wait Count [Accumulate]", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "transformations": [], - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "$$hashKey": "object:523", - "format": "none", - "label": "", - "logBase": 1, - "max": null, - "min": null, - "show": true - }, - { - "$$hashKey": "object:524", - "format": "none", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - }, - { - "datasource": null, - "description": "", - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 10, - "gradientMode": "none", - "hideFrom": { - "graph": false, - "legend": false, - "tooltip": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "never", - "spanNulls": true - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "none" - }, - "overrides": [] - }, - "gridPos": { - "h": 12, - "w": 12, - "x": 12, - "y": 142 - }, - "id": 23, - "interval": null, - "maxDataPoints": null, - "options": { - "graph": {}, - "legend": { - "calcs": [ - "last" - ], - "displayMode": "table", - "placement": "right" - }, - "tooltipOptions": { - "mode": "single" - } - }, - "pluginVersion": "7.5.5", - "targets": [ - { - "exemplar": false, - "expr": "delta(oracledb_concurrency_background_wait_event_waits_bg[2m])", - "format": "time_series", - "instant": false, - "interval": "", - "intervalFactor": 1, - "legendFormat": "{{event_name}}", - "refId": "waits" - } - ], - "timeFrom": null, - "timeShift": null, - "title": "Background Wait Event - Wait Count [2m Delta]", - "transformations": [], - "type": "timeseries" - }, - { - "datasource": null, - "description": "", - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "none" - }, - "overrides": [] - }, - "gridPos": { - "h": 14, - "w": 12, - "x": 0, - "y": 154 - }, - "id": 24, - "interval": null, - "maxDataPoints": null, - "options": { - "displayMode": "basic", - "orientation": "horizontal", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "showUnfilled": true, - "text": {} - }, - "pluginVersion": "7.5.5", - "targets": [ - { - "exemplar": false, - "expr": "oracledb_concurrency_background_wait_event_average_wait_bg", - "format": "time_series", - "instant": false, - "interval": "", - "legendFormat": "{{event_name}}", - "refId": "waits" - } - ], - "title": "Background Wait Event - Average Wait Time (s) [Accumulate]", - "transformations": [], - "type": "bargauge" - } - ], - "schemaVersion": 27, - "style": "dark", - "tags": [], - "templating": { - "list": [] - }, - "time": { - "from": "now-1h", - "to": "now" - }, - "timepicker": {}, - "timezone": "", - "title": "Oracle App-Specific -- Concurrency Level - v1.1", - "uid": "YbcLrLW7z", - "version": 30 -} diff --git a/docker_vol/graf_app_vol/dashboard_io.json b/docker_vol/graf_app_vol/dashboard_io.json deleted file mode 100644 index 44111c0..0000000 --- a/docker_vol/graf_app_vol/dashboard_io.json +++ /dev/null @@ -1,493 +0,0 @@ -{ - "annotations": { - "list": [ - { - "builtIn": 1, - "datasource": "-- Grafana --", - "enable": true, - "hide": true, - "iconColor": "rgba(0, 211, 255, 1)", - "name": "Annotations & Alerts", - "type": "dashboard" - } - ] - }, - "editable": true, - "gnetId": null, - "graphTooltip": 0, - "id": 4, - "links": [], - "panels": [ - { - "datasource": null, - "description": "", - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "custom": { - "align": null, - "filterable": false - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [ - { - "matcher": { - "id": "byName", - "options": "%Time-out" - }, - "properties": [ - { - "id": "mappings", - "value": [ - { - "from": "0", - "id": 1, - "text": "0", - "to": "0.005", - "type": 2 - } - ] - } - ] - } - ] - }, - "gridPos": { - "h": 8, - "w": 24, - "x": 0, - "y": 0 - }, - "id": 2, - "interval": null, - "maxDataPoints": null, - "options": { - "frameIndex": 1, - "showHeader": true, - "sortBy": [ - { - "desc": true, - "displayName": "Value #read_data" - } - ] - }, - "pluginVersion": "7.5.5", - "targets": [ - { - "exemplar": false, - "expr": "oracledb_io_function_read_data_l + oracledb_io_function_read_data_s", - "format": "table", - "instant": true, - "interval": "", - "legendFormat": "", - "refId": "read_data" - }, - { - "exemplar": true, - "expr": "(oracledb_io_function_read_req_l + oracledb_io_function_read_req_s) / oracledb_io_function_time", - "format": "table", - "hide": false, - "instant": true, - "interval": "", - "intervalFactor": 1, - "legendFormat": "", - "refId": "read_req_per_sec" - }, - { - "exemplar": true, - "expr": "(oracledb_io_function_read_data_l + oracledb_io_function_read_data_s) / oracledb_io_function_time", - "format": "table", - "hide": false, - "instant": true, - "interval": "", - "legendFormat": "", - "refId": "read_data_per_sec" - }, - { - "exemplar": true, - "expr": "(oracledb_io_function_write_data_l + oracledb_io_function_write_data_s)", - "format": "table", - "hide": false, - "instant": true, - "interval": "", - "legendFormat": "", - "refId": "write_data" - }, - { - "exemplar": true, - "expr": "(oracledb_io_function_write_req_l + oracledb_io_function_write_req_s) / oracledb_io_function_time", - "format": "table", - "hide": false, - "instant": true, - "interval": "", - "legendFormat": "", - "refId": "write_req_per_sec" - }, - { - "exemplar": true, - "expr": "(oracledb_io_function_write_data_l + oracledb_io_function_write_data_s) / oracledb_io_function_time", - "format": "table", - "hide": false, - "instant": true, - "interval": "", - "legendFormat": "", - "refId": "write_data_per_sec" - }, - { - "exemplar": true, - "expr": "oracledb_io_function_number_of_waits", - "format": "table", - "hide": false, - "instant": true, - "interval": "", - "legendFormat": "", - "refId": "wait_count" - }, - { - "exemplar": true, - "expr": "oracledb_io_function_time / oracledb_io_function_number_of_waits", - "format": "table", - "hide": false, - "instant": true, - "interval": "", - "legendFormat": "", - "refId": "avg_wait_time" - } - ], - "title": "IOStat by Function summary", - "transformations": [ - { - "id": "filterFieldsByName", - "options": { - "include": { - "names": [ - "function_name", - "Value #read_data", - "Value #read_req_per_sec", - "Value #read_data_per_sec", - "Value #write_data", - "Value #write_req_per_sec", - "Value #write_data_per_sec", - "Value #wait_count", - "Value #avg_wait_time", - "Value #total_read_data" - ] - } - } - }, - { - "id": "merge", - "options": {} - }, - { - "id": "organize", - "options": { - "excludeByName": {}, - "indexByName": { - "Value #avg_wait_time": 8, - "Value #read_data": 1, - "Value #read_data_per_sec": 3, - "Value #read_req_per_sec": 2, - "Value #wait_count": 7, - "Value #write_data": 4, - "Value #write_data_per_sec": 6, - "Value #write_req_per_sec": 5, - "function_name": 0 - }, - "renameByName": { - "Value #%cpu": "%CPU", - "Value #%db_time": "%DB time", - "Value #%io": "%IO", - "Value #%time-out": "%Time-out", - "Value #%total": "%Total", - "Value #avg_wait_time": "Avg Wait Time (s)", - "Value #elapsed_time": "Elapsed Time(s)", - "Value #elapsed_time / Value #executions": "Elapsed Time per Exec (s)", - "Value #executions": "Executions", - "Value #total_wait_time": "Total Wait Time (s)", - "Value #waits": "Waits", - "module": "Module", - "sql_id": "SQL ID", - "wait_class_name": "Wait Class" - } - } - }, - { - "id": "sortBy", - "options": { - "fields": {}, - "sort": [ - { - "desc": true, - "field": "Total Wait Time (s)" - } - ] - } - } - ], - "type": "table" - }, - { - "datasource": null, - "description": "", - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "custom": { - "align": null, - "filterable": false - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [ - { - "matcher": { - "id": "byName", - "options": "%Time-out" - }, - "properties": [ - { - "id": "mappings", - "value": [ - { - "from": "0", - "id": 1, - "text": "0", - "to": "0.005", - "type": 2 - } - ] - } - ] - } - ] - }, - "gridPos": { - "h": 8, - "w": 24, - "x": 0, - "y": 8 - }, - "id": 3, - "interval": null, - "maxDataPoints": null, - "options": { - "frameIndex": 1, - "showHeader": true, - "sortBy": [ - { - "desc": true, - "displayName": "Value #read_data" - } - ] - }, - "pluginVersion": "7.5.5", - "targets": [ - { - "exemplar": false, - "expr": "oracledb_io_filetype_read_data_l + oracledb_io_filetype_read_data_s", - "format": "table", - "instant": true, - "interval": "", - "legendFormat": "", - "refId": "read_data" - }, - { - "exemplar": true, - "expr": "(oracledb_io_filetype_read_req_l + oracledb_io_filetype_read_req_s) / (oracledb_io_filetype_read_stime_l + oracledb_io_filetype_read_stime_s)", - "format": "table", - "hide": false, - "instant": true, - "interval": "", - "intervalFactor": 1, - "legendFormat": "", - "refId": "read_req_per_sec" - }, - { - "exemplar": true, - "expr": "(oracledb_io_filetype_read_data_l + oracledb_io_filetype_read_data_s) / (oracledb_io_filetype_read_stime_l + oracledb_io_filetype_read_stime_s)", - "format": "table", - "hide": false, - "instant": true, - "interval": "", - "legendFormat": "", - "refId": "read_data_per_sec" - }, - { - "exemplar": true, - "expr": "(oracledb_io_filetype_write_data_l + oracledb_io_filetype_write_data_s)", - "format": "table", - "hide": false, - "instant": true, - "interval": "", - "legendFormat": "", - "refId": "write_data" - }, - { - "exemplar": true, - "expr": "(oracledb_io_filetype_write_req_l + oracledb_io_filetype_write_req_l) / (oracledb_io_filetype_write_stime_l + oracledb_io_filetype_write_stime_s)", - "format": "table", - "hide": false, - "instant": true, - "interval": "", - "legendFormat": "", - "refId": "write_req_per_sec" - }, - { - "exemplar": true, - "expr": "(oracledb_io_filetype_write_data_l + oracledb_io_filetype_write_data_s) / (oracledb_io_filetype_write_stime_l + oracledb_io_filetype_write_stime_s)", - "format": "table", - "hide": false, - "instant": true, - "interval": "", - "legendFormat": "", - "refId": "write_data_per_sec" - }, - { - "exemplar": true, - "expr": "(oracledb_io_filetype_read_stime_l + oracledb_io_filetype_read_stime_s)", - "format": "table", - "hide": false, - "instant": true, - "interval": "", - "legendFormat": "", - "refId": "read_service_time" - }, - { - "exemplar": true, - "expr": "(oracledb_io_filetype_write_stime_l + oracledb_io_filetype_write_stime_s)", - "format": "table", - "hide": false, - "instant": true, - "interval": "", - "legendFormat": "", - "refId": "write_service_time" - } - ], - "title": "IOStat by Filetype summary", - "transformations": [ - { - "id": "filterFieldsByName", - "options": { - "include": { - "names": [ - "filetype_name", - "Value #read_data", - "Value #read_req_per_sec", - "Value #read_data_per_sec", - "Value #write_data", - "Value #write_req_per_sec", - "Value #write_data_per_sec" - ] - } - } - }, - { - "id": "merge", - "options": {} - }, - { - "id": "calculateField", - "options": { - "mode": "reduceRow", - "reduce": { - "include": [ - "Value #read_data", - "Value #write_data" - ], - "reducer": "sum" - } - } - }, - { - "id": "organize", - "options": { - "excludeByName": { - "Total": true - }, - "indexByName": { - "Value #read_data": 1, - "filetype_name": 0 - }, - "renameByName": { - "Total": "total_data", - "Value #%cpu": "%CPU", - "Value #%db_time": "%DB time", - "Value #%io": "%IO", - "Value #%time-out": "%Time-out", - "Value #%total": "%Total", - "Value #avg_wait_time": "Avg Wait Time (s)", - "Value #elapsed_time": "Elapsed Time(s)", - "Value #elapsed_time / Value #executions": "Elapsed Time per Exec (s)", - "Value #executions": "Executions", - "Value #total_wait_time": "Total Wait Time (s)", - "Value #waits": "Waits", - "module": "Module", - "sql_id": "SQL ID", - "wait_class_name": "Wait Class" - } - } - }, - { - "id": "sortBy", - "options": { - "fields": {}, - "sort": [ - { - "desc": true, - "field": "total" - } - ] - } - } - ], - "type": "table" - } - ], - "schemaVersion": 27, - "style": "dark", - "tags": [], - "templating": { - "list": [] - }, - "time": { - "from": "now-6h", - "to": "now" - }, - "timepicker": {}, - "timezone": "", - "title": "Oracle App-Specific -- I/O Level - v1.1", - "uid": "T_w0nsZ7z", - "version": 6 -} diff --git a/docker_vol/graf_app_vol/dashboard_query.json b/docker_vol/graf_app_vol/dashboard_query.json deleted file mode 100644 index ed128d6..0000000 --- a/docker_vol/graf_app_vol/dashboard_query.json +++ /dev/null @@ -1,1664 +0,0 @@ -{ - "annotations": { - "list": [ - { - "builtIn": 1, - "datasource": "-- Grafana --", - "enable": true, - "hide": true, - "iconColor": "rgba(0, 211, 255, 1)", - "name": "Annotations & Alerts", - "type": "dashboard" - } - ] - }, - "editable": true, - "gnetId": null, - "graphTooltip": 0, - "id": 1, - "iteration": 1628201886738, - "links": [], - "panels": [ - { - "collapsed": false, - "datasource": null, - "gridPos": { - "h": 1, - "w": 24, - "x": 0, - "y": 0 - }, - "id": 6, - "panels": [], - "title": "SQL Statistics", - "type": "row" - }, - { - "datasource": null, - "description": "%Total - Elapsed Time as a percentage of Total DB time\n\n%CPU - CPU Time as a percentage of Elapsed Time\n\n%IO - User I/O Time as a percentage of Elapsed Time", - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "custom": { - "align": null, - "filterable": false - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [ - { - "matcher": { - "id": "byName", - "options": "%Total" - }, - "properties": [ - { - "id": "custom.width", - "value": 96 - }, - { - "id": "unit", - "value": "percentunit" - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "%CPU" - }, - "properties": [ - { - "id": "unit", - "value": "percentunit" - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "%IO" - }, - "properties": [ - { - "id": "unit", - "value": "percentunit" - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Module" - }, - "properties": [ - { - "id": "custom.width", - "value": 400 - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Elapsed Time" - }, - "properties": [ - { - "id": "unit", - "value": "Β΅s" - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Elapsed Time per Exec" - }, - "properties": [ - { - "id": "unit", - "value": "Β΅s" - } - ] - } - ] - }, - "gridPos": { - "h": 12, - "w": 24, - "x": 0, - "y": 1 - }, - "id": 22, - "interval": null, - "links": [ - { - "targetBlank": true, - "title": "V$SQL Official Document", - "url": "https://docs.oracle.com/en/database/oracle/oracle-database/19/refrn/V-SQL.html#GUID-2B9340D7-4AA8-4894-94C0-D5990F67BE75" - } - ], - "maxDataPoints": null, - "options": { - "frameIndex": 0, - "showHeader": true, - "sortBy": [ - { - "desc": true, - "displayName": "Elapsed Time" - } - ] - }, - "pluginVersion": "7.5.5", - "targets": [ - { - "exemplar": true, - "expr": "oracledb_sql_query_elapsed_time_order_elapsed_time", - "format": "table", - "instant": true, - "interval": "", - "legendFormat": "", - "refId": "elapsed_time" - }, - { - "exemplar": true, - "expr": "oracledb_sql_query_elapsed_time_order_executions", - "format": "table", - "hide": false, - "instant": true, - "interval": "", - "intervalFactor": 1, - "legendFormat": "", - "refId": "executions" - }, - { - "exemplar": true, - "expr": "oracledb_sql_query_elapsed_time_order_elapsed_time / on(inst_id, instance, job) group_left oracledb_system_time_value{stat_name=\"DB time\"}", - "format": "table", - "hide": false, - "instant": true, - "interval": "", - "legendFormat": "", - "refId": "%total" - }, - { - "exemplar": true, - "expr": "oracledb_sql_query_elapsed_time_order_cpu_time / oracledb_sql_query_elapsed_time_order_elapsed_time", - "format": "table", - "hide": false, - "instant": true, - "interval": "", - "legendFormat": "", - "refId": "%cpu" - }, - { - "exemplar": true, - "expr": "oracledb_sql_query_elapsed_time_order_user_io_wait_time / oracledb_sql_query_elapsed_time_order_elapsed_time", - "format": "table", - "hide": false, - "instant": true, - "interval": "", - "legendFormat": "", - "refId": "%io" - } - ], - "title": "SQL ordered by Elapsed Time (Top 10)", - "transformations": [ - { - "id": "filterFieldsByName", - "options": { - "include": { - "names": [ - "sql_id", - "Value #elapsed_time", - "Value #executions", - "Value #%total", - "Value #%cpu", - "Value #%io", - "module" - ] - } - } - }, - { - "id": "merge", - "options": {} - }, - { - "id": "calculateField", - "options": { - "binary": { - "left": "Value #elapsed_time", - "operator": "/", - "reducer": "sum", - "right": "Value #executions" - }, - "mode": "binary", - "reduce": { - "reducer": "sum" - } - } - }, - { - "id": "organize", - "options": { - "excludeByName": {}, - "indexByName": { - "Value #%cpu": 6, - "Value #%io": 7, - "Value #%total": 5, - "Value #elapsed_time": 2, - "Value #elapsed_time / Value #executions": 4, - "Value #executions": 3, - "module": 1, - "sql_id": 0 - }, - "renameByName": { - "Value #%cpu": "%CPU", - "Value #%io": "%IO", - "Value #%total": "%Total", - "Value #elapsed_time": "Elapsed Time", - "Value #elapsed_time / Value #executions": "Elapsed Time per Exec", - "Value #executions": "Executions", - "module": "Module", - "sql_id": "SQL ID" - } - } - }, - { - "id": "sortBy", - "options": { - "fields": {}, - "sort": [ - { - "desc": true, - "field": "Elapsed Time" - } - ] - } - } - ], - "type": "table" - }, - { - "datasource": null, - "description": "%Total - CPU Time as a percentage of Total DB CPU\n\n%CPU - CPU Time as a percentage of Elapsed Time\n\n%IO - User I/O Time as a percentage of Elapsed Time", - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "custom": { - "align": null, - "filterable": false - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [ - { - "matcher": { - "id": "byName", - "options": "%CPU" - }, - "properties": [ - { - "id": "unit", - "value": "percentunit" - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "CPU Time" - }, - "properties": [ - { - "id": "unit", - "value": "Β΅s" - }, - { - "id": "decimals", - "value": 3 - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "%Total" - }, - "properties": [ - { - "id": "unit", - "value": "percentunit" - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "%IO" - }, - "properties": [ - { - "id": "unit", - "value": "percentunit" - }, - { - "id": "custom.width", - "value": 228 - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "CPU Time per Exec" - }, - "properties": [ - { - "id": "unit", - "value": "Β΅s" - } - ] - } - ] - }, - "gridPos": { - "h": 12, - "w": 24, - "x": 0, - "y": 13 - }, - "id": 14, - "interval": null, - "maxDataPoints": null, - "options": { - "frameIndex": 0, - "showHeader": true, - "sortBy": [] - }, - "pluginVersion": "7.5.5", - "targets": [ - { - "exemplar": true, - "expr": "oracledb_sql_query_cpu_time_order_cpu_time", - "format": "table", - "instant": true, - "interval": "", - "legendFormat": "", - "refId": "cpu_time" - }, - { - "exemplar": true, - "expr": "oracledb_sql_query_cpu_time_order_executions", - "format": "table", - "hide": false, - "instant": true, - "interval": "", - "intervalFactor": 1, - "legendFormat": "", - "refId": "executions" - }, - { - "exemplar": true, - "expr": "oracledb_sql_query_cpu_time_order_elapsed_time / on(inst_id, instance, job) group_left oracledb_system_time_value{stat_name=\"DB CPU\"}", - "format": "table", - "hide": false, - "instant": true, - "interval": "", - "legendFormat": "", - "refId": "%total" - }, - { - "exemplar": true, - "expr": "oracledb_sql_query_cpu_time_order_cpu_time / oracledb_sql_query_cpu_time_order_elapsed_time", - "format": "table", - "hide": false, - "instant": true, - "interval": "", - "legendFormat": "", - "refId": "%cpu" - }, - { - "exemplar": true, - "expr": "oracledb_sql_query_cpu_time_order_user_io_wait_time / oracledb_sql_query_cpu_time_order_elapsed_time", - "format": "table", - "hide": false, - "instant": true, - "interval": "", - "legendFormat": "", - "refId": "%io" - } - ], - "title": "SQL Ordered by CPU Time (Top 10)", - "transformations": [ - { - "id": "filterFieldsByName", - "options": { - "include": { - "names": [ - "sql_id", - "Value #cpu_time", - "Value #executions", - "Value #%total", - "Value #%cpu", - "Value #%io", - "module" - ] - } - } - }, - { - "id": "merge", - "options": {} - }, - { - "id": "sortBy", - "options": { - "fields": {}, - "sort": [ - { - "desc": true, - "field": "Value #cpu_time" - } - ] - } - }, - { - "id": "calculateField", - "options": { - "alias": "CPU Time per Exec", - "binary": { - "left": "Value #cpu_time", - "operator": "/", - "reducer": "sum", - "right": "Value #executions" - }, - "mode": "binary", - "reduce": { - "reducer": "sum" - }, - "replaceFields": false - } - }, - { - "id": "organize", - "options": { - "excludeByName": { - "Value #cpu_time": false - }, - "indexByName": { - "CPU Time per Exec (s)": 4, - "Value #%cpu": 6, - "Value #%io": 7, - "Value #%total": 5, - "Value #cpu_time": 2, - "Value #executions": 3, - "module": 1, - "sql_id": 0 - }, - "renameByName": { - "Value #%cpu": "%CPU", - "Value #%io": "%IO", - "Value #%total": "%Total", - "Value #cpu_time": "CPU Time", - "Value #elapsed_time": "Elapsed Time(s)", - "Value #executions": "Executions", - "module": "Module ", - "sql_id": "SQL ID" - } - } - } - ], - "type": "table" - }, - { - "datasource": null, - "description": "%Total - User I/O Time as a percentage of Total User I/O Wait time\n\n%CPU - CPU Time as a percentage of Elapsed Time\n\n%IO - User I/O Time as a percentage of Elapsed Time", - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "custom": { - "align": null, - "filterable": false - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [ - { - "matcher": { - "id": "byName", - "options": "User I/O Time" - }, - "properties": [ - { - "id": "unit", - "value": "Β΅s" - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "%Total" - }, - "properties": [ - { - "id": "unit", - "value": "percentunit" - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "%CPU" - }, - "properties": [ - { - "id": "unit", - "value": "percentunit" - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "%IO" - }, - "properties": [ - { - "id": "unit", - "value": "percentunit" - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "UIO per Exec" - }, - "properties": [ - { - "id": "unit", - "value": "Β΅s" - } - ] - } - ] - }, - "gridPos": { - "h": 12, - "w": 24, - "x": 0, - "y": 25 - }, - "id": 15, - "interval": null, - "maxDataPoints": null, - "options": { - "frameIndex": 0, - "showHeader": true - }, - "pluginVersion": "7.5.5", - "targets": [ - { - "exemplar": true, - "expr": "oracledb_sql_query_uio_time_order_user_io_wait_time", - "format": "table", - "instant": true, - "interval": "", - "legendFormat": "", - "refId": "user_io_time" - }, - { - "exemplar": true, - "expr": "oracledb_sql_query_uio_time_order_executions", - "format": "table", - "hide": false, - "instant": true, - "interval": "", - "intervalFactor": 1, - "legendFormat": "", - "refId": "executions" - }, - { - "exemplar": true, - "expr": "oracledb_sql_query_uio_time_order_user_io_wait_time / on(inst_id, instance, job) group_left oracledb_sql_query_total_io_wait_time", - "format": "table", - "hide": false, - "instant": true, - "interval": "", - "legendFormat": "", - "refId": "%total" - }, - { - "exemplar": true, - "expr": "oracledb_sql_query_uio_time_order_cpu_time / oracledb_sql_query_uio_time_order_elapsed_time", - "format": "table", - "hide": false, - "instant": true, - "interval": "", - "legendFormat": "", - "refId": "%cpu" - }, - { - "exemplar": true, - "expr": "oracledb_sql_query_uio_time_order_user_io_wait_time / oracledb_sql_query_uio_time_order_elapsed_time", - "format": "table", - "hide": false, - "instant": true, - "interval": "", - "legendFormat": "", - "refId": "%io" - } - ], - "title": "SQL Ordered by User I/O Wait Time (Top 10)", - "transformations": [ - { - "id": "filterFieldsByName", - "options": { - "include": { - "names": [ - "sql_id", - "Value #user_io_time", - "Value #executions", - "Value #%total", - "Value #%cpu", - "Value #%io", - "module" - ] - } - } - }, - { - "id": "merge", - "options": {} - }, - { - "id": "sortBy", - "options": { - "fields": {}, - "sort": [ - { - "desc": true, - "field": "Value #user_io_time" - } - ] - } - }, - { - "id": "calculateField", - "options": { - "alias": "UIO per Exec", - "binary": { - "left": "Value #user_io_time", - "operator": "/", - "reducer": "sum", - "right": "Value #executions" - }, - "mode": "binary", - "reduce": { - "reducer": "sum" - }, - "replaceFields": false - } - }, - { - "id": "organize", - "options": { - "excludeByName": { - "Value #cpu_time": false - }, - "indexByName": { - "UIO per Exec (s)": 4, - "Value #%cpu": 6, - "Value #%io": 7, - "Value #%total": 5, - "Value #executions": 3, - "Value #user_io_time": 2, - "module": 1, - "sql_id": 0 - }, - "renameByName": { - "Value #%cpu": "%CPU", - "Value #%io": "%IO", - "Value #%total": "%Total", - "Value #cpu_time": "CPU Time", - "Value #elapsed_time": "Elapsed Time(s)", - "Value #executions": "Executions", - "Value #user_io_time": "User I/O Time", - "module": "Module", - "sql_id": "SQL ID" - } - } - } - ], - "type": "table" - }, - { - "datasource": null, - "description": "%Total - Buffer Gets as a percentage of Total Buffer Gets\n\n%CPU - CPU Time as a percentage of Elapsed Time\n\n%IO - User I/O Time as a percentage of Elapsed Time", - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "custom": { - "align": null, - "filterable": false - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [ - { - "matcher": { - "id": "byName", - "options": "%Total" - }, - "properties": [ - { - "id": "unit", - "value": "percentunit" - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "%CPU" - }, - "properties": [ - { - "id": "unit", - "value": "percentunit" - }, - { - "id": "mappings", - "value": [ - { - "from": "1", - "id": 1, - "text": "100%", - "to": "2", - "type": 2 - } - ] - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "%IO" - }, - "properties": [ - { - "id": "unit", - "value": "percentunit" - } - ] - } - ] - }, - "gridPos": { - "h": 12, - "w": 24, - "x": 0, - "y": 37 - }, - "id": 16, - "interval": null, - "maxDataPoints": null, - "options": { - "frameIndex": 0, - "showHeader": true - }, - "pluginVersion": "7.5.5", - "targets": [ - { - "exemplar": true, - "expr": "oracledb_sql_query_gets_order_buffer_gets", - "format": "table", - "instant": true, - "interval": "", - "legendFormat": "", - "refId": "buffer_gets" - }, - { - "exemplar": true, - "expr": "oracledb_sql_query_gets_order_executions", - "format": "table", - "hide": false, - "instant": true, - "interval": "", - "intervalFactor": 1, - "legendFormat": "", - "refId": "executions" - }, - { - "exemplar": true, - "expr": "oracledb_sql_query_gets_order_buffer_gets / on(inst_id, instance, job) group_left oracledb_sql_query_total_buffer_gets", - "format": "table", - "hide": false, - "instant": true, - "interval": "", - "legendFormat": "", - "refId": "%total" - }, - { - "exemplar": true, - "expr": "oracledb_sql_query_gets_order_cpu_time / oracledb_sql_query_gets_order_elapsed_time", - "format": "table", - "hide": false, - "instant": true, - "interval": "", - "legendFormat": "", - "refId": "%cpu" - }, - { - "exemplar": true, - "expr": "oracledb_sql_query_gets_order_user_io_wait_time / oracledb_sql_query_gets_order_elapsed_time", - "format": "table", - "hide": false, - "instant": true, - "interval": "", - "legendFormat": "", - "refId": "%io" - } - ], - "title": "SQL Ordered by Gets (Top 10)", - "transformations": [ - { - "id": "filterFieldsByName", - "options": { - "include": { - "names": [ - "sql_id", - "Value #buffer_gets", - "Value #executions", - "Value #%total", - "Value #%cpu", - "Value #%io", - "module" - ] - } - } - }, - { - "id": "merge", - "options": {} - }, - { - "id": "sortBy", - "options": { - "fields": {}, - "sort": [ - { - "desc": true, - "field": "Value #buffer_gets" - } - ] - } - }, - { - "id": "calculateField", - "options": { - "alias": "Gets per Exec", - "binary": { - "left": "Value #buffer_gets", - "operator": "/", - "reducer": "sum", - "right": "Value #executions" - }, - "mode": "binary", - "reduce": { - "reducer": "sum" - }, - "replaceFields": false - } - }, - { - "id": "organize", - "options": { - "excludeByName": { - "Value #cpu_time": false - }, - "indexByName": { - "Gets per Exec": 4, - "Value #%cpu": 6, - "Value #%io": 7, - "Value #%total": 5, - "Value #buffer_gets": 2, - "Value #executions": 3, - "module": 1, - "sql_id": 0 - }, - "renameByName": { - "Value #%cpu": "%CPU", - "Value #%io": "%IO", - "Value #%total": "%Total", - "Value #buffer_gets": "Buffer Get", - "Value #cpu_time": "CPU Time", - "Value #elapsed_time": "Elapsed Time(s)", - "Value #executions": "Executions", - "module": "Module", - "sql_id": "SQL ID" - } - } - } - ], - "type": "table" - }, - { - "datasource": null, - "description": "%Total - Physical Reads as a percentage of Total Disk Reads\n\n%CPU - CPU Time as a percentage of Elapsed Time\n\n%IO - User I/O Time as a percentage of Elapsed Time", - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "custom": { - "align": null, - "filterable": false - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [ - { - "matcher": { - "id": "byName", - "options": "%Total" - }, - "properties": [ - { - "id": "unit", - "value": "percentunit" - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "%CPU" - }, - "properties": [ - { - "id": "unit", - "value": "percentunit" - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "%IO" - }, - "properties": [ - { - "id": "unit", - "value": "percentunit" - } - ] - } - ] - }, - "gridPos": { - "h": 12, - "w": 24, - "x": 0, - "y": 49 - }, - "id": 17, - "interval": null, - "maxDataPoints": null, - "options": { - "frameIndex": 0, - "showHeader": true - }, - "pluginVersion": "7.5.5", - "targets": [ - { - "exemplar": true, - "expr": "oracledb_sql_query_reads_order_disk_reads", - "format": "table", - "instant": true, - "interval": "", - "legendFormat": "", - "refId": "disk_reads" - }, - { - "exemplar": true, - "expr": "oracledb_sql_query_reads_order_executions", - "format": "table", - "hide": false, - "instant": true, - "interval": "", - "intervalFactor": 1, - "legendFormat": "", - "refId": "executions" - }, - { - "exemplar": true, - "expr": "oracledb_sql_query_reads_order_disk_reads / on(inst_id, instance, job) group_left oracledb_sql_query_total_disk_reads", - "format": "table", - "hide": false, - "instant": true, - "interval": "", - "legendFormat": "", - "refId": "%total" - }, - { - "exemplar": true, - "expr": "oracledb_sql_query_reads_order_cpu_time / oracledb_sql_query_reads_order_elapsed_time", - "format": "table", - "hide": false, - "instant": true, - "interval": "", - "legendFormat": "", - "refId": "%cpu" - }, - { - "exemplar": true, - "expr": "oracledb_sql_query_reads_order_user_io_wait_time / oracledb_sql_query_reads_order_elapsed_time", - "format": "table", - "hide": false, - "instant": true, - "interval": "", - "legendFormat": "", - "refId": "%io" - } - ], - "title": "SQL Ordered by Reads (Top 10)", - "transformations": [ - { - "id": "filterFieldsByName", - "options": { - "include": { - "names": [ - "sql_id", - "Value #disk_reads", - "Value #executions", - "Value #%total", - "Value #%cpu", - "Value #%io", - "module" - ] - } - } - }, - { - "id": "merge", - "options": {} - }, - { - "id": "sortBy", - "options": { - "fields": {}, - "sort": [ - { - "desc": true, - "field": "Value #disk_reads" - } - ] - } - }, - { - "id": "calculateField", - "options": { - "alias": "Reads per Exec", - "binary": { - "left": "Value #disk_reads", - "operator": "/", - "reducer": "sum", - "right": "Value #executions" - }, - "mode": "binary", - "reduce": { - "reducer": "sum" - }, - "replaceFields": false - } - }, - { - "id": "organize", - "options": { - "excludeByName": { - "Value #cpu_time": false - }, - "indexByName": { - "Reads per Exec": 4, - "Value #%cpu": 6, - "Value #%io": 7, - "Value #%total": 5, - "Value #disk_reads": 2, - "Value #executions": 3, - "module": 1, - "sql_id": 0 - }, - "renameByName": { - "Value #%cpu": "%CPU", - "Value #%io": "%IO", - "Value #%total": "%Total", - "Value #buffer_gets": "Buffer Get(s)", - "Value #cpu_time": "CPU Time", - "Value #disk_reads": "Disk Reads", - "Value #elapsed_time": "Elapsed Time(s)", - "Value #executions": "Executions", - "module": "Module", - "sql_id": "SQL ID" - } - } - } - ], - "type": "table" - }, - { - "datasource": null, - "description": "%CPU - CPU Time as a percentage of Elapsed Time\n\n%IO - User I/O Time as a percentage of Elapsed Time", - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "custom": { - "align": null, - "filterable": false - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [ - { - "matcher": { - "id": "byName", - "options": "Elapsed Time" - }, - "properties": [ - { - "id": "unit", - "value": "Β΅s" - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "%CPU" - }, - "properties": [ - { - "id": "unit", - "value": "percentunit" - }, - { - "id": "mappings", - "value": [ - { - "from": "1", - "id": 1, - "text": "100%", - "to": "2", - "type": 2 - } - ] - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "%IO" - }, - "properties": [ - { - "id": "unit", - "value": "percentunit" - } - ] - } - ] - }, - "gridPos": { - "h": 12, - "w": 24, - "x": 0, - "y": 61 - }, - "id": 18, - "interval": null, - "maxDataPoints": null, - "options": { - "frameIndex": 0, - "showHeader": true - }, - "pluginVersion": "7.5.5", - "targets": [ - { - "exemplar": true, - "expr": "oracledb_sql_query_exec_order_rows_processed", - "format": "table", - "instant": true, - "interval": "", - "legendFormat": "", - "refId": "rows_processed" - }, - { - "exemplar": true, - "expr": "oracledb_sql_query_exec_order_executions", - "format": "table", - "hide": false, - "instant": true, - "interval": "", - "intervalFactor": 1, - "legendFormat": "", - "refId": "executions" - }, - { - "exemplar": true, - "expr": "oracledb_sql_query_exec_order_elapsed_time", - "format": "table", - "hide": false, - "instant": true, - "interval": "", - "legendFormat": "", - "refId": "elapsed_time" - }, - { - "exemplar": true, - "expr": "oracledb_sql_query_exec_order_cpu_time / oracledb_sql_query_exec_order_elapsed_time", - "format": "table", - "hide": false, - "instant": true, - "interval": "", - "legendFormat": "", - "refId": "%cpu" - }, - { - "exemplar": true, - "expr": "oracledb_sql_query_exec_order_user_io_wait_time / oracledb_sql_query_exec_order_elapsed_time", - "format": "table", - "hide": false, - "instant": true, - "interval": "", - "legendFormat": "", - "refId": "%io" - } - ], - "title": "SQL Ordered by Executions (Top 10)", - "transformations": [ - { - "id": "filterFieldsByName", - "options": { - "include": { - "names": [ - "sql_id", - "Value #rows_processed", - "Value #executions", - "Value #elapsed_time", - "Value #%cpu", - "Value #%io", - "module" - ] - } - } - }, - { - "id": "merge", - "options": {} - }, - { - "id": "sortBy", - "options": { - "fields": {}, - "sort": [ - { - "desc": true, - "field": "Value #executions" - } - ] - } - }, - { - "id": "calculateField", - "options": { - "alias": "Rows per Exec", - "binary": { - "left": "Value #rows_processed", - "operator": "/", - "reducer": "sum", - "right": "Value #executions" - }, - "mode": "binary", - "reduce": { - "reducer": "sum" - }, - "replaceFields": false - } - }, - { - "id": "organize", - "options": { - "excludeByName": { - "Value #cpu_time": false - }, - "indexByName": { - "Rows per Exec": 4, - "Value #%cpu": 6, - "Value #%io": 7, - "Value #elapsed_time": 5, - "Value #executions": 2, - "Value #rows_processed": 3, - "module": 1, - "sql_id": 0 - }, - "renameByName": { - "Value #%cpu": "%CPU", - "Value #%io": "%IO", - "Value #%total": "%Total", - "Value #buffer_gets": "Buffer Get(s)", - "Value #cpu_time": "CPU Time", - "Value #disk_reads": "Disk Reads", - "Value #elapsed_time": "Elapsed Time", - "Value #executions": "Executions", - "Value #rows_processed": "Rows Processed", - "module": "Module", - "sql_id": "SQL ID" - } - } - } - ], - "type": "table" - }, - { - "datasource": null, - "description": "", - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "custom": { - "align": null, - "filterable": false - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [ - { - "matcher": { - "id": "byName", - "options": "%Total" - }, - "properties": [ - { - "id": "unit", - "value": "percentunit" - } - ] - } - ] - }, - "gridPos": { - "h": 12, - "w": 24, - "x": 0, - "y": 73 - }, - "id": 19, - "interval": null, - "maxDataPoints": null, - "options": { - "frameIndex": 0, - "showHeader": true - }, - "pluginVersion": "7.5.5", - "targets": [ - { - "exemplar": true, - "expr": "oracledb_sql_query_parse_calls_order_parse_calls", - "format": "table", - "instant": true, - "interval": "", - "legendFormat": "", - "refId": "parse_call" - }, - { - "exemplar": true, - "expr": "oracledb_sql_query_parse_calls_order_executions", - "format": "table", - "hide": false, - "instant": true, - "interval": "", - "intervalFactor": 1, - "legendFormat": "", - "refId": "executions" - }, - { - "exemplar": true, - "expr": "oracledb_sql_query_parse_calls_order_parse_calls / on (inst_id, instance, job) group_left oracledb_sql_query_total_parse_calls", - "format": "table", - "hide": false, - "instant": true, - "interval": "", - "legendFormat": "", - "refId": "%total" - } - ], - "title": "SQL Ordered by Parse Calls (Top 10)", - "transformations": [ - { - "id": "filterFieldsByName", - "options": { - "include": { - "names": [ - "sql_id", - "Value #parse_call", - "Value #executions", - "Value #%total", - "module" - ] - } - } - }, - { - "id": "merge", - "options": {} - }, - { - "id": "sortBy", - "options": { - "fields": {}, - "sort": [ - { - "desc": true, - "field": "Value #parse_call" - } - ] - } - }, - { - "id": "organize", - "options": { - "excludeByName": { - "Value #cpu_time": false - }, - "indexByName": { - "Value #%total": 4, - "Value #executions": 3, - "Value #parse_call": 2, - "module": 1, - "sql_id": 0 - }, - "renameByName": { - "Value #%cpu": "%CPU", - "Value #%io": "%IO", - "Value #%total": "%Total", - "Value #buffer_gets": "Buffer Get(s)", - "Value #cpu_time": "CPU Time", - "Value #disk_reads": "Disk Reads", - "Value #elapsed_time": "Elapsed Time(s)", - "Value #executions": "Executions", - "Value #parse_call": "Parse Call", - "Value #rows_processed": "Rows Processed", - "module": "Module", - "sql_id": "SQL ID" - } - } - } - ], - "type": "table" - } - ], - "refresh": "30s", - "schemaVersion": 27, - "style": "dark", - "tags": [], - "templating": { - "list": [ - { - "allValue": null, - "current": { - "selected": true, - "text": "1", - "value": "1" - }, - "datasource": null, - "definition": "label_values(oracledb_instance_dummy, inst_id)", - "description": null, - "error": null, - "hide": 0, - "includeAll": false, - "label": null, - "multi": false, - "name": "instance", - "options": [ - { - "selected": true, - "text": "1", - "value": "1" - } - ], - "query": { - "query": "label_values(oracledb_instance_dummy, inst_id)", - "refId": "StandardVariableQuery" - }, - "refresh": 0, - "regex": "", - "skipUrlSync": false, - "sort": 0, - "tagValuesQuery": "", - "tags": [], - "tagsQuery": "", - "type": "query", - "useTags": false - } - ] - }, - "time": { - "from": "now-6h", - "to": "now" - }, - "timepicker": {}, - "timezone": "", - "title": "Oracle App-specific - Query Level - v1.1", - "uid": "83irx6rGz", - "version": 15 -} diff --git a/docker_vol/graf_app_vol/dashboard_sys.json b/docker_vol/graf_app_vol/dashboard_sys.json deleted file mode 100644 index 8ed0623..0000000 --- a/docker_vol/graf_app_vol/dashboard_sys.json +++ /dev/null @@ -1,3098 +0,0 @@ -{ - "annotations": { - "list": [ - { - "builtIn": 1, - "datasource": "-- Grafana --", - "enable": true, - "hide": true, - "iconColor": "rgba(0, 211, 255, 1)", - "name": "Annotations & Alerts", - "type": "dashboard" - } - ] - }, - "editable": true, - "gnetId": null, - "graphTooltip": 0, - "id": 2, - "iteration": 1627678976274, - "links": [], - "panels": [ - { - "collapsed": false, - "datasource": null, - "gridPos": { - "h": 1, - "w": 24, - "x": 0, - "y": 0 - }, - "id": 50, - "panels": [], - "title": "Database and Instance Info", - "type": "row" - }, - { - "cacheTimeout": null, - "datasource": null, - "description": "", - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "none" - }, - "overrides": [] - }, - "gridPos": { - "h": 3, - "w": 11, - "x": 0, - "y": 1 - }, - "id": 48, - "interval": null, - "links": [], - "maxDataPoints": 100, - "options": { - "colorMode": "value", - "graphMode": "none", - "justifyMode": "auto", - "orientation": "horizontal", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "/^banner$/", - "values": false - }, - "text": {}, - "textMode": "value" - }, - "pluginVersion": "7.5.5", - "targets": [ - { - "exemplar": true, - "expr": "oracledb_database_version_dummy", - "format": "table", - "hide": false, - "instant": true, - "interval": "", - "legendFormat": "", - "refId": "A" - } - ], - "timeFrom": null, - "timeShift": null, - "title": "Database Version", - "transformations": [ - { - "id": "labelsToFields", - "options": {} - } - ], - "type": "stat" - }, - { - "cacheTimeout": null, - "datasource": null, - "fieldConfig": { - "defaults": { - "color": { - "fixedColor": "green", - "mode": "fixed" - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "none" - }, - "overrides": [] - }, - "gridPos": { - "h": 3, - "w": 4, - "x": 11, - "y": 1 - }, - "id": 14, - "interval": null, - "links": [], - "maxDataPoints": 100, - "options": { - "colorMode": "value", - "graphMode": "none", - "justifyMode": "center", - "orientation": "auto", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "/^instance_name$/", - "values": false - }, - "text": {}, - "textMode": "value" - }, - "pluginVersion": "7.5.5", - "repeat": null, - "targets": [ - { - "exemplar": true, - "expr": "max(oracledb_instance_dummy{inst_id=\"[[Instance]]\"}) by (instance_name)", - "format": "table", - "instant": false, - "interval": "", - "legendFormat": "", - "refId": "A" - } - ], - "timeFrom": null, - "timeShift": null, - "title": "Instance Name", - "type": "stat" - }, - { - "cacheTimeout": null, - "datasource": "Prometheus", - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "decimals": 1, - "mappings": [ - { - "$$hashKey": "object:1228", - "id": 0, - "op": "=", - "text": "UP", - "type": 1, - "value": "1" - }, - { - "$$hashKey": "object:1229", - "id": 1, - "op": "=", - "text": "DOWN", - "type": 1, - "value": "0" - } - ], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "s" - }, - "overrides": [] - }, - "gridPos": { - "h": 3, - "w": 2, - "x": 15, - "y": 1 - }, - "id": 2, - "interval": "", - "links": [], - "maxDataPoints": 100, - "options": { - "colorMode": "value", - "graphMode": "none", - "justifyMode": "auto", - "orientation": "horizontal", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "text": {}, - "textMode": "auto" - }, - "pluginVersion": "7.5.5", - "targets": [ - { - "calculatedInterval": "10m", - "datasourceErrors": {}, - "errors": {}, - "expr": "oracledb_up", - "format": "time_series", - "hide": false, - "instant": true, - "interval": "", - "intervalFactor": 1, - "legendFormat": "", - "metric": "", - "refId": "A", - "step": 300 - } - ], - "title": "Database Status", - "type": "stat" - }, - { - "cacheTimeout": null, - "datasource": "Prometheus", - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "decimals": 1, - "mappings": [ - { - "$$hashKey": "object:1228", - "id": 0, - "op": "=", - "text": "YES", - "type": 1, - "value": "1" - }, - { - "$$hashKey": "object:1229", - "id": 1, - "op": "=", - "text": "NO", - "type": 1, - "value": "0" - } - ], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "s" - }, - "overrides": [] - }, - "gridPos": { - "h": 3, - "w": 2, - "x": 17, - "y": 1 - }, - "id": 59, - "interval": "", - "links": [], - "maxDataPoints": 100, - "options": { - "colorMode": "value", - "graphMode": "none", - "justifyMode": "auto", - "orientation": "horizontal", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "text": {}, - "textMode": "auto" - }, - "pluginVersion": "7.5.5", - "targets": [ - { - "calculatedInterval": "10m", - "datasourceErrors": {}, - "errors": {}, - "exemplar": true, - "expr": "oracledb_database_rac_value", - "format": "time_series", - "hide": false, - "instant": true, - "interval": "", - "intervalFactor": 1, - "legendFormat": "", - "metric": "", - "refId": "A", - "step": 300 - } - ], - "title": "RAC DB", - "type": "stat" - }, - { - "cacheTimeout": null, - "datasource": null, - "fieldConfig": { - "defaults": { - "color": { - "fixedColor": "green", - "mode": "fixed" - }, - "mappings": [ - { - "$$hashKey": "object:571", - "id": 0, - "op": "=", - "text": "N/A", - "type": 1, - "value": "null" - } - ], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "none" - }, - "overrides": [] - }, - "gridPos": { - "h": 3, - "w": 5, - "x": 19, - "y": 1 - }, - "id": 16, - "interval": null, - "links": [], - "maxDataPoints": 100, - "options": { - "colorMode": "value", - "graphMode": "none", - "justifyMode": "auto", - "orientation": "horizontal", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "/^host_name$/", - "values": false - }, - "text": {}, - "textMode": "auto" - }, - "pluginVersion": "7.5.5", - "targets": [ - { - "expr": "max(oracledb_instance_dummy) by (host_name)", - "format": "table", - "instant": true, - "refId": "A" - } - ], - "timeFrom": null, - "timeShift": null, - "title": "Host Name", - "type": "stat" - }, - { - "cacheTimeout": null, - "colorBackground": false, - "colorPostfix": false, - "colorPrefix": false, - "colorValue": false, - "colors": [ - "#73BF69", - "rgba(237, 129, 40, 0.89)", - "#d44a3a" - ], - "datasource": null, - "fieldConfig": { - "defaults": {}, - "overrides": [] - }, - "format": "none", - "gauge": { - "maxValue": 100, - "minValue": 0, - "show": false, - "thresholdLabels": false, - "thresholdMarkers": true - }, - "gridPos": { - "h": 3, - "w": 12, - "x": 0, - "y": 4 - }, - "id": 18, - "interval": null, - "links": [], - "mappingType": 1, - "mappingTypes": [ - { - "$$hashKey": "object:1519", - "name": "value to text", - "value": 1 - }, - { - "$$hashKey": "object:1520", - "name": "range to text", - "value": 2 - } - ], - "maxDataPoints": 100, - "nullPointMode": "connected", - "nullText": null, - "postfix": "", - "postfixFontSize": "50%", - "prefix": "", - "prefixFontSize": "50%", - "rangeMaps": [ - { - "from": "null", - "text": "N/A", - "to": "null" - } - ], - "sparkline": { - "fillColor": "rgba(31, 118, 189, 0.18)", - "full": false, - "lineColor": "rgb(31, 120, 193)", - "show": false, - "ymax": null, - "ymin": null - }, - "tableColumn": "uptime", - "targets": [ - { - "exemplar": true, - "expr": "max(oracledb_instance_dummy) by (uptime)", - "format": "table", - "instant": false, - "interval": "", - "legendFormat": "", - "refId": "A" - } - ], - "thresholds": "", - "timeFrom": null, - "timeShift": null, - "title": "Uptime", - "type": "singlestat", - "valueFontSize": "70%", - "valueMaps": [], - "valueName": "current" - }, - { - "cacheTimeout": null, - "colorBackground": false, - "colorPostfix": false, - "colorPrefix": false, - "colorValue": true, - "colors": [ - "#73BF69", - "rgba(237, 129, 40, 0.89)", - "#d44a3a" - ], - "datasource": null, - "fieldConfig": { - "defaults": {}, - "overrides": [] - }, - "format": "none", - "gauge": { - "maxValue": 100, - "minValue": 0, - "show": false, - "thresholdLabels": false, - "thresholdMarkers": true - }, - "gridPos": { - "h": 3, - "w": 12, - "x": 12, - "y": 4 - }, - "id": 17, - "interval": null, - "links": [], - "mappingType": 1, - "mappingTypes": [ - { - "$$hashKey": "object:281", - "name": "value to text", - "value": 1 - }, - { - "$$hashKey": "object:282", - "name": "range to text", - "value": 2 - } - ], - "maxDataPoints": 100, - "nullPointMode": "connected", - "nullText": null, - "postfix": "", - "postfixFontSize": "50%", - "prefix": "", - "prefixFontSize": "50%", - "rangeMaps": [ - { - "from": "null", - "text": "N/A", - "to": "null" - } - ], - "sparkline": { - "fillColor": "rgba(31, 118, 189, 0.18)", - "full": false, - "lineColor": "rgb(31, 120, 193)", - "show": false, - "ymax": null, - "ymin": null - }, - "tableColumn": "stime", - "targets": [ - { - "expr": "max(oracledb_instance_dummy) by (stime)", - "format": "table", - "instant": true, - "legendFormat": "", - "refId": "A" - } - ], - "thresholds": "", - "timeFrom": null, - "timeShift": null, - "title": "Startup Time", - "type": "singlestat", - "valueFontSize": "80%", - "valueMaps": [], - "valueName": "current" - }, - { - "collapsed": false, - "datasource": null, - "gridPos": { - "h": 1, - "w": 24, - "x": 0, - "y": 7 - }, - "id": 52, - "panels": [], - "title": "CPU and Memory", - "type": "row" - }, - { - "cacheTimeout": null, - "colorBackground": false, - "colorValue": true, - "colors": [ - "#37872D", - "rgba(237, 129, 40, 0.89)", - "rgba(50, 172, 45, 0.97)" - ], - "datasource": "Prometheus", - "editable": true, - "error": false, - "fieldConfig": { - "defaults": {}, - "overrides": [] - }, - "format": "none", - "gauge": { - "maxValue": 100, - "minValue": 0, - "show": false, - "thresholdLabels": false, - "thresholdMarkers": true - }, - "gridPos": { - "h": 4, - "w": 6, - "x": 0, - "y": 8 - }, - "height": "125px", - "id": 4, - "interval": "", - "links": [], - "mappingType": 1, - "mappingTypes": [ - { - "$$hashKey": "object:350", - "name": "value to text", - "value": 1 - }, - { - "$$hashKey": "object:351", - "name": "range to text", - "value": 2 - } - ], - "maxDataPoints": 100, - "nullPointMode": "connected", - "nullText": null, - "postfix": "", - "postfixFontSize": "100%", - "prefix": "", - "prefixFontSize": "100%", - "rangeMaps": [ - { - "from": "null", - "text": "N/A", - "to": "null" - } - ], - "sparkline": { - "fillColor": "rgba(31, 118, 189, 0.18)", - "full": false, - "lineColor": "rgb(31, 120, 193)", - "show": false - }, - "tableColumn": "", - "targets": [ - { - "exemplar": true, - "expr": "oracledb_os_value{stat_name=\"NUM_CPUS\"}", - "format": "time_series", - "instant": true, - "interval": "", - "intervalFactor": 1, - "legendFormat": "", - "refId": "A", - "step": 300 - } - ], - "thresholds": "", - "title": "Number of CPUs", - "type": "singlestat", - "valueFontSize": "120%", - "valueMaps": [ - { - "$$hashKey": "object:353", - "op": "=", - "text": "N/A", - "value": "null" - } - ], - "valueName": "current" - }, - { - "datasource": null, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "displayName": "", - "mappings": [], - "max": 1, - "min": 0, - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "#EAB839", - "value": 0.5 - }, - { - "color": "red", - "value": 0.8 - } - ] - }, - "unit": "percentunit" - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 6, - "x": 6, - "y": 8 - }, - "id": 33, - "options": { - "orientation": "auto", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "showThresholdLabels": true, - "showThresholdMarkers": true, - "text": {} - }, - "pluginVersion": "7.5.5", - "targets": [ - { - "expr": "rate(oracledb_os_value{stat_name=\"BUSY_TIME\"}[2m]) / ignoring(stat_name) oracledb_os_value{stat_name=\"NUM_CPUS\"} / ignoring(stat_name) rate(job:os:total_cpu_elpased_time[2m])", - "instant": true, - "refId": "A" - } - ], - "timeFrom": null, - "timeShift": null, - "title": "DB CPU Usage", - "type": "gauge" - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": null, - "decimals": 2, - "description": "BUSY_TIME: Number of hundredths of a second that a processor has been busy executing user or kernel code, totalled over all processors\n\nIO_WAIT_TIME: Number of hundredths of a second that a processor has been waiting for I/O to complete, totalled over all processors\n\nSYS_TIME: Number of hundredths of a second that a processor has been busy executing kernel code, totalled over all processors\n\nUSER_TIME: Number of hundredths of a second that a processor has been busy executing user code, totalled over all processors\n\nNICE_TIME: Number of hundredths of a second that a processor has been busy executing low-priority user code, totalled over all processors\n\n", - "fieldConfig": { - "defaults": { - "links": [] - }, - "overrides": [] - }, - "fill": 6, - "fillGradient": 2, - "gridPos": { - "h": 13, - "w": 12, - "x": 12, - "y": 8 - }, - "hiddenSeries": false, - "id": 8, - "legend": { - "alignAsTable": true, - "avg": true, - "current": true, - "hideEmpty": false, - "hideZero": false, - "max": true, - "min": true, - "rightSide": false, - "show": true, - "sideWidth": null, - "total": false, - "values": true - }, - "lines": true, - "linewidth": 1, - "links": [ - { - "targetBlank": true, - "title": "Introduction to each statistic name", - "url": "https://docs.oracle.com/en/database/oracle/oracle-database/19/refrn/V-OSSTAT.html#GUID-E1E48692-47FA-4AE3-9402-82477E66FFC0" - } - ], - "nullPointMode": "null", - "options": { - "alertThreshold": true - }, - "percentage": false, - "pluginVersion": "7.5.5", - "pointradius": 2, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "expr": "rate(oracledb_os_value{stat_name=\"BUSY_TIME\"}[2m]) / ignoring(stat_name) oracledb_os_value{stat_name=\"NUM_CPUS\"} / ignoring(stat_name) rate(job:os:total_cpu_elpased_time[2m])", - "instant": false, - "intervalFactor": 1, - "legendFormat": "busy", - "refId": "A" - }, - { - "expr": "rate(oracledb_os_value{stat_name=\"IOWAIT_TIME\"}[2m]) / ignoring(stat_name) rate(job:os:total_cpu_elpased_time[2m])", - "legendFormat": "iowait", - "refId": "B" - }, - { - "expr": "rate(oracledb_os_value{stat_name=\"SYS_TIME\"}[2m]) / ignoring(stat_name) rate(job:os:total_cpu_elpased_time[2m])", - "legendFormat": "sys", - "refId": "C" - }, - { - "expr": "rate(oracledb_os_value{stat_name=\"USER_TIME\"}[2m]) / ignoring(stat_name) rate(job:os:total_cpu_elpased_time[2m])", - "legendFormat": "user", - "refId": "D" - }, - { - "expr": "rate(oracledb_os_value{stat_name=\"NICE_TIME\"}[2m]) / ignoring(stat_name) rate(job:os:total_cpu_elpased_time[2m])", - "legendFormat": "nice", - "refId": "E" - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "CPU Usage", - "tooltip": { - "shared": true, - "sort": 2, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "$$hashKey": "object:3194", - "decimals": 2, - "format": "percentunit", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - }, - { - "$$hashKey": "object:3195", - "decimals": 5, - "format": "percent", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": false - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - }, - { - "cacheTimeout": null, - "colorBackground": false, - "colorValue": true, - "colors": [ - "rgba(50, 172, 45, 0.97)", - "rgba(237, 129, 40, 0.89)", - "#FADE2A" - ], - "datasource": "Prometheus", - "editable": true, - "error": false, - "fieldConfig": { - "defaults": {}, - "overrides": [] - }, - "format": "none", - "gauge": { - "maxValue": 100, - "minValue": 0, - "show": false, - "thresholdLabels": false, - "thresholdMarkers": true - }, - "gridPos": { - "h": 4, - "w": 6, - "x": 0, - "y": 12 - }, - "height": "125px", - "id": 11, - "interval": "", - "links": [], - "mappingType": 1, - "mappingTypes": [ - { - "name": "value to text", - "value": 1 - }, - { - "name": "range to text", - "value": 2 - } - ], - "maxDataPoints": 100, - "nullPointMode": "connected", - "nullText": null, - "postfix": "", - "postfixFontSize": "50%", - "prefix": "", - "prefixFontSize": "80%", - "rangeMaps": [ - { - "from": "null", - "text": "N/A", - "to": "null" - } - ], - "sparkline": { - "fillColor": "rgba(31, 118, 189, 0.18)", - "full": false, - "lineColor": "rgb(31, 120, 193)", - "show": false - }, - "tableColumn": "", - "targets": [ - { - "expr": "oracledb_os_value{stat_name=\"NUM_CPU_CORES\"}", - "format": "time_series", - "instant": true, - "interval": "", - "intervalFactor": 1, - "legendFormat": "", - "refId": "A", - "step": 300 - } - ], - "thresholds": "", - "title": "Number of CPU Cores", - "type": "singlestat", - "valueFontSize": "120%", - "valueMaps": [ - { - "op": "=", - "text": "N/A", - "value": "null" - } - ], - "valueName": "current" - }, - { - "cacheTimeout": null, - "colorBackground": false, - "colorValue": false, - "colors": [ - "#73BF69", - "rgba(237, 129, 40, 0.89)", - "#d44a3a" - ], - "datasource": null, - "decimals": null, - "fieldConfig": { - "defaults": {}, - "overrides": [] - }, - "format": "bytes", - "gauge": { - "maxValue": 100, - "minValue": 0, - "show": false, - "thresholdLabels": false, - "thresholdMarkers": true - }, - "gridPos": { - "h": 5, - "w": 6, - "x": 0, - "y": 16 - }, - "id": 35, - "interval": null, - "links": [], - "mappingType": 1, - "mappingTypes": [ - { - "name": "value to text", - "value": 1 - }, - { - "name": "range to text", - "value": 2 - } - ], - "maxDataPoints": 100, - "nullPointMode": "connected", - "nullText": null, - "postfix": "", - "postfixFontSize": "50%", - "prefix": "", - "prefixFontSize": "50%", - "rangeMaps": [ - { - "from": "null", - "text": "N/A", - "to": "null" - } - ], - "sparkline": { - "fillColor": "rgba(31, 118, 189, 0.18)", - "full": false, - "lineColor": "rgb(31, 120, 193)", - "show": false, - "ymax": null, - "ymin": null - }, - "tableColumn": "", - "targets": [ - { - "expr": "oracledb_os_value{stat_name=\"PHYSICAL_MEMORY_BYTES\"} ", - "instant": true, - "refId": "A" - } - ], - "thresholds": "", - "timeFrom": null, - "timeShift": null, - "title": "Total Physical Memory(OS)", - "type": "singlestat", - "valueFontSize": "150%", - "valueMaps": [ - { - "op": "=", - "text": "N/A", - "value": "null" - } - ], - "valueName": "current" - }, - { - "cacheTimeout": null, - "colorBackground": false, - "colorValue": false, - "colors": [ - "#73BF69", - "rgba(237, 129, 40, 0.89)", - "#d44a3a" - ], - "datasource": null, - "fieldConfig": { - "defaults": {}, - "overrides": [] - }, - "format": "bytes", - "gauge": { - "maxValue": 100, - "minValue": 0, - "show": false, - "thresholdLabels": false, - "thresholdMarkers": true - }, - "gridPos": { - "h": 5, - "w": 6, - "x": 6, - "y": 16 - }, - "id": 39, - "interval": null, - "links": [ - { - "targetBlank": true, - "title": "V$SGASTAT displays detailed information on the system global area (SGA).", - "url": "https://docs.oracle.com/en/database/oracle/oracle-database/19/refrn/V-SGASTAT.html#GUID-60D2578E-2293-45F5-91C1-35FDF047E520" - } - ], - "mappingType": 1, - "mappingTypes": [ - { - "$$hashKey": "object:2102", - "name": "value to text", - "value": 1 - }, - { - "$$hashKey": "object:2103", - "name": "range to text", - "value": 2 - } - ], - "maxDataPoints": 100, - "nullPointMode": "connected", - "nullText": null, - "postfix": "", - "postfixFontSize": "50%", - "prefix": "", - "prefixFontSize": "50%", - "rangeMaps": [ - { - "from": "null", - "text": "N/A", - "to": "null" - } - ], - "sparkline": { - "fillColor": "rgba(31, 118, 189, 0.18)", - "full": false, - "lineColor": "rgb(31, 120, 193)", - "show": false, - "ymax": null, - "ymin": null - }, - "tableColumn": "", - "targets": [ - { - "expr": "oracledb_sga_total_b", - "instant": true, - "refId": "A" - } - ], - "thresholds": "", - "timeFrom": null, - "timeShift": null, - "title": "SGA Total Memory", - "type": "singlestat", - "valueFontSize": "120%", - "valueMaps": [ - { - "$$hashKey": "object:2105", - "op": "=", - "text": "N/A", - "value": "null" - } - ], - "valueName": "current" - }, - { - "cacheTimeout": null, - "datasource": "Prometheus", - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "mappings": [], - "max": 1, - "min": 0, - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "#EAB839", - "value": 0.5 - }, - { - "color": "red", - "value": 0.8 - } - ] - }, - "unit": "percentunit" - }, - "overrides": [] - }, - "gridPos": { - "h": 7, - "w": 6, - "x": 0, - "y": 21 - }, - "id": 12, - "links": [], - "options": { - "orientation": "horizontal", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "showThresholdLabels": true, - "showThresholdMarkers": true, - "text": {} - }, - "pluginVersion": "7.5.5", - "targets": [ - { - "expr": "oracledb_os_value{stat_name=\"FREE_MEMORY_BYTES\"} / ignoring(stat_name) oracledb_os_value{stat_name=\"PHYSICAL_MEMORY_BYTES\"}", - "format": "time_series", - "instant": true, - "interval": "", - "intervalFactor": 1, - "legendFormat": "", - "refId": "A", - "step": 300 - } - ], - "title": "Memory Usage(OS)", - "type": "gauge" - }, - { - "datasource": null, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "mappings": [], - "max": 1, - "min": 0, - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 0.8 - } - ] - }, - "unit": "percentunit" - }, - "overrides": [] - }, - "gridPos": { - "h": 7, - "w": 6, - "x": 6, - "y": 21 - }, - "id": 41, - "options": { - "orientation": "auto", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "showThresholdLabels": true, - "showThresholdMarkers": true, - "text": {} - }, - "pluginVersion": "7.5.5", - "targets": [ - { - "expr": "oracledb_sga_used_b / ignoring(inst_id) oracledb_sga_total_b", - "instant": true, - "refId": "A" - } - ], - "timeFrom": null, - "timeShift": null, - "title": "SGA Memory Usage", - "type": "gauge" - }, - { - "aliasColors": { - "Load 1m": "dark-orange" - }, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": null, - "fieldConfig": { - "defaults": { - "links": [] - }, - "overrides": [] - }, - "fill": 8, - "fillGradient": 0, - "gridPos": { - "h": 7, - "w": 12, - "x": 12, - "y": 21 - }, - "hiddenSeries": false, - "id": 20, - "interval": "", - "legend": { - "alignAsTable": true, - "avg": true, - "current": true, - "max": true, - "min": true, - "rightSide": false, - "show": true, - "total": false, - "values": true - }, - "lines": true, - "linewidth": 1, - "nullPointMode": "null", - "options": { - "alertThreshold": true - }, - "percentage": false, - "pluginVersion": "7.5.5", - "pointradius": 2, - "points": false, - "renderer": "flot", - "seriesOverrides": [ - { - "alias": "sga free", - "color": "#1F60C4" - }, - { - "alias": "sga used", - "color": "#8AB8FF" - } - ], - "spaceLength": 10, - "stack": true, - "steppedLine": false, - "targets": [ - { - "expr": "oracledb_sga_free_b", - "interval": "", - "legendFormat": "sga free", - "refId": "A" - }, - { - "expr": "oracledb_sga_used_b", - "legendFormat": "sga used", - "refId": "B" - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "SGA Memory", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "format": "decbytes", - "label": "", - "logBase": 1, - "max": null, - "min": "0", - "show": true - }, - { - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": false - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - }, - { - "aliasColors": { - "Load 1m": "dark-orange" - }, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": null, - "fieldConfig": { - "defaults": { - "links": [] - }, - "overrides": [] - }, - "fill": 5, - "fillGradient": 0, - "gridPos": { - "h": 7, - "w": 12, - "x": 0, - "y": 28 - }, - "hiddenSeries": false, - "id": 26, - "interval": "", - "legend": { - "alignAsTable": true, - "avg": true, - "current": true, - "max": true, - "min": true, - "rightSide": false, - "show": true, - "total": false, - "values": true - }, - "lines": true, - "linewidth": 1, - "nullPointMode": "null", - "options": { - "alertThreshold": true - }, - "percentage": false, - "pluginVersion": "7.5.5", - "pointradius": 2, - "points": false, - "renderer": "flot", - "seriesOverrides": [ - { - "alias": "read", - "color": "#96D98D" - }, - { - "alias": "write", - "color": "#FADE2A" - } - ], - "spaceLength": 10, - "stack": true, - "steppedLine": false, - "targets": [ - { - "exemplar": true, - "expr": "rate(oracledb_system_stat_value{name=\"physical read total bytes\"}[5m])", - "instant": false, - "interval": "", - "legendFormat": "read", - "refId": "A" - }, - { - "exemplar": true, - "expr": "rate(oracledb_system_stat_value{name=\"physical write total bytes\"}[5m])", - "instant": false, - "interval": "", - "legendFormat": "write", - "refId": "B" - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Total Physical Read/Write Rate", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "format": "Bps", - "label": "", - "logBase": 1, - "max": null, - "min": null, - "show": true - }, - { - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": false - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": null, - "fieldConfig": { - "defaults": { - "links": [] - }, - "overrides": [] - }, - "fill": 2, - "fillGradient": 0, - "gridPos": { - "h": 7, - "w": 12, - "x": 12, - "y": 28 - }, - "hiddenSeries": false, - "id": 37, - "legend": { - "alignAsTable": true, - "avg": true, - "current": true, - "max": true, - "min": true, - "show": true, - "total": false, - "values": true - }, - "lines": true, - "linewidth": 1, - "nullPointMode": "null", - "options": { - "alertThreshold": true - }, - "percentage": false, - "pluginVersion": "7.5.5", - "pointradius": 2, - "points": false, - "renderer": "flot", - "seriesOverrides": [ - { - "alias": "read", - "color": "#C4162A" - }, - { - "alias": "write", - "color": "#37872D" - } - ], - "spaceLength": 10, - "stack": true, - "steppedLine": false, - "targets": [ - { - "exemplar": true, - "expr": "rate(oracledb_system_stat_value{name=\"physical read total IO requests\"}[5m])", - "interval": "", - "legendFormat": "read", - "refId": "A" - }, - { - "exemplar": true, - "expr": "rate(oracledb_system_stat_value{name=\"physical write total IO requests\"}[5m])", - "interval": "", - "legendFormat": "write", - "refId": "B" - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Total Physical Read/Write IO Request Rate", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - }, - { - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - }, - { - "collapsed": false, - "datasource": null, - "gridPos": { - "h": 1, - "w": 24, - "x": 0, - "y": 35 - }, - "id": 54, - "panels": [], - "title": "Session", - "type": "row" - }, - { - "cacheTimeout": null, - "colorBackground": false, - "colorValue": true, - "colors": [ - "#73BF69", - "rgba(237, 129, 40, 0.89)", - "#d44a3a" - ], - "datasource": null, - "fieldConfig": { - "defaults": {}, - "overrides": [] - }, - "format": "none", - "gauge": { - "maxValue": 100, - "minValue": 0, - "show": false, - "thresholdLabels": false, - "thresholdMarkers": true - }, - "gridPos": { - "h": 3, - "w": 6, - "x": 0, - "y": 36 - }, - "id": 28, - "interval": null, - "links": [], - "mappingType": 1, - "mappingTypes": [ - { - "name": "value to text", - "value": 1 - }, - { - "name": "range to text", - "value": 2 - } - ], - "maxDataPoints": 100, - "nullPointMode": "connected", - "nullText": null, - "postfix": "", - "postfixFontSize": "50%", - "prefix": "", - "prefixFontSize": "50%", - "rangeMaps": [ - { - "from": "null", - "text": "N/A", - "to": "null" - } - ], - "sparkline": { - "fillColor": "rgba(31, 118, 189, 0.18)", - "full": false, - "lineColor": "rgb(31, 120, 193)", - "show": true, - "ymax": null, - "ymin": null - }, - "tableColumn": "", - "targets": [ - { - "expr": "oracledb_session_value{status=\"ACTIVE\", type=\"USER\"}", - "instant": true, - "refId": "A" - } - ], - "thresholds": "", - "timeFrom": null, - "timeShift": null, - "title": "Active User Session", - "type": "singlestat", - "valueFontSize": "150%", - "valueMaps": [ - { - "op": "=", - "text": "N/A", - "value": "null" - } - ], - "valueName": "current" - }, - { - "aliasColors": { - "Load 1m": "dark-orange" - }, - "bars": true, - "dashLength": 10, - "dashes": false, - "datasource": null, - "fieldConfig": { - "defaults": { - "links": [] - }, - "overrides": [] - }, - "fill": 1, - "fillGradient": 0, - "gridPos": { - "h": 9, - "w": 10, - "x": 6, - "y": 36 - }, - "hiddenSeries": false, - "id": 22, - "interval": "", - "legend": { - "alignAsTable": true, - "avg": true, - "current": false, - "max": true, - "min": true, - "rightSide": false, - "show": true, - "total": false, - "values": true - }, - "lines": true, - "linewidth": 2, - "nullPointMode": "null", - "options": { - "alertThreshold": true - }, - "percentage": false, - "pluginVersion": "7.5.5", - "pointradius": 2, - "points": false, - "renderer": "flot", - "seriesOverrides": [ - { - "$$hashKey": "object:2215", - "alias": "active background", - "color": "#C4162A" - }, - { - "$$hashKey": "object:2216", - "alias": "active user", - "color": "#FA6400" - }, - { - "$$hashKey": "object:2217", - "alias": "inactive user", - "color": "rgb(19, 195, 189)" - } - ], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "expr": "oracledb_session_value{status=\"ACTIVE\", type=\"BACKGROUND\"}", - "interval": "", - "legendFormat": "active background", - "refId": "A" - }, - { - "expr": "oracledb_session_value{status=\"ACTIVE\", type=\"USER\"}", - "legendFormat": "active user", - "refId": "B" - }, - { - "expr": "oracledb_session_value{status=\"INACTIVE\", type=\"USER\"}", - "legendFormat": "inactive user", - "refId": "C" - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Session", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "format": "none", - "label": "", - "logBase": 1, - "max": null, - "min": "0", - "show": true - }, - { - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": false - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - }, - { - "datasource": null, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "custom": { - "align": null, - "filterable": false - }, - "decimals": 0, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "none" - }, - "overrides": [ - { - "matcher": { - "id": "byName", - "options": "Time" - }, - "properties": [ - { - "id": "displayName", - "value": "Time" - }, - { - "id": "unit", - "value": "time: YYYY-MM-DD HH:mm:ss" - }, - { - "id": "custom.align", - "value": null - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Value #A" - }, - "properties": [ - { - "id": "displayName", - "value": "Count" - } - ] - } - ] - }, - "gridPos": { - "h": 9, - "w": 8, - "x": 16, - "y": 36 - }, - "id": 43, - "options": { - "frameIndex": 0, - "showHeader": true - }, - "pluginVersion": "7.5.5", - "targets": [ - { - "exemplar": true, - "expr": "oracledb_session_mvalue{inst_id=\"[[Instance]]\"}", - "format": "table", - "instant": true, - "interval": "", - "intervalFactor": 1, - "legendFormat": "", - "refId": "A" - } - ], - "timeFrom": null, - "timeShift": null, - "title": "Sessions by Machine", - "transformations": [ - { - "id": "filterFieldsByName", - "options": { - "include": { - "names": [ - "Time", - "machine", - "status", - "type", - "Value #A" - ] - } - } - } - ], - "type": "table" - }, - { - "cacheTimeout": null, - "datasource": null, - "fieldConfig": { - "defaults": { - "color": { - "mode": "fixed" - }, - "mappings": [ - { - "$$hashKey": "object:1328", - "id": 0, - "op": "=", - "text": "0", - "type": 1, - "value": "null" - } - ], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "none" - }, - "overrides": [] - }, - "gridPos": { - "h": 3, - "w": 6, - "x": 0, - "y": 39 - }, - "id": 30, - "interval": null, - "links": [], - "maxDataPoints": 100, - "options": { - "colorMode": "value", - "graphMode": "none", - "justifyMode": "auto", - "orientation": "horizontal", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false - }, - "text": {}, - "textMode": "auto" - }, - "pluginVersion": "7.5.5", - "targets": [ - { - "exemplar": true, - "expr": "oracledb_session_value{status=\"INACTIVE\", type=\"USER\"}", - "instant": false, - "interval": "", - "legendFormat": "", - "refId": "A" - } - ], - "timeFrom": null, - "timeShift": null, - "title": "Inactive User Session", - "type": "stat" - }, - { - "cacheTimeout": null, - "colorBackground": false, - "colorValue": true, - "colors": [ - "#FF9830", - "rgba(237, 129, 40, 0.89)", - "#d44a3a" - ], - "datasource": null, - "fieldConfig": { - "defaults": {}, - "overrides": [] - }, - "format": "none", - "gauge": { - "maxValue": 100, - "minValue": 0, - "show": false, - "thresholdLabels": false, - "thresholdMarkers": true - }, - "gridPos": { - "h": 3, - "w": 6, - "x": 0, - "y": 42 - }, - "id": 29, - "interval": null, - "links": [], - "mappingType": 1, - "mappingTypes": [ - { - "name": "value to text", - "value": 1 - }, - { - "name": "range to text", - "value": 2 - } - ], - "maxDataPoints": 100, - "nullPointMode": "connected", - "nullText": null, - "postfix": "", - "postfixFontSize": "50%", - "prefix": "", - "prefixFontSize": "50%", - "rangeMaps": [ - { - "from": "null", - "text": "N/A", - "to": "null" - } - ], - "sparkline": { - "fillColor": "rgba(31, 118, 189, 0.18)", - "full": false, - "lineColor": "rgb(31, 120, 193)", - "show": true, - "ymax": null, - "ymin": null - }, - "tableColumn": "", - "targets": [ - { - "expr": "oracledb_session_value{status=\"ACTIVE\", type=\"BACKGROUND\"}", - "instant": true, - "refId": "A" - } - ], - "thresholds": "", - "timeFrom": null, - "timeShift": null, - "title": "Active Background Session", - "type": "singlestat", - "valueFontSize": "150%", - "valueMaps": [ - { - "op": "=", - "text": "N/A", - "value": "null" - } - ], - "valueName": "current" - }, - { - "collapsed": false, - "datasource": null, - "gridPos": { - "h": 1, - "w": 24, - "x": 0, - "y": 45 - }, - "id": 56, - "panels": [], - "title": "User Activity", - "type": "row" - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": null, - "fieldConfig": { - "defaults": { - "links": [] - }, - "overrides": [] - }, - "fill": 2, - "fillGradient": 0, - "gridPos": { - "h": 13, - "w": 12, - "x": 0, - "y": 46 - }, - "hiddenSeries": false, - "id": 45, - "legend": { - "alignAsTable": true, - "avg": true, - "current": true, - "max": true, - "min": true, - "rightSide": false, - "show": true, - "total": false, - "values": true - }, - "lines": true, - "linewidth": 1, - "nullPointMode": "null", - "options": { - "alertThreshold": true - }, - "percentage": false, - "pluginVersion": "7.5.5", - "pointradius": 2, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "exemplar": true, - "expr": "rate(oracledb_system_stat_value{name=\"user calls\"}[2m])", - "interval": "", - "legendFormat": "{{name}}", - "refId": "A" - }, - { - "exemplar": true, - "expr": "rate(oracledb_system_stat_value{name=\"user commits\"}[2m])", - "interval": "", - "legendFormat": "{{name}}", - "refId": "B" - }, - { - "expr": "rate(oracledb_system_value{name=\"user rollbacks\"}[2m])", - "legendFormat": "{{name}}", - "refId": "C" - }, - { - "expr": "rate(oracledb_system_value{name=\"execute count\"}[2m])", - "legendFormat": "{{name}}", - "refId": "D" - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "User Activity Rate", - "tooltip": { - "shared": false, - "sort": 2, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "format": "none", - "label": "requests per minute", - "logBase": 1, - "max": null, - "min": null, - "show": true - }, - { - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - }, - { - "aliasColors": { - "Load 1m": "dark-orange" - }, - "bars": true, - "dashLength": 10, - "dashes": false, - "datasource": null, - "decimals": 2, - "fieldConfig": { - "defaults": { - "links": [] - }, - "overrides": [] - }, - "fill": 6, - "fillGradient": 2, - "gridPos": { - "h": 13, - "w": 12, - "x": 12, - "y": 46 - }, - "hiddenSeries": false, - "id": 24, - "interval": "", - "legend": { - "alignAsTable": true, - "avg": true, - "current": false, - "max": true, - "min": true, - "rightSide": false, - "show": true, - "total": false, - "values": true - }, - "lines": false, - "linewidth": 2, - "nullPointMode": "null", - "options": { - "alertThreshold": true - }, - "percentage": false, - "pluginVersion": "7.5.5", - "pointradius": 2, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": true, - "steppedLine": false, - "targets": [ - { - "exemplar": true, - "expr": "increase(oracledb_system_stat_value{name=\"execute count\"}[1h])", - "instant": false, - "interval": "1h", - "legendFormat": "execute count", - "refId": "A" - }, - { - "exemplar": true, - "expr": "increase(oracledb_system_stat_value{name=\"user calls\"}[1h])", - "instant": false, - "interval": "1h", - "legendFormat": "user calls", - "refId": "B" - }, - { - "expr": "increase(oracledb_system_value{name=\"user commits\"}[1h])", - "interval": "1h", - "legendFormat": "user commits", - "refId": "C" - }, - { - "expr": "increase(oracledb_system_value{name=\"user rollbacks\"}[1h])", - "interval": "1h", - "legendFormat": "rollbacks", - "refId": "D" - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "User Activity Hourly", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "min": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "format": "short", - "label": "", - "logBase": 1, - "max": null, - "min": "0", - "show": true - }, - { - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": "0", - "show": true - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - }, - { - "aliasColors": { - "Load 1m": "dark-orange" - }, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": null, - "fieldConfig": { - "defaults": { - "links": [] - }, - "overrides": [] - }, - "fill": 1, - "fillGradient": 0, - "gridPos": { - "h": 12, - "w": 12, - "x": 0, - "y": 59 - }, - "hiddenSeries": false, - "id": 21, - "interval": "", - "legend": { - "alignAsTable": true, - "avg": true, - "current": false, - "max": true, - "min": true, - "rightSide": false, - "show": true, - "total": false, - "values": true - }, - "lines": true, - "linewidth": 1, - "nullPointMode": "null", - "options": { - "alertThreshold": true - }, - "percentage": false, - "pluginVersion": "7.5.5", - "pointradius": 2, - "points": false, - "renderer": "flot", - "seriesOverrides": [ - { - "alias": "inbound", - "color": "#F2495C" - }, - { - "alias": "outbound", - "color": "#B877D9" - } - ], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "expr": "irate(oracledb_network_received_from_client_b[5m])", - "interval": "", - "legendFormat": "inbound", - "refId": "A" - }, - { - "expr": "irate(oracledb_network_sent_to_client_b[5m])", - "legendFormat": "outbound", - "refId": "B" - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Network Traffic", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "format": "Bps", - "label": "", - "logBase": 1, - "max": null, - "min": null, - "show": true - }, - { - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": false - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - }, - { - "aliasColors": { - "Load 1m": "dark-orange" - }, - "bars": true, - "dashLength": 10, - "dashes": false, - "datasource": null, - "fieldConfig": { - "defaults": { - "links": [] - }, - "overrides": [] - }, - "fill": 1, - "fillGradient": 0, - "gridPos": { - "h": 12, - "w": 12, - "x": 12, - "y": 59 - }, - "hiddenSeries": false, - "id": 25, - "interval": "", - "legend": { - "alignAsTable": true, - "avg": true, - "current": false, - "max": true, - "min": true, - "rightSide": false, - "show": true, - "total": false, - "values": true - }, - "lines": false, - "linewidth": 1, - "nullPointMode": "null", - "options": { - "alertThreshold": true - }, - "percentage": false, - "pluginVersion": "7.5.5", - "pointradius": 2, - "points": false, - "renderer": "flot", - "seriesOverrides": [ - { - "alias": "inbound", - "color": "#F2495C" - }, - { - "alias": "outbound", - "color": "#B877D9" - } - ], - "spaceLength": 10, - "stack": true, - "steppedLine": false, - "targets": [ - { - "expr": "increase(oracledb_network_received_from_client_b[1h])", - "instant": false, - "interval": "1h", - "legendFormat": "inbound", - "refId": "A" - }, - { - "expr": "increase(oracledb_network_sent_to_client_b[1h])", - "instant": false, - "interval": "1h", - "legendFormat": "outbound", - "refId": "B" - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Network Traffic Hourly", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "format": "decbytes", - "label": "", - "logBase": 1, - "max": null, - "min": null, - "show": true - }, - { - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": false - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - }, - { - "collapsed": true, - "datasource": null, - "gridPos": { - "h": 1, - "w": 24, - "x": 0, - "y": 71 - }, - "id": 58, - "panels": [ - { - "datasource": null, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "custom": { - "align": null, - "filterable": false - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [ - { - "matcher": { - "id": "byName", - "options": "Value #A" - }, - "properties": [ - { - "id": "displayName", - "value": "Time Wait Count" - }, - { - "id": "unit", - "value": "short" - }, - { - "id": "decimals", - "value": 0 - }, - { - "id": "custom.align", - "value": null - } - ] - } - ] - }, - "gridPos": { - "h": 20, - "w": 8, - "x": 0, - "y": 11 - }, - "id": 31, - "options": { - "showHeader": true, - "sortBy": [ - { - "desc": true, - "displayName": "wait time (s)" - } - ] - }, - "pluginVersion": "7.5.5", - "targets": [ - { - "exemplar": true, - "expr": "oracledb_system_wait_time_waited / 100", - "format": "table", - "instant": true, - "interval": "", - "intervalFactor": 1, - "legendFormat": "{{wait_class}}", - "refId": "A" - } - ], - "timeFrom": null, - "timeShift": null, - "title": "System: Amount of time spent in the wait class", - "transformations": [ - { - "id": "filterFieldsByName", - "options": { - "include": { - "names": [ - "wait_class", - "Value" - ] - } - } - }, - { - "id": "organize", - "options": { - "excludeByName": {}, - "indexByName": {}, - "renameByName": { - "Value": "wait time (s)", - "wait_class": "wait class" - } - } - } - ], - "type": "table" - }, - { - "datasource": null, - "description": "Seconds spent in the current wait condition", - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "custom": { - "align": null, - "filterable": false - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [ - { - "matcher": { - "id": "byName", - "options": "wait time" - }, - "properties": [ - { - "id": "unit" - } - ] - } - ] - }, - "gridPos": { - "h": 20, - "w": 15, - "x": 8, - "y": 11 - }, - "id": 47, - "options": { - "showHeader": true, - "sortBy": [ - { - "desc": true, - "displayName": "wait time (s)" - } - ] - }, - "pluginVersion": "7.5.5", - "targets": [ - { - "exemplar": true, - "expr": "oracledb_session_wait_seconds_in_wait", - "format": "table", - "instant": true, - "interval": "", - "legendFormat": "", - "refId": "A" - } - ], - "timeFrom": null, - "timeShift": null, - "title": "Session: Amount of time spent in the wait class", - "transformations": [ - { - "id": "filterFieldsByName", - "options": { - "include": { - "names": [ - "event", - "state", - "username", - "wait_class", - "Value" - ] - } - } - }, - { - "id": "organize", - "options": { - "excludeByName": {}, - "indexByName": {}, - "renameByName": { - "Value": "wait time (s)" - } - } - } - ], - "type": "table" - } - ], - "title": "Wait Time", - "type": "row" - }, - { - "collapsed": true, - "datasource": null, - "gridPos": { - "h": 1, - "w": 24, - "x": 0, - "y": 72 - }, - "id": 61, - "panels": [ - { - "aliasColors": { - "Load 1m": "dark-orange" - }, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": null, - "description": "Number of processes running or waiting on the run queue", - "fieldConfig": { - "defaults": { - "links": [] - }, - "overrides": [] - }, - "fill": 3, - "fillGradient": 0, - "gridPos": { - "h": 9, - "w": 12, - "x": 0, - "y": 12 - }, - "hiddenSeries": false, - "id": 10, - "interval": "", - "legend": { - "alignAsTable": true, - "avg": true, - "current": true, - "max": false, - "min": false, - "rightSide": false, - "show": true, - "total": false, - "values": true - }, - "lines": true, - "linewidth": 1, - "links": [ - { - "targetBlank": true, - "title": "V$OSSTAT", - "url": "https://docs.oracle.com/en/database/oracle/oracle-database/19/refrn/V-OSSTAT.html#GUID-E1E48692-47FA-4AE3-9402-82477E66FFC0" - } - ], - "nullPointMode": "null", - "options": { - "alertThreshold": true - }, - "percentage": false, - "pluginVersion": "7.5.5", - "pointradius": 2, - "points": false, - "renderer": "flot", - "seriesOverrides": [ - { - "$$hashKey": "object:2164", - "alias": "Load 1m", - "color": "rgb(248, 80, 0)" - }, - { - "$$hashKey": "object:2165", - "alias": "Load 5m", - "color": "#FF9830" - }, - { - "$$hashKey": "object:2166", - "alias": "Load 15m", - "color": "#FADE2A" - } - ], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "exemplar": true, - "expr": " sum(sum_over_time(oracledb_os_value{stat_name=\"LOAD\"}[1m])) / sum(count_over_time(oracledb_os_value{stat_name=\"LOAD\"}[1m]))", - "interval": "", - "legendFormat": "Load 1m", - "refId": "A" - }, - { - "expr": " sum(sum_over_time(oracledb_os_value{stat_name=\"LOAD\"}[5m])) / sum(count_over_time(oracledb_os_value{stat_name=\"LOAD\"}[5m]))", - "legendFormat": "Load 5m", - "refId": "B" - }, - { - "expr": " sum(sum_over_time(oracledb_os_value{stat_name=\"LOAD\"}[15m])) / sum(count_over_time(oracledb_os_value{stat_name=\"LOAD\"}[15m]))", - "legendFormat": "Load 15m", - "refId": "C" - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Process Load", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "format": "short", - "label": "", - "logBase": 1, - "max": null, - "min": null, - "show": true - }, - { - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - } - ], - "title": "Process", - "type": "row" - } - ], - "refresh": "30s", - "schemaVersion": 27, - "style": "dark", - "tags": [], - "templating": { - "list": [ - { - "allValue": null, - "current": { - "selected": true, - "text": "1", - "value": "1" - }, - "datasource": null, - "definition": "label_values(oracledb_instance_dummy, inst_id)", - "description": null, - "error": null, - "hide": 0, - "includeAll": false, - "label": null, - "multi": false, - "name": "Instance", - "options": [ - { - "selected": true, - "text": "1", - "value": "1" - } - ], - "query": { - "query": "label_values(oracledb_instance_dummy, inst_id)", - "refId": "StandardVariableQuery" - }, - "refresh": 0, - "regex": "", - "skipUrlSync": false, - "sort": 3, - "tagValuesQuery": "", - "tags": [], - "tagsQuery": "", - "type": "query", - "useTags": false - } - ] - }, - "time": { - "from": "now-3h", - "to": "now" - }, - "timepicker": { - "refresh_intervals": [ - "5s", - "10s", - "30s", - "1m", - "5m", - "15m", - "30m", - "1h", - "2h", - "1d" - ] - }, - "timezone": "browser", - "title": "Oracle App-Specific -- Database System Level - v1.1", - "uid": "fjZKiJbgz", - "version": 16 -} diff --git a/docker_vol/prom_app_vol/config.yml b/docker_vol/prom_app_vol/config.yml deleted file mode 100644 index 7d85fa1..0000000 --- a/docker_vol/prom_app_vol/config.yml +++ /dev/null @@ -1,25 +0,0 @@ -# -# -# Copyright (c) 2021 Oracle and/or its affiliates. -# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ -# -# - -global: - scrape_interval: 30s - scrape_timeout: 30s - evaluation_interval: 30s - -scrape_configs: - - job_name: 'TEQ Monitor' - scheme: https - static_configs: - - targets: ['exporter:9161'] - basic_auth: - username: auth.username - password: auth.password - tls_config: - insecure_skip_verify: true - -rule_files: - - "/etc/prometheus/prometheus_vol/myrules.yml" diff --git a/docker_vol/prom_app_vol/myrules.yml b/docker_vol/prom_app_vol/myrules.yml deleted file mode 100644 index 09c9436..0000000 --- a/docker_vol/prom_app_vol/myrules.yml +++ /dev/null @@ -1,14 +0,0 @@ -# -# -# Copyright (c) 2021 Oracle and/or its affiliates. -# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ -# -# - - -groups: - - name: database-system-rules - rules: - - record: job:os:total_cpu_elpased_time - expr: oracledb_os_value{stat_name="BUSY_TIME"} + ignoring(stat_name) oracledb_os_value{stat_name="IDLE_TIME"} - \ No newline at end of file diff --git a/docker_vol/prom_app_vol/web.yml b/docker_vol/prom_app_vol/web.yml deleted file mode 100644 index c24c7f1..0000000 --- a/docker_vol/prom_app_vol/web.yml +++ /dev/null @@ -1,12 +0,0 @@ -# -# -# Copyright (c) 2021 Oracle and/or its affiliates. -# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ -# -# - -tls_server_config: - cert_file: /localhost.crt - key_file: /localhost.key -basic_auth_users: - mntmgr: prom.auth.pwd diff --git a/exporter/Dockerfile b/exporter/Dockerfile deleted file mode 100644 index 35ac751..0000000 --- a/exporter/Dockerfile +++ /dev/null @@ -1,16 +0,0 @@ -# -# -# Copyright (c) 2021 Oracle and/or its affiliates. -# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ -# -# - -FROM oracle-db-monitoring-exporter:0.3.0-oraclelinux - -COPY default-metrics.toml /opt/ -COPY auth_config.yml / -COPY localhost.crt / -COPY localhost.key / - -ENTRYPOINT ["/oracledb_exporter", "--default.metrics", "/opt/default-metrics.toml", "--auth.config", "/auth_config.yml", \ -"--web.secured-metrics", "--web.ssl-server-cert", "/localhost.crt", "--web.ssl-server-key", "/localhost.key","--query.timeout", "60"] diff --git a/exporter/auth_config.yml b/exporter/auth_config.yml deleted file mode 100644 index 5c82fe5..0000000 --- a/exporter/auth_config.yml +++ /dev/null @@ -1,9 +0,0 @@ -# -# -# Copyright (c) 2021 Oracle and/or its affiliates. -# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ -# -# - -username: auth.username -password: auth.password \ No newline at end of file diff --git a/exporter/default-metrics.toml b/exporter/default-metrics.toml deleted file mode 100644 index 44e30c1..0000000 --- a/exporter/default-metrics.toml +++ /dev/null @@ -1,733 +0,0 @@ -# -# -# Copyright (c) 2021 Oracle and/or its affiliates. -# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ -# -# - -[[metric]] -context = "database_version" -labels = ["inst_id", "con_id", "banner"] -metricsdesc = { dummy = "full database banner info"} -request = ''' -SELECT - 1 as dummy, - inst_id, - con_id, - banner_full as banner -FROM - sys.gv_$version -''' - -[[metric]] -context = "database_rac" -labels = ["inst_id", "name", "con_id"] -metricsdesc = { dummy = "full database banner info", value = "1 for true, 0 for false if rac"} -request = ''' -SELECT - inst_id, - name, - con_id, - case - when value = 'TRUE' then 1 - when value = 'FALSE' then 0 - end as value -FROM - sys.gv_$parameter -WHERE - name = 'cluster_database' -''' - - -[[metric]] -context = "system_stat" -labels = ["inst_id", "name"] -metricsdesc = { value = "system metric values from sys.gv_$sysstat in user, sql, cache" } -request = ''' -SELECT - inst_id, - name, - value -FROM - sys.gv_$sysstat -WHERE name IN ( - 'user commits', - 'user rollbacks', - 'user calls', - 'execute count', - 'physical read total IO requests', - 'physical write total IO requests', - 'physical read total bytes', - 'physical write total bytes' - ) -''' - -[[metric]] -context = "system_wait" -labels = ["inst_id", "con_id", "wait_class"] -metricsdesc = { total_waits = "Number of times waits of the class occurred", time_waited = "Amount of time spent in the wait by all sessions in the instance" } -request = ''' -SELECT - inst_id, - con_id, - wait_class, - total_waits, - time_waited -FROM - sys.gv_$system_wait_class -''' - - -[[metric]] -context = "system_time" -labels = ["inst_id", "con_id", "stat_name"] -metricsdesc = {value = "system-wide accumulated time(in microseconds) for each operations"} -request = ''' -SELECT - inst_id, - con_id, - stat_name, - value -FROM - sys.gv_$sys_time_model -WHERE stat_name IN ( - 'DB time', - 'DB CPU', - 'SQL execute elapsed time', - 'PL/SQL execution elapsed time', - 'PL/SQL compilation elapsed time', - 'Java execution elapsed time' - ) -''' - - -[[metric]] -context = "sga" -labels = ["inst_id"] -metricsdesc = { used_b = "sga used memory in b", free_b = "sga free memory in b", total_b = "sga total memory in b" } -request = ''' -SELECT - used.inst_id AS inst_id, - used.bytes AS used_b, - free.bytes AS free_b, - total.bytes AS total_b -FROM - ( ( - SELECT - inst_id, - SUM(bytes) bytes - FROM - sys.gv_$sgastat - WHERE - name != 'free memory' - GROUP BY - inst_id - ) used - INNER JOIN ( - SELECT - inst_id, - SUM(bytes) bytes - FROM - sys.gv_$sgastat - WHERE - name = 'free memory' - GROUP BY - inst_id - ) free ON used.inst_id = free.inst_id - INNER JOIN ( - SELECT - inst_id, - SUM(bytes) bytes - FROM - sys.gv_$sgastat - GROUP BY - inst_id - ) total ON free.inst_id = total.inst_id ) -''' - -[[metric]] -context = "network" -labels = ["inst_id"] -metricsdesc = { received_from_client_b = "network traffic received from client in b", sent_to_client_b = "network traffic sent to client in b" } -request = ''' -SELECT - t1.inst_id AS inst_id, - t1.received_from_client AS received_from_client_b, - t2.sent_to_client AS sent_to_client_b -FROM - ( - SELECT - inst_id, - value AS received_from_client - FROM - sys.gv_$sysstat - WHERE - name LIKE '%received via SQL*Net from client%' - ORDER BY - value DESC - ) t1 - LEFT JOIN ( - SELECT - inst_id, - value AS sent_to_client - FROM - sys.gv_$sysstat - WHERE - name LIKE '%sent via SQL*Net to client%' - ORDER BY - value DESC - ) t2 ON t1.inst_id = t2.inst_id -''' - -[[metric]] -context = "instance" -labels = ["inst_id", "host_name", "instance_name", "stime", "uptime", "version_full"] -metricsdesc = { dummy = "do nothing" } -request = ''' -SELECT - 1 as dummy, - inst_id, - host_name, - instance_name, - version_full, - to_char(startup_time, 'DD-MON-YYYY HH24:MI:SS') AS stime, - floor(sysdate - startup_time) - || ' days(s) ' - || trunc(24 *((sysdate - startup_time) - trunc(sysdate - startup_time))) - || ' hour(s) ' AS uptime -FROM - sys.gv_$instance -''' - - - -[[metric]] -context = "session_wait" -labels = ["inst_id", "username", "event", "wait_class", "state"] -metricsdesc = { sid = "Session id", serial_number = "Session serial number", wait_time = "Session's last wait time", seconds_in_wait = "Seconds spent in the current wait condition" } -request = ''' -SELECT - sw.inst_id AS inst_id, - nvl(s.username, '(oracle)') AS username, - s.sid AS sid, - s.serial# AS serial_number, - sw.event AS event, - sw.wait_class AS wait_class, - sw.wait_time AS wait_time, - sw.seconds_in_wait AS seconds_in_wait, - sw.state AS state -FROM - sys.gv_$session_wait sw, - sys.gv_$session s -WHERE - s.sid = sw.sid -ORDER BY - sw.seconds_in_wait DESC -''' - -[[metric]] -context = "session" -labels = ["inst_id", "status", "type"] -metricsdesc = { value = "session metric values from sys.gv_$session with count of sessions by status and type" } -request = ''' -SELECT - inst_id, - status, - type, - COUNT(*) AS value -FROM - sys.gv_$session -GROUP BY - status, - type, - inst_id -''' - -[[metric]] -context = "session" -labels = ["inst_id", "status", "type", "machine"] -metricsdesc = { mvalue = "session count group by connected machine" } -request = ''' -SELECT - inst_id, - status, - machine, - type, - COUNT(*) AS mvalue -FROM - sys.gv_$session -GROUP BY - machine, - STATUS, - type, - inst_id -''' - -[[metric]] -context = "os" -labels = ["inst_id", "stat_name"] -metricsdesc = { value = "os metric values from sys.gv_$osstat" } -request = ''' -SELECT - inst_id, - stat_name, - value -FROM - sys.gv_$osstat -WHERE stat_name IN ( - 'NUM_CPUS', - 'NUM_CPU_CORES', - 'LOAD', - 'IDLE_TIME', - 'BUSY_TIME', - 'USER_TIME', - 'SYS_TIME', - 'IOWAIT_TIME', - 'NICE_TIME', - 'PHYSICAL_MEMORY_BYTES', - 'FREE_MEMORY_BYTES' -) -''' - - -[[metric]] -context = "tablespace" -labels = [ "tablespace", "type" ] -metricsdesc = { bytes = "Generic counter metric of tablespaces bytes in Oracle.", max_bytes = "Generic counter metric of tablespaces max bytes in Oracle.", free = "Generic counter metric of tablespaces free bytes in Oracle." } -request = ''' -SELECT - dt.tablespace_name AS tablespace, - dt.contents AS type, - dtum.used_percent AS used, - dt.block_size * dtum.used_space AS bytes, - dt.block_size * dtum.tablespace_size AS max_bytes, - dt.block_size * ( dtum.tablespace_size - dtum.used_space ) AS free -FROM - dba_tablespace_usage_metrics dtum, - dba_tablespaces dt -WHERE - dtum.tablespace_name = dt.tablespace_name -ORDER BY - tablespace -''' - - - -[[metric]] -context = "sql_query_elapsed_time_order" -labels = ["inst_id", "module", "sql_id"] -metricsdesc = { elapsed_time = "Elapsed time (in microseconds) used by this cursor for parsing, executing, and fetching", executions = "Number of executions that took place on this object since it was brought into the library cache", cpu_time = "CPU time (in microseconds) used by this cursor for parsing, executing, and fetching", user_io_wait_time = "User I/O Wait Time (in microseconds)"} -request = ''' -SELECT - inst_id, - elapsed_time, - executions, - cpu_time, - user_io_wait_time, - module, - sql_id -FROM - sys.gv_$sql -ORDER BY elapsed_time DESC -FETCH FIRST 10 ROWS WITH TIES -''' - - -[[metric]] -context = "sql_query_cpu_time_order" -labels = ["inst_id", "module", "sql_id"] -metricsdesc = { elapsed_time = "Elapsed time (in microseconds) used by this cursor for parsing, executing, and fetching", executions = "Number of executions that took place on this object since it was brought into the library cache", cpu_time = "CPU time (in microseconds) used by this cursor for parsing, executing, and fetching", user_io_wait_time = "User I/O Wait Time (in microseconds)"} -request = ''' -SELECT - inst_id, - elapsed_time, - executions, - cpu_time, - user_io_wait_time, - module, - sql_id -FROM - sys.gv_$sql -ORDER BY cpu_time DESC -FETCH FIRST 10 ROWS WITH TIES -''' - - -[[metric]] -context = "sql_query_uio_time_order" -labels = ["inst_id", "module", "sql_id"] -metricsdesc = { elapsed_time = "Elapsed time (in microseconds) used by this cursor for parsing, executing, and fetching", executions = "Number of executions that took place on this object since it was brought into the library cache", cpu_time = "CPU time (in microseconds) used by this cursor for parsing, executing, and fetching", user_io_wait_time = "User I/O Wait Time (in microseconds)"} -request = ''' -SELECT - inst_id, - elapsed_time, - executions, - cpu_time, - user_io_wait_time, - module, - sql_id -FROM - sys.gv_$sql -ORDER BY user_io_wait_time DESC -FETCH FIRST 10 ROWS WITH TIES -''' - -[[metric]] -context = "sql_query_total" -labels = ["inst_id"] -metricsdesc = {io_wait_time = "total user io wait time", buffer_gets = "total number of buffer gets", disk_reads = "total number of disk reads", parse_calls = "total number of parse calls"} -request = ''' -SELECT - inst_id, - SUM(user_io_wait_time) AS io_wait_time, - SUM(buffer_gets) AS buffer_gets, - SUM(disk_reads) AS disk_reads, - SUM(parse_calls) AS parse_calls -FROM - sys.gv_$sql -GROUP BY inst_id -''' - - -[[metric]] -context = "sql_query_gets_order" -labels = ["inst_id", "module", "sql_id"] -metricsdesc = { elapsed_time = "Elapsed time (in microseconds) used by this cursor for parsing, executing, and fetching", executions = "Number of executions that took place on this object since it was brought into the library cache", cpu_time = "CPU time (in microseconds) used by this cursor for parsing, executing, and fetching", buffer_gets = "Number of buffer gets for all cursors with this SQL text and plan", user_io_wait_time = "User I/O Wait Time (in microseconds)"} -request = ''' -SELECT - inst_id, - elapsed_time, - executions, - cpu_time, - buffer_gets, - user_io_wait_time, - module, - sql_id -FROM - sys.gv_$sql -ORDER BY buffer_gets DESC -FETCH FIRST 10 ROWS WITH TIES -''' - -[[metric]] -context = "sql_query_reads_order" -labels = ["inst_id", "module", "sql_id"] -metricsdesc = { elapsed_time = "Elapsed time (in microseconds) used by this cursor for parsing, executing, and fetching", executions = "Number of executions that took place on this object since it was brought into the library cache", cpu_time = "CPU time (in microseconds) used by this cursor for parsing, executing, and fetching", disk_reads = "Number of disk reads for this child cursor", user_io_wait_time = "User I/O Wait Time (in microseconds)"} -request = ''' -SELECT - inst_id, - elapsed_time, - executions, - disk_reads, - cpu_time, - user_io_wait_time, - module, - sql_id -FROM - sys.gv_$sql -ORDER BY disk_reads DESC -FETCH FIRST 10 ROWS WITH TIES -''' - - -[[metric]] -context = "sql_query_exec_order" -labels = ["inst_id", "module", "sql_id"] -metricsdesc = { elapsed_time = "Elapsed time (in microseconds) used by this cursor for parsing, executing, and fetching", executions = "Number of executions that took place on this object since it was brought into the library cache", cpu_time = "CPU time (in microseconds) used by this cursor for parsing, executing, and fetching", rows_processed = "Total number of rows the parsed SQL statement returns", user_io_wait_time = "User I/O Wait Time (in microseconds)"} -request = ''' -SELECT - inst_id, - elapsed_time, - executions, - rows_processed, - cpu_time, - user_io_wait_time, - module, - sql_id -FROM - sys.gv_$sql -ORDER BY rows_processed DESC -FETCH FIRST 10 ROWS WITH TIES -''' - - -[[metric]] -context = "sql_query_parse_calls_order" -labels = ["inst_id", "module", "sql_id"] -metricsdesc = { elapsed_time = "Elapsed time (in microseconds) used by this cursor for parsing, executing, and fetching", executions = "Number of executions that took place on this object since it was brought into the library cache", parse_calls = "Number of parse calls for this child cursor", user_io_wait_time = "User I/O Wait Time (in microseconds)"} -request = ''' -SELECT - inst_id, - elapsed_time, - executions, - parse_calls, - user_io_wait_time, - module, - sql_id -FROM - sys.gv_$sql -ORDER BY parse_calls DESC -FETCH FIRST 10 ROWS WITH TIES -''' - - -[[metric]] -context = "concurrency_foreground_wait_class" -labels = ["inst_id", "con_id", "wait_class_name"] -metricsdesc = {waits_fg = "Total number of waits for the class, from foreground sessions", timeouts_fg = "Total number of timeouts for the class, from foreground sessions", total_wait_time_fg = "Total amount of time (in second) waited for the class, from foreground sessions", avg_wait_time_fg = "Average amount of time (in second) waited for the class, from foreground sessions"} -request = ''' -SELECT - inst_id, - con_id, - wait_class AS wait_class_name, - SUM(total_waits_fg) AS waits_fg, - SUM(total_timeouts_fg) as timeouts_fg, - SUM(time_waited_fg)/100 AS total_wait_time_fg, - SUM(average_wait_fg)/100 AS avg_wait_time_fg -FROM - sys.gv_$system_event -WHERE - wait_class NOT IN ('Idle') -GROUP BY - inst_id, con_id, wait_class -ORDER BY - total_wait_time_fg DESC, waits_fg DESC -''' - - -[[metric]] -context = "concurrency_sysevent" -labels = ["inst_id", "con_id"] -metricsdesc = {total_timeout = "Total number of timeouts for the event, from foreground sessions"} -request = ''' -SELECT - inst_id, - con_id, - SUM(total_timeouts) AS total_timeout -FROM - sys.gv_$system_event -GROUP BY - inst_id, con_id -''' - -[[metric]] -context = "concurrency_sysstat" -labels = ["inst_id", "con_id"] -metricsdesc = {db_time = "Total database time (in second), not including idle"} -request = ''' -SELECT - inst_id, - con_id, - value/1000 AS db_time -FROM - sys.gv_$SYSSTAT -WHERE NAME = 'DB time' -''' - -[[metric]] -context = "concurrency_foreground_wait_event" -labels = ["inst_id", "con_id", "event_name"] -metricsdesc = {waits_fg = "Total number of waits for the event, from foreground sessions", timeouts_fg = "Total number of timeouts for the event, from foreground sessions", time_waited_fg = "Total amount of time (in milliseconds) waited for the event, from foreground sessions", average_wait_fg = "Average amount of time (in second) waited for the event, from foreground sessions"} -request = ''' -SELECT - inst_id, - con_id, - event AS event_name, - total_waits_fg AS waits_fg, - total_timeouts_fg AS timeouts_fg, - time_waited_micro_fg / 1000000 AS time_waited_fg, - average_wait_fg * 10 AS average_wait_fg -FROM - sys.gv_$system_event -WHERE - wait_class NOT IN ('Idle') -ORDER BY - time_waited_fg DESC, waits_fg DESC -FETCH FIRST 20 ROWS WITH TIES -''' - - -[[metric]] -context = "concurrency_background_wait_event" -labels = ["inst_id", "con_id", "event_name"] -metricsdesc = {waits_bg = "Total number of waits for the event, from background sessions", timeouts_bg = "Total number of timeouts for the event, from background sessions", time_waited_bg = "Total amount of time (in milliseconds) waited for the event, from background sessions", average_wait_bg = "Average amount of time (in second) waited for the event, from background sessions"} -request = ''' -SELECT - inst_id, - con_id, - event AS event_name, - (total_waits - total_waits_fg) AS waits_bg, - (total_timeouts - total_timeouts_fg) AS timeouts_bg, - (time_waited_micro - time_waited_micro_fg) / 1000 AS time_waited_bg, - average_wait * 10 AS average_wait_bg -FROM - sys.gv_$system_event -WHERE - wait_class NOT IN ('Idle') -ORDER BY - time_waited_bg DESC, waits_bg DESC -FETCH FIRST 20 ROWS WITH TIES -''' - -[[metric]] -context = "concurrency_sysstat" -labels = ["inst_id", "con_id"] -metricsdesc = {total_time_waited_bg = "total background wait time"} -request = ''' -SELECT - inst_id, - con_id, - (total_wait_time - total_wait_time_fg) / 100 AS total_time_waited_bg -FROM - (SELECT - inst_id, - con_id, - SUM(TIME_WAITED) AS total_wait_time, - SUM(TIME_WAITED_FG) AS total_wait_time_fg - FROM - sys.gv_$system_event - GROUP BY inst_id, con_id) -''' - - - -[[metric]] -context = "io_function" -labels = ["inst_id", "con_id", "function_name"] -metricsdesc = {read_data_s = "Number of single block megabytes read", read_req_s = "Number of single block read requests", write_data_s = "Number of megabytes written via single block write requests", write_req_s = "Number of single block write requests", read_data_l = "Number of megabytes read via multiblock read requests", read_req_l = "Number of multiblock read requests", write_data_l = "Number of megabytes written via multiblock write requests", write_req_l = "Number of multiblock write requests", number_of_waits = "Number of synchronous I/O waits by functionality", time = "Total synchronous I/O wait time (in seconds)"} -request = ''' -SELECT - inst_id, - con_id, - function_name, - read_data_s, - read_req_s, - write_data_s, - write_req_s, - read_data_l, - read_req_l, - write_data_l, - write_req_l, - number_of_waits, - time / 1000 AS time -FROM - (SELECT - inst_id, - con_id, - function_name, - SUM(small_read_megabytes) AS read_data_s, - SUM(small_read_reqs) AS read_req_s, - SUM(small_write_megabytes) AS write_data_s, - SUM(small_write_reqs) AS write_req_s, - SUM(large_read_megabytes) AS read_data_l, - SUM(large_read_reqs) AS read_req_l, - SUM(large_write_megabytes) AS write_data_l, - SUM(large_write_reqs) AS write_req_l, - SUM(number_of_waits) AS number_of_waits, - SUM(wait_time) AS time - FROM - sys.gv_$IOSTAT_FUNCTION - GROUP BY function_name, inst_id, con_id) -WHERE (read_data_s + write_data_s + read_data_l + write_data_l) > 0 -ORDER BY (read_data_s + write_data_s + read_data_l + write_data_l) DESC -FETCH FIRST 5 ROWS WITH TIES -''' - -[[metric]] -context = "io_function" -labels = ["inst_id", "con_id", "function_name"] -metricsdesc = {total_read_data_s = "Total Number of single block megabytes read", total_read_req_s = "Total Number of single block read requests", total_write_data_s = "Number of megabytes written via single block write requests", total_write_req_s = "Number of single block write requests", total_read_data_l = "Number of megabytes read via multiblock read requests", total_read_req_l = "Number of multiblock read requests", total_write_data_l = "Number of megabytes written via multiblock write requests", total_write_req_l = "Number of multiblock write requests", total_number_of_waits = "Number of synchronous I/O waits by functionality", total_time = "Total synchronous I/O wait time (in seconds)"} -request = ''' -SELECT - inst_id, - con_id, - 'total' AS function_name, - SUM(small_read_megabytes) AS total_read_data_s, - SUM(small_read_reqs) AS total_read_req_s, - SUM(small_write_megabytes) AS total_write_data_s, - SUM(small_write_reqs) AS total_write_req_s, - SUM(large_read_megabytes) AS total_read_data_l, - SUM(large_read_reqs) AS total_read_req_l, - SUM(large_write_megabytes) AS total_write_data_l, - SUM(large_write_reqs) AS total_write_req_l, - SUM(number_of_waits) AS total_number_of_waits, - SUM(wait_time) AS total_time -FROM - sys.gv_$IOSTAT_FUNCTION -GROUP BY inst_id, con_id -''' - -[[metric]] -context = "io_filetype" -labels = ["inst_id", "con_id", "filetype_name"] -metricsdesc = {read_data_s = "Number of single block megabytes read", read_req_s = "Number of single block read requests", write_data_s = "Number of megabytes written via single block write requests", write_req_s = "Number of single block write requests", read_data_l = "Number of megabytes read via multiblock read requests", read_req_l = "Number of multiblock read requests", write_data_l = "Number of megabytes written via multiblock write requests", write_req_l = "Number of multiblock write requests", read_stime_s = "Total service time (in seconds) for single block read requests", write_stime_s = "Total service time (in seconds) for single block write requests", read_stime_l = "Total service time (in seconds) for multiblock read requests", write_stime_l = "Total service time (in seconds) for multiblock write requests"} -request = ''' -SELECT - inst_id, - con_id, - filetype_name, - read_data_s, - read_req_s, - write_data_s, - write_req_s, - read_data_l, - read_req_l, - write_data_l, - write_req_l, - read_stime_s / 1000 AS read_stime_s, - write_stime_s / 1000 AS write_stime_s, - read_stime_l / 1000 AS read_stime_l, - write_stime_l / 1000 AS write_stime_l -FROM - (SELECT - inst_id, - con_id, - filetype_name, - SUM(small_read_megabytes) AS read_data_s, - SUM(small_read_reqs) AS read_req_s, - SUM(small_write_megabytes) AS write_data_s, - SUM(small_write_reqs) AS write_req_s, - SUM(large_read_megabytes) AS read_data_l, - SUM(large_read_reqs) AS read_req_l, - SUM(large_write_megabytes) AS write_data_l, - SUM(large_write_reqs) AS write_req_l, - SUM(small_read_servicetime) AS read_stime_s, - SUM(small_write_servicetime) AS write_stime_s, - SUM(large_read_servicetime) AS read_stime_l, - SUM(large_write_servicetime) AS write_stime_l - FROM - sys.gv_$IOSTAT_FILE - WHERE con_id = 1 - GROUP BY inst_id, con_id, filetype_name) -WHERE (read_data_s + write_data_s + read_data_l + write_data_l) > 0 -ORDER BY (read_data_s + write_data_s + read_data_l + write_data_l) DESC -FETCH FIRST 5 ROWS WITH TIES -''' - - -[[metric]] -context = "io_filetype" -labels = ["inst_id", "con_id", "filetype_name"] -metricsdesc = {total_read_data_s = "Number of single block megabytes read", total_read_req_s = "Number of single block read requests", total_write_data_s = "Number of megabytes written via single block write requests", total_write_req_s = "Number of single block write requests", total_read_data_l = "Number of megabytes read via multiblock read requests", total_read_req_l = "Number of multiblock read requests", total_write_data_l = "Number of megabytes written via multiblock write requests", total_write_req_l = "Number of multiblock write requests", total_read_stime_s = "Total service time (in seconds) for single block read requests", total_write_stime_s = "Total service time (in seconds) for single block write requests", total_read_stime_l = "Total service time (in seconds) for multiblock read requests", total_write_stime_l = "Total service time (in seconds) for multiblock write requests"} -request = ''' -SELECT - inst_id, - con_id, - 'total' AS filetype_name, - SUM(small_read_megabytes) AS total_read_data_s, - SUM(small_read_reqs) AS total_read_req_s, - SUM(small_write_megabytes) AS total_write_data_s, - SUM(small_write_reqs) AS total_write_req_s, - SUM(large_read_megabytes) AS total_read_data_l, - SUM(large_read_reqs) AS total_read_req_l, - SUM(large_write_megabytes) AS total_write_data_l, - SUM(large_write_reqs) AS total_write_req_l, - SUM(small_read_servicetime) AS total_read_stime_s, - SUM(small_write_servicetime) AS total_write_stime_s, - SUM(large_read_servicetime) AS total_read_stime_l, - SUM(large_write_servicetime) AS total_write_stime_l -FROM - sys.gv_$IOSTAT_FILE -WHERE con_id = 1 -GROUP BY inst_id, con_id -''' diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..989da31 --- /dev/null +++ b/go.mod @@ -0,0 +1,40 @@ +module github.com/oracle/oracle-db-appdev-monitoring + +go 1.19 + +require ( + github.com/BurntSushi/toml v1.2.1 + github.com/alecthomas/kingpin/v2 v2.3.2 + github.com/go-kit/log v0.2.1 + github.com/prometheus/client_golang v1.15.1 + github.com/prometheus/common v0.44.0 + github.com/prometheus/exporter-toolkit v0.10.0 +) + +require ( + github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 // indirect + github.com/beorn7/perks v1.0.1 // indirect + github.com/cespare/xxhash/v2 v2.2.0 // indirect + github.com/coreos/go-systemd/v22 v22.5.0 // indirect + github.com/go-logfmt/logfmt v0.6.0 // indirect + github.com/go-logr/logr v1.2.3 // indirect + github.com/godror/godror v0.37.0 // indirect + github.com/godror/knownpb v0.1.0 // indirect + github.com/golang/protobuf v1.5.3 // indirect + github.com/jpillora/backoff v1.0.0 // indirect + github.com/kr/text v0.2.0 // indirect + github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect + github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f // indirect + github.com/prometheus/client_model v0.4.0 // indirect + github.com/prometheus/procfs v0.9.0 // indirect + github.com/xhit/go-str2duration/v2 v2.1.0 // indirect + golang.org/x/crypto v0.8.0 // indirect + golang.org/x/net v0.10.0 // indirect + golang.org/x/oauth2 v0.8.0 // indirect + golang.org/x/sync v0.1.0 // indirect + golang.org/x/sys v0.8.0 // indirect + golang.org/x/text v0.9.0 // indirect + google.golang.org/appengine v1.6.7 // indirect + google.golang.org/protobuf v1.30.0 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..3f487b1 --- /dev/null +++ b/go.sum @@ -0,0 +1,94 @@ +github.com/BurntSushi/toml v1.2.1 h1:9F2/+DoOYIOksmaJFPw1tGFy1eDnIJXg+UHjuD8lTak= +github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= +github.com/alecthomas/kingpin/v2 v2.3.2 h1:H0aULhgmSzN8xQ3nX1uxtdlTHYoPLu5AhHxWrKI6ocU= +github.com/alecthomas/kingpin/v2 v2.3.2/go.mod h1:0gyi0zQnjuFk8xrkNKamJoyUo382HRL7ATRpFZCw6tE= +github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 h1:s6gZFSlWYmbqAuRjVTiNNhvNRfY2Wxp9nhfyel4rklc= +github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE= +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= +github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs= +github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/go-kit/log v0.2.1 h1:MRVx0/zhvdseW+Gza6N9rVzU/IVzaeE1SFI4raAhmBU= +github.com/go-kit/log v0.2.1/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= +github.com/go-logfmt/logfmt v0.6.0 h1:wGYYu3uicYdqXVgoYbvnkrPVXkuLM1p1ifugDMEdRi4= +github.com/go-logfmt/logfmt v0.6.0/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= +github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0= +github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/godror/godror v0.37.0 h1:3wR3/1msywDE49PzuXh9UUiwWOBNri0RVQQcu3HU4UY= +github.com/godror/godror v0.37.0/go.mod h1:jW1+pN+z/V0h28p9XZXVNtEvfZP/2EBfaSjKJLp3E4g= +github.com/godror/knownpb v0.1.0 h1:dJPK8s/I3PQzGGaGcUStL2zIaaICNzKKAK8BzP1uLio= +github.com/godror/knownpb v0.1.0/go.mod h1:4nRFbQo1dDuwKnblRXDxrfCFYeT4hjg3GjMqef58eRE= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= +github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/jpillora/backoff v1.0.0 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2EA= +github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +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= +github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f h1:KUppIJq7/+SVif2QVs3tOP0zanoHgBEVAwHxUSIzRqU= +github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_golang v1.15.1 h1:8tXpTmJbyH5lydzFPoxSIJ0J46jdh3tylbvM1xCv0LI= +github.com/prometheus/client_golang v1.15.1/go.mod h1:e9yaBhRPU2pPNsZwE+JdQl0KEt1N9XgF6zxWmaC0xOk= +github.com/prometheus/client_model v0.4.0 h1:5lQXD3cAg1OXBf4Wq03gTrXHeaV0TQvGfUooCfx1yqY= +github.com/prometheus/client_model v0.4.0/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU= +github.com/prometheus/common v0.44.0 h1:+5BrQJwiBB9xsMygAB3TNvpQKOwlkc25LbISbrdOOfY= +github.com/prometheus/common v0.44.0/go.mod h1:ofAIvZbQ1e/nugmZGz4/qCb9Ap1VoSTIO7x0VV9VvuY= +github.com/prometheus/exporter-toolkit v0.10.0 h1:yOAzZTi4M22ZzVxD+fhy1URTuNRj/36uQJJ5S8IPza8= +github.com/prometheus/exporter-toolkit v0.10.0/go.mod h1:+sVFzuvV5JDyw+Ih6p3zFxZNVnKQa3x5qPmDSiPu4ZY= +github.com/prometheus/procfs v0.9.0 h1:wzCHvIvM5SxWqYvwgVL7yJY8Lz3PKn49KQtpgMYJfhI= +github.com/prometheus/procfs v0.9.0/go.mod h1:+pB4zwohETzFnmlpe6yd2lSc+0/46IYZRB/chUwxUZY= +github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= +github.com/sijms/go-ora/v2 v2.7.6 h1:QyR1CKFxG+VVk2+LdHoHF4NxDSvcQ3deBXtZCrahSq4= +github.com/sijms/go-ora/v2 v2.7.6/go.mod h1:EHxlY6x7y9HAsdfumurRfTd+v8NrEOTR3Xl4FWlH6xk= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= +github.com/xhit/go-str2duration/v2 v2.1.0 h1:lxklc02Drh6ynqX+DdPyp5pCKLUQpRT8bp8Ydu2Bstc= +github.com/xhit/go-str2duration/v2 v2.1.0/go.mod h1:ohY8p+0f07DiV6Em5LKB0s2YpLtXVyJfNt1+BlmyAsU= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.8.0 h1:pd9TJtTueMTVQXzk8E2XESSMQDj/U7OUu0PqJqPXQjQ= +golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE= +golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M= +golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= +golang.org/x/oauth2 v0.8.0 h1:6dkIjl3j3LtZ/O3sTgZTMsLKSftL/B8Zgq4huOIIUu8= +golang.org/x/oauth2 v0.8.0/go.mod h1:yr7u4HXZRm1R1kBWqr/xKNqewf0plRYoB7sla+BCIXE= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= +google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= +google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= diff --git a/main.go b/main.go new file mode 100644 index 0000000..9dd4ac1 --- /dev/null +++ b/main.go @@ -0,0 +1,98 @@ +package main + +import ( + "context" + "net/http" + "os" + + "github.com/go-kit/log/level" + "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/client_golang/prometheus/promhttp" + "github.com/prometheus/common/version" + "github.com/prometheus/exporter-toolkit/web" + webflag "github.com/prometheus/exporter-toolkit/web/kingpinflag" + + "github.com/alecthomas/kingpin/v2" + "github.com/prometheus/common/promlog" + "github.com/prometheus/common/promlog/flag" + + // Required for debugging + // _ "net/http/pprof" + + "github.com/oracle/oracle-db-appdev-monitoring/collector" +) + +var ( + // Version will be set at build time. + Version = "0.0.0.dev" + metricPath = kingpin.Flag("web.telemetry-path", "Path under which to expose metrics. (env: TELEMETRY_PATH)").Default(getEnv("TELEMETRY_PATH", "/metrics")).String() + defaultFileMetrics = kingpin.Flag("default.metrics", "File with default metrics in a TOML file. (env: DEFAULT_METRICS)").Default(getEnv("DEFAULT_METRICS", "default-metrics.toml")).String() + customMetrics = kingpin.Flag("custom.metrics", "File that may contain various custom metrics in a TOML file. (env: CUSTOM_METRICS)").Default(getEnv("CUSTOM_METRICS", "")).String() + queryTimeout = kingpin.Flag("query.timeout", "Query timeout (in seconds). (env: QUERY_TIMEOUT)").Default(getEnv("QUERY_TIMEOUT", "5")).Int() + maxIdleConns = kingpin.Flag("database.maxIdleConns", "Number of maximum idle connections in the connection pool. (env: DATABASE_MAXIDLECONNS)").Default(getEnv("DATABASE_MAXIDLECONNS", "0")).Int() + maxOpenConns = kingpin.Flag("database.maxOpenConns", "Number of maximum open connections in the connection pool. (env: DATABASE_MAXOPENCONNS)").Default(getEnv("DATABASE_MAXOPENCONNS", "10")).Int() + scrapeInterval = kingpin.Flag("scrape.interval", "Interval between each scrape. Default is to scrape on collect requests").Default("0s").Duration() + toolkitFlags = webflag.AddFlags(kingpin.CommandLine, ":9161") +) + +func main() { + promLogConfig := &promlog.Config{} + flag.AddFlags(kingpin.CommandLine, promLogConfig) + kingpin.HelpFlag.Short('\n') + kingpin.Version(version.Print("oracledb_exporter")) + kingpin.Parse() + logger := promlog.New(promLogConfig) + user := os.Getenv("DB_USERNAME") + password := os.Getenv("DB_PASSWORD") + connectString := os.Getenv("DB_CONNECT_STRING") + + config := &collector.Config{ + User: user, + Password: password, + ConnectString: connectString, + MaxOpenConns: *maxOpenConns, + MaxIdleConns: *maxIdleConns, + CustomMetrics: *customMetrics, + QueryTimeout: *queryTimeout, + DefaultMetricsFile: *defaultFileMetrics, + } + exporter, err := collector.NewExporter(logger, config) + if err != nil { + level.Error(logger).Log("unable to connect to DB", err) + } + + if *scrapeInterval != 0 { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + go exporter.RunScheduledScrapes(ctx, *scrapeInterval) + } + + prometheus.MustRegister(exporter) + prometheus.MustRegister(version.NewCollector("oracledb_exporter")) + + level.Info(logger).Log("msg", "Starting oracledb_exporter", "version", version.Info()) + level.Info(logger).Log("msg", "Build context", "build", version.BuildContext()) + level.Info(logger).Log("msg", "Collect from: ", "metricPath", *metricPath) + + opts := promhttp.HandlerOpts{ + ErrorHandling: promhttp.ContinueOnError, + } + http.Handle(*metricPath, promhttp.HandlerFor(prometheus.DefaultGatherer, opts)) + http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + w.Write([]byte("Oracle DB Exporter " + Version + "

Oracle DB Exporter " + Version + "

Metrics

")) + }) + + server := &http.Server{} + if err := web.ListenAndServe(server, toolkitFlags, logger); err != nil { + level.Error(logger).Log("msg", "Listening error", "reason", err) + os.Exit(1) + } +} + +// getEnv returns the value of an environment variable, or returns the provided fallback value +func getEnv(key, fallback string) string { + if value, ok := os.LookupEnv(key); ok { + return value + } + return fallback +} diff --git a/oracle-db-monitoring-exporter/operating-principles.md b/operating-principles.md similarity index 88% rename from oracle-db-monitoring-exporter/operating-principles.md rename to operating-principles.md index 133de15..4e69bc9 100644 --- a/oracle-db-monitoring-exporter/operating-principles.md +++ b/operating-principles.md @@ -1,7 +1,7 @@ -Operating principles -==================== +# Operating principles + +The exporter itself is dumb and does not do much. The initialization is done as follows: -The exporter itself is simple and does not do much. The initialization is done as follows: - Parse flags options - Load the default toml file (default-metrics.toml) and store each metrics in a Metric struct - Load the custom toml file (if a custom toml file is given) diff --git a/oracle-db-monitoring-exporter/Makefile b/oracle-db-monitoring-exporter/Makefile deleted file mode 100644 index bd8ec8d..0000000 --- a/oracle-db-monitoring-exporter/Makefile +++ /dev/null @@ -1,68 +0,0 @@ -VERSION ?= 0.3.0 -ORACLE_VERSION ?= 19.13 -LDFLAGS := -X main.Version=$(VERSION) -GOFLAGS := -ldflags "$(LDFLAGS) -s -w" -ARCH ?= $(shell uname -m) -GOARCH ?= $(subst x86_64,amd64,$(patsubst i%86,386,$(ARCH))) -RPM_VERSION ?= $(ORACLE_VERSION).0.0.0-1 -ORA_RPM = oracle-instantclient$(ORACLE_VERSION)-basic-$(RPM_VERSION).$(ARCH).rpm -LD_LIBRARY_PATH = /usr/lib/oracle/$(ORACLE_VERSION)/client64/lib -BUILD_ARGS = --build-arg VERSION=$(VERSION) --build-arg ORACLE_VERSION=$(ORACLE_VERSION) -LEGACY_TABLESPACE = --build-arg LEGACY_TABLESPACE=.legacy-tablespace -DIST_DIR = oracledb_exporter.$(VERSION)-ora$(ORACLE_VERSION).linux-${GOARCH} -ARCHIVE = oracledb_exporter.$(VERSION)-ora$(ORACLE_VERSION).linux-${GOARCH}.tar.gz - -export LD_LIBRARY_PATH ORACLE_VERSION - -%.rpm: - wget -q http://yum.oracle.com/repo/OracleLinux/OL7/oracle/instantclient/$(ARCH)/getPackage/$@ - -download-rpms: $(ORA_RPM) - -prereq: download-rpms - @echo deps - sudo apt-get update - sudo apt-get install --no-install-recommends -qq libaio1 rpm - sudo rpm -Uvh --nodeps --force oracle*rpm - echo $(LD_LIBRARY_PATH) | sudo tee /etc/ld.so.conf.d/oracle.conf - sudo ldconfig - -oci.pc: - sed "s/@ORACLE_VERSION@/$(ORACLE_VERSION)/g" oci8.pc.template > oci8.pc - -linux: oci.pc - @echo build linux - mkdir -p ./dist/$(DIST_DIR) - PKG_CONFIG_PATH=${PWD} GOOS=linux go build $(GOFLAGS) -o ./dist/$(DIST_DIR)/oracledb_exporter - cp default-metrics.toml ./dist/$(DIST_DIR) - (cd dist ; tar cfz $(ARCHIVE) $(DIST_DIR)) - -darwin: oci.pc - @echo build darwin - mkdir -p ./dist/oracledb_exporter.$(VERSION).darwin-${GOARCH} - PKG_CONFIG_PATH=${PWD} GOOS=darwin go build $(GOFLAGS) -o ./dist/oracledb_exporter.$(VERSION).darwin-${GOARCH}/oracledb_exporter - cp default-metrics.toml ./dist/oracledb_exporter.$(VERSION).darwin-${GOARCH} - (cd dist ; tar cfz oracledb_exporter.$(VERSION).darwin-${GOARCH}.tar.gz oracledb_exporter.$(VERSION).darwin-${GOARCH}) - -local-build: linux - -build: docker - -deps: - @PKG_CONFIG_PATH=${PWD} go get - -test: - @echo test - @PKG_CONFIG_PATH=${PWD} go test $$(go list ./... | grep -v /vendor/) - -clean: - rm -rf ./dist sgerrand.rsa.pub glibc-2.29-r0.apk oci8.pc - -docker: oraclelinux-image - - -oraclelinux-image: $(ORA_RPM) - docker build -f oraclelinux/Dockerfile $(BUILD_ARGS) -t "oracle-db-monitoring-exporter:$(VERSION)-oraclelinux" . - docker build -f oraclelinux/Dockerfile $(BUILD_ARGS) $(LEGACY_TABLESPACE) -t "oracle-db-monitoring-exporter:$(VERSION)-oraclelinux_legacy-tablespace" . - docker tag "oracle-db-monitoring-exporter:$(VERSION)-oraclelinux" "oracle-db-monitoring-exporter:oraclelinux" - diff --git a/oracle-db-monitoring-exporter/go.mod b/oracle-db-monitoring-exporter/go.mod deleted file mode 100644 index d668f46..0000000 --- a/oracle-db-monitoring-exporter/go.mod +++ /dev/null @@ -1,13 +0,0 @@ -module github.com/oracle/oracle-db-appdev-monitoring/oracle-db-monitoring-exporter - -go 1.14 - -require ( - github.com/BurntSushi/toml v0.3.1 - github.com/mattn/go-oci8 v0.0.8 - github.com/oracle/oci-go-sdk/v49 v49.2.0 - github.com/prometheus/client_golang v1.0.0 - github.com/prometheus/common v0.6.0 - gopkg.in/alecthomas/kingpin.v2 v2.2.6 - gopkg.in/yaml.v2 v2.4.0 -) diff --git a/oracle-db-monitoring-exporter/go.sum b/oracle-db-monitoring-exporter/go.sum deleted file mode 100644 index 867524f..0000000 --- a/oracle-db-monitoring-exporter/go.sum +++ /dev/null @@ -1,81 +0,0 @@ -github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc h1:cAKDfWh5VpdgMhJosfJnn5/FoN2SRZ4p7fJNX58YPaU= -github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf h1:qet1QNfXsQxTZqLG4oE62mJzwPIB8+Tee4RNCL9ulrY= -github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= -github.com/beorn7/perks v1.0.0 h1:HWo1m869IqiPhD389kmkxeTalrjNbbJTC8LXupb+sl0= -github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= -github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= -github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg= -github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= -github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= -github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk= -github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= -github.com/mattn/go-oci8 v0.0.8 h1:D0lVs4vwDEsZ1uPKsE6v26OS6FfHeAI43yf3l8h3X48= -github.com/mattn/go-oci8 v0.0.8/go.mod h1:wjDx6Xm9q7dFtHJvIlrI99JytznLw5wQ4R+9mNXJwGI= -github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= -github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= -github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/oracle/oci-go-sdk/v49 v49.2.0 h1:l4PUk81EKdTDD4mDg5wrELpdWFqYeE9KYejfNgtsyUI= -github.com/oracle/oci-go-sdk/v49 v49.2.0/go.mod h1:E8q2DXmXnSozLdXHUFF+o3L2gzcWbiFIPFYOYWdqOfc= -github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= -github.com/prometheus/client_golang v1.0.0 h1:vrDKnkGzuGvhNAL56c7DBz29ZL+KxnoR0x7enabFceM= -github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= -github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= -github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90 h1:S/YWwWx/RA8rT8tKFRuGUZhuA90OyIBpPCXkcbwU8DE= -github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/common v0.6.0 h1:kRhiuYSXR3+uv2IbVbZhUxK5zVD/2pp3Gd2PpvPkpEo= -github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc= -github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.2 h1:6LJUbpNm42llc4HRCuvApCSWB/WfhuNo9K98Q9sNGfs= -github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= -github.com/sirupsen/logrus v1.2.0 h1:juTguoYk5qI21pwyTXY3B3Y5cOTH3ZUyZCg1v/mihuo= -github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= -github.com/sony/gobreaker v0.4.2-0.20210216022020-dd874f9dd33b h1:br+bPNZsJWKicw/5rALEo67QHs5weyD5tf8WST+4sJ0= -github.com/sony/gobreaker v0.4.2-0.20210216022020-dd874f9dd33b/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A= -github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= -github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -gopkg.in/alecthomas/kingpin.v2 v2.2.6 h1:jMFz6MfLP0/4fUyZle81rXUoxOBFi19VUFKVDOQfozc= -gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= -gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/oracle-db-monitoring-exporter/main.go b/oracle-db-monitoring-exporter/main.go deleted file mode 100644 index e91fde6..0000000 --- a/oracle-db-monitoring-exporter/main.go +++ /dev/null @@ -1,710 +0,0 @@ -package main - -import ( - "bytes" - "context" - "crypto/sha256" - "crypto/subtle" - "database/sql" - "errors" - "github.com/iamseth/oracledb_exporter/orasys" - "hash" - "io" - "io/ioutil" - "net/http" - "os" - "strconv" - "strings" - "sync" - "time" - - "github.com/BurntSushi/toml" - - _ "github.com/mattn/go-oci8" - - "fmt" - - "github.com/prometheus/client_golang/prometheus" - "github.com/prometheus/client_golang/prometheus/promhttp" - "github.com/prometheus/common/log" - "gopkg.in/alecthomas/kingpin.v2" - "gopkg.in/yaml.v2" - //Required for debugging - //_ "net/http/pprof" - "github.com/oracle/oci-go-sdk/v49/common/auth" - "github.com/oracle/oci-go-sdk/v49/common" - "github.com/oracle/oci-go-sdk/v49/vault" - "encoding/base64" -) - -var ( - // Version will be set at build time. - Version = "0.0.0.dev" - listenAddress = kingpin.Flag("web.listen-address", "Address to listen on for web interface and telemetry. (env: LISTEN_ADDRESS)").Default(getEnv("LISTEN_ADDRESS", ":9161")).String() - metricPath = kingpin.Flag("web.telemetry-path", "Path under which to expose metrics. (env: TELEMETRY_PATH)").Default(getEnv("TELEMETRY_PATH", "/metrics")).String() - defaultFileMetrics = kingpin.Flag("default.metrics", "File with default metrics in a TOML file. (env: DEFAULT_METRICS)").Default(getEnv("DEFAULT_METRICS", "default-metrics.toml")).String() - customMetrics = kingpin.Flag("custom.metrics", "File that may contain various custom metrics in a TOML file. (env: CUSTOM_METRICS)").Default(getEnv("CUSTOM_METRICS", "")).String() - queryTimeout = kingpin.Flag("query.timeout", "Query timeout (in seconds). (env: QUERY_TIMEOUT)").Default(getEnv("QUERY_TIMEOUT", "5")).String() - maxIdleConns = kingpin.Flag("database.maxIdleConns", "Number of maximum idle connections in the connection pool. (env: DATABASE_MAXIDLECONNS)").Default(getEnv("DATABASE_MAXIDLECONNS", "0")).Int() - maxOpenConns = kingpin.Flag("database.maxOpenConns", "Number of maximum open connections in the connection pool. (env: DATABASE_MAXOPENCONNS)").Default(getEnv("DATABASE_MAXOPENCONNS", "10")).Int() - securedMetrics = kingpin.Flag("web.secured-metrics", "Expose metrics using https.").Default("false").Bool() - serverCert = kingpin.Flag("web.ssl-server-cert", "Path to the PEM encoded certificate").ExistingFile() - serverKey = kingpin.Flag("web.ssl-server-key", "Path to the PEM encoded key").ExistingFile() - authconfig = kingpin.Flag("auth.config", "Configuration file of http authentication. (env: AUTH_CONFIG").Default(getEnv("AUTH_CONFIG", "auth_config.yml")).String() - dockerpath = "/run/secrets/" -) - -// Metric name parts. -const ( - namespace = "oracledb" - exporter = "exporter" -) - -// Metrics object description -type Metric struct { - Context string - Labels []string - MetricsDesc map[string]string - MetricsType map[string]string - MetricsBuckets map[string]map[string]string - FieldToAppend string - Request string - IgnoreZeroResult bool -} - -// Used to load multiple metrics from file -type Metrics struct { - Metric []Metric -} - -// Metrics to scrap. Use external file (default-metrics.toml and custom if provided) -var ( - metricsToScrap Metrics - additionalMetrics Metrics - hashMap map[int][]byte -) - -// Exporter collects Oracle DB metrics. It implements prometheus.Collector. -type Exporter struct { - dsn string - duration, error prometheus.Gauge - totalScrapes prometheus.Counter - scrapeErrors *prometheus.CounterVec - up prometheus.Gauge - db *sql.DB -} - -// getEnv returns the value of an environment variable, or returns the provided fallback value -func getEnv(key, fallback string) string { - if value, ok := os.LookupEnv(key); ok { - return value - } - return fallback -} - -func atoi(stringValue string) int { - intValue, err := strconv.Atoi(stringValue) - if err != nil { - log.Fatal("error while converting to int:", err) - panic(err) - } - return intValue -} - -func connect(dsn string) *sql.DB { - log.Debugln("Launching connection: ", dsn) - db, err := sql.Open("oci8", dsn) - if err != nil { - log.Errorln("Error while connecting to", dsn) - panic(err) - } - log.Debugln("set max idle connections to ", *maxIdleConns) - db.SetMaxIdleConns(*maxIdleConns) - log.Debugln("set max open connections to ", *maxOpenConns) - db.SetMaxOpenConns(*maxOpenConns) - log.Debugln("Successfully connected to: ", dsn) - return db -} - -// NewExporter returns a new Oracle DB exporter for the provided DSN. -func NewExporter(dsn string) *Exporter { - db := connect(dsn) - return &Exporter{ - dsn: dsn, - duration: prometheus.NewGauge(prometheus.GaugeOpts{ - Namespace: namespace, - Subsystem: exporter, - Name: "last_scrape_duration_seconds", - Help: "Duration of the last scrape of metrics from Oracle DB.", - }), - totalScrapes: prometheus.NewCounter(prometheus.CounterOpts{ - Namespace: namespace, - Subsystem: exporter, - Name: "scrapes_total", - Help: "Total number of times Oracle DB was scraped for metrics.", - }), - scrapeErrors: prometheus.NewCounterVec(prometheus.CounterOpts{ - Namespace: namespace, - Subsystem: exporter, - Name: "scrape_errors_total", - Help: "Total number of times an error occured scraping a Oracle database.", - }, []string{"collector"}), - error: prometheus.NewGauge(prometheus.GaugeOpts{ - Namespace: namespace, - Subsystem: exporter, - Name: "last_scrape_error", - Help: "Whether the last scrape of metrics from Oracle DB resulted in an error (1 for error, 0 for success).", - }), - up: prometheus.NewGauge(prometheus.GaugeOpts{ - Namespace: namespace, - Name: "up", - Help: "Whether the Oracle database server is up.", - }), - db: db, - } -} - -// Describe describes all the metrics exported by the Oracle DB exporter. -func (e *Exporter) Describe(ch chan<- *prometheus.Desc) { - // We cannot know in advance what metrics the exporter will generate - // So we use the poor man's describe method: Run a collect - // and send the descriptors of all the collected metrics. The problem - // here is that we need to connect to the Oracle DB. If it is currently - // unavailable, the descriptors will be incomplete. Since this is a - // stand-alone exporter and not used as a library within other code - // implementing additional metrics, the worst that can happen is that we - // don't detect inconsistent metrics created by this exporter - // itself. Also, a change in the monitored Oracle instance may change the - // exported metrics during the runtime of the exporter. - - metricCh := make(chan prometheus.Metric) - doneCh := make(chan struct{}) - - go func() { - for m := range metricCh { - ch <- m.Desc() - } - close(doneCh) - }() - - e.Collect(metricCh) - close(metricCh) - <-doneCh - -} - -// Collect implements prometheus.Collector. -func (e *Exporter) Collect(ch chan<- prometheus.Metric) { - e.scrape(ch) - ch <- e.duration - ch <- e.totalScrapes - ch <- e.error - e.scrapeErrors.Collect(ch) - ch <- e.up -} - -func (e *Exporter) scrape(ch chan<- prometheus.Metric) { - e.totalScrapes.Inc() - var err error - defer func(begun time.Time) { - e.duration.Set(time.Since(begun).Seconds()) - if err == nil { - e.error.Set(0) - } else { - e.error.Set(1) - } - }(time.Now()) - - if err = e.db.Ping(); err != nil { - if strings.Contains(err.Error(), "sql: database is closed") { - log.Infoln("Reconnecting to DB") - e.db = connect(e.dsn) - } - } - if err = e.db.Ping(); err != nil { - log.Errorln("Error pinging oracle:", err) - //e.db.Close() - e.up.Set(0) - return - } else { - log.Debugln("Successfully pinged Oracle database: ") - e.up.Set(1) - } - - if checkIfMetricsChanged() { - reloadMetrics() - } - - wg := sync.WaitGroup{} - - for _, metric := range metricsToScrap.Metric { - wg.Add(1) - metric := metric //https://golang.org/doc/faq#closures_and_goroutines - - go func() { - defer wg.Done() - - log.Debugln("About to scrape metric: ") - log.Debugln("- Metric MetricsDesc: ", metric.MetricsDesc) - log.Debugln("- Metric Context: ", metric.Context) - log.Debugln("- Metric MetricsType: ", metric.MetricsType) - log.Debugln("- Metric MetricsBuckets: ", metric.MetricsBuckets, "(Ignored unless Histogram type)") - log.Debugln("- Metric Labels: ", metric.Labels) - log.Debugln("- Metric FieldToAppend: ", metric.FieldToAppend) - log.Debugln("- Metric IgnoreZeroResult: ", metric.IgnoreZeroResult) - log.Debugln("- Metric Request: ", metric.Request) - - if len(metric.Request) == 0 { - log.Errorln("Error scraping for ", metric.MetricsDesc, ". Did you forget to define request in your toml file?") - return - } - - if len(metric.MetricsDesc) == 0 { - log.Errorln("Error scraping for query", metric.Request, ". Did you forget to define metricsdesc in your toml file?") - return - } - - for column, metricType := range metric.MetricsType { - if metricType == "histogram" { - _, ok := metric.MetricsBuckets[column] - if !ok { - log.Errorln("Unable to find MetricsBuckets configuration key for metric. (metric=" + column + ")") - return - } - } - } - - scrapeStart := time.Now() - if err = ScrapeMetric(e.db, ch, metric); err != nil { - log.Errorln("Error scraping for", metric.Context, "_", metric.MetricsDesc, ":", err) - e.scrapeErrors.WithLabelValues(metric.Context).Inc() - } else { - log.Debugln("Successfully scraped metric: ", metric.Context, metric.MetricsDesc, time.Since(scrapeStart)) - } - }() - } - wg.Wait() -} - -func GetMetricType(metricType string, metricsType map[string]string) prometheus.ValueType { - var strToPromType = map[string]prometheus.ValueType{ - "gauge": prometheus.GaugeValue, - "counter": prometheus.CounterValue, - "histogram": prometheus.UntypedValue, - } - - strType, ok := metricsType[strings.ToLower(metricType)] - if !ok { - return prometheus.GaugeValue - } - valueType, ok := strToPromType[strings.ToLower(strType)] - if !ok { - panic(errors.New("Error while getting prometheus type " + strings.ToLower(strType))) - } - return valueType -} - -// interface method to call ScrapeGenericValues using Metric struct values -func ScrapeMetric(db *sql.DB, ch chan<- prometheus.Metric, metricDefinition Metric) error { - log.Debugln("Calling function ScrapeGenericValues()") - return ScrapeGenericValues(db, ch, metricDefinition.Context, metricDefinition.Labels, - metricDefinition.MetricsDesc, metricDefinition.MetricsType, metricDefinition.MetricsBuckets, - metricDefinition.FieldToAppend, metricDefinition.IgnoreZeroResult, - metricDefinition.Request) -} - -// generic method for retrieving metrics. -func ScrapeGenericValues(db *sql.DB, ch chan<- prometheus.Metric, context string, labels []string, - metricsDesc map[string]string, metricsType map[string]string, metricsBuckets map[string]map[string]string, fieldToAppend string, ignoreZeroResult bool, request string) error { - metricsCount := 0 - genericParser := func(row map[string]string) error { - // Construct labels value - labelsValues := []string{} - for _, label := range labels { - labelsValues = append(labelsValues, row[label]) - } - // Construct Prometheus values to sent back - for metric, metricHelp := range metricsDesc { - value, err := strconv.ParseFloat(strings.TrimSpace(row[metric]), 64) - // If not a float, skip current metric - if err != nil { - log.Errorln("Unable to convert current value to float (metric=" + metric + - ",metricHelp=" + metricHelp + ",value=<" + row[metric] + ">)") - continue - } - log.Debugln("Query result looks like: ", value) - // If metric do not use a field content in metric's name - if strings.Compare(fieldToAppend, "") == 0 { - desc := prometheus.NewDesc( - prometheus.BuildFQName(namespace, context, metric), - metricHelp, - labels, nil, - ) - if metricsType[strings.ToLower(metric)] == "histogram" { - count, err := strconv.ParseUint(strings.TrimSpace(row["count"]), 10, 64) - if err != nil { - log.Errorln("Unable to convert count value to int (metric=" + metric + - ",metricHelp=" + metricHelp + ",value=<" + row["count"] + ">)") - continue - } - buckets := make(map[float64]uint64) - for field, le := range metricsBuckets[metric] { - lelimit, err := strconv.ParseFloat(strings.TrimSpace(le), 64) - if err != nil { - log.Errorln("Unable to convert bucket limit value to float (metric=" + metric + - ",metricHelp=" + metricHelp + ",bucketlimit=<" + le + ">)") - continue - } - counter, err := strconv.ParseUint(strings.TrimSpace(row[field]), 10, 64) - if err != nil { - log.Errorln("Unable to convert ", field, " value to int (metric="+metric+ - ",metricHelp="+metricHelp+",value=<"+row[field]+">)") - continue - } - buckets[lelimit] = counter - } - ch <- prometheus.MustNewConstHistogram(desc, count, value, buckets, labelsValues...) - } else { - ch <- prometheus.MustNewConstMetric(desc, GetMetricType(metric, metricsType), value, labelsValues...) - } - // If no labels, use metric name - } else { - desc := prometheus.NewDesc( - prometheus.BuildFQName(namespace, context, cleanName(row[fieldToAppend])), - metricHelp, - nil, nil, - ) - if metricsType[strings.ToLower(metric)] == "histogram" { - count, err := strconv.ParseUint(strings.TrimSpace(row["count"]), 10, 64) - if err != nil { - log.Errorln("Unable to convert count value to int (metric=" + metric + - ",metricHelp=" + metricHelp + ",value=<" + row["count"] + ">)") - continue - } - buckets := make(map[float64]uint64) - for field, le := range metricsBuckets[metric] { - lelimit, err := strconv.ParseFloat(strings.TrimSpace(le), 64) - if err != nil { - log.Errorln("Unable to convert bucket limit value to float (metric=" + metric + - ",metricHelp=" + metricHelp + ",bucketlimit=<" + le + ">)") - continue - } - counter, err := strconv.ParseUint(strings.TrimSpace(row[field]), 10, 64) - if err != nil { - log.Errorln("Unable to convert ", field, " value to int (metric="+metric+ - ",metricHelp="+metricHelp+",value=<"+row[field]+">)") - continue - } - buckets[lelimit] = counter - } - ch <- prometheus.MustNewConstHistogram(desc, count, value, buckets) - } else { - ch <- prometheus.MustNewConstMetric(desc, GetMetricType(metric, metricsType), value) - } - } - metricsCount++ - } - return nil - } - err := GeneratePrometheusMetrics(db, genericParser, request) - log.Debugln("ScrapeGenericValues() - metricsCount: ", metricsCount) - if err != nil { - return err - } - if !ignoreZeroResult && metricsCount == 0 { - return errors.New("No metrics found while parsing") - } - return err -} - -// inspired by https://kylewbanks.com/blog/query-result-to-map-in-golang -// Parse SQL result and call parsing function to each row -func GeneratePrometheusMetrics(db *sql.DB, parse func(row map[string]string) error, query string) error { - - // Add a timeout - timeout, err := strconv.Atoi(*queryTimeout) - if err != nil { - log.Fatal("error while converting timeout option value: ", err) - panic(err) - } - ctx, cancel := context.WithTimeout(context.Background(), time.Duration(timeout)*time.Second) - defer cancel() - rows, err := db.QueryContext(ctx, query) - - if ctx.Err() == context.DeadlineExceeded { - return errors.New("Oracle query timed out") - } - - if err != nil { - return err - } - cols, err := rows.Columns() - defer rows.Close() - - for rows.Next() { - // Create a slice of interface{}'s to represent each column, - // and a second slice to contain pointers to each item in the columns slice. - 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 { - return err - } - - // 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.Sprintf("%v", *val) - } - // Call function to parse row - if err := parse(m); err != nil { - return err - } - } - - 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.ToLower(s) - return s -} - -func hashFile(h hash.Hash, fn string) error { - f, err := os.Open(fn) - if err != nil { - return err - } - defer f.Close() - if _, err := io.Copy(h, f); err != nil { - return err - } - return nil -} - -func checkIfMetricsChanged() bool { - for i, _customMetrics := range strings.Split(*customMetrics, ",") { - if len(_customMetrics) == 0 { - continue - } - log.Debug("Checking modifications in following metrics definition file:", _customMetrics) - h := sha256.New() - if err := hashFile(h, _customMetrics); err != nil { - log.Errorln("Unable to get file hash", err) - return false - } - // If any of files has been changed reload metrics - if !bytes.Equal(hashMap[i], h.Sum(nil)) { - log.Infoln(_customMetrics, "has been changed. Reloading metrics...") - hashMap[i] = h.Sum(nil) - return true - } - } - return false -} - -func reloadMetrics() { - // Truncate metricsToScrap - metricsToScrap.Metric = []Metric{} - - // Load default metrics - if _, err := toml.DecodeFile(*defaultFileMetrics, &metricsToScrap); err != nil { - log.Errorln(err) - panic(errors.New("Error while loading " + *defaultFileMetrics)) - } else { - log.Infoln("Successfully loaded default metrics from: " + *defaultFileMetrics) - } - - // If custom metrics, load it - if strings.Compare(*customMetrics, "") != 0 { - for _, _customMetrics := range strings.Split(*customMetrics, ",") { - if _, err := toml.DecodeFile(_customMetrics, &additionalMetrics); err != nil { - log.Errorln(err) - panic(errors.New("Error while loading " + _customMetrics)) - } else { - log.Infoln("Successfully loaded custom metrics from: " + _customMetrics) - } - metricsToScrap.Metric = append(metricsToScrap.Metric, additionalMetrics.Metric...) - } - } else { - log.Infoln("No custom metrics defined.") - } -} - -type authinfo struct { - Username string - Password string -} - -func (auth *authinfo) setupAuthConfig() { - // step1: read from config file - authConfigYamlFile, err := ioutil.ReadFile(*authconfig) - if err != nil { - log.Errorln(err) - } - err = yaml.Unmarshal(authConfigYamlFile, auth) - if err != nil { - log.Errorln(err) - } - // step2: read from docker secret - usrname_file := dockerpath + auth.Username - password_file := dockerpath + auth.Password - if _, err := os.Stat(usrname_file); err == nil { - if temp, err := ioutil.ReadFile(usrname_file); err == nil { - auth.Username = string(temp[:len(temp)-1]) - } else { - log.Errorln(err) - } - } else { - log.Errorln(err) - } - - if _, err := os.Stat(password_file); err == nil { - if temp, err := ioutil.ReadFile(password_file); err == nil { - auth.Password = string(temp[:len(temp)-1]) - } else { - log.Errorln(err) - } - } else { - log.Errorln(err) - } -} - -func (auth *authinfo) basicAuth(next http.Handler) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - username, password, ok := r.BasicAuth() - if ok { - usernameHash := sha256.Sum256([]byte(username)) - passwordHash := sha256.Sum256([]byte(password)) - expectedUsernameHash := sha256.Sum256([]byte(auth.Username)) - expectedPasswordHash := sha256.Sum256([]byte(auth.Password)) - - usernameMatch := (subtle.ConstantTimeCompare(usernameHash[:], expectedUsernameHash[:]) == 1) - passwordMatch := (subtle.ConstantTimeCompare(passwordHash[:], expectedPasswordHash[:]) == 1) - - if usernameMatch && passwordMatch { - log.Infoln("Authentication Success!") - next.ServeHTTP(w, r) - return - } else { - log.Errorln("Unmatched password or username of the http authentication") - } - } - log.Errorln("Unauthorized and restricted") - w.Header().Set("WWW-Authenticate", `Basic realm="restricted", charset="UTF-8"`) - http.Error(w, "Unauthorized", http.StatusUnauthorized) - }) -} - -func setUpDSN() string { - dsn_secret_from_vault := getSecretFromVault() - if(dsn_secret_from_vault != "" ) { - return dsn_secret_from_vault - } - dsn_secret_file := dockerpath + os.Getenv("DATA_SOURCE_NAME") - // dsn_secret_file := "/" + os.Getenv("DATA_SOURCE_NAME") - if _, err := os.Stat(dsn_secret_file); err == nil { - if temp, err := ioutil.ReadFile(dsn_secret_file); err == nil { - return string(temp[:len(temp)-1]) - } else { - log.Errorln(err) - } - } else { - log.Errorln(err) - } - return os.Getenv("DATA_SOURCE_NAME") -} - -func getSecretFromVault() string { - vault_secret_ocid := os.Getenv("VAULT_SECRET_OCID") // eg ocid1.compartment.oc1..aaaaaaaasqkh32mmf4zt5j6plkm4l4rdjli3vhtdfmfkmna3nyskui6kcqnq - if (vault_secret_ocid == "") { - return "" - } - oci_region := os.Getenv("OCI_REGION") //eg "us-ashburn-1" ie common.RegionIAD - if (oci_region == "") { - return "" - } - instancePrincipalConfigurationProvider, err := auth.InstancePrincipalConfigurationProviderForRegion(common.RegionIAD) - client, err := vault.NewVaultsClientWithConfigurationProvider(instancePrincipalConfigurationProvider) - if err != nil { - fmt.Printf("failed to create client err = %s", err) - return "" - } - req := vault.GetSecretRequest{SecretId: common.String("ocid1.vaultsecret.oc1.iad.amaaaaaa55avruqaollb43ogvcksuf5tlxy6rxstmggtpp7b5rmjprcjcjmq")} - resp, err := client.GetSecret(context.Background(), req) - if err != nil { - fmt.Printf("failed to create resp err = %s", err) - return "" - } - fmt.Println(resp) - secretValue, err := base64.StdEncoding.DecodeString(resp.Secret.String()) - if err != nil { - fmt.Printf("failed to decode err = %s", err) - return "" - } - return string(secretValue) -} - -func main() { - log.AddFlags(kingpin.CommandLine) - kingpin.Version("oracledb_exporter " + Version) - kingpin.HelpFlag.Short('h') - kingpin.Parse() - - log.Infoln("Starting oracledb_exporter " + Version) - // dsn := os.Getenv("DATA_SOURCE_NAME") - dsn := setUpDSN() - - // Load default and custom metrics - hashMap = make(map[int][]byte) - reloadMetrics() - - exporter := NewExporter(dsn) - prometheus.MustRegister(exporter) - - // See more info on https://github.com/prometheus/client_golang/blob/master/prometheus/promhttp/http.go#L269 - opts := promhttp.HandlerOpts{ - ErrorLog: log.NewErrorLogger(), - ErrorHandling: promhttp.ContinueOnError, - } - - dbuser := new(authinfo) - dbuser.setupAuthConfig() - http.Handle(*metricPath, dbuser.basicAuth(promhttp.HandlerFor(prometheus.DefaultGatherer, opts))) - - http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { - w.Write([]byte("Oracle DB Exporter " + Version + "

Oracle DB Exporter " + Version + "

Metrics

")) - }) - - if *securedMetrics { - if _, err := os.Stat(*serverCert); err != nil { - log.Fatal("Error loading certificate:", err) - panic(err) - } - if _, err := os.Stat(*serverKey); err != nil { - log.Fatal("Error loading key:", err) - panic(err) - } - log.Infoln("Listening TLS server on", *listenAddress) - if err := http.ListenAndServeTLS(*listenAddress, *serverCert, *serverKey, nil); err != nil { - log.Fatal("Failed to start the secure server:", err) - panic(err) - } - } else { - log.Infoln("Listening on", *listenAddress) - log.Fatal(http.ListenAndServe(*listenAddress, nil)) - } -} diff --git a/oracle-db-monitoring-exporter/oci8.pc.template b/oracle-db-monitoring-exporter/oci8.pc.template deleted file mode 100644 index 03610dc..0000000 --- a/oracle-db-monitoring-exporter/oci8.pc.template +++ /dev/null @@ -1,7 +0,0 @@ -libdir=/usr/lib/oracle/@ORACLE_VERSION@/client64/lib -includedir=/usr/include/oracle/@ORACLE_VERSION@/client64 -Name: oci8 -Description: oci8 library -Version: @ORACLE_VERSION@ -Cflags: -I${includedir} -Libs: -L${libdir} -lclntsh diff --git a/oracle-db-monitoring-exporter/oraclelinux/Dockerfile b/oracle-db-monitoring-exporter/oraclelinux/Dockerfile deleted file mode 100644 index e0f089b..0000000 --- a/oracle-db-monitoring-exporter/oraclelinux/Dockerfile +++ /dev/null @@ -1,52 +0,0 @@ -FROM golang:1.14 AS build - -ARG ORACLE_VERSION -ENV ORACLE_VERSION=${ORACLE_VERSION} -ENV LD_LIBRARY_PATH "/usr/lib/oracle/${ORACLE_VERSION}/client64/lib" - -RUN apt-get -qq update && apt-get install --no-install-recommends -qq libaio1 rpm -COPY oci8.pc.template /usr/share/pkgconfig/oci8.pc -RUN sed -i "s/@ORACLE_VERSION@/$ORACLE_VERSION/g" /usr/share/pkgconfig/oci8.pc -COPY oracle*${ORACLE_VERSION}*.rpm / -RUN rpm -Uh --nodeps /oracle-instantclient*.x86_64.rpm && rm /*.rpm -RUN echo $LD_LIBRARY_PATH >> /etc/ld.so.conf.d/oracle.conf && ldconfig - -WORKDIR /go/src/oracledb_exporter -COPY . . -RUN go get -d -v - -ARG VERSION -ENV VERSION ${VERSION:-0.1.0} - -ENV PKG_CONFIG_PATH /go/src/oracledb_exporter -ENV GOOS linux - -RUN go build -v -ldflags "-X main.Version=${VERSION} -s -w" - - -FROM oraclelinux:7-slim - -ARG ORACLE_VERSION -ENV ORACLE_VERSION=${ORACLE_VERSION} -RUN yum -y install oracle-release-el7 && \ - yum -y --setopt=tsflags=nodocs update && \ - # yum list oracle-instantclient* && \ - yum -y --setopt=tsflags=nodocs install oracle-instantclient${ORACLE_VERSION}-basic.x86_64 && \ - yum clean all - - -ARG LEGACY_TABLESPACE -ENV LEGACY_TABLESPACE=${LEGACY_TABLESPACE} -COPY --from=build /go/src/oracledb_exporter/oracle-db-monitoring-exporter /oracledb_exporter -ADD ./default-metrics${LEGACY_TABLESPACE}.toml /default-metrics.toml - -RUN chmod 755 /oracledb_exporter && \ - chmod 644 /default-metrics.toml && \ - groupadd www-data && useradd -g www-data www-data -USER www-data -ENV DATA_SOURCE_NAME system/oracle@oracle/xe -ENV LD_LIBRARY_PATH "/usr/lib/oracle/${ORACLE_VERSION}/client64/lib" - -EXPOSE 9161 - -ENTRYPOINT ["/oracledb_exporter"] diff --git a/oracledb/Dockerfile b/oracledb/Dockerfile deleted file mode 100644 index 4e8f2e7..0000000 --- a/oracledb/Dockerfile +++ /dev/null @@ -1,10 +0,0 @@ -# -# -# Copyright (c) 2021 Oracle and/or its affiliates. -# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ -# -# - -FROM container-registry.oracle.com/database/enterprise:latest -COPY oracledb_entrypoint.sh / -ENTRYPOINT ["/bin/bash", "/oracledb_entrypoint.sh"] diff --git a/oracledb/oracledb_entrypoint.sh b/oracledb/oracledb_entrypoint.sh deleted file mode 100644 index 7b08070..0000000 --- a/oracledb/oracledb_entrypoint.sh +++ /dev/null @@ -1,26 +0,0 @@ -#!/bin/sh -# -# Copyright (c) 2021 Oracle and/or its affiliates. -# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ -# -# - -: ${SECRETS_DIR:=/run/secrets} - -env_secrets_expand() { - for env_var in $(printenv | cut -f1 -d"=") - do - eval val=\$$env_var - if secret_name=$(expr match "$val" "DOCKER_SECRET@\([^}]\+\)$"); then - secret="${SECRETS_DIR}/${secret_name}" - if [ -f "$secret" ]; then - val=$(cat "${secret}") - export "$env_var"="$val" - fi - fi - done -} - -env_secrets_expand - -/bin/bash /opt/oracle/runOracle.sh diff --git a/prometheus/Dockerfile b/prometheus/Dockerfile deleted file mode 100644 index 1e80f7a..0000000 --- a/prometheus/Dockerfile +++ /dev/null @@ -1,16 +0,0 @@ -# -# -# Copyright (c) 2021 Oracle and/or its affiliates. -# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ -# -# - -FROM prom/prometheus:v2.30.0 - -COPY prom_entrypoint.sh / -COPY localhost.crt / -COPY localhost.key / -USER root -RUN ["chmod", "+x", "/prom_entrypoint.sh"] - -ENTRYPOINT ["/bin/sh", "/prom_entrypoint.sh"] diff --git a/prometheus/prom_entrypoint.sh b/prometheus/prom_entrypoint.sh deleted file mode 100644 index f454e4b..0000000 --- a/prometheus/prom_entrypoint.sh +++ /dev/null @@ -1,22 +0,0 @@ -#!/bin/sh -# -# Copyright (c) 2021 Oracle and/or its affiliates. -# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ -# -# - -: ${WEB_CONFIG:=/etc/prometheus/prometheus_vol/web.yml} -: ${PROM_CONFIG:=/etc/prometheus/prometheus_vol/config.yml} -: ${SECRETS_DIR:=/run/secrets} - -# auth for prometheus -- web.yml -prom_pwd_sec_file=$SECRETS_DIR"/prom.auth.pwd" -sed -e "s|prom.auth.pwd|$(cat $prom_pwd_sec_file)|g" $WEB_CONFIG > /web.yml - -# auth for exporter -- config.yml -exporter_usr_sec_file=$SECRETS_DIR"/auth.username" -exporter_pwd_sec_file=$SECRETS_DIR"/auth.password" -sed -e "s|auth.username|$(cat $exporter_usr_sec_file)|g" $PROM_CONFIG > /config_temp.yml -sed -e "s|auth.password|$(cat $exporter_pwd_sec_file)|g" /config_temp.yml > /config.yml - -/bin/prometheus --config.file=/config.yml --web.config.file=/web.yml diff --git a/systemd-example/oracleasm_exporter.service b/systemd-example/oracleasm_exporter.service new file mode 100644 index 0000000..d3d1060 --- /dev/null +++ b/systemd-example/oracleasm_exporter.service @@ -0,0 +1,24 @@ +[Unit] +Description=Service for oracle asm telemetry client +After=network-online.target + +[Service] +Type=simple +Environment="DATA_SOURCE_NAME=asmsnmp/password@//host:1521/+ASM?as=sysdba" +Environment="LD_LIBRARY_PATH=/u01/app/oracle/product/19.0.0/dbhome_1/lib" +Environment="ORACLE_HOME=/u01/app/oracle/product/19.0.0/dbhome_1" +User=oracledb_exporter +Group=oracledb_exporter +ExecStart=/usr/local/bin/oracledb_exporter \ + --default.metrics "/etc/oracledb_exporter/default-asm-metrics.toml" \ + --log.level "error" \ + --web.listen-address 0.0.0.0:9163 \ + --log.format "logger:syslog?appname=oracleasm_exporter&local=7" + +KillMode=process +RemainAfterExit=no +Restart=on-failure +RestartSec=5s + +[Install] +WantedBy=multi-user.target diff --git a/systemd-example/oracledb_exporter.service b/systemd-example/oracledb_exporter.service new file mode 100644 index 0000000..b58b274 --- /dev/null +++ b/systemd-example/oracledb_exporter.service @@ -0,0 +1,30 @@ +# +# Ansible managed +# + +[Unit] +Description=Service for oracle telemetry client +After=network-online.target + +[Service] +Type=simple +Environment="DATA_SOURCE_NAME=dbsnmp/password@//host:1521/service?transport_connect_timeout=5&retry_count=3" +Environment="LD_LIBRARY_PATH=/u01/app/oracle/product/19.0.0/dbhome_1/lib" +Environment="ORACLE_HOME=/u01/app/oracle/product/19.0.0/dbhome_1" +#Environment="PATH=$PATH:/u01/app/oracle/product/19.0.0/dbhome_1/bin" +#Environment="TNS_ADMIN=/u01/app/oracle/product/19.0.0/dbhome_1/network/admin" +User=oracledb_exporter +Group=oracledb_exporter +ExecStart=/usr/local/bin/oracledb_exporter \ + --default.metrics "/etc/oracledb_exporter/default-metrics.toml" \ + --log.level "error" \ + --web.listen-address 0.0.0.0:9161 \ + --log.format "logger:syslog?appname=oracledb_exporter&local=7" + +KillMode=process +RemainAfterExit=no +Restart=on-failure +RestartSec=5s + +[Install] +WantedBy=multi-user.target diff --git a/teq-default-metrics.toml b/teq-default-metrics.toml new file mode 100755 index 0000000..a95f993 --- /dev/null +++ b/teq-default-metrics.toml @@ -0,0 +1,255 @@ +[[metric]] +context = "ownership" +metricsdesc = { inst_id = "Owner instance of the current queues." } +request = ''' +SELECT + inst_id +FROM + gv$persistent_queues +WHERE + ROWNUM < 2 +''' + +[[metric]] +context = "teq" +metricsdesc = { curr_inst_id = "ID of current instance" } +request = "SELECT instance_number AS curr_inst_id FROM v$instance" + +[[metric]] +context = "teq" +labels = ["inst_id"] +metricsdesc = { total_queues = "Total number of queues"} +request = ''' +SELECT + inst_id, + COUNT(*) AS total_queues +FROM + ( + SELECT DISTINCT + t1.inst_id, + t2.queue_name + FROM + gv$aq_sharded_subscriber_stat t1 + JOIN gv$persistent_queues t2 ON t1.queue_id = t2.queue_id + ) +GROUP BY + inst_id +''' + +[[metric]] +context = "teq" +labels = ["inst_id"] +metricsdesc = { alt_total_queues = "Total number of queues"} +request = ''' +SELECT + 0 as inst_id, + COUNT(*) AS total_queues +FROM + ( + select name from all_queues where queue_type = 'NORMAL_QUEUE' and sharded = 'TRUE' + ) +GROUP BY 1 +''' + +[[metric]] +context = "teq" +labels = ["inst_id"] +metricsdesc = { total_subscribers = "Total number of subscribers"} +request = ''' +SELECT + inst_id, + COUNT(*) AS total_subscribers +FROM + ( + SELECT DISTINCT + inst_id, + subscriber_id + FROM + gv$aq_sharded_subscriber_stat + ) +GROUP BY + inst_id +''' + +[[metric]] +context = "teq" +labels = ["inst_id", "queue_name", "subscriber_name"] +metricsdesc = { enqueued_msgs = "Total enqueued messages.", dequeued_msgs = "Total dequeued messages.", remained_msgs = "Total remained messages.", time_since_last_dequeue = "Time since last dequeue.", estd_time_to_drain_no_enq = "Estimated time to drain if no enqueue.", message_latency_1 = "Message latency for last 5 mins.", message_latency_2 = "Message latency for last 1 hour.", message_latency_3 = "Message latency for last 5 hours."} +request = ''' +SELECT DISTINCT + t1.inst_id, + t1.queue_id, + t2.queue_name, + t1.subscriber_id AS subscriber_name, + t1.enqueued_msgs, + t1.dequeued_msgs, + t1.remained_msgs, + t1.time_since_last_dequeue, + t1.estd_time_to_drain_no_enq, + t1.message_latency_1, + t1.message_latency_2, + t1.message_latency_3 +FROM + ( + SELECT + inst_id, + queue_id, + subscriber_id, + SUM(enqueued_msgs) AS enqueued_msgs, + SUM(dequeued_msgs) AS dequeued_msgs, + SUM(enqueued_msgs - dequeued_msgs) AS remained_msgs, + MIN(time_since_last_dequeue) AS time_since_last_dequeue, + MAX(estd_time_to_drain_no_enq) AS estd_time_to_drain_no_enq, + AVG(10) AS message_latency_1, + AVG(20) AS message_latency_2, + AVG(30) AS message_latency_3 + FROM + gv$aq_sharded_subscriber_stat + GROUP BY + queue_id, + subscriber_id, + inst_id + ) t1 + JOIN gv$persistent_queues t2 ON t1.queue_id = t2.queue_id +''' + +[[metric]] +context = "teq_sessions" +labels = ["inst_id", "status", "type"] +metricsdesc = { value = "Gauge metric with count of sessions by status and type." } +request = ''' +SELECT + inst_id, + status, + type, + COUNT(*) AS value +FROM + gv$session +GROUP BY + status, + type, + inst_id +''' + + +[[metric]] +context = "asm_diskgroup" +labels = ["inst_id", "name"] +metricsdesc = { total = "Total size of ASM disk group.", free = "Free space available on ASM disk group." } +request = ''' +SELECT + inst_id, + name, + total_mb * 1024 * 1024 AS total, + free_mb * 1024 * 1024 AS free +FROM + gv$asm_diskgroup +''' +ignorezeroresult = true + + +[[metric]] +context = "activity" +labels = ["inst_id", "name"] +metricsdesc = { value = "Generic counter metric from gv$sysstat view in Oracle." } +request = ''' +SELECT + inst_id, + name, + value +FROM + gv$sysstat +WHERE + name IN ( + 'parse count (total)', + 'execute count', + 'user commits', + 'user rollbacks' + ) +''' + +[[metric]] +context = "teq_process" +labels = ["inst_id"] +metricsdesc = { count = "Gauge metric with count of processes." } +request = ''' +SELECT + inst_id, + COUNT(*) AS count +FROM + gv$process +GROUP BY + inst_id +''' + +[[metric]] +context = "wait_class" +labels = ["inst_id", "wait_class"] +metricsdesc = { total_waits = "Number of times waits of the class occurred", time_waited = "Amount of time spent in the wait by all sessions in the instance" } +request = ''' +SELECT + inst_id, + wait_class, + total_waits, + time_waited +FROM + gv$system_wait_class +''' + +[[metric]] +context = "system" +labels = ["inst_id", "stat_name"] +metricsdesc = { value = "os metric from gv$osstat view in Oracle." } +request = ''' +SELECT + inst_id, + stat_name, + value +FROM + gv$osstat +WHERE + upper(stat_name) IN ( + 'NUM_CPUS', + 'LOAD', + 'IDLE_TIME', + 'BUSY_TIME', + 'USER_TIME', + 'PHYSICAL_MEMORY_BYTES', + 'FREE_MEMORY_BYTES' + ) +''' + +[[metric]] +context = "system_network" +labels = ["inst_id"] +metricsdesc = { received_from_client = "Bytes received from client.", sent_to_client = "Bytes sent to client." } +request = ''' +SELECT + t1.inst_id AS inst_id, + t1.received_from_client AS received_from_client, + t2.sent_to_client AS sent_to_client +FROM + ( + SELECT + inst_id, + value AS received_from_client + FROM + gv$sysstat + WHERE + lower(name) LIKE '%received via sql*net from client%' + ORDER BY + value DESC + ) t1 + LEFT JOIN ( + SELECT + inst_id, + value AS sent_to_client + FROM + gv$sysstat + WHERE + lower(name) LIKE '%sent via sql*net to client%' + ORDER BY + value DESC + ) t2 ON t1.inst_id = t2.inst_id +''' + diff --git a/tests/1000-integers.toml b/tests/1000-integers.toml new file mode 100644 index 0000000..e95f78d --- /dev/null +++ b/tests/1000-integers.toml @@ -0,0 +1,12 @@ +[[metric]] +context = "integers" +labels = [ "value" ] +metricsdesc = { value= "Gauge metric integers counting up." } +request = ''' +select value +from dual +model + dimension by ( 0 as key ) + measures ( 0 as value ) + rules upsert ( value[ for key from 1 to 1000 increment 1 ] = cv(key) ) +''' From 0bd14e246f4e37caec2ee0eee2653a98493d514f Mon Sep 17 00:00:00 2001 From: Mark Nelson Date: Tue, 5 Sep 2023 16:52:05 -0400 Subject: [PATCH 02/17] wip docs for release Signed-off-by: Mark Nelson --- README.md | 56 +- THIRD_PARTY_LICENSES.txt | 1273 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 1321 insertions(+), 8 deletions(-) create mode 100644 THIRD_PARTY_LICENSES.txt diff --git a/README.md b/README.md index 71b44d2..bb80308 100644 --- a/README.md +++ b/README.md @@ -1,21 +1,61 @@ -# Oracle DB Exporter +# Unified Observability for Oracle Database -[![Build Status](https://travis-ci.org/iamseth/oracledb_exporter.svg)](https://travis-ci.org/iamseth/oracledb_exporter) -[![GoDoc](https://godoc.org/github.com/iamseth/oracledb_exporter?status.svg)](http://godoc.org/github.com/iamseth/oracledb_exporter) -[![Report card](https://goreportcard.com/badge/github.com/iamseth/oracledb_exporter)](https://goreportcard.com/badge/github.com/iamseth/oracledb_exporter) +This project aims to provide observability for the Oracle Database so that users can understand performance and diagnose issues easily across applications and database. Over time, this project will provide not just metrics, but also logging and tracing support, and integration into popular frameworks like Spring Boot. The project aims to deliver functionality to support both cloud and on-premises databases, including those running in Kubernetes and containers. -##### Table of Contents +In the first production release, v1.0, this project provides a [Prometheus](https://prometheus.io/) exporter for Oracle Database that is based in part on a Prometheus exporter created by [iamseth](https://github.com/iamseth/oracledb_exporter) with various changes to comply with various Oracle standards and policies. -[Description](#description) +Customers with an active support agreement for Oracle Database may open a Service Request in My Oracle Support for support with any issues using this exporter. Community support is available through GitHub issues, etc., for other users. + +Contributions are welcome - please see [contributing](CONTRIBUTING.md). + + +### Table of Contents + +[Roadmap](#roadmap) +[Standard metrics](#standard-metrics) [Installation](#installation) [Running](#running) [Grafana](#grafana) [Troubleshooting](#troubleshooting) [Operating principles](operating-principles.md) -# Description +# Roadmap + +## Version 1.0 + +The first production release, v1.0, includes the following features: + +- A number of [standard metrics](#standard-metrics) are exposed, +- Users can define [custom metrics](#custom-metrics), +- Oracle regularly reviews third-party licenses and scans the code and images, including transitive/recursive dependencies for issues, +- Connection to Oracle can be a basic connection or use an Oracle Wallet and TLS - connection to Oracle Autonomous Database is supported, +- Metrics for Oracle Transactional Event Queues are also supported, +- A Grafana dashboard is provided for Transacational Event Queues, and +- A pre-built container image is provided, based on Oracle Linux, and optimized for size and security. + +Note that this exporter uses a different Oracle Database driver which in turn uses code directly written by Oracle to access the database. This driver does require an Oracle client. In this initial release, the client is bundled into the container image, however we intend to make that optional in order to minimize the image size. + +The interfaces for this version have been kept as close as possible to those of earlier alpha releases in this repository to assist with migration. However, it should be expected that there may be breaking changes in future releases. + +## Plans + +We always welcome input on features you would like to see supported. Please open an issue in this repository with your suggestions. + +Currently, we plan to address the following key features: + +- Implement multiple database support - allow the exporter to publish metrics for multiple database instances, +- Implement vault support - allow the exporter to obtain database connection information from a secure vault, +- Implement connection storm protection - prevent the exporter from repeatedly connecting when the credentials fail, to prevent a storm of connections causing accounts to be locked across a large number of databases, +- Provide the option to have the Oracle client outside of the container image, e.g., on a shared volume, +- Implement the ability to update the configuration dynamically, i.e., without a restart, +- Implement support for exporting logs, including audit logs for example, from the database, +- Implement support for tracing within the database, e.g., using an execution context ID provide by an external caller, +- Provide additional pre-built Grafana dashboards, +- Integration with Spring Observability, e.g., Micrometer, +- Provide additional documentation and samples, and +- Integrate with the Oracle Database Operator for Kubernetes. -A [Prometheus](https://prometheus.io/) exporter for Oracle modeled after the MySQL exporter. I'm not a DBA or seasoned Go developer so PRs definitely welcomed. +# Standard metrics The following metrics are exposed currently. diff --git a/THIRD_PARTY_LICENSES.txt b/THIRD_PARTY_LICENSES.txt new file mode 100644 index 0000000..6931f24 --- /dev/null +++ b/THIRD_PARTY_LICENSES.txt @@ -0,0 +1,1273 @@ +=== Public License Template === + +------------------------------ Top-Level License ------------------------------- +MIT-0fd6492b + +---------------------------------- Copyright ----------------------------------- +Copyright (c) 2016 Seth Miller +Copyright (c) 2023 Oracle and/or its affiliates. + +-------------------------- Fourth Party Dependencies --------------------------- + +----------------------------------- Licenses ----------------------------------- +- Apache-2.0 +- BSD-3-Clause--modified-by-Google +- MIT +- UPL-1.0 + +--------------------------------- (separator) ---------------------------------- + +== Dependency +github.com/BurntSushi/toml + +== License Type +SPDX:MIT + +== Copyright +Copyright (c) 2013 TOML authors +Copyright (c) 2018 TOML authors +Copyright 2010 The Go Authors. All rights reserved. + +--------------------------------- (separator) ---------------------------------- + +== Dependency +github.com/alecthomas/kingpin/v2 + +== License Type +SPDX:MIT + +== Copyright +Copyright (C) 2014 Alec Thomas + +--------------------------------- (separator) ---------------------------------- + +== Dependency +github.com/alecthomas/units + +== License Type +SPDX:MIT + +== Copyright +Copyright (C) 2014 Alec Thomas + +--------------------------------- (separator) ---------------------------------- + +== Dependency +github.com/beorn7/perks + +== License Type +SPDX:MIT + +== Copyright +Copyright (C) 2013 Blake Mizerany + +--------------------------------- (separator) ---------------------------------- + +== Dependency +github.com/cespare/xxhash/v2 + +== License Type +SPDX:MIT + +== Copyright +Copyright (c) 2016 Caleb Spare + +--------------------------------- (separator) ---------------------------------- + +== Dependency +github.com/coreos/go-systemd/v22 + +== License Type +SPDX:Apache-2.0 + +== Copyright +Copyright 2014 Docker, Inc. +Copyright 2015 CoreOS Inc. +Copyright 2015 CoreOS, Inc. +Copyright 2015 RedHat, Inc. +Copyright 2015, 2018 CoreOS, Inc. +Copyright 2015-2018 CoreOS, Inc. +Copyright 2016 CoreOS, Inc. +Copyright 2018 CoreOS, Inc +Copyright 2018 CoreOS, Inc. +Copyright 2019 CoreOS, Inc. +Copyright 2020 CoreOS, Inc. +Copyright 2022 CoreOS, Inc. + +== Notices +CoreOS Project +Copyright 2018 CoreOS, Inc + +This product includes software developed at CoreOS, Inc. +(http://www.coreos.com/). + + +--------------------------------- (separator) ---------------------------------- + +== Dependency +github.com/go-kit/log + +== License Type +SPDX:MIT + +== Copyright +Copyright (c) 2014 Simon Eskildsen +Copyright (c) 2021 Go kit +Copyright 2011 The Go Authors. All rights reserved. +Copyright 2013 The Go Authors. All rights reserved. + +--------------------------------- (separator) ---------------------------------- + +== Dependency +github.com/go-logfmt/logfmt + +== License Type +SPDX:MIT + +== Copyright +Copyright (c) 2015 go-logfmt +Copyright 2010 The Go Authors. All rights reserved. + +--------------------------------- (separator) ---------------------------------- + +== Dependency +github.com/go-logr/logr + +== License Type +SPDX:Apache-2.0 + +== Copyright +Copyright 2019 The logr Authors. +Copyright 2020 The logr Authors. +Copyright 2021 The logr Authors. + +--------------------------------- (separator) ---------------------------------- + +== Dependency +github.com/godror/godror + +== License Type +===BSD-3-Clause--modified-by-Google-039067db + +godror +======= + +Copyright 2017, 2020 TamΓ‘s GulΓ‘csi + +You can use either the + Apache License, Version 2.0 (APL-2.0), +or the + Universal Permissive License, Version 1.0 (UPL-1.0). + +ODPI-C +====== + +Copyright (c) 2016, 2018 Oracle and/or its affiliates. All rights reserved. + +This program is free software: you can modify it and/or redistribute it under +the terms of: + +(i) the Universal Permissive License v 1.0 or at your option, any + later version (); and/or + +(ii) the Apache License v 2.0. () + + +The Universal Permissive License (UPL), Version 1.0 +=================================================== + +Subject to the condition set forth below, permission is hereby granted to any +person obtaining a copy of this software, associated documentation and/or data +(collectively the "Software"), free of charge and under any and all copyright +rights in the Software, and any and all patent rights owned or freely +licensable by each licensor hereunder covering either (i) the unmodified +Software as contributed to or provided by such licensor, or (ii) the Larger +Works (as defined below), to deal in both + +(a) the Software, and + +(b) any piece of software and/or hardware listed in the lrgrwrks.txt file if + one is included with the Software (each a "Larger Work" to which the + Software is contributed by such licensors), + +without restriction, including without limitation the rights to copy, create +derivative works of, display, perform, and distribute the Software and make, +use, sell, offer for sale, import, export, have made, and have sold the +Software and the Larger Work(s), and to sublicense the foregoing rights on +either these or other terms. + +This license is subject to the following condition: + +The above copyright notice and either this complete permission notice or at a +minimum a reference to the UPL must be included in all copies or substantial +portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +Apache License +============== + +Version 2.0, January 2004 + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. **Definitions**. + + "License" shall mean the terms and conditions for use, reproduction, and + distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by the + copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all other + entities that control, are controlled by, or are under common control with + that entity. For the purposes of this definition, "control" means (i) the + power, direct or indirect, to cause the direction or management of such + entity, whether by contract or otherwise, or (ii) ownership of fifty + percent (50%) or more of the outstanding shares, or (iii) beneficial + ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity exercising + permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation source, + and configuration files. + + "Object" form shall mean any form resulting from mechanical transformation + or translation of a Source form, including but not limited to compiled + object code, generated documentation, and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or Object form, + made available under the License, as indicated by a copyright notice that + is included in or attached to the work (an example is provided in the + Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object form, + that is based on (or derived from) the Work and for which the editorial + revisions, annotations, elaborations, or other modifications represent, as + a whole, an original work of authorship. For the purposes of this License, + Derivative Works shall not include works that remain separable from, or + merely link (or bind by name) to the interfaces of, the Work and Derivative + Works thereof. + + "Contribution" shall mean any work of authorship, including the original + version of the Work and any modifications or additions to that Work or + Derivative Works thereof, that is intentionally submitted to Licensor for + inclusion in the Work by the copyright owner or by an individual or Legal + Entity authorized to submit on behalf of the copyright owner. For the + purposes of this definition, "submitted" means any form of electronic, + verbal, or written communication sent to the Licensor or its + representatives, including but not limited to communication on electronic + mailing lists, source code control systems, and issue tracking systems that + are managed by, or on behalf of, the Licensor for the purpose of discussing + and improving the Work, but excluding communication that is conspicuously + marked or otherwise designated in writing by the copyright owner as "Not a + Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity on + behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. **Grant of Copyright License.** Subject to the terms and conditions of this + License, each Contributor hereby grants to You a perpetual, worldwide, + non-exclusive, no-charge, royalty-free, irrevocable copyright license to + reproduce, prepare Derivative Works of, publicly display, publicly perform, + sublicense, and distribute the Work and such Derivative Works in Source or + Object form. + +3. **Grant of Patent License.** Subject to the terms and conditions of this + License, each Contributor hereby grants to You a perpetual, worldwide, + non-exclusive, no-charge, royalty-free, irrevocable (except as stated in + this section) patent license to make, have made, use, offer to sell, sell, + import, and otherwise transfer the Work, where such license applies only to + those patent claims licensable by such Contributor that are necessarily + infringed by their Contribution(s) alone or by combination of their + Contribution(s) with the Work to which such Contribution(s) was submitted. + If You institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work or a + Contribution incorporated within the Work constitutes direct or + contributory patent infringement, then any patent licenses granted to You + under this License for that Work shall terminate as of the date such + litigation is filed. + +4. **Redistribution.** You may reproduce and distribute copies of the Work or + Derivative Works thereof in any medium, with or without modifications, and + in Source or Object form, provided that You meet the following conditions: + + 1. You must give any other recipients of the Work or Derivative Works a + copy of this License; and + + 2. You must cause any modified files to carry prominent notices stating + that You changed the files; and + + 3. You must retain, in the Source form of any Derivative Works that You + distribute, all copyright, patent, trademark, and attribution notices + from the Source form of the Work, excluding those notices that do not + pertain to any part of the Derivative Works; and + + 4. If the Work includes a "NOTICE" text file as part of its distribution, + then any Derivative Works that You distribute must include a readable + copy of the attribution notices contained within such NOTICE file, + excluding those notices that do not pertain to any part of the + Derivative Works, in at least one of the following places: within a + NOTICE text file distributed as part of the Derivative Works; within + the Source form or documentation, if provided along with the Derivative + Works; or, within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents of the + NOTICE file are for informational purposes only and do not modify the + License. You may add Your own attribution notices within Derivative + Works that You distribute, alongside or as an addendum to the NOTICE + text from the Work, provided that such additional attribution notices + cannot be construed as modifying the License. + + You may add Your own copyright statement to Your modifications and may + provide additional or different license terms and conditions for use, + reproduction, or distribution of Your modifications, or for any such + Derivative Works as a whole, provided Your use, reproduction, and + distribution of the Work otherwise complies with the conditions stated + in this License. + +5. **Submission of Contributions.** Unless You explicitly state otherwise, any + Contribution intentionally submitted for inclusion in the Work by You to + the Licensor shall be under the terms and conditions of this License, + without any additional terms or conditions. Notwithstanding the above, + nothing herein shall supersede or modify the terms of any separate license + agreement you may have executed with Licensor regarding such Contributions. + +6. **Trademarks.** This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, except + as required for reasonable and customary use in describing the origin of + the Work and reproducing the content of the NOTICE file. + +7. **Disclaimer of Warranty.** Unless required by applicable law or agreed to + in writing, Licensor provides the Work (and each Contributor provides its + Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied, including, without limitation, any + warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or + FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for + determining the appropriateness of using or redistributing the Work and + assume any risks associated with Your exercise of permissions under this + License. + +8. **Limitation of Liability.** In no event and under no legal theory, whether + in tort (including negligence), contract, or otherwise, unless required by + applicable law (such as deliberate and grossly negligent acts) or agreed to + in writing, shall any Contributor be liable to You for damages, including + any direct, indirect, special, incidental, or consequential damages of any + character arising as a result of this License or out of the use or + inability to use the Work (including but not limited to damages for loss of + goodwill, work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor has been + advised of the possibility of such damages. + +9. **Accepting Warranty or Additional Liability.** While redistributing the + Work or Derivative Works thereof, You may choose to offer, and charge a fee + for, acceptance of support, warranty, indemnity, or other liability + obligations and/or rights consistent with this License. However, in + accepting such obligations, You may act only on Your own behalf and on Your + sole responsibility, not on behalf of any other Contributor, and only if + You agree to indemnify, defend, and hold each Contributor harmless for any + liability incurred by, or claims asserted against, such Contributor by + reason of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + + +============================================================ +github.com/go-logfmt/logfmt v0.5.0 +============================================================ +The MIT License (MIT) + +Copyright (c) 2015 go-logfmt + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +============================================================ +github.com/google/go-cmp v0.4.0 +============================================================ +Copyright (c) 2017 The Go Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +============================================================ +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e +============================================================ +Copyright (c) 2009 The Go Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + + +== Copyright +Copyright (c) 2009 The Go Authors. All rights reserved. +Copyright (c) 2015 go-logfmt +Copyright (c) 2016, 2018 Oracle and/or its affiliates. All rights reserved. +Copyright (c) 2016, 2022 Oracle and/or its affiliates. +Copyright (c) 2016, 2022, Oracle and/or its affiliates. +Copyright (c) 2017 The Go Authors. All rights reserved. +Copyright (c) 2019 Josh Bleecher Snyder +Copyright 2016 TamΓ‘s GulΓ‘csi +Copyright 2017, 2020 TamΓ‘s GulΓ‘csi +Copyright 2017, 2020 The Godror Authors +Copyright 2017, 2021 The Godror Authors +Copyright 2017, 2022 The Godror Authors +Copyright 2018, 2020 The Godror Authors +Copyright 2019 TamΓ‘s GulΓ‘csi +Copyright 2019, 2020 The Godror Authors +Copyright 2019, 2021 The Godror Authors +Copyright 2019, 2022 The Godror Authors +Copyright 2020 TamΓ‘s GulΓ‘csi +Copyright 2020 The Godror Authors +Copyright 2020, 2022 The Godror Authors +Copyright 2021 The Godror Authors +Copyright 2021, 2022 The Godror Authors +Copyright 2022 The Godror Authors + +--------------------------------- (separator) ---------------------------------- + +== Dependency +github.com/godror/knownpb + +== License Type +SPDX:Apache-2.0 + +== Copyright +Copyright 2014, 2021 TamΓ‘s GulΓ‘csi +Copyright 2019, 2021 TamΓ‘s GulΓ‘csi + +--------------------------------- (separator) ---------------------------------- + +== Dependency +github.com/golang/protobuf + +== License Type +SPDX:BSD-3-Clause--modified-by-Google + +== Copyright +Copyright 2010 The Go Authors. All rights reserved. +Copyright 2010 The Go Authors. All rights reserved. +Copyright 2011 The Go Authors. All rights reserved. +Copyright 2014 The Go Authors. All rights reserved. +Copyright 2015 The Go Authors. All rights reserved. +Copyright 2015 The Go Authors. All rights reserved. +Copyright 2016 The Go Authors. All rights reserved. +Copyright 2017 The Go Authors. All rights reserved. +Copyright 2018 The Go Authors. All rights reserved. +Copyright 2019 The Go Authors. All rights reserved. +Copyright 2020 The Go Authors. All rights reserved. + +--------------------------------- (separator) ---------------------------------- + +== Dependency +github.com/jpillora/backoff + +== License Type +SPDX:MIT + +== Copyright +Copyright (c) 2017 Jaime Pillora + +--------------------------------- (separator) ---------------------------------- + +== Dependency +github.com/matttproud/golang_protobuf_extensions + +== License Type +SPDX:Apache-2.0 + +== Copyright +Copyright 2012 Matt T. Proud (matt.proud@gmail.com) +Copyright 2013 Matt T. Proud +Copyright 2016 Matt T. Proud + +== Notices +Copyright 2012 Matt T. Proud (matt.proud@gmail.com) + + +--------------------------------- (separator) ---------------------------------- + +== Dependency +github.com/mwitkow/go-conntrack + +== License Type +SPDX:Apache-2.0 + +== Copyright +Copyright 2016 Michal Witkowski. All Rights Reserved. + +--------------------------------- (separator) ---------------------------------- + +== Dependency +github.com/prometheus/client_golang + +== License Type +SPDX:Apache-2.0 + +== Copyright +Copyright (c) 2013, The Prometheus Authors +Copyright (c) 2015 BjΓΆrn Rabenstein +Copyright 2010 The Go Authors +Copyright 2012-2015 The Prometheus Authors +Copyright 2013 Matt T. Proud +Copyright 2013-2015 Blake Mizerany, BjΓΆrn Rabenstein +Copyright 2014 The Prometheus Authors +Copyright 2015 The Prometheus Authors +Copyright 2016 The Prometheus Authors +Copyright 2017 The Prometheus Authors +Copyright 2018 The Prometheus Authors +Copyright 2019 The Prometheus Authors +Copyright 2020 The Prometheus Authors +Copyright 2021 The Prometheus Authors +Copyright 2022 The Prometheus Authors + +== Notices +Prometheus instrumentation library for Go applications +Copyright 2012-2015 The Prometheus Authors + +This product includes software developed at +SoundCloud Ltd. (http://soundcloud.com/). + + +The following components are included in this product: + +perks - a fork of https://github.com/bmizerany/perks +https://github.com/beorn7/perks +Copyright 2013-2015 Blake Mizerany, BjΓΆrn Rabenstein +See https://github.com/beorn7/perks/blob/master/README.md for license details. + +Go support for Protocol Buffers - Google's data interchange format +http://github.com/golang/protobuf/ +Copyright 2010 The Go Authors +See source code for license details. + +Support for streaming Protocol Buffer messages for the Go language (golang). +https://github.com/matttproud/golang_protobuf_extensions +Copyright 2013 Matt T. Proud +Licensed under the Apache License, Version 2.0 + + +--------------------------------- (separator) ---------------------------------- + +== Dependency +github.com/prometheus/client_model + +== License Type +SPDX:Apache-2.0 + +== Copyright +Copyright 2012-2015 The Prometheus Authors +Copyright 2013 Prometheus Team + +== Notices +Data model artifacts for Prometheus. +Copyright 2012-2015 The Prometheus Authors + +This product includes software developed at +SoundCloud Ltd. (http://soundcloud.com/). + + +--------------------------------- (separator) ---------------------------------- + +== Dependency +github.com/prometheus/common + +== License Type +SPDX:Apache-2.0 + +== Copyright +Copyright (c) 2011, Open Knowledge Foundation Ltd. +Copyright 2013 The Prometheus Authors +Copyright 2014 The Prometheus Authors +Copyright 2015 The Prometheus Authors +Copyright 2016 The Prometheus Authors +Copyright 2017 The Prometheus Authors +Copyright 2018 The Prometheus Authors +Copyright 2019 The Prometheus Authors +Copyright 2020 The Prometheus Authors +Copyright 2020 The Prometheus-operator Authors +Copyright 2021 The Prometheus Authors +Copyright 2022 The Prometheus Authors + +== Notices +Common libraries shared by Prometheus Go components. +Copyright 2015 The Prometheus Authors + +This product includes software developed at +SoundCloud Ltd. (http://soundcloud.com/). + + +--------------------------------- (separator) ---------------------------------- + +== Dependency +github.com/prometheus/exporter-toolkit + +== License Type +SPDX:Apache-2.0 + +== Copyright +Copyright 2015 Matthew Holt and The Caddy Authors +Copyright 2018 The Prometheus Authors +Copyright 2019 The Prometheus Authors +Copyright 2020 The Prometheus Authors +Copyright 2021 The Prometheus Authors +Copyright 2023 The Prometheus Authors + +--------------------------------- (separator) ---------------------------------- + +== Dependency +github.com/prometheus/procfs + +== License Type +SPDX:Apache-2.0 + +== Copyright +Copyright 2014 Prometheus Team +Copyright 2014-2015 The Prometheus Authors +Copyright 2017 Prometheus Team +Copyright 2017 The Prometheus Authors +Copyright 2018 The Prometheus Authors +Copyright 2019 The Prometheus Authors +Copyright 2020 The Prometheus Authors +Copyright 2021 The Prometheus Authors +Copyright 2022 The Prometheus Authors + +== Notices +procfs provides functions to retrieve system, kernel and process +metrics from the pseudo-filesystem proc. + +Copyright 2014-2015 The Prometheus Authors + +This product includes software developed at +SoundCloud Ltd. (http://soundcloud.com/). + + +--------------------------------- (separator) ---------------------------------- + +== Dependency +github.com/xhit/go-str2duration/v2 + +== License Type +SPDX:BSD-3-Clause--modified-by-Google + +== Copyright +Copyright (c) 2009 The Go Authors. All rights reserved. +Copyright 2010 The Go Authors. All rights reserved. + +--------------------------------- (separator) ---------------------------------- + +== Dependency +golang.org/x/crypto + +== License Type +SPDX:BSD-3-Clause--modified-by-Google + +== Copyright +Copyright (c) 2009 The Go Authors. All rights reserved. +Copyright (c) 2017 The Go Authors. All rights reserved. +Copyright (c) 2019 The Go Authors. All rights reserved. +Copyright (c) 2020 The Go Authors. All rights reserved. +Copyright (c) 2021 The Go Authors. All rights reserved. +Copyright 2009 The Go Authors. All rights reserved. +Copyright 2010 The Go Authors. All rights reserved. +Copyright 2011 The Go Authors. All rights reserved. +Copyright 2012 The Go Authors. All rights reserved. +Copyright 2013 The Go Authors. All rights reserved. +Copyright 2014 The Go Authors. All rights reserved. +Copyright 2015 The Go Authors. All rights reserved. +Copyright 2016 The Go Authors. All rights reserved. +Copyright 2017 The Go Authors. All rights reserved. +Copyright 2018 The Go Authors. All rights reserved. +Copyright 2019 The Go Authors. All rights reserved. +Copyright 2020 The Go Authors. All rights reserved. +Copyright 2022 The Go Authors. All rights reserved. + +== Patents +Additional IP Rights Grant (Patents) + +"This implementation" means the copyrightable works distributed by +Google as part of the Go project. + +Google hereby grants to You a perpetual, worldwide, non-exclusive, +no-charge, royalty-free, irrevocable (except as stated in this section) +patent license to make, have made, use, offer to sell, sell, import, +transfer and otherwise run, modify and propagate the contents of this +implementation of Go, where such license applies only to those patent +claims, both currently owned or controlled by Google and acquired in +the future, licensable by Google that are necessarily infringed by this +implementation of Go. This grant does not include claims that would be +infringed only as a consequence of further modification of this +implementation. If you or your agent or exclusive licensee institute or +order or agree to the institution of patent litigation against any +entity (including a cross-claim or counterclaim in a lawsuit) alleging +that this implementation of Go or any code incorporated within this +implementation of Go constitutes direct or contributory patent +infringement, or inducement of patent infringement, then any patent +rights granted to you under this License for this implementation of Go +shall terminate as of the date such litigation is filed. + + +--------------------------------- (separator) ---------------------------------- + +== Dependency +golang.org/x/net + +== License Type +SPDX:BSD-3-Clause--modified-by-Google + +== Copyright +Copyright (c) 2009 The Go Authors. All rights reserved. +Copyright 2009 The Go Authors. All rights reserved. +Copyright 2010 The Go Authors. All rights reserved. +Copyright 2011 The Go Authors. All rights reserved. +Copyright 2012 The Go Authors. All rights reserved. +Copyright 2013 The Go Authors. All rights reserved. +Copyright 2014 The Go Authors. All rights reserved. +Copyright 2015 The Go Authors. All rights reserved. +Copyright 2016 The Go Authors. All rights reserved. +Copyright 2017 The Go Authors. All rights reserved. +Copyright 2018 The Go Authors. All rights reserved. +Copyright 2019 The Go Authors. All rights reserved. +Copyright 2020 The Go Authors. All rights reserved. +Copyright 2021 The Go Authors. All rights reserved. +Copyright 2022 The Go Authors. All rights reserved. +Copyright 2023 The Go Authors. All rights reserved. + +== Patents +Additional IP Rights Grant (Patents) + +"This implementation" means the copyrightable works distributed by +Google as part of the Go project. + +Google hereby grants to You a perpetual, worldwide, non-exclusive, +no-charge, royalty-free, irrevocable (except as stated in this section) +patent license to make, have made, use, offer to sell, sell, import, +transfer and otherwise run, modify and propagate the contents of this +implementation of Go, where such license applies only to those patent +claims, both currently owned or controlled by Google and acquired in +the future, licensable by Google that are necessarily infringed by this +implementation of Go. This grant does not include claims that would be +infringed only as a consequence of further modification of this +implementation. If you or your agent or exclusive licensee institute or +order or agree to the institution of patent litigation against any +entity (including a cross-claim or counterclaim in a lawsuit) alleging +that this implementation of Go or any code incorporated within this +implementation of Go constitutes direct or contributory patent +infringement, or inducement of patent infringement, then any patent +rights granted to you under this License for this implementation of Go +shall terminate as of the date such litigation is filed. + + +--------------------------------- (separator) ---------------------------------- + +== Dependency +golang.org/x/oauth2 + +== License Type +SPDX:BSD-3-Clause--modified-by-Google + +== Copyright +Copyright (c) 2009 The Go Authors. All rights reserved. +Copyright 2014 The Go Authors. All rights reserved. +Copyright 2015 The Go Authors. All rights reserved. +Copyright 2015 The oauth2 Authors. All rights reserved. +Copyright 2016 The Go Authors. All rights reserved. +Copyright 2017 The Go Authors. All rights reserved. +Copyright 2017 The oauth2 Authors. All rights reserved. +Copyright 2018 The Go Authors. All rights reserved. +Copyright 2018 The oauth2 Authors. All rights reserved. +Copyright 2019 The Go Authors. All rights reserved. +Copyright 2020 The Go Authors. All rights reserved. +Copyright 2021 The Go Authors. All rights reserved. +Copyright 2022 The Go Authors. All rights reserved. + +--------------------------------- (separator) ---------------------------------- + +== Dependency +golang.org/x/sync + +== License Type +SPDX:BSD-3-Clause--modified-by-Google + +== Copyright +Copyright (c) 2009 The Go Authors. All rights reserved. +Copyright 2013 The Go Authors. All rights reserved. +Copyright 2016 The Go Authors. All rights reserved. +Copyright 2017 The Go Authors. All rights reserved. +Copyright 2019 The Go Authors. All rights reserved. + +== Patents +Additional IP Rights Grant (Patents) + +"This implementation" means the copyrightable works distributed by +Google as part of the Go project. + +Google hereby grants to You a perpetual, worldwide, non-exclusive, +no-charge, royalty-free, irrevocable (except as stated in this section) +patent license to make, have made, use, offer to sell, sell, import, +transfer and otherwise run, modify and propagate the contents of this +implementation of Go, where such license applies only to those patent +claims, both currently owned or controlled by Google and acquired in +the future, licensable by Google that are necessarily infringed by this +implementation of Go. This grant does not include claims that would be +infringed only as a consequence of further modification of this +implementation. If you or your agent or exclusive licensee institute or +order or agree to the institution of patent litigation against any +entity (including a cross-claim or counterclaim in a lawsuit) alleging +that this implementation of Go or any code incorporated within this +implementation of Go constitutes direct or contributory patent +infringement, or inducement of patent infringement, then any patent +rights granted to you under this License for this implementation of Go +shall terminate as of the date such litigation is filed. + + +--------------------------------- (separator) ---------------------------------- + +== Dependency +golang.org/x/sys + +== License Type +SPDX:BSD-3-Clause--modified-by-Google + +== Copyright +Copyright (c) 2009 The Go Authors. All rights reserved. +Copyright 2009 The Go Authors. All rights reserved. +Copyright 2009,2010 The Go Authors. All rights reserved. +Copyright 2010 The Go Authors. All rights reserved. +Copyright 2011 The Go Authors. All rights reserved. +Copyright 2012 The Go Authors. All rights reserved. +Copyright 2013 The Go Authors. All rights reserved. +Copyright 2014 The Go Authors. All rights reserved. +Copyright 2015 The Go Authors. All rights reserved. +Copyright 2016 The Go Authors. All rights reserved. +Copyright 2017 The Go Authors. All right reserved. +Copyright 2017 The Go Authors. All rights reserved. +Copyright 2018 The Go Authors. All rights reserved. +Copyright 2019 The Go Authors. All rights reserved. +Copyright 2020 The Go Authors. All rights reserved. +Copyright 2021 The Go Authors. All rights reserved. +Copyright 2022 The Go Authors. All rights reserved. +Copyright 2023 The Go Authors. All rights reserved. + +== Patents +Additional IP Rights Grant (Patents) + +"This implementation" means the copyrightable works distributed by +Google as part of the Go project. + +Google hereby grants to You a perpetual, worldwide, non-exclusive, +no-charge, royalty-free, irrevocable (except as stated in this section) +patent license to make, have made, use, offer to sell, sell, import, +transfer and otherwise run, modify and propagate the contents of this +implementation of Go, where such license applies only to those patent +claims, both currently owned or controlled by Google and acquired in +the future, licensable by Google that are necessarily infringed by this +implementation of Go. This grant does not include claims that would be +infringed only as a consequence of further modification of this +implementation. If you or your agent or exclusive licensee institute or +order or agree to the institution of patent litigation against any +entity (including a cross-claim or counterclaim in a lawsuit) alleging +that this implementation of Go or any code incorporated within this +implementation of Go constitutes direct or contributory patent +infringement, or inducement of patent infringement, then any patent +rights granted to you under this License for this implementation of Go +shall terminate as of the date such litigation is filed. + + +--------------------------------- (separator) ---------------------------------- + +== Dependency +golang.org/x/text + +== License Type +SPDX:BSD-3-Clause--modified-by-Google + +== Copyright +Copyright (c) 2009 The Go Authors. All rights reserved. +Copyright 2009 The Go Authors. All rights reserved. +Copyright 2011 The Go Authors. All rights reserved. +Copyright 2012 The Go Authors. All rights reserved. +Copyright 2013 The Go Authors. All rights reserved. +Copyright 2014 The Go Authors. All rights reserved. +Copyright 2015 The Go Authors. All rights reserved. +Copyright 2016 The Go Authors. All rights reserved. +Copyright 2017 The Go Authors. All rights reserved. +Copyright 2018 The Go Authors. All rights reserved. +Copyright 2019 The Go Authors. All rights reserved. +Copyright 2021 The Go Authors. All rights reserved. + +== Patents +Additional IP Rights Grant (Patents) + +"This implementation" means the copyrightable works distributed by +Google as part of the Go project. + +Google hereby grants to You a perpetual, worldwide, non-exclusive, +no-charge, royalty-free, irrevocable (except as stated in this section) +patent license to make, have made, use, offer to sell, sell, import, +transfer and otherwise run, modify and propagate the contents of this +implementation of Go, where such license applies only to those patent +claims, both currently owned or controlled by Google and acquired in +the future, licensable by Google that are necessarily infringed by this +implementation of Go. This grant does not include claims that would be +infringed only as a consequence of further modification of this +implementation. If you or your agent or exclusive licensee institute or +order or agree to the institution of patent litigation against any +entity (including a cross-claim or counterclaim in a lawsuit) alleging +that this implementation of Go or any code incorporated within this +implementation of Go constitutes direct or contributory patent +infringement, or inducement of patent infringement, then any patent +rights granted to you under this License for this implementation of Go +shall terminate as of the date such litigation is filed. + + +--------------------------------- (separator) ---------------------------------- + +== Dependency +google.golang.org/protobuf + +== License Type +SPDX:BSD-3-Clause--modified-by-Google + +== Copyright +Copyright (c) 2018 The Go Authors. All rights reserved. +Copyright 2008 Google Inc. All rights reserved. +Copyright 2018 The Go Authors. All rights reserved. +Copyright 2018 The Go Authors. All rights reserved.", +Copyright 2019 The Go Authors. All rights reserved. +Copyright 2019 The Go Authors. All rights reserved.", +Copyright 2020 The Go Authors. All rights reserved. +Copyright 2021 The Go Authors. All rights reserved. +Copyright 2022 The Go Authors. All rights reserved. +Copyright 2023 The Go Authors. All rights reserved. + +== Patents +Additional IP Rights Grant (Patents) + +"This implementation" means the copyrightable works distributed by +Google as part of the Go project. + +Google hereby grants to You a perpetual, worldwide, non-exclusive, +no-charge, royalty-free, irrevocable (except as stated in this section) +patent license to make, have made, use, offer to sell, sell, import, +transfer and otherwise run, modify and propagate the contents of this +implementation of Go, where such license applies only to those patent +claims, both currently owned or controlled by Google and acquired in +the future, licensable by Google that are necessarily infringed by this +implementation of Go. This grant does not include claims that would be +infringed only as a consequence of further modification of this +implementation. If you or your agent or exclusive licensee institute or +order or agree to the institution of patent litigation against any +entity (including a cross-claim or counterclaim in a lawsuit) alleging +that this implementation of Go or any code incorporated within this +implementation of Go constitutes direct or contributory patent +infringement, or inducement of patent infringement, then any patent +rights granted to you under this License for this implementation of Go +shall terminate as of the date such litigation is filed. + + +--------------------------------- (separator) ---------------------------------- + +== Dependency +gopkg.in/yaml.v2 + +== License Type +SPDX:Apache-2.0 + +== Copyright +Copyright (c) 2006 Kirill Simonov +Copyright 2011-2016 Canonical Ltd. + +== Notices +Copyright 2011-2016 Canonical Ltd. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + + +----------------------------------- Licenses ----------------------------------- + +--------------------------------- (separator) ---------------------------------- +== SPDX:Apache-2.0 + +Apache License + +Version 2.0, January 2004 + +http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + +"License" shall mean the terms and conditions for use, reproduction, and +distribution as defined by Sections 1 through 9 of this document. + +"Licensor" shall mean the copyright owner or entity authorized by the +copyright owner that is granting the License. + +"Legal Entity" shall mean the union of the acting entity and all other +entities that control, are controlled by, or are under common control with +that entity. For the purposes of this definition, "control" means (i) the +power, direct or indirect, to cause the direction or management of such +entity, whether by contract or otherwise, or (ii) ownership of fifty percent +(50%) or more of the outstanding shares, or (iii) beneficial ownership of such +entity. + +"You" (or "Your") shall mean an individual or Legal Entity exercising +permissions granted by this License. + +"Source" form shall mean the preferred form for making modifications, +including but not limited to software source code, documentation source, and +configuration files. + +"Object" form shall mean any form resulting from mechanical transformation or +translation of a Source form, including but not limited to compiled object +code, generated documentation, and conversions to other media types. + +"Work" shall mean the work of authorship, whether in Source or Object form, +made available under the License, as indicated by a copyright notice that is +included in or attached to the work (an example is provided in the Appendix +below). + +"Derivative Works" shall mean any work, whether in Source or Object form, that +is based on (or derived from) the Work and for which the editorial revisions, +annotations, elaborations, or other modifications represent, as a whole, an +original work of authorship. For the purposes of this License, Derivative +Works shall not include works that remain separable from, or merely link (or +bind by name) to the interfaces of, the Work and Derivative Works thereof. + +"Contribution" shall mean any work of authorship, including the original +version of the Work and any modifications or additions to that Work or +Derivative Works thereof, that is intentionally submitted to Licensor for +inclusion in the Work by the copyright owner or by an individual or Legal +Entity authorized to submit on behalf of the copyright owner. For the purposes +of this definition, "submitted" means any form of electronic, verbal, or +written communication sent to the Licensor or its representatives, including +but not limited to communication on electronic mailing lists, source code +control systems, and issue tracking systems that are managed by, or on behalf +of, the Licensor for the purpose of discussing and improving the Work, but +excluding communication that is conspicuously marked or otherwise designated +in writing by the copyright owner as "Not a Contribution." + +"Contributor" shall mean Licensor and any individual or Legal Entity on behalf +of whom a Contribution has been received by Licensor and subsequently +incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: + +(a) You must give any other recipients of the Work or Derivative Works a copy +of this License; and + +(b) You must cause any modified files to carry prominent notices stating that +You changed the files; and + +(c) You must retain, in the Source form of any Derivative Works that You +distribute, all copyright, patent, trademark, and attribution notices from the +Source form of the Work, excluding those notices that do not pertain to any +part of the Derivative Works; and + +(d) If the Work includes a "NOTICE" text file as part of its distribution, +then any Derivative Works that You distribute must include a readable copy of +the attribution notices contained within such NOTICE file, excluding those +notices that do not pertain to any part of the Derivative Works, in at least +one of the following places: within a NOTICE text file distributed as part of +the Derivative Works; within the Source form or documentation, if provided +along with the Derivative Works; or, within a display generated by the +Derivative Works, if and wherever such third-party notices normally appear. +The contents of the NOTICE file are for informational purposes only and do not +modify the License. You may add Your own attribution notices within Derivative +Works that You distribute, alongside or as an addendum to the NOTICE text from +the Work, provided that such additional attribution notices cannot be +construed as modifying the License. + +You may add Your own copyright statement to Your modifications and may provide +additional or different license terms and conditions for use, reproduction, or +distribution of Your modifications, or for any such Derivative Works as a +whole, provided Your use, reproduction, and distribution of the Work otherwise +complies with the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + +To apply the Apache License to your work, attach the following boilerplate +notice, with the fields enclosed by brackets "[]" replaced with your own +identifying information. (Don't include the brackets!) The text should be +enclosed in the appropriate comment syntax for the file format. We also +recommend that a file or class name and description of purpose be included on +the same "printed page" as the copyright notice for easier identification +within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); + +you may not use this file except in compliance with the License. + +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software + +distributed under the License is distributed on an "AS IS" BASIS, + +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + +See the License for the specific language governing permissions and + +limitations under the License. + + + +--------------------------------- (separator) ---------------------------------- +== SPDX:BSD-3-Clause--modified-by-Google + +Redistribution and use in source and binary forms, with +or without modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +--------------------------------- (separator) ---------------------------------- +== SPDX:MIT + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +=== ATTRIBUTION-HELPER-GENERATED: +=== Attribution helper version: {Major:0 Minor:11 GitVersion:0.10.0-69-g9cf205e3-dirty GitCommit:9cf205e3ce436f506f0901d927c1e417e72f384f GitTreeState:dirty BuildDate:2023-01-26T09:24:19Z GoVersion:go1.16 Compiler:gc Platform:linux/amd64} +=== License file based on go.mod with md5 sum: d714700fac22c61e9370575079b3dde6 From 5904be6379fc364c8ccc966e50fb026481b55911 Mon Sep 17 00:00:00 2001 From: Mark Nelson Date: Wed, 6 Sep 2023 15:40:11 -0400 Subject: [PATCH 03/17] working on doc and docker-compose sample Signed-off-by: Mark Nelson --- README.md | 61 +++++++++++++---------- docker-compose/compose.yaml | 47 +++++++++++++++++ docker-compose/grafana/datasources.yaml | 9 ++++ docker-compose/prometheus/prometheus.yaml | 21 ++++++++ 4 files changed, 113 insertions(+), 25 deletions(-) create mode 100644 docker-compose/compose.yaml create mode 100644 docker-compose/grafana/datasources.yaml create mode 100644 docker-compose/prometheus/prometheus.yaml diff --git a/README.md b/README.md index bb80308..0a4d11e 100644 --- a/README.md +++ b/README.md @@ -87,52 +87,63 @@ The following metrics are exposed currently. # Installation -## Docker +## Docker, Podman, etc. -You can run via Docker using an existing image. Since version 0.4, the images are available on the github registry. +You can run the exporter in a local container using a conatiner image from [Oracle Container Registry](https://container-registry.oracle.com). The container image is available in the "observability-exporter" repository in the "Database" category. No authentication or license presentment/acceptance are required to pull this image from the registry. -Here an example to retrieve the version 0.5.0: +To run the exporter in a container and expose the port, use this command: ```bash -docker pull ghcr.io/iamseth/oracledb_exporter:0.5.0 +docker run -it --rm -p 9161:9161 container-registry.oracle.com/database/observability-exporter:1.0.0 ``` -And here a command to run it and forward the port: +If you need an Oracle Database to test the exporter, you can use this command to start up an instance of [Oracle Database 23c Free](https://www.oracle.com/database/free/) which also requires no authentication or license presentment/acceptance to pull the image. ```bash -docker run -it --rm -p 9161:9161 ghcr.io/iamseth/oracledb_exporter:0.5.0 +docker run --name free23c -d -p 1521:1521 -e ORACLE_PWD=Welcome12345 container-registry.oracle.com/database/free:latest ``` -If you don't already have an Oracle server, you can run one locally in a container and then link the exporter to it. +This will pull the image and start up the database with a listener on port 1521. It will also create a pluggable database (a database container) called "FREEPDB1" and will set the admin passwords to the password you specified on this command. + +You can tail the logs to see when the database is ready to use: + +```bash +docker logs -f free23c + +(look for this message...) +######################### +DATABASE IS READY TO USE! +######################### +``` + +To obtain the IP address of the container, which you will need to connect to the database, use this command. Note: depending on your platform and container runtime, you may be able to access the database at "localhost": ```bash -docker run -d --name oracle -p 1521:1521 wnameless/oracle-xe-11g-r2:18.04-apex -docker run -d --name oracledb_exporter --link=oracle -p 9161:9161 -e DATA_SOURCE_NAME=oracle://system:oracle@oracle:1521/xe ghcr.io/iamseth/oracledb_exporter:0.5.0 +docker inspect free23c | grep IPA + "SecondaryIPAddresses": null, + "IPAddress": "172.17.0.2", + "IPAMConfig": null, + "IPAddress": "172.17.0.2", ``` -Since 0.2.1, the exporter image exist with Alpine flavor. Watch out for their use. It is for the moment a test. + +## Test/demo environment with Docker Compose + +If you would like to set up a test environment with the exporter, you can use the provided "Docker Compose" file in this repository which will start an Oracle Database instance, the exporter, Prometheus and Grafana. ```bash -docker run -d --name oracledb_exporter --link=oracle -p 9161:9161 -e DATA_SOURCE_NAME=oracle://system:oracle@oracle/xe iamseth/oracledb_exporter:alpine +cd docker-compose +docker-compose up -d ``` -### Different Docker Images +The containers will take a short time to start. The first time, the Oracle container might take a few minutes to start while it creates the database instance, but this is a one-time operation, and subequent restarts will be much faster (a few seconds). -Different Linux Distros: +Once the containers are all running, you can access the services using these URLs: -- `x.y.z` - Ubuntu Linux image -- `x.y.z-oraclelinux` - Oracle Enterprise Linux image -- `x.y.z-Alpine` - Alpine Linux image +- [Exporter](http://localhost:9161/metrics) +- [Prometheus](http://localhost:9000) - try a query for "oracle". +- [Grafana](http://localhost:3000) - username is "admin" and password is "grafana". Try creating a dashboard using one of the metrics from the exporter (use the Prometheus datasource and choose a metric with "oracle" in the name). -Forked Version: -All the above docker images have a duplicate image tag ending in -`_legacy-tablespace`. These versions use the older/deprecated tablespace -utilization calculation based on the aggregate sum of file sizes in a given -tablespace. The newer mechanism takes into account block sizes, extents, and -fragmentation aligning with the same metrics reported from the Oracle Enterprise -Manager. See https://github.com/iamseth/oracledb_exporter/issues/153 for -details. The versions above should have a more useful tablespace utilization -calculation going forward. ## Binary Release diff --git a/docker-compose/compose.yaml b/docker-compose/compose.yaml new file mode 100644 index 0000000..c47ab94 --- /dev/null +++ b/docker-compose/compose.yaml @@ -0,0 +1,47 @@ + +services: + prometheus: + image: prom/prometheus + container_name: prometheus + command: + - '--config.file=/etc/prometheus/prometheus.yaml' + ports: + - 9090:9090 + restart: unless-stopped + volumes: + - ./prometheus:/etc/prometheus + - prom_data:/prometheus + + grafana: + image: grafana/grafana + container_name: grafana + ports: + - 3000:3000 + restart: unless-stopped + environment: + - GF_SECURITY_ADMIN_USER=admin + - GF_SECURITY_ADMIN_PASSWORD=grafana + volumes: + - ./grafana:/etc/grafana/provisioning/datasources + + free23c: + image: container-registry.oracle.com/database/free:latest + container_name: free23c + ports: + - 1521:1521 + environment: + - ORACLE_PWD=Welcome12345 + + exporter: + #image: container-registry.oracle.com/database/observability-exporter:1.0.0 + image: oracle/observability-exporter:0.99.6-oraclelinux + container_name: exporter + ports: + - 9161:9161 + environment: + - DB_USERNAME=pdbadmin + - DB_PASSWORD=Welcome12345 + - DB_CONNECT_STRING=free23c:1521/freepdb + +volumes: + prom_data: \ No newline at end of file diff --git a/docker-compose/grafana/datasources.yaml b/docker-compose/grafana/datasources.yaml new file mode 100644 index 0000000..4431f20 --- /dev/null +++ b/docker-compose/grafana/datasources.yaml @@ -0,0 +1,9 @@ +apiVersion: 1 + +datasources: +- name: Prometheus + type: prometheus + url: http://prometheus:9090 + isDefault: true + access: proxy + editable: true \ No newline at end of file diff --git a/docker-compose/prometheus/prometheus.yaml b/docker-compose/prometheus/prometheus.yaml new file mode 100644 index 0000000..027b7eb --- /dev/null +++ b/docker-compose/prometheus/prometheus.yaml @@ -0,0 +1,21 @@ +global: + scrape_interval: 15s + scrape_timeout: 10s + evaluation_interval: 15s +scrape_configs: + - job_name: prometheus + honor_timestamps: true + scrape_interval: 15s + scrape_timeout: 10s + metrics_path: /metrics + scheme: http + static_configs: + - targets: + - localhost:9090 + - job_name: 'oracle-exporter' + metrics_path: '/metrics' + scrape_interval: 15s + scrape_timeout: 10s + static_configs: + - targets: + - exporter:9161 \ No newline at end of file From 109221430cb2fae0dccda4909d6f7cb9a900ba0e Mon Sep 17 00:00:00 2001 From: Mark Nelson Date: Wed, 6 Sep 2023 15:50:28 -0400 Subject: [PATCH 04/17] working on doc and docker-compose sample Signed-off-by: Mark Nelson --- README.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/README.md b/README.md index 0a4d11e..fa849c5 100644 --- a/README.md +++ b/README.md @@ -87,6 +87,14 @@ The following metrics are exposed currently. # Installation +There are a number of ways to run the exporter. In this section you will find information on running the exporter: + +- In a container runtime like [Docker, Podman, etc](#docker-podman-etc) +- In a test/demo environment using [Docker Compose](#testdemo-environment-with-docker-compose) +- In [Kubernetes](#kubernetes) +- As a [standalone binary](#standalone-binary) + + ## Docker, Podman, etc. You can run the exporter in a local container using a conatiner image from [Oracle Container Registry](https://container-registry.oracle.com). The container image is available in the "observability-exporter" repository in the "Database" category. No authentication or license presentment/acceptance are required to pull this image from the registry. @@ -144,6 +152,16 @@ Once the containers are all running, you can access the services using these URL - [Prometheus](http://localhost:9000) - try a query for "oracle". - [Grafana](http://localhost:3000) - username is "admin" and password is "grafana". Try creating a dashboard using one of the metrics from the exporter (use the Prometheus datasource and choose a metric with "oracle" in the name). +## Kubernetes + +write me + +## Standalone binary + +write me + + +# END ## Binary Release From af71069e22a9dc3eb3d0d18f06f4f0b04276d7ed Mon Sep 17 00:00:00 2001 From: Mark Nelson Date: Wed, 6 Sep 2023 16:38:38 -0400 Subject: [PATCH 05/17] working on doc Signed-off-by: Mark Nelson --- README.md | 55 ++++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 50 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index fa849c5..feccf6e 100644 --- a/README.md +++ b/README.md @@ -99,11 +99,7 @@ There are a number of ways to run the exporter. In this section you will find i You can run the exporter in a local container using a conatiner image from [Oracle Container Registry](https://container-registry.oracle.com). The container image is available in the "observability-exporter" repository in the "Database" category. No authentication or license presentment/acceptance are required to pull this image from the registry. -To run the exporter in a container and expose the port, use this command: - -```bash -docker run -it --rm -p 9161:9161 container-registry.oracle.com/database/observability-exporter:1.0.0 -``` +### Oracle Database If you need an Oracle Database to test the exporter, you can use this command to start up an instance of [Oracle Database 23c Free](https://www.oracle.com/database/free/) which also requires no authentication or license presentment/acceptance to pull the image. @@ -134,6 +130,55 @@ docker inspect free23c | grep IPA "IPAddress": "172.17.0.2", ``` +### Exporter + +You need to give the exporter the connection details for the Oracle Database that you want it to run against. You can use a simple connection, or a wallet. + +#### Simple connection + +For a simple connection, you will provide the details using these variables: + +- `DB_USERNAME` is the database username, e.g., `pdbadmin` +- `DB_PASSWORD` is the password for that user, e.g., `Welcome12345` +- `DB_CONNECT_STRING` is the connection string, e.g., `free23c:1521/freepdb` + +To run the exporter in a container and expose the port, use a command like this, with the appropriate values for the environment variables: + +```bash +docker run -it --rm \ + -e DB_USERNAME=pdbadmin \ + -e DB_PASSWORD=Welcome12345 \ + -e DB_CONNECT_STRING=free23c:1521/freepdb \ + -p 9161:9161 \ + container-registry.oracle.com/database/observability-exporter:1.0.0 +``` + +#### Using a wallet + +For a wallet connection, you must first set up the wallet. If you are using Oracle Autonomous Database, for example, you can download the wallet from the Oracle Cloud Infrastructure (OCI) console. + +1. Unzip the wallet into a new directory, e.g., called `wallet`. +1. Edit the `sqlnet.ora` file and set the `DIRECTORY` to `/wallet`. This is the path inside the exporter container where you will provide the wallet. +1. Take a note of the TNS name from the `tnsnames.ora` that will be used to connect to the database, e.g., `devdb_tp`. + +Now, you provide the connection details using these variables: + +- `DB_USERNAME` is the database username, e.g., `pdbadmin` +- `DB_PASSWORD` is the password for that user, e.g., `Welcome12345` +- `DB_CONNECT_STRING` is the connection string, e.g., `devdb_tp?TNS_ADMIN=/wallet` + +To run the exporter in a container and expose the port, use a command like this, with the appropriate values for the environment variables, and mounting your `wallet` directory to provide access to the wallet: + +```bash +docker run -it --rm \ + -e DB_USERNAME=pdbadmin \ + -e DB_PASSWORD=Welcome12345 \ + -e DB_CONNECT_STRING=devdb_tp \ + -v ./wallet:/wallet \ + -p 9161:9161 \ + container-registry.oracle.com/database/observability-exporter:1.0.0 +``` + ## Test/demo environment with Docker Compose From 2b20f202a9b4cfeb50c8ffd17fb186da2e937a69 Mon Sep 17 00:00:00 2001 From: Mark Nelson Date: Thu, 7 Sep 2023 10:52:59 -0400 Subject: [PATCH 06/17] working on readme - k8s instructions Signed-off-by: Mark Nelson --- README.md | 134 +++++++++-- kubernetes/metrics-exporter-deployment.yaml | 67 ++++++ kubernetes/metrics-exporter-service.yaml | 18 ++ kubernetes/metrics-service-monitor.yaml | 17 ++ kubernetes/txeventq-metrics.toml | 240 ++++++++++++++++++++ 5 files changed, 460 insertions(+), 16 deletions(-) create mode 100755 kubernetes/metrics-exporter-deployment.yaml create mode 100755 kubernetes/metrics-exporter-service.yaml create mode 100755 kubernetes/metrics-service-monitor.yaml create mode 100755 kubernetes/txeventq-metrics.toml diff --git a/README.md b/README.md index feccf6e..c278e21 100644 --- a/README.md +++ b/README.md @@ -94,6 +94,20 @@ There are a number of ways to run the exporter. In this section you will find i - In [Kubernetes](#kubernetes) - As a [standalone binary](#standalone-binary) +# Database permissions required + +For the built-in default metrics, the database user that the exporter uses to connect to the Oracle Database instance must have the `SYS` privilege and/or `SELECT` permission on the following tables. + +- dba_tablespace_usage_metrics +- dba_tablespaces +- v$system_wait_class +- v$asm_diskgroup_stat +- v$datafile +- v$sysstat +- v$process +- v$waitclassmetric +- v$session +- v$resource_limit ## Docker, Podman, etc. @@ -167,7 +181,7 @@ Now, you provide the connection details using these variables: - `DB_PASSWORD` is the password for that user, e.g., `Welcome12345` - `DB_CONNECT_STRING` is the connection string, e.g., `devdb_tp?TNS_ADMIN=/wallet` -To run the exporter in a container and expose the port, use a command like this, with the appropriate values for the environment variables, and mounting your `wallet` directory to provide access to the wallet: +To run the exporter in a container and expose the port, use a command like this, with the appropriate values for the environment variables, and mounting your `wallet` directory as `/wallet` in the container to provide access to the wallet: ```bash docker run -it --rm \ @@ -199,7 +213,108 @@ Once the containers are all running, you can access the services using these URL ## Kubernetes -write me +To run the exporter in Kubernetes, you need to complete the following steps. All steps must be completed in the same Kunernetes namespace. The examples below assume you want to use a namespace called `exporter`, you must change the commands if you wish to use a different namespace. + +### Create a secret with credentials for connecting to the Oracle Database + +Create a secret with the Oracle database user and password that the exporter should use to connect to the database using this command. You must specify the correct user and password for your environment. This example uses `pdbadmin` as the user and `Welcome12345` as the password: + +```bash +kubectl create secret generic db-secret \ + --from-literal=username=pdbadmin \ + --from-literal=password=Welcome12345 \ + -n exporter +``` + +### Create a config map for the wallet (optional) + +Create a config map with the wallet (if you are using one) using this command. Run this command in the `wallet` directory you created earlier. + +```bash +kubectl create cm db-metrics-tns-admin \ + --from-file=cwallet.sso \ + --from-file=ewallet.p12 \ + --from-file=ewallet.pem \ + --from-file=keystore.jks \ + --from-file=ojdbc.properties \ + --from-file=sqlnet.ora \ + --from-file=tnsnames.ora \ + --from-file=truststore.jks \ + -n exporter +``` + +### Create a config map for you metrics definition file (optional) + +If you have defined any [custom metrics](#custom-metrics), you must create a config map for the metrics definition file. For example, if you created a configuration file called `txeventq-metrics.toml`, then create the config map with this command: + +```bash +kubectl create cm db-metrics-txeventq-exporter-config \ + --from-file=txeventq-metrics.toml \ + -n exporter +``` + +### Deploy the Oracle Database Observability exporter + +A sample Kubernetes manifest is provided [here](/kubernetes/metrics-exporter-deployment.yaml). You must edit this file to set the namespace you wish to use, the database connect string to use, and if you have any custom metrics, you will need to uncomment and customize some sections in this file. + +Once you have made the necessary updates, apply the file to your cluster using this command: + +```bash +kubectl apply -f metrics-exporter-deployment.yaml +``` + +You can check the deployment was successful and monitor the exporter startup with this command: + +```bash +kubectl get pods -n exporter -w +``` + +You can view the exporter's logs with this command: + +```bash +kubectl logs -f svc/metrics-exporter -n exporter +``` + +### Create a Kubernetes service for the exporter + +Create a Kubernetes service to allow access to the exporter pod(s). A sample Kubernetes manifest is provided [here](/kubernetes/metrics-exporter-service.yaml). You may need to customize this file to update the namespace. + +Once you have made any necessary udpates, apply the file to your cluster using this command: + +```bash +kubectl aspply -f metrics-exporter-service.yaml +``` + +### Create a Kubernetes service monitor + +Create a Kubernetes service monitor to tell Prometheus (for example) to collect metrics from the exporter. A sample Kubernetes manifest is provided [here](/kubernetes/metrics-service-monitor.yaml). You may need to customize this file to update the namespace. + +Once you have made any necessary udpates, apply the file to your cluster using this command: + +```bash +kubectl aspply -f metrics-service-monitor.yaml +``` + +### Configure a Prometheus target (optional) + +You may need to update your Prometheus configuration to add a target. If so, you can use this example job definition as a guide: + +```yaml + - job_name: 'oracle-exporter' + metrics_path: '/metrics' + scrape_interval: 15s + scrape_timeout: 10s + static_configs: + - targets: + - metrics-exporter.exporter.svc.cluster.local:9161 +``` + +### Import Grafana dashboard definition(s) (optional) + +Some sample Grafana dashboard definitions are provided [in this directory](/grafana). + +TODO TODO TODO write me + ## Standalone binary @@ -239,20 +354,7 @@ export DATA_SOURCE_NAME=oracle://user:password@primaryhost:1521,standbyhost:1521 # Then run the exporter /path/to/binary/oracledb_exporter --log.level error --web.listen-address 0.0.0.0:9161 ``` -## Default-metrics requirement -Make sure to grant `SYS` privilege on `SELECT` statement for the monitoring user, on the following tables. -``` -dba_tablespace_usage_metrics -dba_tablespaces -v$system_wait_class -v$asm_diskgroup_stat -v$datafile -v$sysstat -v$process -v$waitclassmetric -v$session -v$resource_limit -``` + # Integration with System D diff --git a/kubernetes/metrics-exporter-deployment.yaml b/kubernetes/metrics-exporter-deployment.yaml new file mode 100755 index 0000000..c9c9883 --- /dev/null +++ b/kubernetes/metrics-exporter-deployment.yaml @@ -0,0 +1,67 @@ +## Copyright (c) 2021, 2023, Oracle and/or its affiliates. +## Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: metrics-exporter + namespace: exporter +spec: + replicas: 1 + selector: + matchLabels: + app: metrics-exporter + template: + metadata: + labels: + app: metrics-exporter + spec: + containers: + - name: metrics-exporter + image: container-registry.oracle.com/database/observability-exporter:1.0.0 + imagePullPolicy: Always + env: + # uncomment and customize the next item if you want to provide custom metrics definitions + #- name: CUSTOM_METRICS + # value: /oracle/observability/txeventq-metrics.toml + - name: TNS_ADMIN + value: "/oracle/tns_admin" + - name: DB_USERNAME + valueFrom: + secretKeyRef: + name: db-secret + key: username + optional: false + - name: DB_PASSWORD + valueFrom: + secretKeyRef: + name: db-secret + key: password + optional: false + # update the connect string below for your database - can be simple format, or use a tns name as shown: + - name: DB_CONNECT_STRING + value: "DEVDB_TP?TNS_ADMIN=$(TNS_ADMIN)" + volumeMounts: + - name: tns-admin + mountPath: /oracle/tns_admin + # uncomment and customize the next item if you want to provide custom metrics definitions + #- name: config-volume + # mountPath: /oracle/observability/txeventq-metrics.toml + # subPath: txeventq-metrics.toml + resources: + requests: + memory: "64Mi" + cpu: "250m" + limits: + memory: "128Mi" + cpu: "500m" + ports: + - containerPort: 8080 + restartPolicy: Always + volumes: + - name: tns-admin + configMap: + name: db-metrics-tns-admin + # uncomment and customize the next item if you want to provide custom metrics definitions + #- name: config-volume + # configMap: + # name: db-metrics-txeventq-exporter-config diff --git a/kubernetes/metrics-exporter-service.yaml b/kubernetes/metrics-exporter-service.yaml new file mode 100755 index 0000000..2a63765 --- /dev/null +++ b/kubernetes/metrics-exporter-service.yaml @@ -0,0 +1,18 @@ +## Copyright (c) 2021, 2023, Oracle and/or its affiliates. +## Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ +apiVersion: v1 +kind: Service +metadata: + name: metrics-exporter-svc + namespace: exporter + labels: + app: metrics-exporter + release: stable +spec: + type: ClusterIP + ports: + - port: 9161 + name: metrics + targetPort: 9161 + selector: + app: metrics-exporter diff --git a/kubernetes/metrics-service-monitor.yaml b/kubernetes/metrics-service-monitor.yaml new file mode 100755 index 0000000..bfad758 --- /dev/null +++ b/kubernetes/metrics-service-monitor.yaml @@ -0,0 +1,17 @@ +## Copyright (c) 2021, 2023, Oracle and/or its affiliates. +## Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ +apiVersion: monitoring.coreos.com/v1 +kind: ServiceMonitor +metadata: + name: prometheus-metrics-exporter + namespace: exporter + labels: + app: metrics-exporter + release: stable +spec: + endpoints: + - interval: 20s + port: metrics + selector: + matchLabels: + app: metrics-exporter diff --git a/kubernetes/txeventq-metrics.toml b/kubernetes/txeventq-metrics.toml new file mode 100755 index 0000000..4a1774e --- /dev/null +++ b/kubernetes/txeventq-metrics.toml @@ -0,0 +1,240 @@ +[[metric]] +context = "ownership" +metricsdesc = { inst_id = "Owner instance of the current queues." } +request = ''' +SELECT + inst_id +FROM + gv$persistent_queues +WHERE + ROWNUM < 2 +''' + +[[metric]] +context = "teq" +metricsdesc = { curr_inst_id = "ID of current instance" } +request = "SELECT instance_number AS curr_inst_id FROM v$instance" + +[[metric]] +context = "teq" +labels = ["inst_id"] +metricsdesc = { total_queues = "Total number of queues"} +request = ''' +SELECT + inst_id, + COUNT(*) AS total_queues +FROM + ( + SELECT DISTINCT + t1.inst_id, + t2.queue_name + FROM + gv$aq_sharded_subscriber_stat t1 + JOIN gv$persistent_queues t2 ON t1.queue_id = t2.queue_id + ) +GROUP BY + inst_id +''' + +[[metric]] +context = "teq" +labels = ["inst_id"] +metricsdesc = { total_subscribers = "Total number of subscribers"} +request = ''' +SELECT + inst_id, + COUNT(*) AS total_subscribers +FROM + ( + SELECT DISTINCT + inst_id, + subscriber_id + FROM + gv$aq_sharded_subscriber_stat + ) +GROUP BY + inst_id +''' + +[[metric]] +context = "teq" +labels = ["inst_id", "queue_name", "subscriber_name"] +metricsdesc = { enqueued_msgs = "Total enqueued messages.", dequeued_msgs = "Total dequeued messages.", remained_msgs = "Total remained messages.", time_since_last_dequeue = "Time since last dequeue.", estd_time_to_drain_no_enq = "Estimated time to drain if no enqueue.", message_latency_1 = "Message latency for last 5 mins.", message_latency_2 = "Message latency for last 1 hour.", message_latency_3 = "Message latency for last 5 hours."} +request = ''' +SELECT DISTINCT + t1.inst_id, + t1.queue_id, + t2.queue_name, + t1.subscriber_id AS subscriber_name, + t1.enqueued_msgs, + t1.dequeued_msgs, + t1.remained_msgs, + t1.time_since_last_dequeue, + t1.estd_time_to_drain_no_enq, + t1.message_latency_1, + t1.message_latency_2, + t1.message_latency_3 +FROM + ( + SELECT + inst_id, + queue_id, + subscriber_id, + SUM(enqueued_msgs) AS enqueued_msgs, + SUM(dequeued_msgs) AS dequeued_msgs, + SUM(enqueued_msgs - dequeued_msgs) AS remained_msgs, + MIN(time_since_last_dequeue) AS time_since_last_dequeue, + MAX(estd_time_to_drain_no_enq) AS estd_time_to_drain_no_enq, + AVG(10) AS message_latency_1, + AVG(20) AS message_latency_2, + AVG(30) AS message_latency_3 + FROM + gv$aq_sharded_subscriber_stat + GROUP BY + queue_id, + subscriber_id, + inst_id + ) t1 + JOIN gv$persistent_queues t2 ON t1.queue_id = t2.queue_id +''' + +[[metric]] +context = "sessions" +labels = ["inst_id", "status", "type"] +metricsdesc = { value = "Gauge metric with count of sessions by status and type." } +request = ''' +SELECT + inst_id, + status, + type, + COUNT(*) AS value +FROM + gv$session +GROUP BY + status, + type, + inst_id +''' + + +[[metric]] +context = "asm_diskgroup" +labels = ["inst_id", "name"] +metricsdesc = { total = "Total size of ASM disk group.", free = "Free space available on ASM disk group." } +request = ''' +SELECT + inst_id, + name, + total_mb * 1024 * 1024 AS total, + free_mb * 1024 * 1024 AS free +FROM + gv$asm_diskgroup +''' +ignorezeroresult = true + + +[[metric]] +context = "activity" +labels = ["inst_id", "name"] +metricsdesc = { value = "Generic counter metric from gv$sysstat view in Oracle." } +request = ''' +SELECT + inst_id, + name, + value +FROM + gv$sysstat +WHERE + name IN ( + 'parse count (total)', + 'execute count', + 'user commits', + 'user rollbacks' + ) +''' + +[[metric]] +context = "process" +labels = ["inst_id"] +metricsdesc = { count = "Gauge metric with count of processes." } +request = ''' +SELECT + inst_id, + COUNT(*) AS count +FROM + gv$process +GROUP BY + inst_id +''' + +[[metric]] +context = "wait_class" +labels = ["inst_id", "wait_class"] +metricsdesc = { total_waits = "Number of times waits of the class occurred", time_waited = "Amount of time spent in the wait by all sessions in the instance" } +request = ''' +SELECT + inst_id, + wait_class, + total_waits, + time_waited +FROM + gv$system_wait_class +''' + +[[metric]] +context = "system" +labels = ["inst_id", "stat_name"] +metricsdesc = { value = "os metric from gv$osstat view in Oracle." } +request = ''' +SELECT + inst_id, + stat_name, + value +FROM + gv$osstat +WHERE + upper(stat_name) IN ( + 'NUM_CPUS', + 'LOAD', + 'IDLE_TIME', + 'BUSY_TIME', + 'USER_TIME', + 'PHYSICAL_MEMORY_BYTES', + 'FREE_MEMORY_BYTES' + ) +''' + +[[metric]] +context = "system_network" +labels = ["inst_id"] +metricsdesc = { received_from_client = "Bytes received from client.", sent_to_client = "Bytes sent to client." } +request = ''' +SELECT + t1.inst_id AS inst_id, + t1.received_from_client AS received_from_client, + t2.sent_to_client AS sent_to_client +FROM + ( + SELECT + inst_id, + value AS received_from_client + FROM + gv$sysstat + WHERE + lower(name) LIKE '%received via sql*net from client%' + ORDER BY + value DESC + ) t1 + LEFT JOIN ( + SELECT + inst_id, + value AS sent_to_client + FROM + gv$sysstat + WHERE + lower(name) LIKE '%sent via sql*net to client%' + ORDER BY + value DESC + ) t2 ON t1.inst_id = t2.inst_id +''' + From df19fc2ef43b6edc7a7a4c674096cbf1d0c964ea Mon Sep 17 00:00:00 2001 From: Mark Nelson Date: Thu, 7 Sep 2023 11:03:32 -0400 Subject: [PATCH 07/17] working on doc Signed-off-by: Mark Nelson --- README.md | 245 ++++++------------------------------------------------ 1 file changed, 27 insertions(+), 218 deletions(-) diff --git a/README.md b/README.md index c278e21..441c150 100644 --- a/README.md +++ b/README.md @@ -11,13 +11,19 @@ Contributions are welcome - please see [contributing](CONTRIBUTING.md). ### Table of Contents -[Roadmap](#roadmap) -[Standard metrics](#standard-metrics) -[Installation](#installation) -[Running](#running) -[Grafana](#grafana) -[Troubleshooting](#troubleshooting) -[Operating principles](operating-principles.md) +- [Roadmap](#roadmap) +- [Standard metrics](#standard-metrics) +- [Database permissions required](#database-permissions-required) +- [Installation](#installation) + - [Docker, podman, etc.](#docker-podman-etc) + - [Test/demo environment using Docker Compose](#testdemo-environment-with-docker-compose) + - [Kubernetes](#kubernetes) + - [Standalone binary](#standalone-binary) +- [Usage](#usage) +- [Custom metrics](#custom-metrics) +- [Grafana dashboards](#grafana-dashboards) +- [Developer notes](#developer-notes) + # Roadmap @@ -85,15 +91,6 @@ The following metrics are exposed currently. - oracledb_resource_current_utilization - oracledb_resource_limit_value -# Installation - -There are a number of ways to run the exporter. In this section you will find information on running the exporter: - -- In a container runtime like [Docker, Podman, etc](#docker-podman-etc) -- In a test/demo environment using [Docker Compose](#testdemo-environment-with-docker-compose) -- In [Kubernetes](#kubernetes) -- As a [standalone binary](#standalone-binary) - # Database permissions required For the built-in default metrics, the database user that the exporter uses to connect to the Oracle Database instance must have the `SYS` privilege and/or `SELECT` permission on the following tables. @@ -109,6 +106,15 @@ For the built-in default metrics, the database user that the exporter uses to co - v$session - v$resource_limit +# Installation + +There are a number of ways to run the exporter. In this section you will find information on running the exporter: + +- In a container runtime like [Docker, Podman, etc](#docker-podman-etc) +- In a test/demo environment using [Docker Compose](#testdemo-environment-with-docker-compose) +- In [Kubernetes](#kubernetes) +- As a [standalone binary](#standalone-binary) + ## Docker, Podman, etc. You can run the exporter in a local container using a conatiner image from [Oracle Container Registry](https://container-registry.oracle.com). The container image is available in the "observability-exporter" repository in the "Database" category. No authentication or license presentment/acceptance are required to pull this image from the registry. @@ -311,9 +317,7 @@ You may need to update your Prometheus configuration to add a target. If so, yo ### Import Grafana dashboard definition(s) (optional) -Some sample Grafana dashboard definitions are provided [in this directory](/grafana). - -TODO TODO TODO write me +See [Grafana dashboards](#grafana-dashboards) below. ## Standalone binary @@ -356,49 +360,6 @@ export DATA_SOURCE_NAME=oracle://user:password@primaryhost:1521,standbyhost:1521 ``` -# Integration with System D - -Create **oracledb_exporter** user with disabled login and **oracledb_exporter** group\ -mkdir /etc/oracledb_exporter\ -chown root:oracledb_exporter /etc/oracledb_exporter -chmod 775 /etc/oracledb_exporter -Put config files to **/etc/oracledb_exporter** -Put binary to **/usr/local/bin** - -Create file **/etc/systemd/system/oracledb_exporter.service** with the following content: - -```bash -[Unit] -Description=Service for oracle telemetry client -After=network.target -[Service] -Type=oneshot -#!!! Set your values and uncomment -#User=oracledb_exporter -#Group=oracledb_exporter -#Environment="DATA_SOURCE_NAME=dbsnmp/Bercut01@//primaryhost:1521,standbyhost:1521/myservice?transport_connect_timeout=5&retry_count=3" -#Environment="LD_LIBRARY_PATH=/u01/app/oracle/product/19.0.0/dbhome_1/lib" -#Environment="ORACLE_HOME=/u01/app/oracle/product/19.0.0/dbhome_1" -#Environment="CUSTOM_METRICS=/etc/oracledb_exporter/custom-metrics.toml" -ExecStart=/usr/local/bin/oracledb_exporter \ - --default.metrics "/etc/oracledb_exporter/default-metrics.toml" \ - --log.level error --web.listen-address 0.0.0.0:9161 -[Install] -WantedBy=multi-user.target -``` - -Then tell System D to read files: - - systemctl daemon-reload - -Start this new service: - - systemctl start oracledb_exporter - -Check service status: - - systemctl status oracledb_exporter - ## Usage ```bash @@ -425,11 +386,6 @@ Usage of oracledb_exporter: Path to configuration file that can enable TLS or authentication. ``` -# Default metrics - -This exporter comes with a set of default metrics defined in **default-metrics.toml**. You can modify this file or -provide a different one using `default.metrics` option. - # Custom metrics > NOTE: Do not put a `;` at the end of your SQL queries as this will **NOT** work. @@ -534,80 +490,13 @@ COPY custom-metrics.toml / ENTRYPOINT ["/oracledb_exporter", "--custom.metrics", "/custom-metrics.toml"] ``` -# Using a multiple host data source name - -> NOTE: This has been tested with v0.2.6a and will most probably work on versions above. - -> NOTE: While `user/password@//database1.example.com:1521,database3.example.com:1521/DBPRIM` works with SQLPlus, it doesn't seem to work with oracledb-exporter v0.2.6a. - -In some cases, one might want to scrape metrics from the currently available database when having a active-passive replication setup. - -This will try to connect to any available database to scrape for the metrics. With some replication options, the secondary database is not available when replicating. This allows the scraper to automatically fall back in case of the primary one failing. - -This example allows to achieve this: - -### Files & Folder: - -- tns_admin folder: `/path/to/tns_admin` -- tnsnames.ora file: `/path/to/tns_admin/tnsnames.ora` - -Example of a tnsnames.ora file: - -``` -database = -(DESCRIPTION = - (ADDRESS_LIST = - (ADDRESS = (PROTOCOL = TCP)(HOST = database1.example.com)(PORT = 1521)) - (ADDRESS = (PROTOCOL = TCP)(HOST = database2.example.com)(PORT = 1521)) - ) - (CONNECT_DATA = - (SERVICE_NAME = DBPRIM) - ) -) -``` - -### Environment Variables - -- `TNS_ENTRY`: Name of the entry to use (`database` in the example file above) -- `TNS_ADMIN`: Path you choose for the tns admin folder (`/path/to/tns_admin` in the example file above) -- `DATA_SOURCE_NAME`: Datasource pointing to the `TNS_ENTRY` (`user:password@database` in the example file above) +# Grafana dashboards -# TLS connection to database - -First, set the following variables: - - export WALLET_PATH=/wallet/path/to/use - export TNS_ENTRY=tns_entry - export DB_USERNAME=db_username - export TNS_ADMIN=/tns/admin/path/to/use - -Create the wallet and set the credential: - - mkstore -wrl $WALLET_PATH -create - mkstore -wrl $WALLET_PATH -createCredential $TNS_ENTRY $DB_USERNAME - -Then, update sqlnet.ora: - - echo " - WALLET_LOCATION = (SOURCE = (METHOD = FILE) (METHOD_DATA = (DIRECTORY = $WALLET_PATH ))) - SQLNET.WALLET_OVERRIDE = TRUE - SSL_CLIENT_AUTHENTICATION = FALSE - " >> $TNS_ADMIN/sqlnet.ora - -To use the wallet, use the wallet_location parameter. You may need to disable ssl verification with the -ssl_server_dn_match parameter. - -Here a complete example of string connection: - - DATA_SOURCE_NAME=oracle://username:password@server:port/service?ssl_server_dn_match=false&wallet_location=wallet_path - -For more details, have a look at the following location: https://github.com/iamseth/oracledb_exporter/issues/84 - -# Integration with Grafana +Some sample Grafana dashboard definitions are provided [in this directory](/grafana). An example Grafana dashboard is available [here](https://grafana.com/grafana/dashboards/3333-oracledb/). -# Build +# Developer notes ## Docker build @@ -675,85 +564,5 @@ Here is a small snippet of an example usage of the exporter in code: ``` -# FAQ/Troubleshooting - -## Unable to convert current value to float (metric=par,metri...in.go:285 - -Oracle is trying to send a value that we cannot convert to float. This could be anything like 'UNLIMITED' or 'UNDEFINED' or 'WHATEVER'. - -In this case, you must handle this problem by testing it in the SQL request. Here an example available in default metrics: - -```toml -[[metric]] -context = "resource" -labels = [ "resource_name" ] -metricsdesc = { current_utilization= "Generic counter metric from v$resource_limit view in Oracle (current value).", limit_value="Generic counter metric from v$resource_limit view in Oracle (UNLIMITED: -1)." } -request="SELECT resource_name,current_utilization,CASE WHEN TRIM(limit_value) LIKE 'UNLIMITED' THEN '-1' ELSE TRIM(limit_value) END as limit_value FROM v$resource_limit" -``` - -If the value of limite_value is 'UNLIMITED', the request send back the value -1. - -You can increase the log level (`--log.level debug`) in order to get the statement generating this error. - -## error while loading shared libraries: libclntsh.so.xx.x: cannot open shared object file: No such file or directory - -This exporter use libs from Oracle in order to connect to Oracle Database. If you are running the binary version, you -must install the Oracle binaries somewhere on your machine and **you must install the good version number**. If the -error talk about the version 18.3, you **must** install 18.3 binary version. If it's 12.2, you **must** install 12.2. - -An alternative is to run this exporter using a Docker container. This way, you don't have to worry about Oracle binaries -version as they are embedded in the container. - -Here an example to run this exporter (to scrap metrics from system/oracle@//host:1521/service-or-sid) and bind the exporter port (9161) to the global machine: - -`docker run -it --rm -p 9161:9161 -e DATA_SOURCE_NAME=oracle://system/oracle@//host:1521/service-or-sid iamseth/oracledb_exporter:0.2.6a` - -## Error scraping for wait_time - -If you experience an error `Error scraping for wait_time: sql: Scan error on column index 1: converting driver.Value type string (",01") to a float64: invalid syntax source="main.go:144"` you may need to set the NLS_LANG variable. - -```bash - -export NLS_LANG=AMERICAN_AMERICA.WE8ISO8859P1 -export DATA_SOURCE_NAME=system/oracle@myhost -/path/to/binary --log.level error --web.listen-address :9161 -``` - -If using Docker, set the same variable using the -e flag. - -## An Oracle instance generates a lot of trace files being monitored by exporter - -As being said, Oracle instance may (and probably does) generate a lot of trace files alongside its alert log file, one trace file per scraping event. The trace file contains the following lines - -``` -... -*** MODULE NAME:(prometheus_oracle_exporter-amd64@hostname) -... -kgxgncin: clsssinit: CLSS init failed with status 3 -kgxgncin: clsssinit: return status 3 (0 SKGXN not av) from CLSS -``` - -The root cause is Oracle's reaction of quering ASM-related views without ASM used. The current workaround proposed is to setup a regular task to cleanup these trace files from the filesystem, as example - -``` -$ find $ORACLE_BASE/diag/rdbms -name '*.tr[cm]' -mtime +14 -delete -``` - -## TLS and basic authentication - -Apache Exporter supports TLS and basic authentication. This enables better -control of the various HTTP endpoints. - -To use TLS and/or basic authentication, you need to pass a configuration file -using the `--web.config` parameter. The format of the file is described -[in the exporter-toolkit repository](https://github.com/prometheus/exporter-toolkit/blob/master/docs/web-configuration.md). - -Note that the TLS and basic authentication settings affect all HTTP endpoints: -/metrics for scraping, /probe for probing, and the web UI. - - -## Multi-target support - -This exporter supports the multi-target pattern. This allows running a single instance of this exporter for multiple Oracle targets. -To use the multi-target functionality, send a http request to the endpoint `/scrape?target=foo:1521` where target is set to the DSN of the Oracle instance to scrape metrics from. +TODO - move operating principals stuff in here \ No newline at end of file From 7eb26ff368835c4c7993022af9423e4b0ce79f08 Mon Sep 17 00:00:00 2001 From: Mark Nelson Date: Thu, 7 Sep 2023 13:35:53 -0400 Subject: [PATCH 08/17] doc updates and preparing for release Signed-off-by: Mark Nelson --- README.md | 112 ++++------------- default-asm-metrics.toml | 139 --------------------- default-metrics.legacy-tablespace.toml | 113 ----------------- operating-principles.md | 20 --- systemd-example/oracleasm_exporter.service | 24 ---- systemd-example/oracledb_exporter.service | 30 ----- 6 files changed, 27 insertions(+), 411 deletions(-) delete mode 100644 default-asm-metrics.toml delete mode 100644 default-metrics.legacy-tablespace.toml delete mode 100644 operating-principles.md delete mode 100644 systemd-example/oracleasm_exporter.service delete mode 100644 systemd-example/oracledb_exporter.service diff --git a/README.md b/README.md index 441c150..a0d7812 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ This project aims to provide observability for the Oracle Database so that users can understand performance and diagnose issues easily across applications and database. Over time, this project will provide not just metrics, but also logging and tracing support, and integration into popular frameworks like Spring Boot. The project aims to deliver functionality to support both cloud and on-premises databases, including those running in Kubernetes and containers. -In the first production release, v1.0, this project provides a [Prometheus](https://prometheus.io/) exporter for Oracle Database that is based in part on a Prometheus exporter created by [iamseth](https://github.com/iamseth/oracledb_exporter) with various changes to comply with various Oracle standards and policies. +In the first production release, v1.0, this project provides a [Prometheus](https://prometheus.io/) exporter for Oracle Database that is based in part on a Prometheus exporter created by [Seth Miller](https://github.com/iamseth/oracledb_exporter) with various changes to comply with various Oracle standards and policies. Customers with an active support agreement for Oracle Database may open a Service Request in My Oracle Support for support with any issues using this exporter. Community support is available through GitHub issues, etc., for other users. @@ -322,46 +322,16 @@ See [Grafana dashboards](#grafana-dashboards) below. ## Standalone binary -write me - - -# END - -## Binary Release - -Pre-compiled versions for Linux 64 bit and Mac OSX 64 bit can be found under [releases](https://github.com/iamseth/oracledb_exporter/releases). +Pre-compiled versions for Linux 64 bit can be found under [releases](https://github.com/oracle/oracle-db-appdev-monitoring/releases). In order to run, you'll need the [Oracle Instant Client Basic](http://www.oracle.com/technetwork/database/features/instant-client/index-097480.html) for your operating system. Only the basic version is required for execution. -# Running -Ensure that the environment variable DATA_SOURCE_NAME is set correctly before starting. -DATA_SOURCE_NAME should be in Oracle Database connection string format: - -```conn - oracle://user:pass@server/service_name[?OPTION1=VALUE1[&OPTIONn=VALUEn]...] -``` - -For Example: - -```bash -# export Oracle location: -export DATA_SOURCE_NAME=oracle://system:password@oracle-sid -# or using a complete url: -export DATA_SOURCE_NAME=oracle://user:password@myhost:1521/service -# 19c client for primary/standby configuration -export DATA_SOURCE_NAME=oracle://user:password@primaryhost:1521,standbyhost:1521/service -# 19c client for primary/standby configuration with options -export DATA_SOURCE_NAME=oracle://user:password@primaryhost:1521,standbyhost:1521/service?connect_timeout=5&transport_connect_timeout=3&retry_count=3 -# 19c client for ASM instance connection (requires SYSDBA) -export DATA_SOURCE_NAME=oracle://user:password@primaryhost:1521,standbyhost:1521/+ASM?as=sysdba -# Then run the exporter -/path/to/binary/oracledb_exporter --log.level error --web.listen-address 0.0.0.0:9161 -``` - ## Usage +This section lists the command line arguments (flags) that can be passed to the exporter. + ```bash Usage of oracledb_exporter: --log.format value @@ -496,73 +466,45 @@ Some sample Grafana dashboard definitions are provided [in this directory](/graf An example Grafana dashboard is available [here](https://grafana.com/grafana/dashboards/3333-oracledb/). -# Developer notes - -## Docker build -To build Ubuntu and Alpine image, run the following command: - - make docker - -You can also build only Ubuntu image: +# Developer notes - make ubuntu-image +The exporter itself is fairly simple. The initialization is done as follows: -Or Alpine: +- Parse flags options +- Load the default toml file (`default-metrics.toml`) and store each metric in a `Metric` struct +- Load the custom toml file (if a custom toml file is given) +- Create an `Exporter` object +- Register exporter in prometheus library +- Launching a web server to handle incoming requests - make alpine-image +These operations are mainly done in the `main` function. -## Building Binaries +After this initialization phase, the exporter will wait for the arrival of a request. -Run build: - -```sh - make go-build -``` +Each time, it will iterate over the content of the `metricsToScrape` structure (in the function scrape `func (e * Export) scrape (ch chan <- prometheus.Metric)`). -will output binaries and archive inside the `dist` folder for the building operating system. +For each element (of `Metric` type), a call to the `ScrapeMetric` function will be made which will itself make a call to the `ScrapeGenericValues` function. -## Import into your Golang Application +The `ScrapeGenericValues` function will read the information from the `Metric` structure and, depending on the parameters, will generate the metrics to return. In particular, it will use the `GeneratePrometheusMetrics` function which will make SQL calls to the database. -The `oracledb_exporter` can also be imported into your Go based applications. The [Grafana Agent](https://github.com/grafana/agent/) uses this pattern to implement the [OracleDB integration](https://grafana.com/docs/grafana-cloud/data-configuration/integrations/integration-reference/integration-oracledb/). Feel free to modify the code to fit your application's use case. -Here is a small snippet of an example usage of the exporter in code: +## Docker/container build -```go - promLogConfig := &promlog.Config{} - // create your own config - logger := promlog.New(promLogConfig) +To build a container image, run the following command: - // replace with your connection string - connectionString := "oracle://username:password@localhost:1521/orcl.localnet" - oeExporter, err := oe.NewExporter(logger, &oe.Config{ - DSN: connectionString, - MaxIdleConns: 0, - MaxOpenConns: 10, - QueryTimeout: 5, - }) - - if err != nil { - panic(err) - } +```bash +make docker +``` - metricChan := make(chan prometheus.Metric, len(oeExporter.DefaultMetrics().Metric)) - oeExporter.Collect(metricChan) - // alternatively its possible to run scrapes on an interval - // and Collect() calls will only return updated data once - // that intervaled scrape is run - // please note this is a blocking call so feel free to run - // in a separate goroutine - // oeExporter.RunScheduledScrapes(context.Background(), time.Minute) +## Building Binaries - for r := range metricChan { - // Write to the client of your choice - // or spin up a promhttp.Server to serve these metrics - r.Write(&dto.Metric{}) - } +Run build: +```bash +make go-build ``` +This will create binaries and archives inside the `dist` folder for the building operating system. -TODO - move operating principals stuff in here \ No newline at end of file diff --git a/default-asm-metrics.toml b/default-asm-metrics.toml deleted file mode 100644 index e890093..0000000 --- a/default-asm-metrics.toml +++ /dev/null @@ -1,139 +0,0 @@ -[[metric]] -context = "diskgroup_size" -labels = [ "inst_id", "node_name" , "instance_name", "diskgroup_name" ] -metricsdesc = { total = "Total size of ASM disk group in MB.", free = "Free space available on ASM disk group in MB." } -request = ''' -SELECT instance_number AS inst_id, - host_name AS node_name, - instance_name, - name AS diskgroup_name, - total_mb * 1024 * 1024 AS total, - free_mb * 1024 * 1024 AS free - FROM v$asm_diskgroup_stat, v$instance -''' -ignorezeroresult = true - -[[metric]] -context = "asmuptime" -labels = [ "inst_id", "node_name", "instance_name"] -metricsdesc = { uptime = "ASM uptime" } -request = ''' -SELECT instance_number AS inst_id, - host_name AS node_name, - instance_name, - (SYSDATE - startup_time) * 86400 AS uptime - FROM v$instance -''' - -#[[metric]] -#context = "asm_dg_stat" -#labels = [ "inst_id", "diskgroup_name", "node_name", "instance_name" ] -#metricsdesc = { reads = "Total number of I/O read requests for the DG.", writes = "Total number of I/O write requests for the DG.", bytes_read = "Total number of bytes read from the DG", bytes_written = "Total number of bytes written from the DG", iops = "Total number of I/O requests for the DG" } -#metricstype = { reads = "counter", writes = "counter", bytes_read = "counter", bytes_written = "counter", iops = "counter" } -#request = ''' -# SELECT i.instance_number AS inst_id, -# i.host_name AS node_name, -# i.instance_name, -# g.name AS diskgroup_name, -# SUM (ds.reads) AS reads, -# SUM (ds.writes) AS writes, -# SUM (ds.bytes_read) AS bytes_read, -# SUM (ds.bytes_written) AS bytes_written, -# SUM (ds.reads + ds.writes) AS iops -# FROM v$asm_disk_stat ds, v$asm_diskgroup_stat g, v$instance i -# WHERE ds.mount_status = 'CACHED' AND ds.group_number = g.group_number -#GROUP BY i.instance_number, -# i.host_name, -# i.instance_name, -# g.name -#''' - -[[metric]] -context = "asm_disk_stat" -labels = [ "inst_id", "node_name", "instance_name", "diskgroup_name", "disk_number", "failgroup", "path" ] -metricsdesc = { reads = "Total number of I/O read requests for the DG.", writes = "Total number of I/O write requests for the DG.", read_time = "Total I/O time (in hundreths of a second) for read requests for the disk", write_time = "Total I/O time (in hundreths of a second) for write requests for the disk", bytes_read = "Total number of bytes read from the DG", bytes_written = "Total number of bytes written from the DG", iops = "Total number of I/O requests for the DG" } -metricstype = { reads = "counter", writes = "counter", bytes_read = "counter", read_time = "counter", write_time = "counter", bytes_written = "counter", iops = "counter" } -request = ''' - SELECT i.instance_number AS inst_id, - i.host_name AS node_name, - i.instance_name, - g.name AS diskgroup_name, - ds.disk_number AS disk_number, - ds.failgroup AS failgroup, - ds.reads AS reads, - ds.writes AS writes, - ds.read_time * 1000 AS read_time, - ds.write_time * 1000 AS write_time, - ds.bytes_read AS bytes_read, - ds.bytes_written AS bytes_written, - REGEXP_REPLACE (ds.PATH, '.*/\', '\') AS PATH, - ds.reads + ds.writes AS iops - FROM v$asm_disk_stat ds, v$asm_diskgroup_stat g, v$instance i - WHERE ds.mount_status = 'CACHED' AND ds.group_number = g.group_number -''' - -[[metric]] -context = "asm_space_consumers" -labels = [ "inst_id", "diskgroup_name", "node_name", "instance_name", "sid", "file_type" ] -metricsdesc = { size_mb = "Total space usage by db by file_type" , files = "Number of files by db by type" } -request = ''' - SELECT i.instance_number AS inst_id, - i.host_name AS node_name, - i.instance_name, - gname AS diskgroup_name, - dbname AS sid, - file_type, - ROUND (SUM (space) / 1024 / 1024) size_mb, - COUNT (*) AS files - FROM v$instance i, - (SELECT gname, - REGEXP_SUBSTR (full_alias_path, - '[[:alnum:]_]*', - 1, - 4) dbname, - file_type, - space, - aname, - system_created, - alias_directory - FROM ( SELECT CONCAT ('+' || gname, - SYS_CONNECT_BY_PATH (aname, '/')) - full_alias_path, - system_created, - alias_directory, - file_type, - space, - LEVEL, - gname, - aname - FROM (SELECT b.name gname, - a.parent_index pindex, - a.name aname, - a.reference_index rindex, - a.system_created, - a.alias_directory, - c.TYPE file_type, - c.space - FROM v$asm_alias a, v$asm_diskgroup b, v$asm_file c - WHERE a.group_number = b.group_number - AND a.group_number = c.group_number(+) - AND a.file_number = c.file_number(+) - AND a.file_incarnation = c.incarnation(+)) - START WITH (MOD (pindex, POWER (2, 24))) = 0 - AND rindex IN - (SELECT a.reference_index - FROM v$asm_alias a, v$asm_diskgroup b - WHERE a.group_number = - b.group_number - AND (MOD (a.parent_index, - POWER (2, 24))) = - 0) - CONNECT BY PRIOR rindex = pindex) - WHERE NOT file_type IS NULL AND system_created = 'Y') -GROUP BY i.instance_number, - i.host_name, - i.instance_name, - gname, - dbname, - file_type -''' diff --git a/default-metrics.legacy-tablespace.toml b/default-metrics.legacy-tablespace.toml deleted file mode 100644 index 715bbb5..0000000 --- a/default-metrics.legacy-tablespace.toml +++ /dev/null @@ -1,113 +0,0 @@ -[[metric]] -context = "sessions" -labels = [ "status", "type" ] -metricsdesc = { value= "Gauge metric with count of sessions by status and type." } -request = "SELECT status, type, COUNT(*) as value FROM v$session GROUP BY status, type" - -[[metric]] -context = "resource" -labels = [ "resource_name" ] -metricsdesc = { current_utilization= "Generic counter metric from v$resource_limit view in Oracle (current value).", limit_value="Generic counter metric from v$resource_limit view in Oracle (UNLIMITED: -1)." } -request="SELECT resource_name,current_utilization,CASE WHEN TRIM(limit_value) LIKE 'UNLIMITED' THEN '-1' ELSE TRIM(limit_value) END as limit_value FROM v$resource_limit" - -[[metric]] -context = "asm_diskgroup" -labels = [ "name" ] -metricsdesc = { total = "Total size of ASM disk group.", free = "Free space available on ASM disk group." } -request = "SELECT name,total_mb*1024*1024 as total,free_mb*1024*1024 as free FROM v$asm_diskgroup_stat" -ignorezeroresult = true - -[[metric]] -context = "activity" -metricsdesc = { value="Generic counter metric from v$sysstat view in Oracle." } -fieldtoappend = "name" -request = "SELECT name, value FROM v$sysstat WHERE name IN ('parse count (total)', 'execute count', 'user commits', 'user rollbacks')" - -[[metric]] -context = "process" -metricsdesc = { count="Gauge metric with count of processes." } -request = "SELECT COUNT(*) as count FROM v$process" - -[[metric]] -context = "wait_time" -metricsdesc = { value="Generic counter metric from v$waitclassmetric view in Oracle." } -fieldtoappend= "wait_class" -request = ''' -SELECT - n.wait_class as WAIT_CLASS, - round(m.time_waited/m.INTSIZE_CSEC,3) as VALUE -FROM - v$waitclassmetric m, v$system_wait_class n -WHERE - m.wait_class_id=n.wait_class_id AND n.wait_class != 'Idle' -''' - -[[metric]] -context = "tablespace" -labels = [ "tablespace", "type" ] -metricsdesc = { bytes = "Generic counter metric of tablespaces bytes in Oracle.", max_bytes = "Generic counter metric of tablespaces max bytes in Oracle.", free = "Generic counter metric of tablespaces free bytes in Oracle." } -request = ''' -SELECT - df.tablespace_name as tablespace, - df.type as type, - nvl(sum(df.bytes),0) as bytes, - nvl(sum(df.max_bytes),0) as max_bytes, - nvl(sum(f.free),0) as free -FROM - ( - SELECT - ddf.file_id, - dt.contents as type, - ddf.file_name, - ddf.tablespace_name, - TRUNC(ddf.bytes) as bytes, - TRUNC(GREATEST(ddf.bytes,ddf.maxbytes)) as max_bytes - FROM - dba_data_files ddf, - dba_tablespaces dt - WHERE ddf.tablespace_name = dt.tablespace_name - ) df, - ( - SELECT - TRUNC(SUM(bytes)) AS free, - file_id - FROM dba_free_space - GROUP BY file_id - ) f -WHERE df.file_id = f.file_id (+) -GROUP BY df.tablespace_name, df.type -UNION ALL -SELECT - Y.name as tablespace_name, - Y.type as type, - SUM(Y.bytes) as bytes, - SUM(Y.max_bytes) as max_bytes, - MAX(nvl(Y.free_bytes,0)) as free -FROM - ( - SELECT - dtf.tablespace_name as name, - dt.contents as type, - dtf.status as status, - dtf.bytes as bytes, - ( - SELECT - ((f.total_blocks - s.tot_used_blocks)*vp.value) - FROM - (SELECT tablespace_name, sum(used_blocks) tot_used_blocks FROM gv$sort_segment WHERE tablespace_name!='DUMMY' GROUP BY tablespace_name) s, - (SELECT tablespace_name, sum(blocks) total_blocks FROM dba_temp_files where tablespace_name !='DUMMY' GROUP BY tablespace_name) f, - (SELECT value FROM v$parameter WHERE name = 'db_block_size') vp - WHERE f.tablespace_name=s.tablespace_name AND f.tablespace_name = dtf.tablespace_name - ) as free_bytes, - CASE - WHEN dtf.maxbytes = 0 THEN dtf.bytes - ELSE dtf.maxbytes - END as max_bytes - FROM - sys.dba_temp_files dtf, - sys.dba_tablespaces dt - WHERE dtf.tablespace_name = dt.tablespace_name - ) Y -GROUP BY Y.name, Y.type -ORDER BY tablespace -''' diff --git a/operating-principles.md b/operating-principles.md deleted file mode 100644 index 4e69bc9..0000000 --- a/operating-principles.md +++ /dev/null @@ -1,20 +0,0 @@ -# Operating principles - -The exporter itself is dumb and does not do much. The initialization is done as follows: - -- Parse flags options -- Load the default toml file (default-metrics.toml) and store each metrics in a Metric struct -- Load the custom toml file (if a custom toml file is given) -- Create an Exporter object -- Register exporter in prometheus library -- Launching a web server to handle incoming requests - -These operations are mainly done in the `main` function. - -After this initialization phase, the exporter will wait for the arrival of the request. - -Each time, it will iterate over the content of the metricsToScrap structure (in the function scrape `func (e * Export) scrape (ch chan <- prometheus.Metric)`). - -For each element (of Metric type), a call to the `ScrapeMetric` function will be made which will itself make a call to the` ScrapeGenericValues` function. - -The `ScrapeGenericValues` function will read the information from the Metric structure and - depending on the parameters - will generate the metrics to return. In particular, it will use the `GeneratePrometheusMetrics` function which will make SQL calls to the database. diff --git a/systemd-example/oracleasm_exporter.service b/systemd-example/oracleasm_exporter.service deleted file mode 100644 index d3d1060..0000000 --- a/systemd-example/oracleasm_exporter.service +++ /dev/null @@ -1,24 +0,0 @@ -[Unit] -Description=Service for oracle asm telemetry client -After=network-online.target - -[Service] -Type=simple -Environment="DATA_SOURCE_NAME=asmsnmp/password@//host:1521/+ASM?as=sysdba" -Environment="LD_LIBRARY_PATH=/u01/app/oracle/product/19.0.0/dbhome_1/lib" -Environment="ORACLE_HOME=/u01/app/oracle/product/19.0.0/dbhome_1" -User=oracledb_exporter -Group=oracledb_exporter -ExecStart=/usr/local/bin/oracledb_exporter \ - --default.metrics "/etc/oracledb_exporter/default-asm-metrics.toml" \ - --log.level "error" \ - --web.listen-address 0.0.0.0:9163 \ - --log.format "logger:syslog?appname=oracleasm_exporter&local=7" - -KillMode=process -RemainAfterExit=no -Restart=on-failure -RestartSec=5s - -[Install] -WantedBy=multi-user.target diff --git a/systemd-example/oracledb_exporter.service b/systemd-example/oracledb_exporter.service deleted file mode 100644 index b58b274..0000000 --- a/systemd-example/oracledb_exporter.service +++ /dev/null @@ -1,30 +0,0 @@ -# -# Ansible managed -# - -[Unit] -Description=Service for oracle telemetry client -After=network-online.target - -[Service] -Type=simple -Environment="DATA_SOURCE_NAME=dbsnmp/password@//host:1521/service?transport_connect_timeout=5&retry_count=3" -Environment="LD_LIBRARY_PATH=/u01/app/oracle/product/19.0.0/dbhome_1/lib" -Environment="ORACLE_HOME=/u01/app/oracle/product/19.0.0/dbhome_1" -#Environment="PATH=$PATH:/u01/app/oracle/product/19.0.0/dbhome_1/bin" -#Environment="TNS_ADMIN=/u01/app/oracle/product/19.0.0/dbhome_1/network/admin" -User=oracledb_exporter -Group=oracledb_exporter -ExecStart=/usr/local/bin/oracledb_exporter \ - --default.metrics "/etc/oracledb_exporter/default-metrics.toml" \ - --log.level "error" \ - --web.listen-address 0.0.0.0:9161 \ - --log.format "logger:syslog?appname=oracledb_exporter&local=7" - -KillMode=process -RemainAfterExit=no -Restart=on-failure -RestartSec=5s - -[Install] -WantedBy=multi-user.target From 51ab8bb9a3060a16553f2cf13e40c9102078a182 Mon Sep 17 00:00:00 2001 From: Mark Nelson Date: Thu, 7 Sep 2023 13:42:29 -0400 Subject: [PATCH 09/17] doc updates and preparing for release Signed-off-by: Mark Nelson --- .github/config/lint/errcheck.exclude.txt | 0 .github/workflow-config.json | 20 -- .github/workflows/pull-request.yml | 329 ------------------ .github/workflows/release.yml | 228 ------------ README.md | 4 +- .../txeventq-metrics.toml | 0 6 files changed, 2 insertions(+), 579 deletions(-) delete mode 100644 .github/config/lint/errcheck.exclude.txt delete mode 100644 .github/workflow-config.json delete mode 100644 .github/workflows/pull-request.yml delete mode 100644 .github/workflows/release.yml rename {kubernetes => custom-metrics-example}/txeventq-metrics.toml (100%) diff --git a/.github/config/lint/errcheck.exclude.txt b/.github/config/lint/errcheck.exclude.txt deleted file mode 100644 index e69de29..0000000 diff --git a/.github/workflow-config.json b/.github/workflow-config.json deleted file mode 100644 index d419d6c..0000000 --- a/.github/workflow-config.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "go-version": "1.19.4", - "deploy": { - "pull-request": true, - "sign-docker-image": false, - "pull-request-images": [ - { - "name": "ubuntu" - } - ], - "release-images": [ - { - "name": "ubuntu" - }, - { - "name": "oraclelinux" - } - ] - } -} diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml deleted file mode 100644 index 739a37b..0000000 --- a/.github/workflows/pull-request.yml +++ /dev/null @@ -1,329 +0,0 @@ -name: Pull request -run-name: "${{ github.event.pull_request.title }} / [${{ github.actor }}] is testing${{ github.event.pull_request.head.repo.full_name != github.event.pull_request.base.repo.full_name && format(' from fork [{0}]', github.event.pull_request.head.repo.full_name) || format(' from current repo') }} πŸš€" - -on: - pull_request: - branches: - - main - - master - paths-ignore: - - ".github/**/*.yml" - - "**/*.md" - - "README.md" - -permissions: - contents: write - packages: write - security-events: write - pull-requests: write - -concurrency: - group: ${{ github.event.pull_request.head.repo.full_name }}-${{ github.head_ref }}/${{ github.ref }} - cancel-in-progress: true - -env: - PULL_REQUEST_NUMBER: ${{ github.event.pull_request.number }} - PULL_REQUEST_BRANCH: ${{ github.head_ref }} - PULL_REQUEST_HEAD: ${{ github.event.pull_request.head.sha }} - CONCURRENCY_GROUP: ${{ github.event.pull_request.head.repo.full_name }}-${{ github.head_ref }}/${{ github.ref }} - BRANCH: ${{ github.event.pull_request.head.ref }} - REPOSITORY: ${{ github.event.pull_request.head.repo.full_name }} - REGISTRY: ghcr.io - IMAGE_NAME: "ghcr.io/${{ github.repository }}" - RELEASE: false - FORKED: ${{ github.event.pull_request.head.repo.full_name != github.event.pull_request.base.repo.full_name }} - -jobs: - config: - if: github.triggering_actor != 'dependabot[bot]' - runs-on: ubuntu-latest - outputs: - go-version: ${{ fromJson(steps.config.outputs.config).go-version }} - deploy-pull-request: ${{ fromJson(steps.config.outputs.config).deploy.pull-request }} - deploy-sign-docker-image: ${{ fromJson(steps.config.outputs.config).deploy.sign-docker-image }} - deploy-pre-release-matrix: ${{ steps.pre-release-matrix.outputs.matrix }} - deploy-release-matrix: ${{ steps.release-matrix.outputs.matrix }} - is-forked: ${{ github.event.pull_request.head.repo.full_name != github.event.pull_request.base.repo.full_name }} - steps: - - name: Checkout - uses: actions/checkout@v3 - with: - ref: ${{ env.PULL_REQUEST_HEAD }} - - - name: Read config - id: config - run: echo "config=$(jq -M -c '.' ./.github/workflow-config.json)" >> $GITHUB_OUTPUT - - - name: List Pre-release profiles - id: deploy-pr-images-profiles - run: echo "profiles=$(jq -c -M '.deploy."pull-request-images"' .github/workflow-config.json)" >> $GITHUB_OUTPUT - - - name: Deploy pre-release matrix - id: pre-release-matrix - run: echo 'matrix={"include":${{ steps.deploy-pr-images-profiles.outputs.profiles }} }' >> $GITHUB_OUTPUT - - - name: List release profiles - id: deploy-images-profiles - run: echo "profiles=$(jq -c -M '.deploy."release-images"' .github/workflow-config.json)" >> $GITHUB_OUTPUT - - - name: Deploy release matrix - id: release-matrix - run: echo 'matrix={"include":${{ steps.deploy-images-profiles.outputs.profiles }} }' >> $GITHUB_OUTPUT - - build: - runs-on: ubuntu-latest - needs: - - config - outputs: - version: ${{ steps.version.outputs.version }} - steps: - - name: Checkout - uses: actions/checkout@v3 - with: - ref: ${{ env.PULL_REQUEST_HEAD }} - - - name: Setup Golang - uses: actions/setup-go@v3 - with: - go-version: ${{ needs.config.outputs.go-version }} - - - name: Get makefile versions - id: version - run: | - version=$(make version) - echo "version=$version" >> $GITHUB_OUTPUT - - - name: Setup Golang cache - uses: actions/cache@v3 - with: - path: | - ~/go/pkg/mod - ~/.cache/go-build - key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} - restore-keys: | - ${{ runner.os }}-go- - - - name: Build artifact - run: make go-build - - - name: Run test - run: make go-test - - - name: Go lint - continue-on-error: true - uses: golangci/golangci-lint-action@v3 - with: - args: --issues-exit-code=0 - skip-pkg-cache: true - skip-build-cache: true - - - name: Package artifact - run: | - mv dist/*.tar.gz application.tar.gz - - - name: Upload build output - uses: actions/upload-artifact@v3 - with: - name: application - path: "./application.tar.gz" - - - name: Upload test coverage - uses: actions/upload-artifact@v3 - with: - name: test-coverage - path: "./test-coverage.out" - - release: - runs-on: ubuntu-latest - needs: - - config - - build - outputs: - version: ${{ steps.version.outputs.version }} - steps: - - name: Checkout - uses: actions/checkout@v3 - with: - ref: ${{ env.PULL_REQUEST_HEAD }} - - - name: Set alpha version - id: version - run: echo "version=${{ needs.build.outputs.version }}-rc.pr-${{ env.PULL_REQUEST_NUMBER }}-${{ github.run_number }}" >> $GITHUB_OUTPUT - - - name: Download artifact - uses: actions/download-artifact@v3 - with: - name: application - path: "." - - - name: Rename artifact - run: mv application.tar.gz ${{ github.event.repository.name }}.tar.gz - - - name: Create release - if: ${{ env.FORKED == 'false' }} - uses: softprops/action-gh-release@v1 - id: create-release - with: - name: Pre-release ${{ steps.version.outputs.version }} - tag_name: ${{ steps.version.outputs.version }} - token: ${{ secrets.GITHUB_TOKEN }} - body: ${{ steps.changelog.outputs.changelog }} - files: | - ./${{ github.event.repository.name }}.tar.gz - draft: false - prerelease: true - generate_release_notes: true - - deploy: - name: deploy-[${{ matrix.name }}] - runs-on: ubuntu-latest - strategy: - max-parallel: 1 - matrix: ${{ fromJson(needs.config.outputs.deploy-pre-release-matrix) }} - if: ${{ !failure() && needs.release.result == 'success' && needs.config.outputs.deploy-pull-request == 'true' }} - needs: - - config - - build - - release - steps: - - name: Checkout - uses: actions/checkout@v3 - with: - ref: ${{ env.PULL_REQUEST_HEAD }} - - - name: Build image - id: docker-meta - env: - VERSION: "${{ needs.release.outputs.version }}" - run: | - make ${{ matrix.name }}-image - TAG_SUFFIX=$(echo "-${{ matrix.name }}" | sed s/-ubuntu//) - echo "image-id=$IMAGE_NAME" >> $GITHUB_OUTPUT - echo "image-version=${VERSION}${TAG_SUFFIX}" >> $GITHUB_OUTPUT - - - name: Log in to registry - if: ${{ env.FORKED == 'false' }} - run: echo "${{ secrets.GITHUB_TOKEN }}" | docker login ${{ env.REGISTRY }} -u $ --password-stdin - - - name: Push image - if: ${{ env.FORKED == 'false' }} - env: - VERSION: "${{ needs.release.outputs.version }}" - run: | - make push-${{ matrix.name }}-image - - - name: Format current time - if: ${{ env.FORKED == 'false' }} - id: time_now - run: echo "time_now_formatted=$(date +'%Y-%m-%d %H:%M:%S')" >> "$GITHUB_OUTPUT" - - - name: Find releases comment - if: ${{ env.FORKED == 'false' }} - uses: peter-evans/find-comment@v2 - id: find_comment - with: - issue-number: ${{ github.event.pull_request.number }} - comment-author: "github-actions[bot]" - body-includes: "Following docker image(s) have been created for this PR." - - - name: Create releases comment - uses: peter-evans/create-or-update-comment@v2 - if: ${{ steps.find_comment.outputs.comment-id == '' && env.FORKED == 'false' }} - with: - comment-id: ${{ steps.find_comment.outputs.comment-id }} - issue-number: ${{ github.event.pull_request.number }} - edit-mode: replace - body: | - ## Pull Request information - Following docker image(s) have been created for this PR. - | Time | Tag | - | --- | --- | - | ${{ steps.time_now.outputs.time_now_formatted }} | **${{ steps.docker-meta.outputs.image-version }}** | - - - name: Append releases comment - uses: peter-evans/create-or-update-comment@v2 - if: ${{ steps.find_comment.outputs.comment-id != '' && env.FORKED == 'false' }} - with: - comment-id: ${{ steps.find_comment.outputs.comment-id }} - issue-number: ${{ github.event.pull_request.number }} - edit-mode: append - body: | - | ${{ steps.time_now.outputs.time_now_formatted }} | **${{ steps.docker-meta.outputs.image-version }}** | - - - name: Setup cosign - if: ${{ needs.config.outputs.deploy-sign-docker-image == 'true' && env.FORKED == 'false' }} - uses: sigstore/cosign-installer@main - - - name: Write signing key to disk (only needed for `cosign sign --key`) - if: ${{ needs.config.outputs.deploy-sign-docker-image == 'true' && env.FORKED == 'false' }} - continue-on-error: true - run: echo "${{ secrets.SIGNING_SECRET }}" > cosign.key - - - name: Sign the published Docker image - if: ${{ needs.config.outputs.deploy-sign-docker-image == 'true' && env.FORKED == 'false' }} - continue-on-error: true - env: - COSIGN_PASSWORD: "" - VERSION: "${{ needs.release.outputs.version }}" - run: make sign-${{ matrix.name }}-image - - - name: Container scan - uses: aquasecurity/trivy-action@0.8.0 - env: - image-ref: "${{ steps.docker-meta.outputs.image-id }}:${{ steps.docker-meta.outputs.image-version }}" - with: - image-ref: "${{ env.image-ref }}" - ignore-unfixed: true - vuln-type: "os,library" - severity: "CRITICAL,HIGH" - format: "sarif" - output: "trivy-results.sarif" - - - name: Upload Trivy scan results to GitHub Security tab - uses: github/codeql-action/upload-sarif@v2 - with: - sarif_file: "trivy-results.sarif" - category: trivy-${{ matrix.name }} - - comments: - runs-on: ubuntu-latest - if: ${{ needs.config.outputs.is-forked == 'false' }} - needs: - - config - - release - steps: - - name: Format current time - id: time_now - run: echo "time_now_formatted=$(date +'%Y-%m-%d %H:%M:%S')" >> "$GITHUB_OUTPUT" - - - name: Find releases comment - uses: peter-evans/find-comment@v2 - id: find_comment - with: - issue-number: ${{ github.event.pull_request.number }} - comment-author: "github-actions[bot]" - body-includes: "Following release(s) have been created for this PR." - - - name: Create releases comment - uses: peter-evans/create-or-update-comment@v2 - if: ${{ steps.find_comment.outputs.comment-id == '' }} - with: - comment-id: ${{ steps.find_comment.outputs.comment-id }} - issue-number: ${{ github.event.pull_request.number }} - edit-mode: replace - body: | - ## Pull Request information - Following release(s) have been created for this PR. - | Time | Release | - | --- | --- | - | ${{ steps.time_now.outputs.time_now_formatted }} | **${{ needs.release.outputs.version }}** | - - - name: Append releases comment - uses: peter-evans/create-or-update-comment@v2 - if: ${{ steps.find_comment.outputs.comment-id != '' }} - with: - comment-id: ${{ steps.find_comment.outputs.comment-id }} - issue-number: ${{ github.event.pull_request.number }} - edit-mode: append - body: | - | ${{ steps.time_now.outputs.time_now_formatted }} | **${{ needs.release.outputs.version }}** | diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml deleted file mode 100644 index 761a89d..0000000 --- a/.github/workflows/release.yml +++ /dev/null @@ -1,228 +0,0 @@ -name: Release - -on: - pull_request_target: - types: - - closed - branches: - - master - - main - -concurrency: - group: ${{ github.workflow }} - cancel-in-progress: false - -permissions: - contents: write - packages: write - security-events: write - -env: - PULL_REQUEST_NUMBER: ${{ github.event.pull_request.number }} - PULL_REQUEST_BRANCH: ${{ github.head_ref }} - BRANCH: ${{ github.event.pull_request.head.ref }} - REGISTRY: ghcr.io - IMAGE_NAME: "ghcr.io/${{ github.repository }}" - RELEASE: true - -jobs: - config: - if: github.triggering_actor != 'dependabot[bot]' && github.event.pull_request.merged == true - runs-on: ubuntu-latest - outputs: - go-version: ${{ fromJson(steps.config.outputs.config).go-version }} - deploy-pull-request: ${{ fromJson(steps.config.outputs.config).deploy.pull-request }} - deploy-sign-docker-image: ${{ fromJson(steps.config.outputs.config).deploy.sign-docker-image }} - deploy-pre-release-matrix: ${{ steps.pre-release-matrix.outputs.matrix }} - deploy-release-matrix: ${{ steps.release-matrix.outputs.matrix }} - steps: - - name: Checkout - uses: actions/checkout@v3 - - - name: Read config - id: config - run: echo "config=$(jq -M -c '.' ./.github/workflow-config.json)" >> $GITHUB_OUTPUT - - - name: List Pre-release profiles - id: deploy-pr-images-profiles - run: echo "profiles=$(jq -c -M '.deploy."pull-request-images"' .github/workflow-config.json)" >> $GITHUB_OUTPUT - - - name: Deploy pre-release matrix - id: pre-release-matrix - run: echo 'matrix={"include":${{ steps.deploy-pr-images-profiles.outputs.profiles }} }' >> $GITHUB_OUTPUT - - - name: List release profiles - id: deploy-images-profiles - run: echo "profiles=$(jq -c -M '.deploy."release-images"' .github/workflow-config.json)" >> $GITHUB_OUTPUT - - - name: Deploy release matrix - id: release-matrix - run: echo 'matrix={"include":${{ steps.deploy-images-profiles.outputs.profiles }} }' >> $GITHUB_OUTPUT - - build: - runs-on: ubuntu-latest - needs: - - config - outputs: - version: ${{ steps.version.outputs.version }} - steps: - - name: Checkout - uses: actions/checkout@v3 - - - name: Setup Golang - uses: actions/setup-go@v3 - with: - go-version: ${{ needs.config.outputs.go-version }} - - - name: Get makefile versions - id: version - run: | - version=$(make version) - echo "version=$version" >> $GITHUB_OUTPUT - - - name: Setup Golang cache - uses: actions/cache@v3 - with: - path: | - ~/go/pkg/mod - ~/.cache/go-build - key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} - restore-keys: | - ${{ runner.os }}-go- - - - name: Build artifact - run: make go-build - - - name: Run test - run: make go-test - - - name: Go lint - continue-on-error: true - uses: golangci/golangci-lint-action@v3 - with: - args: --issues-exit-code=0 - skip-pkg-cache: true - skip-build-cache: true - - - name: Package artifact - run: | - mv dist/*.tar.gz application.tar.gz - - - name: Upload build output - uses: actions/upload-artifact@v3 - with: - name: application - path: "./application.tar.gz" - - - name: Upload test coverage - uses: actions/upload-artifact@v3 - with: - name: test-coverage - path: "./test-coverage.out" - - release: - runs-on: ubuntu-latest - needs: - - config - - build - outputs: - version: ${{ steps.version.outputs.version }} - steps: - - name: Checkout - uses: actions/checkout@v3 - - - name: Set releasw version - id: version - run: echo "version=${{ needs.build.outputs.version }}" >> $GITHUB_OUTPUT - - - name: Download artifact - uses: actions/download-artifact@v3 - with: - name: application - path: "." - - - name: Rename artifact - run: mv application.tar.gz ${{ github.event.repository.name }}.tar.gz - - - name: Create release - uses: softprops/action-gh-release@v1 - id: create-release - with: - name: Release ${{ steps.version.outputs.version }} - tag_name: ${{ steps.version.outputs.version }} - token: ${{ secrets.GITHUB_TOKEN }} - body: ${{ steps.changelog.outputs.changelog }} - files: | - ./${{ github.event.repository.name }}.tar.gz - draft: false - prerelease: false - generate_release_notes: true - - deploy: - name: deploy-[${{ matrix.name }}] - runs-on: ubuntu-latest - strategy: - max-parallel: 1 - matrix: ${{ fromJson(needs.config.outputs.deploy-release-matrix) }} - if: ${{ !failure() && needs.release.result == 'success' && needs.config.outputs.deploy-pull-request == 'true' }} - needs: - - config - - build - - release - steps: - - name: Checkout - uses: actions/checkout@v3 - - - name: Build image - id: docker-meta - env: - VERSION: "${{ needs.release.outputs.version }}" - run: | - make ${{ matrix.name }}-image - TAG_SUFFIX=$(echo "-${{ matrix.name }}" | sed s/-ubuntu//) - echo "image-id=$IMAGE_NAME" >> $GITHUB_OUTPUT - echo "image-version=${VERSION}${TAG_SUFFIX}" >> $GITHUB_OUTPUT - - - name: Log in to registry - run: echo "${{ secrets.GITHUB_TOKEN }}" | docker login ${{ env.REGISTRY }} -u $ --password-stdin - - - name: Push image - env: - VERSION: "${{ needs.release.outputs.version }}" - run: | - make push-${{ matrix.name }}-image - - - name: Setup cosign - if: ${{ needs.config.outputs.deploy-sign-docker-image == 'true' }} - uses: sigstore/cosign-installer@main - - - name: Write signing key to disk (only needed for `cosign sign --key`) - if: ${{ needs.config.outputs.deploy-sign-docker-image == 'true' }} - continue-on-error: true - run: echo "${{ secrets.SIGNING_SECRET }}" > cosign.key - - - name: Sign the published Docker image - if: ${{ needs.config.outputs.deploy-sign-docker-image == 'true' }} - continue-on-error: true - env: - COSIGN_PASSWORD: "" - VERSION: "${{ needs.release.outputs.version }}" - run: make sign-${{ matrix.name }}-image - - - name: Container scan - uses: aquasecurity/trivy-action@0.8.0 - env: - image-ref: "${{ steps.docker-meta.outputs.image-id }}:${{ steps.docker-meta.outputs.image-version }}" - with: - image-ref: "${{ env.image-ref }}" - ignore-unfixed: true - vuln-type: "os,library" - severity: "CRITICAL,HIGH" - format: "sarif" - output: "trivy-results.sarif" - - - name: Upload Trivy scan results to GitHub Security tab - uses: github/codeql-action/upload-sarif@v2 - with: - sarif_file: "trivy-results.sarif" - category: trivy-${{ matrix.name }} diff --git a/README.md b/README.md index a0d7812..b42af1f 100644 --- a/README.md +++ b/README.md @@ -328,7 +328,7 @@ In order to run, you'll need the [Oracle Instant Client Basic](http://www.oracle for your operating system. Only the basic version is required for execution. -## Usage +# Usage This section lists the command line arguments (flags) that can be passed to the exporter. @@ -448,7 +448,7 @@ oracledb_test_value_2 2 You can find [here](./custom-metrics-example/custom-metrics.toml) a working example of custom metrics for slow queries, big queries and top 100 tables. -# Customize metrics in a docker image +## Customize metrics in a docker image If you run the exporter as a docker image and want to customize the metrics, you can use the following example: diff --git a/kubernetes/txeventq-metrics.toml b/custom-metrics-example/txeventq-metrics.toml similarity index 100% rename from kubernetes/txeventq-metrics.toml rename to custom-metrics-example/txeventq-metrics.toml From d8f7313c6d63c90c18f32c751bcad295d546d815 Mon Sep 17 00:00:00 2001 From: Mark Nelson Date: Thu, 7 Sep 2023 14:30:46 -0400 Subject: [PATCH 10/17] preparing for 1.0 release Signed-off-by: Mark Nelson --- Dockerfile | 12 ++--- LICENSE.txt | 2 +- Makefile | 88 ++++-------------------------------- collector/collector.go | 4 ++ collector/default_metrics.go | 4 ++ docker-compose/compose.yaml | 5 +- main.go | 4 ++ 7 files changed, 26 insertions(+), 93 deletions(-) diff --git a/Dockerfile b/Dockerfile index bb44d52..bd8cd6c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,4 @@ ARG BASE_IMAGE -# Build is starting here -#FROM docker.io/library/golang:1.19 AS build FROM ${BASE_IMAGE} AS build RUN microdnf install golang-1.19.10 @@ -10,7 +8,7 @@ COPY . . RUN go get -d -v ARG VERSION -ENV VERSION ${VERSION:-0.1.0} +ENV VERSION ${VERSION:-1.0.0} RUN GOOS=linux GOARCH=amd64 go build -v -ldflags "-X main.Version=${VERSION} -s -w" @@ -18,7 +16,7 @@ FROM ${BASE_IMAGE} as exporter LABEL org.opencontainers.image.authors="Oracle America, Inc." LABEL org.opencontainers.image.description="Oracle Database Observability Exporter" -ENV VERSION ${VERSION:-0.1.0} +ENV VERSION ${VERSION:-1.0.0} ENV DEBIAN_FRONTEND=noninteractive RUN microdnf install -y oracle-instantclient-release-el8 && microdnf install -y oracle-instantclient-basic @@ -27,12 +25,8 @@ ENV LD_LIBRARY_PATH /usr/lib/oracle/21/client64/lib ENV PATH $PATH:/usr/lib/oracle/21/client64/bin ENV ORACLE_HOME /usr/lib/oracle/21/client64 -#ARG LEGACY_TABLESPACE -#ENV LEGACY_TABLESPACE=${LEGACY_TABLESPACE} COPY --from=build /go/src/oracledb_exporter/oracle-db-appdev-monitoring /oracledb_exporter -#ADD ./default-metrics${LEGACY_TABLESPACE}.toml /default-metrics.toml - -ENV DATA_SOURCE_NAME system/oracle@oracle/xe +ADD ./default-metrics.toml /default-metrics.toml EXPOSE 9161 diff --git a/LICENSE.txt b/LICENSE.txt index 75a677a..a5adf83 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -21,7 +21,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -- -Copyright (c) 2023 Oracle and/or its affiliates. +Copyright (c) 2021, 2023, Oracle and/or its affiliates. The Universal Permissive License (UPL), Version 1.0 diff --git a/Makefile b/Makefile index 46043ee..00b5488 100644 --- a/Makefile +++ b/Makefile @@ -3,21 +3,17 @@ OS_TYPE ?= $(shell uname -s | tr '[:upper:]' '[:lower:]') ARCH_TYPE ?= $(subst x86_64,amd64,$(patsubst i%86,386,$(ARCH))) GOOS ?= $(shell go env GOOS) GOARCH ?= $(shell go env GOARCH) -VERSION ?= 0.99.6 +VERSION ?= 1.0.0 LDFLAGS := -X main.Version=$(VERSION) GOFLAGS := -ldflags "$(LDFLAGS) -s -w" BUILD_ARGS = --build-arg VERSION=$(VERSION) -LEGACY_TABLESPACE = --build-arg LEGACY_TABLESPACE=.legacy-tablespace OUTDIR = ./dist -IMAGE_NAME ?= oracle/observability-exporter +IMAGE_NAME ?= container-registry.oracle.com/database/observability-exporter IMAGE_ID ?= $(IMAGE_NAME):$(VERSION) IMAGE_ID_LATEST?= $(IMAGE_NAME):latest -RELEASE ?= true -#UBUNTU_BASE_IMAGE ?= docker.io/library/ubuntu:23.04 ORACLE_LINUX_BASE_IMAGE ?= ghcr.io/oracle/oraclelinux:8-slim -#ALPINE_BASE_IMAGE ?= docker.io/library/alpine:3.17 ifeq ($(GOOS), windows) EXT?=.exe @@ -35,8 +31,8 @@ go-build: @echo "Build $(OS_TYPE)" mkdir -p $(OUTDIR)/oracledb_exporter-$(VERSION).$(GOOS)-$(GOARCH)/ go build $(GOFLAGS) -o $(OUTDIR)/oracledb_exporter-$(VERSION).$(GOOS)-$(GOARCH)/oracledb_exporter$(EXT) - #cp default-metrics.toml $(OUTDIR)/$(DIST_DIR) - cp teq-default-metrics.toml $(OUTDIR)/$(DIST_DIR)/default-metrics.toml + cp default-metrics.toml $(OUTDIR)/$(DIST_DIR) + #cp teq-default-metrics.toml $(OUTDIR)/$(DIST_DIR)/default-metrics.toml (cd dist ; tar cfz oracledb_exporter-$(VERSION).$(GOOS)-$(GOARCH).tar.gz oracledb_exporter-$(VERSION).$(GOOS)-$(GOARCH)) .PHONY: go-build-linux-amd64 @@ -81,83 +77,15 @@ go-test: GOOS=$(OS_TYPE) GOARCH=$(ARCH_TYPE) go test -coverprofile="test-coverage.out" $$(go list ./... | grep -v /vendor/) clean: - rm -rf ./dist sgerrand.rsa.pub glibc-*.apk oracle-*.rpm - -#docker: ubuntu-image alpine-image oraclelinux-image -docker: oraclelinux-image + rm -rf ./dist glibc-*.apk oracle-*.rpm push-images: - @make --no-print-directory push-ubuntu-image @make --no-print-directory push-oraclelinux-image - @make --no-print-directory push-alpine-image - -oraclelinux-image: - if DOCKER_CLI_EXPERIMENTAL=enabled docker manifest inspect "$(IMAGE_ID)-oraclelinux" > /dev/null; then \ - echo "Image \"$(IMAGE_ID)-oraclelinux\" already exists on ghcr.io"; \ - else \ - docker build --progress=plain $(BUILD_ARGS) -t "$(IMAGE_ID)-oraclelinux" --build-arg BASE_IMAGE=$(ORACLE_LINUX_BASE_IMAGE) . && \ - #docker build --progress=plain $(BUILD_ARGS) $(LEGACY_TABLESPACE) -t "$(IMAGE_ID)-oraclelinux_legacy-tablespace" --build-arg BASE_IMAGE=$(ORACLE_LINUX_BASE_IMAGE) . && \ - docker tag "$(IMAGE_ID)-oraclelinux" "$(IMAGE_NAME):oraclelinux"; \ - fi + +docker: + docker build --progress=plain $(BUILD_ARGS) -t "$(IMAGE_ID)" --build-arg BASE_IMAGE=$(ORACLE_LINUX_BASE_IMAGE) . push-oraclelinux-image: - docker push $(IMAGE_ID)-oraclelinux -ifeq ("$(RELEASE)", "true") - docker push "$(IMAGE_NAME):oraclelinux" - docker push "$(IMAGE_ID)-oraclelinux_legacy-tablespace" -endif - -sign-oraclelinux-image: -ifneq ("$(wildcard cosign.key)","") - cosign sign --key cosign.key $(IMAGE_ID)-oraclelinux -else - @echo "Can't find cosign.key file" -endif - -ubuntu-image: - if DOCKER_CLI_EXPERIMENTAL=enabled docker manifest inspect "$(IMAGE_ID)" > /dev/null; then \ - echo "Image \"$(IMAGE_ID)\" already exists on ghcr.io"; \ - else \ - docker build --progress=plain $(BUILD_ARGS) --build-arg BASE_IMAGE=$(UBUNTU_BASE_IMAGE) -t "$(IMAGE_ID)" . && \ - docker build --progress=plain $(BUILD_ARGS) --build-arg BASE_IMAGE=$(UBUNTU_BASE_IMAGE) $(LEGACY_TABLESPACE) -t "$(IMAGE_ID)_legacy-tablespace" . && \ - docker tag "$(IMAGE_ID)" "$(IMAGE_ID_LATEST)"; \ - fi - -push-ubuntu-image: docker push $(IMAGE_ID) -ifeq ("$(RELEASE)", "true") - docker push "$(IMAGE_ID_LATEST)" - docker push "$(IMAGE_ID)_legacy-tablespace" -endif - -sign-ubuntu-image: -ifneq ("$(wildcard cosign.key)","") - cosign sign --key cosign.key $(IMAGE_ID) - cosign sign --key cosign.key $(IMAGE_ID_LATEST) -else - @echo "Can't find cosign.key file" -endif - -alpine-image: - if DOCKER_CLI_EXPERIMENTAL=enabled docker manifest inspect "$(IMAGE_ID)-alpine" > /dev/null; then \ - echo "Image \"$(IMAGE_ID)-alpine\" already exists on ghcr.io"; \ - else \ - docker build --progress=plain $(BUILD_ARGS) -t "$(IMAGE_ID)-alpine" --build-arg BASE_IMAGE=$(ALPINE_BASE_IMAGE) . && \ - docker build --progress=plain $(BUILD_ARGS) $(LEGACY_TABLESPACE) --build-arg BASE_IMAGE=$(ALPINE_BASE_IMAGE) -t "$(IMAGE_ID)-alpine_legacy-tablespace" . && \ - docker tag "$(IMAGE_ID)-alpine" "$(IMAGE_NAME):alpine"; \ - fi - -push-alpine-image: - docker push $(IMAGE_ID)-alpine -ifeq ("$(RELEASE)", "true") - docker push "$(IMAGE_NAME):alpine" -endif - -sign-alpine-image: -ifneq ("$(wildcard cosign.key)","") - cosign sign --key cosign.key $(IMAGE_ID)-alpine -else - @echo "Can't find cosign.key file" -endif .PHONY: version build deps go-test clean docker diff --git a/collector/collector.go b/collector/collector.go index ae1dadd..fd530da 100644 --- a/collector/collector.go +++ b/collector/collector.go @@ -1,3 +1,7 @@ +// Copyright (c) 2021, 2023, Oracle and/or its affiliates. +// Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. +// Portions Copyright (c) 2016 Seth Miller + package collector import ( diff --git a/collector/default_metrics.go b/collector/default_metrics.go index 237f5b9..e93105c 100644 --- a/collector/default_metrics.go +++ b/collector/default_metrics.go @@ -1,3 +1,7 @@ +// Copyright (c) 2021, 2023, Oracle and/or its affiliates. +// Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. +// Portions Copyright (c) 2016 Seth Miller + package collector import ( diff --git a/docker-compose/compose.yaml b/docker-compose/compose.yaml index c47ab94..bd84460 100644 --- a/docker-compose/compose.yaml +++ b/docker-compose/compose.yaml @@ -33,8 +33,7 @@ services: - ORACLE_PWD=Welcome12345 exporter: - #image: container-registry.oracle.com/database/observability-exporter:1.0.0 - image: oracle/observability-exporter:0.99.6-oraclelinux + image: container-registry.oracle.com/database/observability-exporter:1.0.0 container_name: exporter ports: - 9161:9161 @@ -44,4 +43,4 @@ services: - DB_CONNECT_STRING=free23c:1521/freepdb volumes: - prom_data: \ No newline at end of file + prom_data: diff --git a/main.go b/main.go index 9dd4ac1..dbedc53 100644 --- a/main.go +++ b/main.go @@ -1,3 +1,7 @@ +// Copyright (c) 2021, 2023, Oracle and/or its affiliates. +// Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. +// Portions Copyright (c) 2016 Seth Miller + package main import ( From 2f43b0e93801fbb6f4560b111b966f2b26560584 Mon Sep 17 00:00:00 2001 From: Mark Nelson Date: Thu, 7 Sep 2023 14:53:01 -0400 Subject: [PATCH 11/17] preparing for 1.0 release Signed-off-by: Mark Nelson --- README.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index b42af1f..b6fb5f0 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,6 @@ Contributions are welcome - please see [contributing](CONTRIBUTING.md). - [Test/demo environment using Docker Compose](#testdemo-environment-with-docker-compose) - [Kubernetes](#kubernetes) - [Standalone binary](#standalone-binary) -- [Usage](#usage) - [Custom metrics](#custom-metrics) - [Grafana dashboards](#grafana-dashboards) - [Developer notes](#developer-notes) @@ -124,7 +123,11 @@ You can run the exporter in a local container using a conatiner image from [Orac If you need an Oracle Database to test the exporter, you can use this command to start up an instance of [Oracle Database 23c Free](https://www.oracle.com/database/free/) which also requires no authentication or license presentment/acceptance to pull the image. ```bash -docker run --name free23c -d -p 1521:1521 -e ORACLE_PWD=Welcome12345 container-registry.oracle.com/database/free:latest +docker run --name free23c \ + -d \ + -p 1521:1521 \ + -e ORACLE_PWD=Welcome12345 \ + container-registry.oracle.com/database/free:latest ``` This will pull the image and start up the database with a listener on port 1521. It will also create a pluggable database (a database container) called "FREEPDB1" and will set the admin passwords to the password you specified on this command. @@ -144,10 +147,10 @@ To obtain the IP address of the container, which you will need to connect to the ```bash docker inspect free23c | grep IPA - "SecondaryIPAddresses": null, + "SecondaryIPAddresses": null, + "IPAddress": "172.17.0.2", + "IPAMConfig": null, "IPAddress": "172.17.0.2", - "IPAMConfig": null, - "IPAddress": "172.17.0.2", ``` ### Exporter @@ -327,10 +330,7 @@ Pre-compiled versions for Linux 64 bit can be found under [releases](https://git In order to run, you'll need the [Oracle Instant Client Basic](http://www.oracle.com/technetwork/database/features/instant-client/index-097480.html) for your operating system. Only the basic version is required for execution. - -# Usage - -This section lists the command line arguments (flags) that can be passed to the exporter. +The following command line arguments (flags) can be passed to the exporter: ```bash Usage of oracledb_exporter: From 39b2fbb5b1fa5b4879f90ba9628221c6924c67ad Mon Sep 17 00:00:00 2001 From: Mark Nelson Date: Tue, 12 Sep 2023 11:17:19 -0400 Subject: [PATCH 12/17] preparing for 1.0 release Signed-off-by: Mark Nelson --- README.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/README.md b/README.md index b6fb5f0..840f2e1 100644 --- a/README.md +++ b/README.md @@ -2,9 +2,7 @@ This project aims to provide observability for the Oracle Database so that users can understand performance and diagnose issues easily across applications and database. Over time, this project will provide not just metrics, but also logging and tracing support, and integration into popular frameworks like Spring Boot. The project aims to deliver functionality to support both cloud and on-premises databases, including those running in Kubernetes and containers. -In the first production release, v1.0, this project provides a [Prometheus](https://prometheus.io/) exporter for Oracle Database that is based in part on a Prometheus exporter created by [Seth Miller](https://github.com/iamseth/oracledb_exporter) with various changes to comply with various Oracle standards and policies. - -Customers with an active support agreement for Oracle Database may open a Service Request in My Oracle Support for support with any issues using this exporter. Community support is available through GitHub issues, etc., for other users. +In the first production release, v1.0, this project provides a [Prometheus](https://prometheus.io/) exporter for Oracle Database that is based in part on a Prometheus exporter created by [Seth Miller](https://github.com/iamseth/oracledb_exporter) with changes to comply with various Oracle standards and policies. Contributions are welcome - please see [contributing](CONTRIBUTING.md). From a781bfe25658b41f865dc94228d1f3358bb176df Mon Sep 17 00:00:00 2001 From: Mark Nelson Date: Tue, 12 Sep 2023 11:33:26 -0400 Subject: [PATCH 13/17] preparing for 1.0 release Signed-off-by: Mark Nelson --- README.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/README.md b/README.md index 840f2e1..296a838 100644 --- a/README.md +++ b/README.md @@ -451,10 +451,8 @@ You can find [here](./custom-metrics-example/custom-metrics.toml) a working exam If you run the exporter as a docker image and want to customize the metrics, you can use the following example: ```Dockerfile -FROM iamseth/oracledb_exporter:latest - +FROM container-registry.oracle.com/database/observability-exporter:1.0.0 COPY custom-metrics.toml / - ENTRYPOINT ["/oracledb_exporter", "--custom.metrics", "/custom-metrics.toml"] ``` From 0080c158af0d05da5b25719f0a397754b431ca11 Mon Sep 17 00:00:00 2001 From: Mark Nelson Date: Tue, 12 Sep 2023 15:49:23 -0400 Subject: [PATCH 14/17] testing to prepare for 1.0 release Signed-off-by: Mark Nelson --- README.md | 2 +- docker-compose/compose.yaml | 21 +- docker-compose/grafana/dashboard.yaml | 12 + .../grafana/dashboards/3333_rev1.json | 1228 +++++++++++++++++ .../{ => datasources}/datasources.yaml | 0 docker-compose/oracle/grant_permissions.sql | 2 + 6 files changed, 1260 insertions(+), 5 deletions(-) create mode 100644 docker-compose/grafana/dashboard.yaml create mode 100755 docker-compose/grafana/dashboards/3333_rev1.json rename docker-compose/grafana/{ => datasources}/datasources.yaml (100%) create mode 100644 docker-compose/oracle/grant_permissions.sql diff --git a/README.md b/README.md index 296a838..ed93b2a 100644 --- a/README.md +++ b/README.md @@ -90,7 +90,7 @@ The following metrics are exposed currently. # Database permissions required -For the built-in default metrics, the database user that the exporter uses to connect to the Oracle Database instance must have the `SYS` privilege and/or `SELECT` permission on the following tables. +For the built-in default metrics, the database user that the exporter uses to connect to the Oracle Database instance must have the `SELECT_CATALOG_ROLE` privilege and/or `SELECT` permission on the following tables. - dba_tablespace_usage_metrics - dba_tablespaces diff --git a/docker-compose/compose.yaml b/docker-compose/compose.yaml index bd84460..b7a54cb 100644 --- a/docker-compose/compose.yaml +++ b/docker-compose/compose.yaml @@ -22,7 +22,9 @@ services: - GF_SECURITY_ADMIN_USER=admin - GF_SECURITY_ADMIN_PASSWORD=grafana volumes: - - ./grafana:/etc/grafana/provisioning/datasources + - ./grafana/datasources:/etc/grafana/provisioning/datasources + - ./grafana/dashboard.yaml:/etc/grafana/provisioning/dashboards/main.yaml + - ./grafana/dashboards:/var/lib/grafana/dashboards free23c: image: container-registry.oracle.com/database/free:latest @@ -30,7 +32,15 @@ services: ports: - 1521:1521 environment: - - ORACLE_PWD=Welcome12345 + - ORACLE_PWD=Welcome12345 + volumes: + - ./oracle:/opt/oracle/scripts/startup + healthcheck: + test: ["CMD-SHELL", "lsnrctl status | grep READY"] + interval: 15s + timeout: 10s + retries: 5 + start_period: 30s exporter: image: container-registry.oracle.com/database/observability-exporter:1.0.0 @@ -38,9 +48,12 @@ services: ports: - 9161:9161 environment: - - DB_USERNAME=pdbadmin + - DB_USERNAME=system - DB_PASSWORD=Welcome12345 - - DB_CONNECT_STRING=free23c:1521/freepdb + - DB_CONNECT_STRING=free23c:1521/free + depends_on: + free23c: + condition: service_healthy volumes: prom_data: diff --git a/docker-compose/grafana/dashboard.yaml b/docker-compose/grafana/dashboard.yaml new file mode 100644 index 0000000..57d3797 --- /dev/null +++ b/docker-compose/grafana/dashboard.yaml @@ -0,0 +1,12 @@ +apiVersion: 1 + +providers: + - name: "Dashboard provider" + orgId: 1 + type: file + disableDeletion: false + updateIntervalSeconds: 10 + allowUiUpdates: false + options: + path: /var/lib/grafana/dashboards + foldersFromFilesStructure: true \ No newline at end of file diff --git a/docker-compose/grafana/dashboards/3333_rev1.json b/docker-compose/grafana/dashboards/3333_rev1.json new file mode 100755 index 0000000..897453d --- /dev/null +++ b/docker-compose/grafana/dashboards/3333_rev1.json @@ -0,0 +1,1228 @@ +{ + "__inputs": [ + { + "name": "Prometheus", + "label": "prometheus", + "description": "", + "type": "datasource", + "pluginId": "prometheus", + "pluginName": "Prometheus" + } + ], + "__requires": [ + { + "type": "grafana", + "id": "grafana", + "name": "Grafana", + "version": "4.5.1" + }, + { + "type": "panel", + "id": "graph", + "name": "Graph", + "version": "" + }, + { + "type": "datasource", + "id": "prometheus", + "name": "Prometheus", + "version": "1.0.0" + }, + { + "type": "panel", + "id": "singlestat", + "name": "Singlestat", + "version": "" + } + ], + "annotations": { + "list": [] + }, + "editable": true, + "gnetId": 3333, + "graphTooltip": 1, + "hideControls": false, + "id": null, + "links": [ + { + "asDropdown": true, + "icon": "external link", + "tags": [], + "type": "dashboards" + } + ], + "refresh": false, + "rows": [ + { + "collapse": false, + "height": "125px", + "panels": [ + { + "cacheTimeout": null, + "colorBackground": false, + "colorValue": true, + "colors": [ + "rgba(245, 54, 54, 0.9)", + "rgba(237, 129, 40, 0.89)", + "rgba(50, 172, 45, 0.97)" + ], + "datasource": "Prometheus", + "decimals": null, + "editable": true, + "error": false, + "format": "none", + "gauge": { + "maxValue": 100, + "minValue": 0, + "show": false, + "thresholdLabels": false, + "thresholdMarkers": true + }, + "height": "125px", + "id": 12, + "interval": "$interval", + "links": [], + "mappingType": 1, + "mappingTypes": [ + { + "name": "value to text", + "value": 1 + }, + { + "name": "range to text", + "value": 2 + } + ], + "maxDataPoints": 100, + "nullPointMode": "connected", + "nullText": null, + "postfix": "", + "postfixFontSize": "80%", + "prefix": "", + "prefixFontSize": "80%", + "rangeMaps": [ + { + "from": "null", + "text": "N/A", + "to": "null" + } + ], + "span": 3, + "sparkline": { + "fillColor": "rgba(31, 118, 189, 0.18)", + "full": false, + "lineColor": "rgb(31, 120, 193)", + "show": false + }, + "tableColumn": "", + "targets": [ + { + "calculatedInterval": "10m", + "datasourceErrors": {}, + "errors": {}, + "expr": "oracledb_up{instance=\"$host\"}", + "format": "time_series", + "interval": "5m", + "intervalFactor": 1, + "legendFormat": "", + "metric": "", + "refId": "A", + "step": 300 + } + ], + "thresholds": "0,1", + "title": "oracledb status", + "transparent": false, + "type": "singlestat", + "valueFontSize": "100%", + "valueMaps": [ + { + "op": "=", + "text": "DEAD", + "value": "0" + }, + { + "op": "=", + "text": "ALIVE", + "value": "1" + } + ], + "valueName": "current" + }, + { + "cacheTimeout": null, + "colorBackground": false, + "colorValue": true, + "colors": [ + "rgba(245, 54, 54, 0.9)", + "rgba(237, 129, 40, 0.89)", + "rgba(50, 172, 45, 0.97)" + ], + "datasource": "Prometheus", + "decimals": null, + "editable": true, + "error": false, + "format": "short", + "gauge": { + "maxValue": 100, + "minValue": 0, + "show": false, + "thresholdLabels": false, + "thresholdMarkers": true + }, + "height": "125px", + "id": 13, + "interval": "$interval", + "links": [], + "mappingType": 1, + "mappingTypes": [ + { + "name": "value to text", + "value": 1 + }, + { + "name": "range to text", + "value": 2 + } + ], + "maxDataPoints": 100, + "nullPointMode": "connected", + "nullText": null, + "postfix": "", + "postfixFontSize": "50%", + "prefix": "", + "prefixFontSize": "80%", + "rangeMaps": [ + { + "from": "null", + "text": "N/A", + "to": "null" + } + ], + "span": 3, + "sparkline": { + "fillColor": "rgba(31, 118, 189, 0.18)", + "full": false, + "lineColor": "rgb(31, 120, 193)", + "show": true + }, + "tableColumn": "", + "targets": [ + { + "calculatedInterval": "10m", + "datasourceErrors": {}, + "errors": {}, + "expr": "oracledb_sessions_active{instance=\"$host\"}", + "format": "time_series", + "interval": "$interval", + "intervalFactor": 1, + "legendFormat": "", + "metric": "", + "refId": "A", + "step": 60 + } + ], + "thresholds": "", + "title": "active sessions", + "transparent": false, + "type": "singlestat", + "valueFontSize": "100%", + "valueMaps": [], + "valueName": "current" + }, + { + "cacheTimeout": null, + "colorBackground": false, + "colorValue": false, + "colors": [ + "rgba(50, 172, 45, 0.97)", + "rgba(237, 129, 40, 0.89)", + "rgba(245, 54, 54, 0.9)" + ], + "datasource": "Prometheus", + "decimals": null, + "editable": true, + "error": false, + "format": "none", + "gauge": { + "maxValue": 100, + "minValue": 0, + "show": false, + "thresholdLabels": false, + "thresholdMarkers": true + }, + "height": "125px", + "id": 51, + "interval": "$interval", + "links": [], + "mappingType": 1, + "mappingTypes": [ + { + "name": "value to text", + "value": 1 + }, + { + "name": "range to text", + "value": 2 + } + ], + "maxDataPoints": 100, + "nullPointMode": "connected", + "nullText": null, + "postfix": "", + "postfixFontSize": "50%", + "prefix": "", + "prefixFontSize": "80%", + "rangeMaps": [ + { + "from": "null", + "text": "N/A", + "to": "null" + } + ], + "span": 3, + "sparkline": { + "fillColor": "rgba(31, 118, 189, 0.18)", + "full": false, + "lineColor": "rgb(31, 120, 193)", + "show": true + }, + "tableColumn": "", + "targets": [ + { + "calculatedInterval": "10m", + "datasourceErrors": {}, + "errors": {}, + "expr": "oracledb_activity_user_commits{instance=\"$host\"}", + "format": "time_series", + "interval": "5m", + "intervalFactor": 1, + "legendFormat": "", + "metric": "", + "refId": "A", + "step": 300 + } + ], + "thresholds": "90,95", + "title": "user commits", + "transparent": false, + "type": "singlestat", + "valueFontSize": "100%", + "valueMaps": [], + "valueName": "current" + }, + { + "cacheTimeout": null, + "colorBackground": false, + "colorValue": true, + "colors": [ + "rgba(245, 54, 54, 0.9)", + "rgba(237, 129, 40, 0.89)", + "rgba(50, 172, 45, 0.97)" + ], + "datasource": "Prometheus", + "decimals": null, + "editable": true, + "error": false, + "format": "none", + "gauge": { + "maxValue": 100, + "minValue": 0, + "show": false, + "thresholdLabels": false, + "thresholdMarkers": true + }, + "height": "125px", + "id": 52, + "interval": "$interval", + "links": [], + "mappingType": 1, + "mappingTypes": [ + { + "name": "value to text", + "value": 1 + }, + { + "name": "range to text", + "value": 2 + } + ], + "maxDataPoints": 100, + "nullPointMode": "connected", + "nullText": null, + "postfix": "", + "postfixFontSize": "50%", + "prefix": "", + "prefixFontSize": "80%", + "rangeMaps": [ + { + "from": "null", + "text": "N/A", + "to": "null" + } + ], + "repeat": null, + "span": 3, + "sparkline": { + "fillColor": "rgba(31, 118, 189, 0.18)", + "full": false, + "lineColor": "rgb(31, 120, 193)", + "show": true + }, + "tableColumn": "", + "targets": [ + { + "calculatedInterval": "10m", + "datasourceErrors": {}, + "errors": {}, + "expr": "oracledb_activity_execute_count{instance=\"$host\"}", + "format": "time_series", + "interval": "5m", + "intervalFactor": 1, + "legendFormat": "", + "metric": "", + "refId": "A", + "step": 300 + } + ], + "thresholds": "", + "title": "execute count", + "transparent": false, + "type": "singlestat", + "valueFontSize": "100%", + "valueMaps": [], + "valueName": "current" + } + ], + "repeat": null, + "repeatIteration": null, + "repeatRowId": null, + "showTitle": false, + "title": "Widgets", + "titleSize": "h6" + }, + { + "collapse": false, + "height": 250, + "panels": [ + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "fill": 1, + "id": 57, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": false, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "oracledb_exporter_last_scrape_duration_seconds{instance=\"$host\"}\t", + "format": "time_series", + "intervalFactor": 2, + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "last scrape duration seconds", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "fill": 1, + "id": 58, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": false, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "oracledb_exporter_scrapes_total{instance=\"$host\"}", + "format": "time_series", + "intervalFactor": 2, + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "total scrapes", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + } + ], + "repeat": null, + "repeatIteration": null, + "repeatRowId": null, + "showTitle": true, + "title": "Exporter Status", + "titleSize": "h6" + }, + { + "collapse": false, + "height": "250px", + "panels": [ + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "decimals": null, + "editable": true, + "error": false, + "fill": 2, + "grid": {}, + "id": 53, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": false, + "show": false, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "calculatedInterval": "2m", + "datasourceErrors": {}, + "errors": {}, + "expr": "oracledb_wait_time_concurrency{instance=\"$host\"}", + "format": "time_series", + "interval": "$interval", + "intervalFactor": 1, + "legendFormat": "", + "metric": "", + "refId": "A", + "step": 60 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "wait time concurrency", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "logBase": 1, + "max": null, + "min": 0, + "show": true + }, + { + "format": "short", + "logBase": 1, + "max": null, + "min": 0, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "decimals": null, + "editable": true, + "error": false, + "fill": 2, + "grid": {}, + "id": 54, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": false, + "show": false, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "calculatedInterval": "2m", + "datasourceErrors": {}, + "errors": {}, + "expr": "oracledb_wait_time_commit{instance=\"$host\"}", + "format": "time_series", + "interval": "$interval", + "intervalFactor": 1, + "legendFormat": "", + "metric": "", + "refId": "A", + "step": 60 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "wait time commit", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "logBase": 1, + "max": null, + "min": 0, + "show": true + }, + { + "format": "short", + "logBase": 1, + "max": null, + "min": 0, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "decimals": null, + "editable": true, + "error": false, + "fill": 2, + "grid": {}, + "id": 55, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": false, + "show": false, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "calculatedInterval": "2m", + "datasourceErrors": {}, + "errors": {}, + "expr": "oracledb_wait_time_system_io{instance=\"$host\"}", + "format": "time_series", + "interval": "$interval", + "intervalFactor": 1, + "legendFormat": "", + "metric": "", + "refId": "A", + "step": 60 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "wait time system io", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "logBase": 1, + "max": null, + "min": 0, + "show": true + }, + { + "format": "short", + "logBase": 1, + "max": null, + "min": 0, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "decimals": null, + "editable": true, + "error": false, + "fill": 2, + "grid": {}, + "id": 56, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": false, + "show": false, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "calculatedInterval": "2m", + "datasourceErrors": {}, + "errors": {}, + "expr": "oracledb_wait_time_user_io{instance=\"$host\"}\t", + "format": "time_series", + "interval": "$interval", + "intervalFactor": 1, + "legendFormat": "", + "metric": "", + "refId": "A", + "step": 60 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "wait time user io", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "logBase": 1, + "max": null, + "min": 0, + "show": true + }, + { + "format": "short", + "logBase": 1, + "max": null, + "min": 0, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "decimals": null, + "editable": true, + "error": false, + "fill": 2, + "grid": {}, + "id": 59, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": false, + "show": false, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "calculatedInterval": "2m", + "datasourceErrors": {}, + "errors": {}, + "expr": "oracledb_wait_time_application{instance=\"$host\"}\t", + "format": "time_series", + "interval": "$interval", + "intervalFactor": 1, + "legendFormat": "", + "metric": "", + "refId": "A", + "step": 60 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "wait time application", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "logBase": 1, + "max": null, + "min": 0, + "show": true + }, + { + "format": "short", + "logBase": 1, + "max": null, + "min": 0, + "show": true + } + ] + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "decimals": null, + "editable": true, + "error": false, + "fill": 2, + "grid": {}, + "id": 60, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": false, + "show": false, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "span": 6, + "stack": false, + "steppedLine": false, + "targets": [ + { + "calculatedInterval": "2m", + "datasourceErrors": {}, + "errors": {}, + "expr": "oracledb_wait_time_network{instance=\"$host\"}\t", + "format": "time_series", + "interval": "$interval", + "intervalFactor": 1, + "legendFormat": "", + "metric": "", + "refId": "A", + "step": 60 + } + ], + "thresholds": [], + "timeFrom": null, + "timeShift": null, + "title": "wait time network", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "logBase": 1, + "max": null, + "min": 0, + "show": true + }, + { + "format": "short", + "logBase": 1, + "max": null, + "min": 0, + "show": true + } + ] + } + ], + "repeat": null, + "repeatIteration": null, + "repeatRowId": null, + "showTitle": false, + "title": "Table Locks", + "titleSize": "h6" + } + ], + "schemaVersion": 14, + "style": "dark", + "tags": [ + "oracle" + ], + "templating": { + "list": [ + { + "allFormat": "glob", + "allValue": null, + "current": {}, + "datasource": "Prometheus", + "hide": 0, + "includeAll": false, + "label": "", + "multi": false, + "multiFormat": "regex values", + "name": "host", + "options": [], + "query": "label_values(oracledb_up, instance)", + "refresh": 1, + "refresh_on_load": false, + "regex": "", + "sort": 1, + "tagValuesQuery": null, + "tags": [], + "tagsQuery": null, + "type": "query", + "useTags": false + }, + { + "allFormat": "glob", + "auto": true, + "auto_count": 200, + "auto_min": "1s", + "current": { + "text": "1m", + "value": "1m" + }, + "datasource": "Prometheus", + "hide": 0, + "includeAll": false, + "label": "Interval", + "multi": false, + "multiFormat": "glob", + "name": "interval", + "options": [ + { + "selected": false, + "text": "auto", + "value": "$__auto_interval" + }, + { + "selected": false, + "text": "1s", + "value": "1s" + }, + { + "selected": false, + "text": "5s", + "value": "5s" + }, + { + "selected": true, + "text": "1m", + "value": "1m" + }, + { + "selected": false, + "text": "5m", + "value": "5m" + }, + { + "selected": false, + "text": "1h", + "value": "1h" + }, + { + "selected": false, + "text": "6h", + "value": "6h" + }, + { + "selected": false, + "text": "1d", + "value": "1d" + } + ], + "query": "1s,5s,1m,5m,1h,6h,1d", + "refresh": 2, + "type": "interval" + } + ] + }, + "time": { + "from": "now-1h", + "to": "now" + }, + "timepicker": { + "collapse": false, + "enable": true, + "notice": false, + "now": true, + "refresh_intervals": [ + "5s", + "10s", + "30s", + "1m", + "5m", + "15m", + "30m", + "1h", + "2h", + "1d" + ], + "status": "Stable", + "time_options": [ + "5m", + "15m", + "1h", + "6h", + "12h", + "24h", + "2d", + "7d", + "30d" + ], + "type": "timepicker" + }, + "timezone": "browser", + "title": "Oracledb", + "version": 18, + "description": "Oracle dashboard monitors db status, active sessions, user commits, wait times and more." +} \ No newline at end of file diff --git a/docker-compose/grafana/datasources.yaml b/docker-compose/grafana/datasources/datasources.yaml similarity index 100% rename from docker-compose/grafana/datasources.yaml rename to docker-compose/grafana/datasources/datasources.yaml diff --git a/docker-compose/oracle/grant_permissions.sql b/docker-compose/oracle/grant_permissions.sql new file mode 100644 index 0000000..9065f9b --- /dev/null +++ b/docker-compose/oracle/grant_permissions.sql @@ -0,0 +1,2 @@ +alter session set container=freepdb1; +grant select_catalog_role to pdbadmin; From c66e97362cd888e86397f0f40000bdda7b42335f Mon Sep 17 00:00:00 2001 From: Mark Nelson Date: Wed, 13 Sep 2023 13:27:26 -0400 Subject: [PATCH 15/17] preparing for release Signed-off-by: Mark Nelson --- README.md | 6 +- doc/oracledb-dashboard.png | Bin 0 -> 111782 bytes grafana/Dockerfile | 15 - grafana/dashboards/all.yml | 14 - grafana/dashboards/dashboard.json | 2544 ----------------------------- grafana/datasources/all.yml | 19 - grafana/grafana.ini | 921 ----------- 7 files changed, 4 insertions(+), 3515 deletions(-) create mode 100755 doc/oracledb-dashboard.png delete mode 100644 grafana/Dockerfile delete mode 100644 grafana/dashboards/all.yml delete mode 100644 grafana/dashboards/dashboard.json delete mode 100644 grafana/datasources/all.yml delete mode 100644 grafana/grafana.ini diff --git a/README.md b/README.md index ed93b2a..0fe8da1 100644 --- a/README.md +++ b/README.md @@ -458,9 +458,11 @@ ENTRYPOINT ["/oracledb_exporter", "--custom.metrics", "/custom-metrics.toml"] # Grafana dashboards -Some sample Grafana dashboard definitions are provided [in this directory](/grafana). +A sample Grafana dashboard definition is provided [in this directory](/docker-compose/grafana/dashboards). You can import this into your Grafana instance, and set it to use the Prometheus datasource that you have defined for the Prometheus instance that is collecting metrics from the exporter. -An example Grafana dashboard is available [here](https://grafana.com/grafana/dashboards/3333-oracledb/). +The dashboard shows some basic information, as shown below: + +![](doc/oracledb-dashboard.png) # Developer notes diff --git a/doc/oracledb-dashboard.png b/doc/oracledb-dashboard.png new file mode 100755 index 0000000000000000000000000000000000000000..eae1a90a36ea334385be9486ab1879c020c9af4b GIT binary patch literal 111782 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 y@KK!kUaIp2bsa>)3eQv|DC24!u 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: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

OR#cj%2#m!2MWO`*QeQwfUv-@_x5E8?}m!*C? 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 literal 0 HcmV?d00001 diff --git a/grafana/Dockerfile b/grafana/Dockerfile deleted file mode 100644 index bc67ca1..0000000 --- a/grafana/Dockerfile +++ /dev/null @@ -1,15 +0,0 @@ -# -# -# Copyright (c) 2021 Oracle and/or its affiliates. -# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ -# -# - -FROM grafana/grafana:7.5.5 - -COPY dashboards/*.yml /etc/grafana/provisioning/dashboards/ -COPY datasources/*.yml /etc/grafana/provisioning/datasources/ -COPY grafana.ini /etc/grafana/grafana.ini -COPY localhost.crt / -COPY localhost.key / -# COPY dashboards/*.json /var/lib/grafana/dashboards/ diff --git a/grafana/dashboards/all.yml b/grafana/dashboards/all.yml deleted file mode 100644 index fbaa00b..0000000 --- a/grafana/dashboards/all.yml +++ /dev/null @@ -1,14 +0,0 @@ -# -# -# Copyright (c) 2021 Oracle and/or its affiliates. -# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ -# -# - -- name: 'App-specific Oracle Database Monitor' - org_id: 1 - folder: '' - type: 'file' - allowUiUpdates: true - options: - folder: '/var/lib/grafana/grafana_vol' diff --git a/grafana/dashboards/dashboard.json b/grafana/dashboards/dashboard.json deleted file mode 100644 index e763d99..0000000 --- a/grafana/dashboards/dashboard.json +++ /dev/null @@ -1,2544 +0,0 @@ -{ - "annotations": { - "list": [ - { - "builtIn": 1, - "datasource": "-- Grafana --", - "enable": true, - "hide": true, - "iconColor": "rgba(0, 211, 255, 1)", - "name": "Annotations & Alerts", - "type": "dashboard" - } - ] - }, - "editable": true, - "gnetId": null, - "graphTooltip": 0, - "id": 4, - "links": [], - "panels": [ - { - "cacheTimeout": null, - "colorBackground": false, - "colorValue": false, - "colors": [ - "#299c46", - "rgba(237, 129, 40, 0.89)", - "#d44a3a" - ], - "datasource": null, - "format": "none", - "gauge": { - "maxValue": 100, - "minValue": 0, - "show": false, - "thresholdLabels": false, - "thresholdMarkers": true - }, - "gridPos": { - "h": 3, - "w": 4, - "x": 0, - "y": 0 - }, - "id": 16, - "interval": null, - "links": [], - "mappingType": 1, - "mappingTypes": [ - { - "name": "value to text", - "value": 1 - }, - { - "name": "range to text", - "value": 2 - } - ], - "maxDataPoints": 100, - "nullPointMode": "connected", - "nullText": null, - "options": {}, - "postfix": "", - "postfixFontSize": "50%", - "prefix": "", - "prefixFontSize": "50%", - "rangeMaps": [ - { - "from": "null", - "text": "N/A", - "to": "null" - } - ], - "sparkline": { - "fillColor": "rgba(31, 118, 189, 0.18)", - "full": false, - "lineColor": "rgb(31, 120, 193)", - "show": false, - "ymax": null, - "ymin": null - }, - "tableColumn": "host_name", - "targets": [ - { - "expr": "max(oracledb_instance_dummy) by (host_name)", - "format": "table", - "instant": true, - "refId": "A" - } - ], - "thresholds": "", - "timeFrom": null, - "timeShift": null, - "title": "Host Name", - "type": "singlestat", - "valueFontSize": "80%", - "valueMaps": [ - { - "op": "=", - "text": "N/A", - "value": "null" - } - ], - "valueName": "first" - }, - { - "cacheTimeout": null, - "colorBackground": false, - "colorPostfix": false, - "colorPrefix": false, - "colorValue": true, - "colors": [ - "#73BF69", - "rgba(237, 129, 40, 0.89)", - "#d44a3a" - ], - "datasource": null, - "format": "none", - "gauge": { - "maxValue": 100, - "minValue": 0, - "show": false, - "thresholdLabels": false, - "thresholdMarkers": true - }, - "gridPos": { - "h": 3, - "w": 4, - "x": 4, - "y": 0 - }, - "id": 14, - "interval": null, - "links": [], - "mappingType": 1, - "mappingTypes": [ - { - "name": "value to text", - "value": 1 - }, - { - "name": "range to text", - "value": 2 - } - ], - "maxDataPoints": 100, - "nullPointMode": "connected", - "nullText": null, - "options": {}, - "postfix": "", - "postfixFontSize": "50%", - "prefix": "", - "prefixFontSize": "50%", - "rangeMaps": [ - { - "from": "null", - "text": "N/A", - "to": "null" - } - ], - "sparkline": { - "fillColor": "rgba(31, 118, 189, 0.18)", - "full": false, - "lineColor": "rgb(31, 120, 193)", - "show": false, - "ymax": null, - "ymin": null - }, - "tableColumn": "instance_name", - "targets": [ - { - "expr": "max(oracledb_instance_dummy) by (instance_name)", - "format": "table", - "instant": true, - "legendFormat": "", - "refId": "A" - } - ], - "thresholds": "", - "timeFrom": null, - "timeShift": null, - "title": "Instance Name", - "type": "singlestat", - "valueFontSize": "120%", - "valueMaps": [], - "valueName": "current" - }, - { - "cacheTimeout": null, - "colorBackground": false, - "colorPostfix": false, - "colorPrefix": false, - "colorValue": false, - "colors": [ - "#73BF69", - "rgba(237, 129, 40, 0.89)", - "#d44a3a" - ], - "datasource": null, - "format": "none", - "gauge": { - "maxValue": 100, - "minValue": 0, - "show": false, - "thresholdLabels": false, - "thresholdMarkers": true - }, - "gridPos": { - "h": 3, - "w": 5, - "x": 8, - "y": 0 - }, - "id": 18, - "interval": null, - "links": [], - "mappingType": 1, - "mappingTypes": [ - { - "name": "value to text", - "value": 1 - }, - { - "name": "range to text", - "value": 2 - } - ], - "maxDataPoints": 100, - "nullPointMode": "connected", - "nullText": null, - "options": {}, - "postfix": "", - "postfixFontSize": "50%", - "prefix": "", - "prefixFontSize": "50%", - "rangeMaps": [ - { - "from": "null", - "text": "N/A", - "to": "null" - } - ], - "sparkline": { - "fillColor": "rgba(31, 118, 189, 0.18)", - "full": false, - "lineColor": "rgb(31, 120, 193)", - "show": false, - "ymax": null, - "ymin": null - }, - "tableColumn": "uptime", - "targets": [ - { - "expr": "max(oracledb_instance_dummy) by (uptime)", - "format": "table", - "instant": true, - "legendFormat": "", - "refId": "A" - } - ], - "thresholds": "", - "timeFrom": null, - "timeShift": null, - "title": "Uptime", - "type": "singlestat", - "valueFontSize": "70%", - "valueMaps": [], - "valueName": "current" - }, - { - "cacheTimeout": null, - "colorBackground": false, - "colorPostfix": false, - "colorPrefix": false, - "colorValue": true, - "colors": [ - "#73BF69", - "rgba(237, 129, 40, 0.89)", - "#d44a3a" - ], - "datasource": null, - "format": "none", - "gauge": { - "maxValue": 100, - "minValue": 0, - "show": false, - "thresholdLabels": false, - "thresholdMarkers": true - }, - "gridPos": { - "h": 3, - "w": 5, - "x": 13, - "y": 0 - }, - "id": 17, - "interval": null, - "links": [], - "mappingType": 1, - "mappingTypes": [ - { - "name": "value to text", - "value": 1 - }, - { - "name": "range to text", - "value": 2 - } - ], - "maxDataPoints": 100, - "nullPointMode": "connected", - "nullText": null, - "options": {}, - "postfix": "", - "postfixFontSize": "50%", - "prefix": "", - "prefixFontSize": "50%", - "rangeMaps": [ - { - "from": "null", - "text": "N/A", - "to": "null" - } - ], - "sparkline": { - "fillColor": "rgba(31, 118, 189, 0.18)", - "full": false, - "lineColor": "rgb(31, 120, 193)", - "show": false, - "ymax": null, - "ymin": null - }, - "tableColumn": "stime", - "targets": [ - { - "expr": "max(oracledb_instance_dummy) by (stime)", - "format": "table", - "instant": true, - "legendFormat": "", - "refId": "A" - } - ], - "thresholds": "", - "timeFrom": null, - "timeShift": null, - "title": "Startup Time", - "type": "singlestat", - "valueFontSize": "80%", - "valueMaps": [], - "valueName": "current" - }, - { - "cacheTimeout": null, - "colorBackground": false, - "colorValue": true, - "colors": [ - "#73BF69", - "rgba(237, 129, 40, 0.89)", - "rgba(50, 172, 45, 0.97)" - ], - "datasource": "Prometheus", - "decimals": 1, - "editable": true, - "error": false, - "format": "s", - "gauge": { - "maxValue": 100, - "minValue": 0, - "show": false, - "thresholdLabels": false, - "thresholdMarkers": true - }, - "gridPos": { - "h": 3, - "w": 3, - "x": 18, - "y": 0 - }, - "height": "125px", - "id": 2, - "interval": "", - "links": [], - "mappingType": 1, - "mappingTypes": [ - { - "name": "value to text", - "value": 1 - }, - { - "name": "range to text", - "value": 2 - } - ], - "maxDataPoints": 100, - "nullPointMode": "connected", - "nullText": null, - "options": {}, - "postfix": "", - "postfixFontSize": "50%", - "prefix": "", - "prefixFontSize": "70%", - "rangeMaps": [ - { - "from": "null", - "text": "N/A", - "to": "null" - } - ], - "sparkline": { - "fillColor": "rgba(31, 118, 189, 0.18)", - "full": false, - "lineColor": "rgb(31, 120, 193)", - "show": false - }, - "tableColumn": "", - "targets": [ - { - "calculatedInterval": "10m", - "datasourceErrors": {}, - "errors": {}, - "expr": "oracledb_up", - "format": "time_series", - "hide": false, - "instant": true, - "interval": "", - "intervalFactor": 1, - "legendFormat": "", - "metric": "", - "refId": "A", - "step": 300 - } - ], - "thresholds": "", - "title": "System Status", - "type": "singlestat", - "valueFontSize": "120%", - "valueMaps": [ - { - "op": "=", - "text": "UP", - "value": "1" - }, - { - "op": "=", - "text": "DOWN", - "value": "0" - } - ], - "valueName": "current" - }, - { - "cacheTimeout": null, - "colorBackground": false, - "colorValue": true, - "colors": [ - "#37872D", - "rgba(237, 129, 40, 0.89)", - "rgba(50, 172, 45, 0.97)" - ], - "datasource": "Prometheus", - "editable": true, - "error": false, - "format": "none", - "gauge": { - "maxValue": 100, - "minValue": 0, - "show": false, - "thresholdLabels": false, - "thresholdMarkers": true - }, - "gridPos": { - "h": 4, - "w": 6, - "x": 0, - "y": 3 - }, - "height": "125px", - "id": 4, - "interval": "", - "links": [], - "mappingType": 1, - "mappingTypes": [ - { - "name": "value to text", - "value": 1 - }, - { - "name": "range to text", - "value": 2 - } - ], - "maxDataPoints": 100, - "nullPointMode": "connected", - "nullText": null, - "options": {}, - "postfix": "", - "postfixFontSize": "100%", - "prefix": "", - "prefixFontSize": "100%", - "rangeMaps": [ - { - "from": "null", - "text": "N/A", - "to": "null" - } - ], - "sparkline": { - "fillColor": "rgba(31, 118, 189, 0.18)", - "full": false, - "lineColor": "rgb(31, 120, 193)", - "show": false - }, - "tableColumn": "", - "targets": [ - { - "expr": "oracledb_os_value{stat_name=\"NUM_CPUS\"}", - "format": "time_series", - "instant": true, - "interval": "", - "intervalFactor": 1, - "legendFormat": "", - "refId": "A", - "step": 300 - } - ], - "thresholds": "", - "title": "Number of CPUs", - "type": "singlestat", - "valueFontSize": "120%", - "valueMaps": [ - { - "op": "=", - "text": "N/A", - "value": "null" - } - ], - "valueName": "current" - }, - { - "datasource": null, - "gridPos": { - "h": 8, - "w": 6, - "x": 6, - "y": 3 - }, - "id": 33, - "options": { - "fieldOptions": { - "calcs": [ - "lastNotNull" - ], - "defaults": { - "mappings": [], - "max": 1, - "min": 0, - "thresholds": [ - { - "color": "green", - "value": null - }, - { - "color": "#EAB839", - "value": 0.5 - }, - { - "color": "red", - "value": 0.8 - } - ], - "title": "", - "unit": "percentunit" - }, - "override": {}, - "values": false - }, - "orientation": "auto", - "showThresholdLabels": true, - "showThresholdMarkers": true - }, - "pluginVersion": "6.4.4", - "targets": [ - { - "expr": "rate(oracledb_os_value{stat_name=\"BUSY_TIME\"}[2m]) / ignoring(stat_name) oracledb_os_value{stat_name=\"NUM_CPUS\"} / ignoring(stat_name) rate(job:os:total_cpu_elpased_time[2m])", - "instant": true, - "refId": "A" - } - ], - "timeFrom": null, - "timeShift": null, - "title": "DB CPU Usage", - "type": "gauge" - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": null, - "decimals": 2, - "fill": 6, - "fillGradient": 2, - "gridPos": { - "h": 12, - "w": 12, - "x": 12, - "y": 3 - }, - "id": 8, - "legend": { - "alignAsTable": true, - "avg": true, - "current": true, - "hideEmpty": false, - "hideZero": false, - "max": true, - "min": true, - "rightSide": false, - "show": true, - "sideWidth": null, - "total": false, - "values": true - }, - "lines": true, - "linewidth": 1, - "nullPointMode": "null", - "options": { - "dataLinks": [] - }, - "percentage": false, - "pointradius": 2, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "expr": "rate(oracledb_os_value{stat_name=\"BUSY_TIME\"}[2m]) / ignoring(stat_name) oracledb_os_value{stat_name=\"NUM_CPUS\"} / ignoring(stat_name) rate(job:os:total_cpu_elpased_time[2m])", - "instant": false, - "intervalFactor": 1, - "legendFormat": "busy", - "refId": "A" - }, - { - "expr": "rate(oracledb_os_value{stat_name=\"IOWAIT_TIME\"}[2m]) / ignoring(stat_name) rate(job:os:total_cpu_elpased_time[2m])", - "legendFormat": "iowait", - "refId": "B" - }, - { - "expr": "rate(oracledb_os_value{stat_name=\"SYS_TIME\"}[2m]) / ignoring(stat_name) rate(job:os:total_cpu_elpased_time[2m])", - "legendFormat": "sys", - "refId": "C" - }, - { - "expr": "rate(oracledb_os_value{stat_name=\"USER_TIME\"}[2m]) / ignoring(stat_name) rate(job:os:total_cpu_elpased_time[2m])", - "legendFormat": "user", - "refId": "D" - }, - { - "expr": "rate(oracledb_os_value{stat_name=\"NICE_TIME\"}[2m]) / ignoring(stat_name) rate(job:os:total_cpu_elpased_time[2m])", - "legendFormat": "nice", - "refId": "E" - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "CPU Usage", - "tooltip": { - "shared": true, - "sort": 2, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "decimals": 2, - "format": "percentunit", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - }, - { - "decimals": 5, - "format": "percent", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": false - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - }, - { - "cacheTimeout": null, - "colorBackground": false, - "colorValue": true, - "colors": [ - "rgba(50, 172, 45, 0.97)", - "rgba(237, 129, 40, 0.89)", - "#FADE2A" - ], - "datasource": "Prometheus", - "editable": true, - "error": false, - "format": "none", - "gauge": { - "maxValue": 100, - "minValue": 0, - "show": false, - "thresholdLabels": false, - "thresholdMarkers": true - }, - "gridPos": { - "h": 4, - "w": 6, - "x": 0, - "y": 7 - }, - "height": "125px", - "id": 11, - "interval": "", - "links": [], - "mappingType": 1, - "mappingTypes": [ - { - "name": "value to text", - "value": 1 - }, - { - "name": "range to text", - "value": 2 - } - ], - "maxDataPoints": 100, - "nullPointMode": "connected", - "nullText": null, - "options": {}, - "postfix": "", - "postfixFontSize": "50%", - "prefix": "", - "prefixFontSize": "80%", - "rangeMaps": [ - { - "from": "null", - "text": "N/A", - "to": "null" - } - ], - "sparkline": { - "fillColor": "rgba(31, 118, 189, 0.18)", - "full": false, - "lineColor": "rgb(31, 120, 193)", - "show": false - }, - "tableColumn": "", - "targets": [ - { - "expr": "oracledb_os_value{stat_name=\"NUM_CPU_CORES\"}", - "format": "time_series", - "instant": true, - "interval": "", - "intervalFactor": 1, - "legendFormat": "", - "refId": "A", - "step": 300 - } - ], - "thresholds": "", - "title": "Number of CPU Cores", - "type": "singlestat", - "valueFontSize": "120%", - "valueMaps": [ - { - "op": "=", - "text": "N/A", - "value": "null" - } - ], - "valueName": "current" - }, - { - "cacheTimeout": null, - "colorBackground": false, - "colorValue": false, - "colors": [ - "#73BF69", - "rgba(237, 129, 40, 0.89)", - "#d44a3a" - ], - "datasource": null, - "decimals": null, - "format": "bytes", - "gauge": { - "maxValue": 100, - "minValue": 0, - "show": false, - "thresholdLabels": false, - "thresholdMarkers": true - }, - "gridPos": { - "h": 4, - "w": 6, - "x": 0, - "y": 11 - }, - "id": 35, - "interval": null, - "links": [], - "mappingType": 1, - "mappingTypes": [ - { - "name": "value to text", - "value": 1 - }, - { - "name": "range to text", - "value": 2 - } - ], - "maxDataPoints": 100, - "nullPointMode": "connected", - "nullText": null, - "options": {}, - "postfix": "", - "postfixFontSize": "50%", - "prefix": "", - "prefixFontSize": "50%", - "rangeMaps": [ - { - "from": "null", - "text": "N/A", - "to": "null" - } - ], - "sparkline": { - "fillColor": "rgba(31, 118, 189, 0.18)", - "full": false, - "lineColor": "rgb(31, 120, 193)", - "show": false, - "ymax": null, - "ymin": null - }, - "tableColumn": "", - "targets": [ - { - "expr": "oracledb_os_value{stat_name=\"PHYSICAL_MEMORY_BYTES\"} ", - "instant": true, - "refId": "A" - } - ], - "thresholds": "", - "timeFrom": null, - "timeShift": null, - "title": "Total Physical Memory(OS)", - "type": "singlestat", - "valueFontSize": "150%", - "valueMaps": [ - { - "op": "=", - "text": "N/A", - "value": "null" - } - ], - "valueName": "current" - }, - { - "cacheTimeout": null, - "colorBackground": false, - "colorValue": false, - "colors": [ - "#73BF69", - "rgba(237, 129, 40, 0.89)", - "#d44a3a" - ], - "datasource": null, - "format": "bytes", - "gauge": { - "maxValue": 100, - "minValue": 0, - "show": false, - "thresholdLabels": false, - "thresholdMarkers": true - }, - "gridPos": { - "h": 4, - "w": 6, - "x": 6, - "y": 11 - }, - "id": 39, - "interval": null, - "links": [], - "mappingType": 1, - "mappingTypes": [ - { - "name": "value to text", - "value": 1 - }, - { - "name": "range to text", - "value": 2 - } - ], - "maxDataPoints": 100, - "nullPointMode": "connected", - "nullText": null, - "options": {}, - "postfix": "", - "postfixFontSize": "50%", - "prefix": "", - "prefixFontSize": "50%", - "rangeMaps": [ - { - "from": "null", - "text": "N/A", - "to": "null" - } - ], - "sparkline": { - "fillColor": "rgba(31, 118, 189, 0.18)", - "full": false, - "lineColor": "rgb(31, 120, 193)", - "show": false, - "ymax": null, - "ymin": null - }, - "tableColumn": "", - "targets": [ - { - "expr": "oracledb_sga_total_b", - "instant": true, - "refId": "A" - } - ], - "thresholds": "", - "timeFrom": null, - "timeShift": null, - "title": "SGA Total Memory", - "type": "singlestat", - "valueFontSize": "120%", - "valueMaps": [ - { - "op": "=", - "text": "N/A", - "value": "null" - } - ], - "valueName": "current" - }, - { - "cacheTimeout": null, - "datasource": "Prometheus", - "gridPos": { - "h": 7, - "w": 6, - "x": 0, - "y": 15 - }, - "id": 12, - "links": [], - "options": { - "fieldOptions": { - "calcs": [ - "lastNotNull" - ], - "defaults": { - "mappings": [], - "max": 1, - "min": 0, - "nullValueMode": "connected", - "thresholds": [ - { - "color": "green", - "value": null - }, - { - "color": "#EAB839", - "value": 0.5 - }, - { - "color": "red", - "value": 0.8 - } - ], - "unit": "percentunit" - }, - "override": {}, - "values": false - }, - "orientation": "horizontal", - "showThresholdLabels": true, - "showThresholdMarkers": true - }, - "pluginVersion": "6.4.4", - "targets": [ - { - "expr": "oracledb_os_value{stat_name=\"FREE_MEMORY_BYTES\"} / ignoring(stat_name) oracledb_os_value{stat_name=\"PHYSICAL_MEMORY_BYTES\"}", - "format": "time_series", - "instant": true, - "interval": "", - "intervalFactor": 1, - "legendFormat": "", - "refId": "A", - "step": 300 - } - ], - "title": "Memory Usage(OS)", - "type": "gauge" - }, - { - "datasource": null, - "gridPos": { - "h": 7, - "w": 6, - "x": 6, - "y": 15 - }, - "id": 41, - "options": { - "fieldOptions": { - "calcs": [ - "lastNotNull" - ], - "defaults": { - "mappings": [], - "max": 1, - "min": 0, - "thresholds": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 0.8 - } - ], - "unit": "percentunit" - }, - "override": {}, - "values": false - }, - "orientation": "auto", - "showThresholdLabels": true, - "showThresholdMarkers": true - }, - "pluginVersion": "6.4.4", - "targets": [ - { - "expr": "oracledb_sga_used_b / ignoring(inst_id) oracledb_sga_total_b", - "instant": true, - "refId": "A" - } - ], - "timeFrom": null, - "timeShift": null, - "title": "SGA Memory Usage", - "type": "gauge" - }, - { - "aliasColors": { - "Load 1m": "dark-orange" - }, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": null, - "fill": 8, - "fillGradient": 0, - "gridPos": { - "h": 7, - "w": 12, - "x": 12, - "y": 15 - }, - "id": 20, - "interval": "", - "legend": { - "alignAsTable": true, - "avg": true, - "current": true, - "max": true, - "min": true, - "rightSide": false, - "show": true, - "total": false, - "values": true - }, - "lines": true, - "linewidth": 1, - "nullPointMode": "null", - "options": { - "dataLinks": [] - }, - "percentage": false, - "pointradius": 2, - "points": false, - "renderer": "flot", - "seriesOverrides": [ - { - "alias": "sga free", - "color": "#1F60C4" - }, - { - "alias": "sga used", - "color": "#8AB8FF" - } - ], - "spaceLength": 10, - "stack": true, - "steppedLine": false, - "targets": [ - { - "expr": "oracledb_sga_free_b", - "interval": "", - "legendFormat": "sga free", - "refId": "A" - }, - { - "expr": "oracledb_sga_used_b", - "legendFormat": "sga used", - "refId": "B" - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "SGA Memory", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "format": "decbytes", - "label": "", - "logBase": 1, - "max": null, - "min": "0", - "show": true - }, - { - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": false - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - }, - { - "cacheTimeout": null, - "colorBackground": false, - "colorValue": true, - "colors": [ - "#73BF69", - "rgba(237, 129, 40, 0.89)", - "#d44a3a" - ], - "datasource": null, - "format": "none", - "gauge": { - "maxValue": 100, - "minValue": 0, - "show": false, - "thresholdLabels": false, - "thresholdMarkers": true - }, - "gridPos": { - "h": 3, - "w": 6, - "x": 0, - "y": 22 - }, - "id": 28, - "interval": null, - "links": [], - "mappingType": 1, - "mappingTypes": [ - { - "name": "value to text", - "value": 1 - }, - { - "name": "range to text", - "value": 2 - } - ], - "maxDataPoints": 100, - "nullPointMode": "connected", - "nullText": null, - "options": {}, - "postfix": "", - "postfixFontSize": "50%", - "prefix": "", - "prefixFontSize": "50%", - "rangeMaps": [ - { - "from": "null", - "text": "N/A", - "to": "null" - } - ], - "sparkline": { - "fillColor": "rgba(31, 118, 189, 0.18)", - "full": false, - "lineColor": "rgb(31, 120, 193)", - "show": true, - "ymax": null, - "ymin": null - }, - "tableColumn": "", - "targets": [ - { - "expr": "oracledb_session_value{status=\"ACTIVE\", type=\"USER\"}", - "instant": true, - "refId": "A" - } - ], - "thresholds": "", - "timeFrom": null, - "timeShift": null, - "title": "Active User Session", - "type": "singlestat", - "valueFontSize": "150%", - "valueMaps": [ - { - "op": "=", - "text": "N/A", - "value": "null" - } - ], - "valueName": "current" - }, - { - "columns": [], - "datasource": null, - "fontSize": "100%", - "gridPos": { - "h": 9, - "w": 6, - "x": 6, - "y": 22 - }, - "id": 43, - "options": {}, - "pageSize": null, - "showHeader": true, - "sort": { - "col": 2, - "desc": false - }, - "styles": [ - { - "alias": "Time", - "dateFormat": "YYYY-MM-DD HH:mm:ss", - "pattern": "Time", - "type": "date" - }, - { - "alias": "", - "colorMode": null, - "colors": [ - "rgba(245, 54, 54, 0.9)", - "rgba(237, 129, 40, 0.89)", - "rgba(50, 172, 45, 0.97)" - ], - "decimals": 2, - "pattern": "/.*/", - "thresholds": [], - "type": "number", - "unit": "short" - } - ], - "targets": [ - { - "expr": "oracledb_session_mvalue", - "format": "time_series", - "instant": true, - "intervalFactor": 1, - "legendFormat": "{{machine}}", - "refId": "A" - } - ], - "timeFrom": null, - "timeShift": null, - "title": "Sessions by Machine", - "transform": "timeseries_to_rows", - "type": "table" - }, - { - "aliasColors": { - "Load 1m": "dark-orange" - }, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": null, - "fill": 5, - "fillGradient": 0, - "gridPos": { - "h": 7, - "w": 12, - "x": 12, - "y": 22 - }, - "id": 26, - "interval": "", - "legend": { - "alignAsTable": true, - "avg": true, - "current": true, - "max": true, - "min": true, - "rightSide": false, - "show": true, - "total": false, - "values": true - }, - "lines": true, - "linewidth": 1, - "nullPointMode": "null", - "options": { - "dataLinks": [] - }, - "percentage": false, - "pointradius": 2, - "points": false, - "renderer": "flot", - "seriesOverrides": [ - { - "alias": "read", - "color": "#96D98D" - }, - { - "alias": "write", - "color": "#FADE2A" - } - ], - "spaceLength": 10, - "stack": true, - "steppedLine": false, - "targets": [ - { - "expr": "rate(oracledb_system_value{name=\"physical read total bytes\"}[5m])", - "instant": false, - "interval": "", - "legendFormat": "read", - "refId": "A" - }, - { - "expr": "rate(oracledb_system_value{name=\"physical write total bytes\"}[5m])", - "instant": false, - "legendFormat": "write", - "refId": "B" - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Total Physical Read/Write Rate", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "format": "Bps", - "label": "", - "logBase": 1, - "max": null, - "min": null, - "show": true - }, - { - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": false - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - }, - { - "cacheTimeout": null, - "colorBackground": false, - "colorValue": true, - "colors": [ - "#5794F2", - "rgba(237, 129, 40, 0.89)", - "#d44a3a" - ], - "datasource": null, - "format": "none", - "gauge": { - "maxValue": 100, - "minValue": 0, - "show": false, - "thresholdLabels": false, - "thresholdMarkers": true - }, - "gridPos": { - "h": 3, - "w": 6, - "x": 0, - "y": 25 - }, - "id": 30, - "interval": null, - "links": [], - "mappingType": 1, - "mappingTypes": [ - { - "name": "value to text", - "value": 1 - }, - { - "name": "range to text", - "value": 2 - } - ], - "maxDataPoints": 100, - "nullPointMode": "connected", - "nullText": null, - "options": {}, - "postfix": "", - "postfixFontSize": "50%", - "prefix": "", - "prefixFontSize": "50%", - "rangeMaps": [ - { - "from": "null", - "text": "N/A", - "to": "null" - } - ], - "sparkline": { - "fillColor": "rgba(31, 118, 189, 0.18)", - "full": false, - "lineColor": "rgb(31, 120, 193)", - "show": true, - "ymax": null, - "ymin": null - }, - "tableColumn": "", - "targets": [ - { - "expr": "oracledb_session_value{status=\"INACTIVE\", type=\"USER\"}", - "instant": true, - "refId": "A" - } - ], - "thresholds": "", - "timeFrom": null, - "timeShift": null, - "title": "Inactive User Session", - "type": "singlestat", - "valueFontSize": "150%", - "valueMaps": [ - { - "op": "=", - "text": "N/A", - "value": "null" - } - ], - "valueName": "current" - }, - { - "cacheTimeout": null, - "colorBackground": false, - "colorValue": true, - "colors": [ - "#FF9830", - "rgba(237, 129, 40, 0.89)", - "#d44a3a" - ], - "datasource": null, - "format": "none", - "gauge": { - "maxValue": 100, - "minValue": 0, - "show": false, - "thresholdLabels": false, - "thresholdMarkers": true - }, - "gridPos": { - "h": 3, - "w": 6, - "x": 0, - "y": 28 - }, - "id": 29, - "interval": null, - "links": [], - "mappingType": 1, - "mappingTypes": [ - { - "name": "value to text", - "value": 1 - }, - { - "name": "range to text", - "value": 2 - } - ], - "maxDataPoints": 100, - "nullPointMode": "connected", - "nullText": null, - "options": {}, - "postfix": "", - "postfixFontSize": "50%", - "prefix": "", - "prefixFontSize": "50%", - "rangeMaps": [ - { - "from": "null", - "text": "N/A", - "to": "null" - } - ], - "sparkline": { - "fillColor": "rgba(31, 118, 189, 0.18)", - "full": false, - "lineColor": "rgb(31, 120, 193)", - "show": true, - "ymax": null, - "ymin": null - }, - "tableColumn": "", - "targets": [ - { - "expr": "oracledb_session_value{status=\"ACTIVE\", type=\"BACKGROUND\"}", - "instant": true, - "refId": "A" - } - ], - "thresholds": "", - "timeFrom": null, - "timeShift": null, - "title": "Active Background Session", - "type": "singlestat", - "valueFontSize": "150%", - "valueMaps": [ - { - "op": "=", - "text": "N/A", - "value": "null" - } - ], - "valueName": "current" - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": null, - "fill": 2, - "fillGradient": 0, - "gridPos": { - "h": 7, - "w": 12, - "x": 12, - "y": 29 - }, - "id": 37, - "legend": { - "alignAsTable": true, - "avg": true, - "current": true, - "max": true, - "min": true, - "show": true, - "total": false, - "values": true - }, - "lines": true, - "linewidth": 1, - "nullPointMode": "null", - "options": { - "dataLinks": [] - }, - "percentage": false, - "pointradius": 2, - "points": false, - "renderer": "flot", - "seriesOverrides": [ - { - "alias": "read", - "color": "#C4162A" - }, - { - "alias": "write", - "color": "#37872D" - } - ], - "spaceLength": 10, - "stack": true, - "steppedLine": false, - "targets": [ - { - "expr": "rate(oracledb_system_value{name=\"physical read total IO requests\"}[5m])", - "legendFormat": "read", - "refId": "A" - }, - { - "expr": "rate(oracledb_system_value{name=\"physical write total IO requests\"}[5m])", - "legendFormat": "write", - "refId": "B" - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Total Physical Read/Write IO Request Rate", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - }, - { - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": null, - "fill": 2, - "fillGradient": 0, - "gridPos": { - "h": 10, - "w": 12, - "x": 0, - "y": 31 - }, - "id": 45, - "legend": { - "alignAsTable": true, - "avg": true, - "current": true, - "max": true, - "min": true, - "rightSide": false, - "show": true, - "total": false, - "values": true - }, - "lines": true, - "linewidth": 1, - "nullPointMode": "null", - "options": { - "dataLinks": [] - }, - "percentage": false, - "pointradius": 2, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "expr": "rate(oracledb_system_value{name=\"user calls\"}[2m])", - "legendFormat": "{{name}}", - "refId": "A" - }, - { - "expr": "rate(oracledb_system_value{name=\"user commits\"}[2m])", - "legendFormat": "{{name}}", - "refId": "B" - }, - { - "expr": "rate(oracledb_system_value{name=\"user rollbacks\"}[2m])", - "legendFormat": "{{name}}", - "refId": "C" - }, - { - "expr": "rate(oracledb_system_value{name=\"execute count\"}[2m])", - "legendFormat": "{{name}}", - "refId": "D" - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "User Activity Rate", - "tooltip": { - "shared": false, - "sort": 2, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "format": "none", - "label": "requests per minute", - "logBase": 1, - "max": null, - "min": null, - "show": true - }, - { - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - }, - { - "aliasColors": { - "Load 1m": "dark-orange" - }, - "bars": true, - "dashLength": 10, - "dashes": false, - "datasource": null, - "decimals": 2, - "fill": 6, - "fillGradient": 2, - "gridPos": { - "h": 16, - "w": 12, - "x": 12, - "y": 36 - }, - "id": 24, - "interval": "", - "legend": { - "alignAsTable": true, - "avg": true, - "current": false, - "max": true, - "min": true, - "rightSide": false, - "show": true, - "total": false, - "values": true - }, - "lines": false, - "linewidth": 2, - "nullPointMode": "null", - "options": { - "dataLinks": [] - }, - "percentage": false, - "pluginVersion": "6.4.4", - "pointradius": 2, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": true, - "steppedLine": false, - "targets": [ - { - "expr": "increase(oracledb_system_value{name=\"execute count\"}[1h])", - "instant": false, - "interval": "1h", - "legendFormat": "execute count", - "refId": "A" - }, - { - "expr": "increase(oracledb_system_value{name=\"user calls\"}[1h])", - "instant": false, - "interval": "1h", - "legendFormat": "user calls", - "refId": "B" - }, - { - "expr": "increase(oracledb_system_value{name=\"user commits\"}[1h])", - "interval": "1h", - "legendFormat": "user commits", - "refId": "C" - }, - { - "expr": "increase(oracledb_system_value{name=\"user rollbacks\"}[1h])", - "interval": "1h", - "legendFormat": "rollbacks", - "refId": "D" - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "User Activity Hourly", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "min": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "format": "short", - "label": "", - "logBase": 1, - "max": null, - "min": "0", - "show": true - }, - { - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": "0", - "show": true - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - }, - { - "aliasColors": { - "Load 1m": "dark-orange" - }, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": null, - "fill": 1, - "fillGradient": 0, - "gridPos": { - "h": 9, - "w": 12, - "x": 0, - "y": 41 - }, - "id": 21, - "interval": "", - "legend": { - "alignAsTable": true, - "avg": true, - "current": false, - "max": true, - "min": true, - "rightSide": false, - "show": true, - "total": false, - "values": true - }, - "lines": true, - "linewidth": 1, - "nullPointMode": "null", - "options": { - "dataLinks": [] - }, - "percentage": false, - "pointradius": 2, - "points": false, - "renderer": "flot", - "seriesOverrides": [ - { - "alias": "inbound", - "color": "#F2495C" - }, - { - "alias": "outbound", - "color": "#B877D9" - } - ], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "expr": "irate(oracledb_network_received_from_client_b[5m])", - "interval": "", - "legendFormat": "inbound", - "refId": "A" - }, - { - "expr": "irate(oracledb_network_sent_to_client_b[5m])", - "legendFormat": "outbound", - "refId": "B" - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Network Traffic", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "format": "Bps", - "label": "", - "logBase": 1, - "max": null, - "min": null, - "show": true - }, - { - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": false - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - }, - { - "columns": [ - { - "text": "Current", - "value": "current" - } - ], - "datasource": null, - "fontSize": "110%", - "gridPos": { - "h": 12, - "w": 12, - "x": 0, - "y": 50 - }, - "id": 31, - "options": {}, - "pageSize": null, - "showHeader": true, - "sort": { - "col": null, - "desc": false - }, - "styles": [ - { - "alias": "Time", - "colorMode": null, - "colors": [ - "rgba(245, 54, 54, 0.9)", - "rgba(237, 129, 40, 0.89)", - "rgba(50, 172, 45, 0.97)" - ], - "dateFormat": "YYYY-MM-DD HH:mm:ss", - "decimals": 2, - "mappingType": 1, - "pattern": "Time", - "thresholds": [], - "type": "date", - "unit": "short" - }, - { - "alias": "Instance ID", - "colorMode": "value", - "colors": [ - "#73BF69", - "rgba(237, 129, 40, 0.89)", - "rgba(50, 172, 45, 0.97)" - ], - "decimals": 0, - "pattern": "inst_id", - "thresholds": [], - "type": "number", - "unit": "none" - }, - { - "alias": "123", - "colorMode": null, - "colors": [ - "rgba(245, 54, 54, 0.9)", - "rgba(237, 129, 40, 0.89)", - "rgba(50, 172, 45, 0.97)" - ], - "dateFormat": "YYYY-MM-DD HH:mm:ss", - "decimals": 2, - "mappingType": 1, - "pattern": "Current", - "thresholds": [], - "type": "number", - "unit": "short" - } - ], - "targets": [ - { - "expr": "oracledb_system_wait_time_waited", - "format": "time_series", - "instant": true, - "interval": "", - "intervalFactor": 1, - "legendFormat": "{{wait_class}}", - "refId": "A" - } - ], - "timeFrom": null, - "timeShift": null, - "title": "System: Amount of time spent in the wait class", - "transform": "timeseries_aggregations", - "type": "table" - }, - { - "aliasColors": { - "Load 1m": "dark-orange" - }, - "bars": true, - "dashLength": 10, - "dashes": false, - "datasource": null, - "fill": 1, - "fillGradient": 0, - "gridPos": { - "h": 10, - "w": 12, - "x": 12, - "y": 52 - }, - "id": 25, - "interval": "", - "legend": { - "alignAsTable": true, - "avg": true, - "current": false, - "max": true, - "min": true, - "rightSide": false, - "show": true, - "total": false, - "values": true - }, - "lines": false, - "linewidth": 1, - "nullPointMode": "null", - "options": { - "dataLinks": [] - }, - "percentage": false, - "pointradius": 2, - "points": false, - "renderer": "flot", - "seriesOverrides": [ - { - "alias": "inbound", - "color": "#F2495C" - }, - { - "alias": "outbound", - "color": "#B877D9" - } - ], - "spaceLength": 10, - "stack": true, - "steppedLine": false, - "targets": [ - { - "expr": "increase(oracledb_network_received_from_client_b[1h])", - "instant": false, - "interval": "1h", - "legendFormat": "inbound", - "refId": "A" - }, - { - "expr": "increase(oracledb_network_sent_to_client_b[1h])", - "instant": false, - "interval": "1h", - "legendFormat": "outbound", - "refId": "B" - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Network Traffic Hourly", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "format": "decbytes", - "label": "", - "logBase": 1, - "max": null, - "min": null, - "show": true - }, - { - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": false - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - }, - { - "columns": [ - { - "text": "Current", - "value": "current" - } - ], - "datasource": null, - "fontSize": "100%", - "gridPos": { - "h": 20, - "w": 24, - "x": 0, - "y": 62 - }, - "id": 47, - "options": {}, - "pageSize": null, - "showHeader": true, - "sort": { - "col": 0, - "desc": true - }, - "styles": [ - { - "alias": "Time", - "dateFormat": "YYYY-MM-DD HH:mm:ss", - "link": false, - "pattern": "Time", - "type": "date" - }, - { - "alias": "", - "colorMode": null, - "colors": [ - "#73BF69", - "rgba(237, 129, 40, 0.89)", - "rgba(50, 172, 45, 0.97)" - ], - "decimals": null, - "pattern": "inst_id", - "thresholds": [], - "type": "number", - "unit": "none" - } - ], - "targets": [ - { - "expr": "oracledb_session_wait_seconds_in_wait", - "format": "table", - "instant": true, - "interval": "", - "legendFormat": "", - "refId": "A" - } - ], - "timeFrom": null, - "timeShift": null, - "title": "System: Amount of time spent in the wait class", - "transform": "table", - "type": "table" - }, - { - "aliasColors": { - "Load 1m": "dark-orange" - }, - "bars": true, - "dashLength": 10, - "dashes": false, - "datasource": null, - "fill": 1, - "fillGradient": 0, - "gridPos": { - "h": 9, - "w": 12, - "x": 0, - "y": 82 - }, - "id": 22, - "interval": "", - "legend": { - "alignAsTable": true, - "avg": true, - "current": false, - "max": true, - "min": true, - "rightSide": false, - "show": true, - "total": false, - "values": true - }, - "lines": true, - "linewidth": 2, - "nullPointMode": "null", - "options": { - "dataLinks": [] - }, - "percentage": false, - "pointradius": 2, - "points": false, - "renderer": "flot", - "seriesOverrides": [ - { - "alias": "active background", - "color": "#C4162A" - }, - { - "alias": "active user", - "color": "#FA6400" - }, - { - "alias": "inactive user", - "color": "rgb(19, 195, 189)" - } - ], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "expr": "oracledb_session_value{status=\"ACTIVE\", type=\"BACKGROUND\"}", - "interval": "", - "legendFormat": "active background", - "refId": "A" - }, - { - "expr": "oracledb_session_value{status=\"ACTIVE\", type=\"USER\"}", - "legendFormat": "active user", - "refId": "B" - }, - { - "expr": "oracledb_session_value{status=\"INACTIVE\", type=\"USER\"}", - "legendFormat": "inactive user", - "refId": "C" - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Processes", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "format": "none", - "label": "", - "logBase": 1, - "max": null, - "min": "0", - "show": true - }, - { - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": false - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - }, - { - "aliasColors": { - "Load 1m": "dark-orange" - }, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": null, - "description": "Number of processes running or waiting on the run queue", - "fill": 3, - "fillGradient": 0, - "gridPos": { - "h": 8, - "w": 12, - "x": 0, - "y": 91 - }, - "id": 10, - "interval": "", - "legend": { - "alignAsTable": true, - "avg": true, - "current": false, - "max": true, - "min": true, - "rightSide": false, - "show": true, - "total": false, - "values": true - }, - "lines": true, - "linewidth": 1, - "nullPointMode": "null", - "options": { - "dataLinks": [] - }, - "percentage": false, - "pointradius": 2, - "points": false, - "renderer": "flot", - "seriesOverrides": [ - { - "alias": "Load 1m", - "color": "rgb(248, 80, 0)" - }, - { - "alias": "Load 5m", - "color": "#FF9830" - }, - { - "alias": "Load 15m", - "color": "#FADE2A" - } - ], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "expr": " sum(sum_over_time(oracledb_os_value{stat_name=\"LOAD\"}[1m])) / sum(count_over_time(oracledb_os_value{stat_name=\"LOAD\"}[1m]))", - "interval": "", - "legendFormat": "Load 1m", - "refId": "A" - }, - { - "expr": " sum(sum_over_time(oracledb_os_value{stat_name=\"LOAD\"}[5m])) / sum(count_over_time(oracledb_os_value{stat_name=\"LOAD\"}[5m]))", - "legendFormat": "Load 5m", - "refId": "B" - }, - { - "expr": " sum(sum_over_time(oracledb_os_value{stat_name=\"LOAD\"}[15m])) / sum(count_over_time(oracledb_os_value{stat_name=\"LOAD\"}[15m]))", - "legendFormat": "Load 15m", - "refId": "C" - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Process Load", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "format": "short", - "label": "", - "logBase": 1, - "max": null, - "min": null, - "show": true - }, - { - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - } - ], - "refresh": "30s", - "schemaVersion": 20, - "style": "dark", - "tags": [], - "templating": { - "list": [] - }, - "time": { - "from": "now-6h", - "to": "now" - }, - "timepicker": { - "refresh_intervals": [ - "5s", - "10s", - "30s", - "1m", - "5m", - "15m", - "30m", - "1h", - "2h", - "1d" - ] - }, - "timezone": "browser", - "title": "Oracle App-Specific -- Database System Level -- Update", - "uid": "fjZKiJbgz", - "version": 3 -} diff --git a/grafana/datasources/all.yml b/grafana/datasources/all.yml deleted file mode 100644 index f002048..0000000 --- a/grafana/datasources/all.yml +++ /dev/null @@ -1,19 +0,0 @@ -# -# -# Copyright (c) 2021 Oracle and/or its affiliates. -# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ -# -# - -datasources: - - name: 'Prometheus' - type: 'prometheus' - access: 'proxy' - org_id: 1 - url: 'https://prometheus:9090' - is_default: true - version: 1 - editable: true - jsonData: - tlsSkipVerify: true - tlsAuthWithCACert: true diff --git a/grafana/grafana.ini b/grafana/grafana.ini deleted file mode 100644 index e3c14f0..0000000 --- a/grafana/grafana.ini +++ /dev/null @@ -1,921 +0,0 @@ -# -# -# Copyright (c) 2021 Oracle and/or its affiliates. -# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ -# -# -##################### Grafana Configuration Example ##################### -# -# Everything has defaults so you only need to uncomment things you want to -# change - -# possible values : production, development -;app_mode = production - -# instance name, defaults to HOSTNAME environment variable value or hostname if HOSTNAME var is empty -;instance_name = ${HOSTNAME} - -#################################### Paths #################################### -[paths] -# Path to where grafana can store temp files, sessions, and the sqlite3 db (if that is used) -;data = /var/lib/grafana - -# Temporary files in `data` directory older than given duration will be removed -;temp_data_lifetime = 24h - -# Directory where grafana can store logs -;logs = /var/log/grafana - -# Directory where grafana will automatically scan and look for plugins -;plugins = /var/lib/grafana/plugins - -# folder that contains provisioning config files that grafana will apply on startup and while running. -;provisioning = conf/provisioning - -#################################### Server #################################### -[server] -# Protocol (http, https, h2, socket) -protocol = https - -# The ip address to bind to, empty will bind to all interfaces -;http_addr = - -# The http port to use -http_port = 3000 - -# The public facing domain name used to access grafana from a browser -;domain = localhost - -# Redirect to correct domain if host header does not match domain -# Prevents DNS rebinding attacks -;enforce_domain = false - -# The full public facing url you use in browser, used for redirects and emails -# If you use reverse proxy and sub path specify full url (with sub path) -root_url = %(protocol)s://%(domain)s:%(http_port)s/ - -# Serve Grafana from subpath specified in `root_url` setting. By default it is set to `false` for compatibility reasons. -;serve_from_sub_path = false - -# Log web requests -;router_logging = false - -# the path relative working path -;static_root_path = public - -# enable gzip -;enable_gzip = false - -# https certs & key file -cert_file = /localhost.crt -cert_key = /localhost.key - -# Unix socket path -;socket = - -# CDN Url -;cdn_url = - -# Sets the maximum time using a duration format (5s/5m/5ms) before timing out read of an incoming request and closing idle connections. -# `0` means there is no timeout for reading the request. -;read_timeout = 0 - -#################################### Database #################################### -[database] -# You can configure the database connection by specifying type, host, name, user and password -# as separate properties or as on string using the url properties. - -# Either "mysql", "postgres" or "sqlite3", it's your choice -;type = sqlite3 -;host = 127.0.0.1:3306 -;name = grafana -;user = root -# If the password contains # or ; you have to wrap it with triple quotes. Ex """#password;""" -;password = - -# Use either URL or the previous fields to configure the database -# Example: mysql://user:secret@host:port/database -;url = - -# For "postgres" only, either "disable", "require" or "verify-full" -;ssl_mode = disable - -;ca_cert_path = -;client_key_path = -;client_cert_path = -;server_cert_name = - -# For "sqlite3" only, path relative to data_path setting -;path = grafana.db - -# Max idle conn setting default is 2 -;max_idle_conn = 2 - -# Max conn setting default is 0 (mean not set) -;max_open_conn = - -# Connection Max Lifetime default is 14400 (means 14400 seconds or 4 hours) -;conn_max_lifetime = 14400 - -# Set to true to log the sql calls and execution times. -;log_queries = - -# For "sqlite3" only. cache mode setting used for connecting to the database. (private, shared) -;cache_mode = private - -################################### Data sources ######################### -[datasources] -# Upper limit of data sources that Grafana will return. This limit is a temporary configuration and it will be deprecated when pagination will be introduced on the list data sources API. -;datasource_limit = 5000 - -#################################### Cache server ############################# -[remote_cache] -# Either "redis", "memcached" or "database" default is "database" -;type = database - -# cache connectionstring options -# database: will use Grafana primary database. -# redis: config like redis server e.g. `addr=127.0.0.1:6379,pool_size=100,db=0,ssl=false`. Only addr is required. ssl may be 'true', 'false', or 'insecure'. -# memcache: 127.0.0.1:11211 -;connstr = - -#################################### Data proxy ########################### -[dataproxy] - -# This enables data proxy logging, default is false -;logging = false - -# How long the data proxy waits before timing out, default is 30 seconds. -# This setting also applies to core backend HTTP data sources where query requests use an HTTP client with timeout set. -;timeout = 30 - -# How many seconds the data proxy waits before sending a keepalive probe request. -;keep_alive_seconds = 30 - -# How many seconds the data proxy waits for a successful TLS Handshake before timing out. -;tls_handshake_timeout_seconds = 10 - -# How many seconds the data proxy will wait for a server's first response headers after -# fully writing the request headers if the request has an "Expect: 100-continue" -# header. A value of 0 will result in the body being sent immediately, without -# waiting for the server to approve. -;expect_continue_timeout_seconds = 1 - -# The maximum number of idle connections that Grafana will keep alive. -;max_idle_connections = 100 - -# How many seconds the data proxy keeps an idle connection open before timing out. -;idle_conn_timeout_seconds = 90 - -# If enabled and user is not anonymous, data proxy will add X-Grafana-User header with username into the request, default is false. -;send_user_header = false - -#################################### Analytics #################################### -[analytics] -# Server reporting, sends usage counters to stats.grafana.org every 24 hours. -# No ip addresses are being tracked, only simple counters to track -# running instances, dashboard and error counts. It is very helpful to us. -# Change this option to false to disable reporting. -;reporting_enabled = true - -# The name of the distributor of the Grafana instance. Ex hosted-grafana, grafana-labs -;reporting_distributor = grafana-labs - -# Set to false to disable all checks to https://grafana.net -# for new versions (grafana itself and plugins), check is used -# in some UI views to notify that grafana or plugin update exists -# This option does not cause any auto updates, nor send any information -# only a GET request to http://grafana.com to get latest versions -;check_for_updates = true - -# Google Analytics universal tracking code, only enabled if you specify an id here -;google_analytics_ua_id = - -# Google Tag Manager ID, only enabled if you specify an id here -;google_tag_manager_id = - -#################################### Security #################################### -[security] -# disable creation of admin user on first start of grafana -;disable_initial_admin_creation = false - -# default admin user, created on startup -;admin_user = admin - -# default admin password, can be changed before first start of grafana, or in profile settings -;admin_password = admin - -# used for signing -;secret_key = SW2YcwTIb9zpOOhoPsMm - -# disable gravatar profile images -;disable_gravatar = false - -# data source proxy whitelist (ip_or_domain:port separated by spaces) -;data_source_proxy_whitelist = - -# disable protection against brute force login attempts -;disable_brute_force_login_protection = false - -# set to true if you host Grafana behind HTTPS. default is false. -;cookie_secure = false - -# set cookie SameSite attribute. defaults to `lax`. can be set to "lax", "strict", "none" and "disabled" -;cookie_samesite = lax - -# set to true if you want to allow browsers to render Grafana in a ,