merge main

This commit is contained in:
Ryan McKinley
2025-06-27 12:49:54 -07:00
864 changed files with 67818 additions and 25883 deletions
+5 -15
View File
@@ -438,7 +438,11 @@ exports[`better eslint`] = {
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],
[0, 0, 0, "Unexpected any. Specify a different type.", "1"],
[0, 0, 0, "Unexpected any. Specify a different type.", "2"],
[0, 0, 0, "Unexpected any. Specify a different type.", "3"]
[0, 0, 0, "Unexpected any. Specify a different type.", "3"],
[0, 0, 0, "Unexpected any. Specify a different type.", "4"],
[0, 0, 0, "Unexpected any. Specify a different type.", "5"],
[0, 0, 0, "Unexpected any. Specify a different type.", "6"],
[0, 0, 0, "Unexpected any. Specify a different type.", "7"]
],
"packages/grafana-prometheus/src/language_provider.ts:5381": [
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],
@@ -1489,10 +1493,6 @@ exports[`better eslint`] = {
"public/app/features/auth-config/utils/data.ts:5381": [
[0, 0, 0, "Do not use any type assertions.", "0"]
],
"public/app/features/browse-dashboards/api/useRecentlyDeletedStateManager.ts:5381": [
[0, 0, 0, "Direct usage of localStorage is not allowed. import store from @grafana/data instead", "0"],
[0, 0, 0, "Direct usage of localStorage is not allowed. import store from @grafana/data instead", "1"]
],
"public/app/features/browse-dashboards/components/BrowseActions/MoveModal.tsx:5381": [
[0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"]
],
@@ -2627,9 +2627,6 @@ exports[`better eslint`] = {
"public/app/features/plugins/admin/components/GetStartedWithPlugin/index.ts:5381": [
[0, 0, 0, "Do not re-export imported variable (\`./GetStartedWithPlugin\`)", "0"]
],
"public/app/features/plugins/admin/components/InstallControls/InstallControlsWarning.tsx:5381": [
[0, 0, 0, "\'HorizontalGroup\' import from \'@grafana/ui\' is restricted from being used by a pattern. Use Stack component instead.", "0"]
],
"public/app/features/plugins/admin/components/InstallControls/index.tsx:5381": [
[0, 0, 0, "Do not re-export imported variable (\`./InstallControlsButton\`)", "0"],
[0, 0, 0, "Do not re-export imported variable (\`./InstallControlsWarning\`)", "1"]
@@ -3609,13 +3606,6 @@ exports[`better eslint`] = {
[0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"],
[0, 0, 0, "Do not use any type assertions.", "1"]
],
"public/app/plugins/datasource/influxdb/components/editor/config/InfluxInfluxQLConfig.tsx:5381": [
[0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"],
[0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "1"],
[0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "2"],
[0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "3"],
[0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "4"]
],
"public/app/plugins/datasource/influxdb/components/editor/config/InfluxSQLConfig.tsx:5381": [
[0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"],
[0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "1"],
+5 -3
View File
@@ -393,13 +393,14 @@ steps:
- packages/grafana-ui/**
- commands:
- npx wait-on@7.0.1 http://$HOST:$PORT
- pa11y-ci --config .pa11yci-pr.conf.js
- pa11y-ci --config e2e/pa11yci.conf.js
depends_on:
- grafana-server
environment:
GRAFANA_MISC_STATS_API_KEY:
from_secret: grafana_misc_stats_api_key
HOST: grafana-server
NO_THRESHOLDS: "false"
PORT: 3001
failure: always
image: grafana/docker-puppeteer:1.1.0
@@ -864,13 +865,14 @@ steps:
- packages/grafana-ui/**
- commands:
- npx wait-on@7.0.1 http://$HOST:$PORT
- pa11y-ci --config .pa11yci.conf.js --json > pa11y-ci-results.json
- pa11y-ci --config e2e/pa11yci.conf.js
depends_on:
- grafana-server
environment:
GRAFANA_MISC_STATS_API_KEY:
from_secret: grafana_misc_stats_api_key
HOST: grafana-server
NO_THRESHOLDS: "true"
PORT: 3001
failure: ignore
image: grafana/docker-puppeteer:1.1.0
@@ -2984,6 +2986,6 @@ kind: secret
name: gcr_credentials
---
kind: signature
hmac: db5485c680a21c2f4032064cd7fd892a3075ebd92cd29422d892fcc375eb6948
hmac: 1198b1489e48a9ced211633a0325d112814553246847fc7320fb5ac2bcb32b7d
...
+10 -8
View File
@@ -48,6 +48,8 @@
/docs/sources/developers/plugins/ @grafana/plugins-platform-frontend @grafana/plugins-platform-backend
/docs/sources/dashboards/share-dashboards-panels/_index.md @imatwawana @jtvdez
/docs/sources/dashboards/share-dashboards-panels/shared-dashboards/index.md @jtvdez
/docs/sources/panels-visualizations/query-transform-data/transform-data/index.md @imatwawana @baldm0mma
/docs/sources/panels-visualizations/query-transform-data/sql-expressions/index.md @lwandz13 @irenerl24
# END Technical documentation
@@ -372,8 +374,8 @@
/public/app/features/gops/ @grafana/alerting-frontend
# Library Services
/pkg/services/libraryelements/ @grafana/dashboards-squad
/pkg/services/librarypanels/ @grafana/dashboards-squad
/pkg/services/libraryelements/ @grafana/sharing-squad
/pkg/services/librarypanels/ @grafana/sharing-squad
# Plugins
/pkg/api/pluginproxy/ @grafana/plugins-platform-backend
@@ -515,8 +517,6 @@ playwright.config.ts @grafana/plugins-platform-frontend
/public/app/features/expressions/ @grafana/grafana-datasources-core-services
/public/app/features/folders/ @grafana/grafana-search-navigate-organise
/public/app/features/inspector/ @grafana/dashboards-squad
/public/app/features/invites/ @grafana/sharing-squad
/public/app/features/library-panels/ @grafana/dashboards-squad
/public/app/features/logs/ @grafana/observability-logs
/public/app/features/live/ @grafana/dashboards-squad
/public/app/features/apiserver/ @grafana/grafana-app-platform-squad
@@ -648,8 +648,6 @@ playwright.config.ts @grafana/plugins-platform-frontend
/scripts/**/generate-transformations* @grafana/datapro
/scripts/webpack/ @grafana/frontend-ops
/scripts/generate-a11y-report.sh @grafana/grafana-frontend-platform
.pa11yci.conf.js @grafana/grafana-frontend-platform
.pa11yci-pr.conf.js @grafana/grafana-frontend-platform
.betterer.results @grafanabot
.betterer.ts @grafana/grafana-frontend-platform
@@ -666,7 +664,7 @@ playwright.config.ts @grafana/plugins-platform-frontend
/public/app/plugins/datasource/graphite/ @grafana/partner-datasources
/public/app/plugins/datasource/influxdb/ @grafana/partner-datasources
/public/app/plugins/datasource/jaeger/ @grafana/oss-big-tent
/public/app/plugins/datasource/loki/ @grafana/oss-big-tent
/public/app/plugins/datasource/loki/ @grafana/oss-big-tent @grafana/observability-logs
/public/app/plugins/datasource/mixed/ @grafana/dashboards-squad
/public/app/plugins/datasource/mssql/ @grafana/partner-datasources
/public/app/plugins/datasource/mysql/ @grafana/oss-big-tent
@@ -686,6 +684,8 @@ playwright.config.ts @grafana/plugins-platform-frontend
/public/app/features/manage-dashboards/components/SnapshotListTable.tsx @grafana/sharing-squad
/pkg/services/dashboardsnapshots/ @grafana/sharing-squad
/public/app/features/explore/QueryLibrary/ @grafana/sharing-squad
/public/app/features/library-panels/ @grafana/sharing-squad
/public/app/features/invites/ @grafana/sharing-squad
# Grafana Enterprise: Public Dashboards & Image Renderer
/pkg/api/render.go @grafana/grafana-operator-experience-squad
@@ -833,6 +833,7 @@ embed.go @grafana/grafana-as-code
/.github/workflows/i18n-crowdin-upload.yml @grafana/grafana-frontend-platform
/.github/workflows/i18n-crowdin-download.yml @grafana/grafana-frontend-platform
/.github/workflows/i18n-crowdin-create-tasks.yml @grafana/grafana-frontend-platform
/.github/workflows/i18n-verify.yml @grafana/grafana-frontend-platform
/.github/workflows/scripts/crowdin/create-tasks.ts @grafana/grafana-frontend-platform
/.github/workflows/pr-go-workspace-check.yml @grafana/grafana-app-platform-squad
/.github/workflows/pr-dependabot-update-go-workspace.yml @grafana/grafana-app-platform-squad
@@ -842,7 +843,8 @@ embed.go @grafana/grafana-as-code
/.github/workflows/trufflehog.yml @Proximyst
/.github/workflows/changelog.yml @zserge
/.github/workflows/shellcheck.yml @grafana/grafana-developer-enablement-squad
/.github/workflows/release-build.yml @grafana/grafana-developer-enablement-squad
/.github/workflows/release-build.yml @grafana/grafana-developer-enablement-squad
/.github/workflows/publish-artifact.yml @grafana/grafana-developer-enablement-squad
/.github/actions/changelog @zserge
/.github/workflows/swagger-gen.yml @grafana/grafana-backend-group
/.github/workflows/pr-frontend-unit-tests.yml @grafana/grafana-frontend-platform
+2 -2
View File
@@ -664,7 +664,7 @@
"name": "area/frontend/library-panels",
"action": "addToProject",
"addToProject": {
"url": "https://github.com/orgs/grafana/projects/202"
"url": "https://github.com/orgs/grafana/projects/482"
}
},
{
@@ -896,7 +896,7 @@
"name": "area/dashboard/library-panel",
"action": "addToProject",
"addToProject": {
"url": "https://github.com/orgs/grafana/projects/202"
"url": "https://github.com/orgs/grafana/projects/482"
}
},
{
@@ -12,6 +12,8 @@ on:
pull_request:
paths:
- 'packages/**'
- '.nvmrc'
- '.github/workflows/detect-breaking-changes-levitate.yml'
branches:
- 'main'
@@ -34,7 +36,7 @@ jobs:
- uses: actions/setup-node@v4
with:
node-version: 22.16.0
node-version-file: './pr/.nvmrc'
- name: Get yarn cache directory path
id: yarn-cache-dir-path
@@ -86,7 +88,7 @@ jobs:
- uses: actions/setup-node@v4
with:
node-version: 22.16.0
node-version-file: './base/.nvmrc'
- name: Get yarn cache directory path
id: yarn-cache-dir-path
@@ -136,7 +138,7 @@ jobs:
- uses: actions/setup-node@v4
with:
node-version: 22.16.0
node-version-file: '.nvmrc'
- name: Get built packages from pr
uses: actions/download-artifact@v4
@@ -3,7 +3,7 @@ name: Run e2e for dashboardNewLayouts
on:
pull_request:
branches:
- '**'
- '**'
paths:
- 'e2e/dashboard-new-layouts/**'
- 'public/app/features/dashboard-scene/**'
@@ -28,7 +28,7 @@ jobs:
- run: go version
- uses: actions/setup-node@v4
with:
node-version: 20
node-version-file: '.nvmrc'
cache: 'yarn'
- name: Install dependencies
run: yarn install --immutable
@@ -12,7 +12,7 @@ jobs:
handle-ephemeral-instances:
if: ${{ github.event.issue.pull_request && (startsWith(github.event.comment.body, '/deploy-to-hg') || github.event.action == 'closed') && github.repository_owner == 'grafana' }}
runs-on:
labels: ubuntu-latest-8-cores
labels: ubuntu-latest-16-cores
continue-on-error: true
permissions:
# For commenting.
-28
View File
@@ -9,34 +9,6 @@ on:
permissions: {}
jobs:
lint-frontend-verify-i18n:
name: Verify i18n
runs-on: ubuntu-latest
permissions:
contents: read
id-token: write
steps:
- uses: actions/checkout@v4
with:
persist-credentials: false
- uses: actions/setup-node@v4
with:
node-version-file: '.nvmrc'
cache: 'yarn'
cache-dependency-path: 'yarn.lock'
- run: yarn install --immutable --check-cache
- run: |
# shellcheck disable=SC2102,SC2016,SC2125 # this is just a string. we _want_ all the bash features to be disabled.
extract_error_message='::error::Extraction failed. Make sure that you have no dynamic translation phrases, such as "t(`preferences.theme.{themeID}`, themeName)" and that no translation key is used twice. Search the output for '[warning]' to find the offending file.'
make i18n-extract || (echo "${extract_error_message}" && false)
- run: |
uncommited_error_message="::error::Translation extraction has not been committed. Please run 'make i18n-extract', commit the changes and push again."
file_diff=$(git diff --dirstat public/locales)
if [ -n "$file_diff" ]; then
echo "$file_diff"
echo "${uncommited_error_message}"
exit 1
fi
lint-frontend-prettier:
permissions:
contents: read
+15
View File
@@ -0,0 +1,15 @@
name: Verify i18n
permissions:
contents: read
on:
pull_request:
push:
branches:
- main
- release-*.*.*
jobs:
verify-i18n:
uses: grafana/grafana-github-actions/.github/workflows/verify-i18n.yml@main
+1 -1
View File
@@ -35,7 +35,7 @@ jobs:
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '22.16.0'
node-version-file: '.nvmrc'
cache: 'yarn'
- name: Install dependencies
+26 -35
View File
@@ -137,42 +137,33 @@ jobs:
path: videos
retention-days: 1
# Skipping this for now while we fix it
# run-a11y-test:
# needs:
# - build-grafana
# - build-e2e-runner
# name: A11y test
# runs-on: ubuntu-latest-8-cores
# permissions:
# contents: read
run-a11y-test:
needs:
- build-grafana
name: A11y test
runs-on: ubuntu-latest-8-cores
permissions:
contents: read
# steps:
# - uses: actions/checkout@v4
# with:
# persist-credentials: false
# - uses: actions/download-artifact@v4
# with:
# name: ${{ needs.build-grafana.outputs.artifact }}
# - uses: actions/download-artifact@v4
# with:
# name: ${{ needs.build-e2e-runner.outputs.artifact }}
# - name: chmod +x
# run: chmod +x ./e2e-runner
# - name: Run PR a11y test
# if: github.event_name == 'pull_request'
# uses: dagger/dagger-for-github@e47aba410ef9bb9ed81a4d2a97df31061e5e842e
# with:
# verb: run
# args: go run ./pkg/build/a11y --package=grafana.tar.gz
# --flags="--json --config ./.pa11yci-pr.conf.js"
# - name: Run non-PR a11y test
# if: github.event_name != 'pull_request'
# uses: dagger/dagger-for-github@e47aba410ef9bb9ed81a4d2a97df31061e5e842e
# with:
# verb: run
# args: go run ./pkg/build/a11y --package=grafana.tar.gz
# --flags="--json --config ./.pa11yci.conf.js"
steps:
- uses: actions/checkout@v4
with:
persist-credentials: false
- uses: actions/download-artifact@v4
with:
name: ${{ needs.build-grafana.outputs.artifact }}
- name: Run PR a11y test
if: github.event_name == 'pull_request'
uses: dagger/dagger-for-github@e47aba410ef9bb9ed81a4d2a97df31061e5e842e
with:
verb: run
args: go run ./pkg/build/a11y --package=grafana.tar.gz
- name: Run non-PR a11y test
if: github.event_name != 'pull_request'
uses: dagger/dagger-for-github@e47aba410ef9bb9ed81a4d2a97df31061e5e842e
with:
verb: run
args: go run ./pkg/build/a11y --package=grafana.tar.gz --no-threshold-fail
# This is the job that is actually required by rulesets.
# We want to only require one job instead of all the individual tests.
+68
View File
@@ -0,0 +1,68 @@
name: Publish artifacts to bucket
on:
workflow_call:
inputs:
pattern:
description: |
(From actinos/download-artifact) Glob pattern of artifacts (instead of `name`)
Be careful when using this option; the contents of the root of each artifact are coalesced, so ensure that they do not collide.
type: string
required: false
name:
description: (From actinos/download-artifact) Name of the GitHub artifact to upload (Ignored if `pattern` is set)
type: string
required: false
bucket:
description: Name of the GCS bucket
type: string
required: true
bucket-path:
description: Path in the GCS bucket
type: string
required: false
default: "."
environment:
description: "'prod' or 'dev'"
type: string
required: false
default: dev
run-id:
type: string
required: true
service-account:
type: string
required: false
default: github-prerelease-writer@grafanalabs-workload-identity.iam.gserviceaccount.com
jobs:
publish:
runs-on: github-hosted-ubuntu-x64-small
name: Publish
permissions:
id-token: write
steps:
- uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093
with:
name: ${{ inputs.name }}
pattern: ${{ inputs.pattern }}
run-id: ${{ inputs.run-id }}
path: ./artifact
- name: Log in to GCS
id: login-to-gcs
uses: grafana/shared-workflows/actions/login-to-gcs@login-to-gcs/v0.2.1
with:
environment: ${{ inputs.environment }}
service_account: ${{ inputs.service-account }}
- name: Coalesce artifacts
run: |
mkdir out
find ./artifact -mindepth 2 -maxdepth 2 -exec cp -r {} out/ \;
ls -al out
- name: Upload artifacts
uses: grafana/shared-workflows/actions/push-to-gcs@push-to-gcs-v0.2.0
with:
bucket: ${{ inputs.bucket }}
environment: ${{ inputs.environment }}
parent: false
path: out
bucket_path: ${{ inputs.bucket-path }}
service_account: ${{ inputs.service-account }}
+76 -23
View File
@@ -6,10 +6,6 @@ on:
- release-*.*.*
- main
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
permissions:
contents: read
@@ -43,8 +39,10 @@ jobs:
setup:
name: setup
runs-on: github-hosted-ubuntu-x64-small
if: github.repository == 'grafana/grafana'
outputs:
version: ${{ steps.output.outputs.version }}
grafana-commit: ${{ steps.output.outputs.grafana_commit }}
permissions:
contents: read
steps:
@@ -63,7 +61,56 @@ jobs:
REF_NAME: ${{ github.ref_name }}
BUILD_ID: ${{ github.run_number }}
- id: output
run: echo "version=$(cat VERSION)" >> "$GITHUB_OUTPUT"
run: |
echo "version=$(cat VERSION)" >> "$GITHUB_OUTPUT"
echo "grafana_commit=$(git rev-parse HEAD)" | tee -a "$GITHUB_OUTPUT"
# Triggers the same workflow in `grafana-enterprise` on the same ref
downstream:
runs-on: github-hosted-ubuntu-x64-small
needs: [setup]
permissions:
contents: read
id-token: write
name: Dispatch grafana-enterprise build
steps:
- id: vault-secrets
uses: grafana/shared-workflows/actions/get-vault-secrets@main
with:
repo_secrets: |
GRAFANA_DELIVERY_BOT_APP_PEM=delivery-bot-app:PRIVATE_KEY
- name: Generate token
id: generate_token
uses: tibdex/github-app-token@3beb63f4bd073e61482598c45c71c1019b59b73a
with:
app_id: ${{ vars.DELIVERY_BOT_APP_ID }}
private_key: ${{ env.GRAFANA_DELIVERY_BOT_APP_PEM }}
repositories: '["grafana-enterprise"]'
permissions: '{"actions": "write"}'
- uses: actions/github-script@v7
env:
REF: ${{ github.ref_name }}
VERSION: ${{ needs.setup.outputs.version }}
BUILD_ID: ${{ github.run_number }}
BUCKET: grafana-prerelease
GRAFANA_COMMIT: ${{ needs.setup.outputs.grafana-commit }}
with:
github-token: ${{ steps.generate_token.outputs.token }}
script: |
const {REF, VERSION, BUILD_ID, BUCKET, GRAFANA_COMMIT} = process.env;
await github.rest.actions.createWorkflowDispatch({
owner: 'grafana',
repo: 'grafana-enterprise',
workflow_id: 'release-build.yml',
ref: REF,
inputs: {
"version": VERSION,
"build-id": String(BUILD_ID),
"bucket": BUCKET,
"grafana-commit": GRAFANA_COMMIT,
}
})
build:
runs-on: github-hosted-ubuntu-x64-large
needs: [setup]
@@ -72,6 +119,7 @@ jobs:
id-token: write
name: ${{ needs.setup.outputs.version }} / ${{ matrix.name }}
strategy:
fail-fast: false
matrix:
# The artifacts in these lists are grouped by their os+arch because the
# build process can reuse the binaries for each artifact.
@@ -97,6 +145,7 @@ jobs:
- name: darwin-arm64
artifacts: targz:grafana:darwin/arm64
steps:
- uses: grafana/shared-workflows/actions/dockerhub-login@main
- uses: actions/checkout@v4
with:
persist-credentials: false
@@ -115,23 +164,27 @@ jobs:
output: artifacts-${{ matrix.name }}.txt
verify: true
build-id: ${{ github.run_number }}
- name: Log in to GCS
id: login-to-gcs
uses: grafana/shared-workflows/actions/login-to-gcs@login-to-gcs/v0.2.1
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02
with:
environment: prod
- name: Upload artifacts
uses: grafana/shared-workflows/actions/push-to-gcs@push-to-gcs-v0.2.0
with:
bucket: ${{ steps.login-to-gcs.outputs.bucket }}
environment: prod
parent: false
path: ${{ steps.build.outputs.dist-dir }}
bucket_path: ${{ needs.setup.outputs.version }}
- name: Upload manifest
uses: grafana/shared-workflows/actions/push-to-gcs@push-to-gcs-v0.2.0
with:
bucket: ${{ steps.login-to-gcs.outputs.bucket }}
environment: prod
name: artifacts-list-${{ matrix.name }}
path: ${{ steps.build.outputs.file }}
bucket_path: ${{ needs.setup.outputs.version }}
retention-days: 1
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02
with:
name: artifacts-${{ matrix.name }}
path: ${{ steps.build.outputs.dist-dir }}
retention-days: 1
publish-artifacts:
name: Upload artifacts
uses: grafana/grafana/.github/workflows/publish-artifact.yml@main
permissions:
id-token: write
needs:
- setup
- build
with:
bucket: grafana-prerelease
pattern: artifacts-*
run-id: ${{ github.run_id }}
bucket-path: ${{ needs.setup.outputs.version }}
environment: prod
@@ -36,7 +36,7 @@ jobs:
- run: go version
- uses: actions/setup-node@v4
with:
node-version: 20
node-version-file: '.nvmrc'
cache: 'yarn'
- name: Cache Node Modules
id: cache-node-modules
+3 -3
View File
@@ -6,7 +6,7 @@ on:
- main
pull_request:
branches:
- '**'
- '**'
env:
ARCH: linux-amd64
@@ -28,7 +28,7 @@ jobs:
- run: go version
- uses: actions/setup-node@v4
with:
node-version: 20
node-version-file: '.nvmrc'
cache: 'yarn'
- name: Install dependencies
run: yarn install --immutable
@@ -40,7 +40,7 @@ jobs:
runTests: false
- name: Run dashboard scenes e2e
run: yarn e2e:schema-v2 || echo "Test failed but marking as success since schema V2 is behind a feature flag and should not block PRs"
- name: Always succeed # This is a workaround to make the job pass even if the previous step fails
if: failure()
run: exit 0
+1 -1
View File
@@ -32,7 +32,7 @@ jobs:
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version-file: 'package.json'
node-version-file: '.nvmrc'
cache: 'yarn'
- name: Install dependencies
+2 -6
View File
@@ -17,7 +17,7 @@ jobs:
verify:
name: Verify committed API specs match
runs-on: ubuntu-latest
if: ${{ (github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork == false) || github.repository == 'grafana/grafana' }}
if: ${{ github.repository == 'grafana/grafana' }}
permissions:
contents: read # clone the repository
id-token: write # required for Vault access
@@ -32,15 +32,11 @@ jobs:
with:
go-version-file: go.mod
- name: Setup Enterprise
if: ${{ github.event.pull_request.head.repo.fork == false }}
uses: ./.github/actions/setup-enterprise
with:
github-app-name: 'grafana-ci-bot'
- name: Set go version
uses: actions/setup-go@v5
with:
go-version-file: go.mod
- name: Generate Swagger specs
run: make swagger-clean && make openapi3-gen
- name: Check for changes
+1
View File
@@ -69,6 +69,7 @@ public/css/*.min.css
!.vscode/launch.json
.vs/
.cursor/
.devcontainer/
.eslintcache
.stylelintcache
+2
View File
@@ -101,6 +101,8 @@ linters:
- '**/pkg/tsdb/tempo/**/*'
- '**/pkg/tsdb/cloudwatch/*'
- '**/pkg/tsdb/cloudwatch/**/*'
- '**/pkg/tsdb/loki/*'
- '**/pkg/tsdb/loki/**/*'
deny:
- pkg: github.com/grafana/grafana/pkg/api
desc: Core plugins are not allowed to depend on Grafana core packages
-140
View File
@@ -1,140 +0,0 @@
var dashboardSettings = [
{
url: '${HOST}/d/O6f11TZWk/panel-tests-bar-gauge?orgId=1&editview=settings',
wait: 500,
rootElement: '.main-view',
},
{
url: '${HOST}/d/O6f11TZWk/panel-tests-bar-gauge?orgId=1&editview=annotations',
wait: 500,
rootElement: '.main-view',
},
{
url: '${HOST}/d/O6f11TZWk/panel-tests-bar-gauge?orgId=1&editview=templating',
wait: 500,
rootElement: '.main-view',
},
{
url: '${HOST}/d/O6f11TZWk/panel-tests-bar-gauge?orgId=1&editview=links',
wait: 500,
rootElement: '.main-view',
},
{
url: '${HOST}/d/O6f11TZWk/panel-tests-bar-gauge?orgId=1&editview=versions',
wait: 500,
rootElement: '.main-view',
},
{
url: '${HOST}/d/O6f11TZWk/panel-tests-bar-gauge?orgId=1&editview=permissions',
wait: 500,
rootElement: '.main-view',
},
{
url: '${HOST}/d/O6f11TZWk/panel-tests-bar-gauge?orgId=1&editview=dashboard_json',
wait: 500,
rootElement: '.main-view',
},
];
var config = {
defaults: {
concurrency: 1,
runners: ['axe'],
useIncognitoBrowserContext: false,
standard: 'WCAG2AA',
chromeLaunchConfig: {
args: ['--no-sandbox'],
},
// see https://github.com/grafana/grafana/pull/41693#issuecomment-979921463 for context
// on why we're ignoring singleValue/react-select-*-placeholder elements
hideElements: '#updateVersion, [class*="-singleValue"], [id^="react-select-"][id$="-placeholder"]',
},
urls: [
{
url: '${HOST}/login',
wait: 500,
rootElement: '.main-view',
},
{
url: '${HOST}/login', //skip password and login
actions: [
"wait for element input[name='user'] to be added",
"set field input[name='user'] to admin",
"set field input[name='password'] to admin",
"click element button[data-testid='data-testid Login button']",
"wait for element button[data-testid='data-testid Skip change password button'] to be visible",
],
wait: 500,
rootElement: '.main-view',
},
{
url: '${HOST}/?orgId=1',
wait: 500,
},
{
url: '${HOST}/d/O6f11TZWk/panel-tests-bar-gauge',
wait: 500,
rootElement: '.main-view',
},
...dashboardSettings,
{
url: '${HOST}/?orgId=1&search=open',
wait: 500,
rootElement: '.main-view',
},
{
url: '${HOST}/alerting/list',
wait: 500,
rootElement: '.main-view',
},
{
url: '${HOST}/datasources',
wait: 500,
rootElement: '.main-view',
},
{
url: '${HOST}/org/users',
wait: 500,
rootElement: '.main-view',
},
{
url: '${HOST}/org/teams',
wait: 500,
rootElement: '.main-view',
},
{
url: '${HOST}/plugins',
wait: 500,
rootElement: '.main-view',
},
{
url: '${HOST}/org',
wait: 500,
rootElement: '.main-view',
},
{
url: '${HOST}/org/apikeys',
wait: 500,
rootElement: '.main-view',
},
{
url: '${HOST}/dashboards',
wait: 500,
rootElement: '.main-view',
},
],
};
function myPa11yCiConfiguration(urls, defaults) {
const HOST_SERVER = process.env.HOST || 'localhost';
const PORT_SERVER = process.env.PORT || '3001';
for (var idx = 0; idx < urls.length; idx++) {
urls[idx] = { ...urls[idx], url: urls[idx].url.replace('${HOST}', `${HOST_SERVER}:${PORT_SERVER}`) };
}
return {
defaults: defaults,
urls: urls,
};
}
module.exports = myPa11yCiConfiguration(config.urls, config.defaults);
-934
View File
File diff suppressed because one or more lines are too long
+942
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -25,4 +25,4 @@ plugins:
path: .yarn/plugins/@yarnpkg/plugin-licenses.cjs
spec: "https://raw.githubusercontent.com/mhassan1/yarn-plugin-licenses/v0.15.0/bundles/@yarnpkg/plugin-licenses.js"
yarnPath: .yarn/releases/yarn-4.6.0.cjs
yarnPath: .yarn/releases/yarn-4.9.2.cjs
@@ -85,7 +85,10 @@ func New(cfg app.Config, log logging.Logger) (app.Runnable, error) {
func (r *Runner) Run(ctx context.Context) error {
logger := r.log.WithContext(ctx)
lastCreated, err := r.checkLastCreated(ctx, logger)
// We still need the context to eventually be cancelled to exit this function
// but we don't want the requests to fail because of it
ctxWithoutCancel := context.WithoutCancel(ctx)
lastCreated, err := r.checkLastCreated(ctxWithoutCancel, logger)
if err != nil {
logger.Error("Error getting last check creation time", "error", err)
// Wait for interval to create the next scheduled check
@@ -93,7 +96,7 @@ func (r *Runner) Run(ctx context.Context) error {
} else {
// do an initial creation if necessary
if lastCreated.IsZero() {
err = r.createChecks(ctx, logger)
err = r.createChecks(ctxWithoutCancel, logger)
if err != nil {
logger.Error("Error creating new check reports", "error", err)
} else {
@@ -109,12 +112,12 @@ func (r *Runner) Run(ctx context.Context) error {
for {
select {
case <-ticker.C:
err = r.createChecks(ctx, logger)
err = r.createChecks(ctxWithoutCancel, logger)
if err != nil {
logger.Error("Error creating new check reports", "error", err)
}
err = r.cleanupChecks(ctx, logger)
err = r.cleanupChecks(ctxWithoutCancel, logger)
if err != nil {
logger.Error("Error cleaning up old check reports", "error", err)
}
@@ -147,7 +150,7 @@ func (r *Runner) checkLastCreated(ctx context.Context, log logging.Logger) (time
// If the check is unprocessed, set it to error
if checks.GetStatusAnnotation(item) == "" {
log.Error("Check is unprocessed", "check", item.GetStaticMetadata().Identifier())
log.Info("Check is unprocessed, marking as error", "check", item.GetStaticMetadata().Identifier())
err := checks.SetStatusAnnotation(ctx, r.client, item, checks.StatusAnnotationError)
if err != nil {
log.Error("Error setting check status to error", "error", err)
@@ -168,7 +171,7 @@ func (r *Runner) createChecks(ctx context.Context, logger logging.Logger) error
allChecksRegistered := len(list.GetItems()) == len(r.checkRegistry.Checks())
retryCount := 0
for !allChecksRegistered && retryCount < waitMaxRetries {
logger.Error("Waiting for all check types to be registered", "retryCount", retryCount, "waitInterval", waitInterval)
logger.Info("Waiting for all check types to be registered", "retryCount", retryCount, "waitInterval", waitInterval)
time.Sleep(waitInterval)
list, err = r.typesClient.List(ctx, r.namespace, resource.ListOptions{})
if err != nil {
@@ -4,6 +4,7 @@ import (
"context"
"fmt"
"maps"
"strings"
"time"
"github.com/grafana/grafana-app-sdk/app"
@@ -81,7 +82,7 @@ func (r *Runner) createOrUpdate(ctx context.Context, log logging.Logger, obj res
_, err = r.client.Update(ctx, id, obj, resource.UpdateOptions{})
if err != nil && !errors.IsAlreadyExists(err) {
// Ignore the error, it's probably due to a race condition
log.Error("Error updating check type", "error", err)
log.Info("Error updating check type, ignoring", "error", err)
}
return nil
}
@@ -123,9 +124,13 @@ func (r *Runner) Run(ctx context.Context) error {
for i := 0; i < r.retryAttempts; i++ {
err := r.createOrUpdate(context.WithoutCancel(ctx), logger, obj)
if err != nil {
logger.Error("Error creating check type, retrying", "error", err, "attempt", i+1)
if strings.Contains(err.Error(), "apiserver is shutting down") {
logger.Debug("Error creating check type, not retrying", "error", err)
return nil
}
logger.Debug("Error creating check type, retrying", "error", err, "attempt", i+1)
if i == r.retryAttempts-1 {
logger.Error("Unable to register check type")
logger.Error("Unable to register check type", "check_type", t.ID(), "error", err)
} else {
// Calculate exponential backoff delay: baseDelay * 2^attempt
delay := r.retryDelay * time.Duration(1<<i)
@@ -123,6 +123,24 @@ func TestCheckTypesRegisterer_Run(t *testing.T) {
},
expectedErr: errors.New("update error"),
},
{
name: "shutting down error",
checks: []checks.Check{
&mockCheck{
id: "check1",
steps: []checks.Step{
&mockStep{id: "step1", title: "Step 1", description: "Description 1"},
},
},
},
createFunc: func(ctx context.Context, id resource.Identifier, obj resource.Object, opts resource.CreateOptions) (resource.Object, error) {
return nil, k8sErrs.NewAlreadyExists(schema.GroupResource{}, obj.GetName())
},
updateFunc: func(ctx context.Context, id resource.Identifier, obj resource.Object, opts resource.UpdateOptions) (resource.Object, error) {
return nil, errors.New("apiserver is shutting down")
},
expectedErr: nil,
},
{
name: "custom namespace",
checks: []checks.Check{
+3 -3
View File
@@ -53,7 +53,7 @@ func processCheck(ctx context.Context, log logging.Logger, client resource.Clien
if setErr != nil {
return setErr
}
return fmt.Errorf("error initializing check: %w", err)
return fmt.Errorf("error listing items for check: %w", err)
}
// Get the check type
var checkType resource.Object
@@ -130,7 +130,7 @@ func processCheckRetry(ctx context.Context, log logging.Logger, client resource.
if setErr != nil {
return setErr
}
return fmt.Errorf("error initializing check: %w", err)
return fmt.Errorf("error getting item for check: %w", err)
}
if item != nil {
// Get the check type
@@ -206,7 +206,7 @@ func runStepsInParallel(ctx context.Context, log logging.Logger, spec *advisorv0
func() {
defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("panic recovered in step %s: %v", step.ID(), r)
log.Error("panic recovered in step", "step", step.ID(), "error", r, "item", item)
}
}()
logger := log.With("step", step.ID())
+2 -3
View File
@@ -178,9 +178,8 @@ func TestProcessCheck_RunRecoversFromPanic(t *testing.T) {
}
err = processCheck(ctx, logging.DefaultLogger, client, typesClient, obj, check)
assert.Error(t, err)
assert.Contains(t, err.Error(), "panic recovered in step")
assert.Equal(t, checks.StatusAnnotationError, obj.GetAnnotations()[checks.StatusAnnotation])
assert.NoError(t, err)
assert.Equal(t, checks.StatusAnnotationProcessed, obj.GetAnnotations()[checks.StatusAnnotation])
}
func TestProcessCheckRetry_NoRetry(t *testing.T) {
+1 -2
View File
@@ -70,14 +70,13 @@ require (
go.etcd.io/etcd/client/v3 v3.5.21 // indirect
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 // indirect
go.opentelemetry.io/otel v1.36.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.36.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.36.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.36.0 // indirect
go.opentelemetry.io/otel/metric v1.36.0 // indirect
go.opentelemetry.io/otel/sdk v1.36.0 // indirect
go.opentelemetry.io/otel/sdk/metric v1.36.0 // indirect
go.opentelemetry.io/otel/trace v1.36.0 // indirect
go.opentelemetry.io/proto/otlp v1.6.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
+2 -2
View File
@@ -215,8 +215,8 @@ go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJyS
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0 h1:x7wzEgXfnzJcHDwStJT+mxOz4etr2EcexjqhBvmoakw=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0/go.mod h1:rg+RlpR5dKwaS95IyyZqj5Wd4E13lk/msnTS0Xl9lJM=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 h1:sbiXRNDSWJOTobXh5HyQKjq6wUC5tNybqjIqDpAY4CU=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0/go.mod h1:69uWxva0WgAA/4bu2Yy70SLDBwZXuQ6PbBpbsa5iZrQ=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 h1:F7Jx+6hwnZ41NSFTO5q4LYDtJRXBf2PD0rNBkeB/lus=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0/go.mod h1:UHB22Z8QsdRDrnAtX4PntOl36ajSxcdUMt1sF7Y6E7Q=
go.opentelemetry.io/otel v1.36.0 h1:UumtzIklRBY6cI/lllNZlALOF5nNIzJVb16APdvgTXg=
go.opentelemetry.io/otel v1.36.0/go.mod h1:/TcFMXYjyRNh8khOAO9ybYkqaDBb/70aVwkNML4pP8E=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.36.0 h1:dNzwXjZKpMpE2JhmO+9HsPl42NIXFIFSUSSs0fiqra0=
+6 -8
View File
@@ -5,7 +5,7 @@ go 1.24.4
require (
cuelang.org/go v0.11.1
github.com/grafana/grafana-app-sdk v0.39.0
github.com/grafana/grafana-plugin-sdk-go v0.277.0
github.com/grafana/grafana-plugin-sdk-go v0.278.0
github.com/grafana/grafana/pkg/apimachinery v0.0.0-20250514132646-acbc7b54ed9e
github.com/stretchr/testify v1.10.0
k8s.io/apimachinery v0.33.1
@@ -14,12 +14,12 @@ require (
require (
github.com/BurntSushi/toml v1.5.0 // indirect
github.com/apache/arrow-go/v18 v18.2.0 // indirect
github.com/apache/arrow-go/v18 v18.3.0 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/cenkalti/backoff/v5 v5.0.2 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/cheekybits/genny v1.0.0 // indirect
github.com/chromedp/cdproto v0.0.0-20240810084448-b931b754e476 // indirect
github.com/chromedp/cdproto v0.0.0-20250429231605-6ed5b53462d4 // indirect
github.com/cockroachdb/apd/v3 v3.2.1 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.6 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
@@ -39,7 +39,6 @@ require (
github.com/gogo/googleapis v1.4.1 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/protobuf v1.5.4 // indirect
github.com/golang/snappy v1.0.0 // indirect
github.com/google/flatbuffers v25.2.10+incompatible // indirect
github.com/google/gnostic-models v0.6.9 // indirect
github.com/google/go-cmp v0.7.0 // indirect
@@ -48,7 +47,7 @@ require (
github.com/grafana/grafana-app-sdk/logging v0.38.2 // indirect
github.com/grafana/otel-profiling-go v0.5.1 // indirect
github.com/grafana/pyroscope-go/godeltaprof v0.1.8 // indirect
github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1 // indirect
github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.1.0 // indirect
github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.2 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3 // indirect
github.com/hashicorp/go-hclog v1.6.3 // indirect
@@ -95,8 +94,8 @@ require (
github.com/zeebo/xxh3 v1.0.2 // indirect
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.60.0 // indirect
go.opentelemetry.io/contrib/propagators/jaeger v1.35.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.61.0 // indirect
go.opentelemetry.io/contrib/propagators/jaeger v1.36.0 // indirect
go.opentelemetry.io/contrib/samplers/jaegerremote v0.30.0 // indirect
go.opentelemetry.io/otel v1.36.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.36.0 // indirect
@@ -117,7 +116,6 @@ require (
golang.org/x/time v0.11.0 // indirect
golang.org/x/tools v0.34.0 // indirect
golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da // indirect
gonum.org/v1/gonum v0.16.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20250519155744-55703ea1f237 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20250519155744-55703ea1f237 // indirect
google.golang.org/grpc v1.73.0 // indirect
+14 -14
View File
@@ -8,8 +8,8 @@ github.com/BurntSushi/toml v1.5.0 h1:W5quZX/G/csjUnuI8SUYlsHs9M38FC7znL0lIO+DvMg
github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
github.com/andybalholm/brotli v1.1.1 h1:PR2pgnyFznKEugtsUo0xLdDop5SKXd5Qf5ysW+7XdTA=
github.com/andybalholm/brotli v1.1.1/go.mod h1:05ib4cKhjx3OQYUY22hTVd34Bc8upXjOLL2rKwwZBoA=
github.com/apache/arrow-go/v18 v18.2.0 h1:QhWqpgZMKfWOniGPhbUxrHohWnooGURqL2R2Gg4SO1Q=
github.com/apache/arrow-go/v18 v18.2.0/go.mod h1:Ic/01WSwGJWRrdAZcxjBZ5hbApNJ28K96jGYaxzzGUc=
github.com/apache/arrow-go/v18 v18.3.0 h1:Xq4A6dZj9Nu33sqZibzn012LNnewkTUlfKVUFD/RX/I=
github.com/apache/arrow-go/v18 v18.3.0/go.mod h1:eEM1DnUTHhgGAjf/ChvOAQbUQ+EPohtDrArffvUjPg8=
github.com/apache/thrift v0.21.0 h1:tdPmh/ptjE1IJnhbhrcl2++TauVjy242rkV/UzJChnE=
github.com/apache/thrift v0.21.0/go.mod h1:W1H8aR/QRtYNvrPeFXBtobyRkd0/YVhTc6i07XIAgDw=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
@@ -22,8 +22,8 @@ github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UF
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cheekybits/genny v1.0.0 h1:uGGa4nei+j20rOSeDeP5Of12XVm7TGUd4dJA9RDitfE=
github.com/cheekybits/genny v1.0.0/go.mod h1:+tQajlRqAUrPI7DOSpB0XAqZYtQakVtB7wXkRAgjxjQ=
github.com/chromedp/cdproto v0.0.0-20240810084448-b931b754e476 h1:VnjHsRXCRti7Av7E+j4DCha3kf68echfDzQ+wD11SBU=
github.com/chromedp/cdproto v0.0.0-20240810084448-b931b754e476/go.mod h1:GKljq0VrfU4D5yc+2qA6OVr8pmO/MBbPEWqWQ/oqGEs=
github.com/chromedp/cdproto v0.0.0-20250429231605-6ed5b53462d4 h1:UZdrvid2JFwnvPlUSEFlE794XZL4Jmrj8fuxfcLECJE=
github.com/chromedp/cdproto v0.0.0-20250429231605-6ed5b53462d4/go.mod h1:NItd7aLkcfOA/dcMXvl8p1u+lQqioRMq/SqDp71Pb/k=
github.com/cockroachdb/apd/v3 v3.2.1 h1:U+8j7t0axsIgvQUqthuNm82HIrYXodOV2iWLWtEaIwg=
github.com/cockroachdb/apd/v3 v3.2.1/go.mod h1:klXJcjp+FffLTHlhIG69tezTDvdP065naDsHzKhYSqc=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
@@ -98,16 +98,16 @@ github.com/grafana/grafana-app-sdk v0.39.0 h1:WC2E9BKXWDX/e2bajdAFjQEyyWf9BFp7Yz
github.com/grafana/grafana-app-sdk v0.39.0/go.mod h1:xRyBQOttgWTc3tGe9pI0upnpEPVhzALf7Mh/61O4zyY=
github.com/grafana/grafana-app-sdk/logging v0.38.2 h1:EdQTRxbbH72zdqJ09Z76zcSjfALJXkpPLgvKEPPnloc=
github.com/grafana/grafana-app-sdk/logging v0.38.2/go.mod h1:Y/bvbDhBiV/tkIle9RW49pgfSPIPSON8Q4qjx3pyqDk=
github.com/grafana/grafana-plugin-sdk-go v0.277.0 h1:VDU2F4Y5NeRS//ejctdZtsAshrGaEdbtW33FsK0EQss=
github.com/grafana/grafana-plugin-sdk-go v0.277.0/go.mod h1:mAUWg68w5+1f5TLDqagIr8sWr1RT9h7ufJl5NMcWJAU=
github.com/grafana/grafana-plugin-sdk-go v0.278.0 h1:5/rIYparLi02pofdaag8wnjspMMVNCi8cZhC4cdC3Ho=
github.com/grafana/grafana-plugin-sdk-go v0.278.0/go.mod h1:+8NXT/XUJ/89GV6FxGQ366NZ3nU+cAXDMd0OUESF9H4=
github.com/grafana/grafana/pkg/apimachinery v0.0.0-20250514132646-acbc7b54ed9e h1:BTKk7LHuG1kmAkucwTA7DuMbKpKvJTKrGdBmUNO4dfQ=
github.com/grafana/grafana/pkg/apimachinery v0.0.0-20250514132646-acbc7b54ed9e/go.mod h1:IA4SOwun8QyST9c5UNs/fN37XL6boXXDvRYFcFwbipg=
github.com/grafana/otel-profiling-go v0.5.1 h1:stVPKAFZSa7eGiqbYuG25VcqYksR6iWvF3YH66t4qL8=
github.com/grafana/otel-profiling-go v0.5.1/go.mod h1:ftN/t5A/4gQI19/8MoWurBEtC6gFw8Dns1sJZ9W4Tls=
github.com/grafana/pyroscope-go/godeltaprof v0.1.8 h1:iwOtYXeeVSAeYefJNaxDytgjKtUuKQbJqgAIjlnicKg=
github.com/grafana/pyroscope-go/godeltaprof v0.1.8/go.mod h1:2+l7K7twW49Ct4wFluZD3tZ6e0SjanjcUUBPVD/UuGU=
github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1 h1:qnpSQwGEnkcRpTqNOIR6bJbR0gAorgP9CSALpRcKoAA=
github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1/go.mod h1:lXGCsh6c22WGtjr+qGHj1otzZpV/1kwTMAqkwZsnWRU=
github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.1.0 h1:QGLs/O40yoNK9vmy4rhUGBVyMf1lISBGtXRpsu/Qu/o=
github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.1.0/go.mod h1:hM2alZsMUni80N33RBe6J0e423LB+odMj7d3EMP9l20=
github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.2 h1:sGm2vDRFUrQJO/Veii4h4zG2vvqG6uWNkBHSTqXOZk0=
github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.2/go.mod h1:wd1YpapPLivG6nQgbf7ZkG1hhSOXDhhn4MLTknx2aAc=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3 h1:5ZPtiqj0JL5oKWmcsq4VMaAW5ukBEgSGXEN89zeH1Jo=
@@ -268,12 +268,12 @@ go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJyS
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0 h1:x7wzEgXfnzJcHDwStJT+mxOz4etr2EcexjqhBvmoakw=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0/go.mod h1:rg+RlpR5dKwaS95IyyZqj5Wd4E13lk/msnTS0Xl9lJM=
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.60.0 h1:0tY123n7CdWMem7MOVdKOt0YfshufLCwfE5Bob+hQuM=
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.60.0/go.mod h1:CosX/aS4eHnG9D7nESYpV753l4j9q5j3SL/PUYd2lR8=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 h1:sbiXRNDSWJOTobXh5HyQKjq6wUC5tNybqjIqDpAY4CU=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0/go.mod h1:69uWxva0WgAA/4bu2Yy70SLDBwZXuQ6PbBpbsa5iZrQ=
go.opentelemetry.io/contrib/propagators/jaeger v1.35.0 h1:UIrZgRBHUrYRlJ4V419lVb4rs2ar0wFzKNAebaP05XU=
go.opentelemetry.io/contrib/propagators/jaeger v1.35.0/go.mod h1:0ciyFyYZxE6JqRAQvIgGRabKWDUmNdW3GAQb6y/RlFU=
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.61.0 h1:lREC4C0ilyP4WibDhQ7Gg2ygAQFP8oR07Fst/5cafwI=
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.61.0/go.mod h1:HfvuU0kW9HewH14VCOLImqKvUgONodURG7Alj/IrnGI=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 h1:F7Jx+6hwnZ41NSFTO5q4LYDtJRXBf2PD0rNBkeB/lus=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0/go.mod h1:UHB22Z8QsdRDrnAtX4PntOl36ajSxcdUMt1sF7Y6E7Q=
go.opentelemetry.io/contrib/propagators/jaeger v1.36.0 h1:SoCgXYF4ISDtNyfLUzsGDaaudZVTx2yJhOyBO0+/GYk=
go.opentelemetry.io/contrib/propagators/jaeger v1.36.0/go.mod h1:VHu48l0YTRKSObdPQ+Sb8xMZvdnJlN7yhHuHoPgNqHM=
go.opentelemetry.io/contrib/samplers/jaegerremote v0.30.0 h1:bQ1Gvah4Sp8z7epSkgJaNTuZm7sutfA6Fji2/7cKFMc=
go.opentelemetry.io/contrib/samplers/jaegerremote v0.30.0/go.mod h1:9b8Q9rH52NgYH3ShiTFB5wf18Vt3RTH/VMB7LDcC1ug=
go.opentelemetry.io/otel v1.21.0/go.mod h1:QZzNPQPm1zLX4gZK4cMi+71eaorMSGT3A4znnUvNNEo=
@@ -0,0 +1,110 @@
package schemaversion
// Shared utility functions for datasource migrations across different schema versions.
// These functions handle the common logic for migrating datasource references from
// string names/UIDs to structured reference objects with uid, type, and apiVersion.
// GetDataSourceRef creates a datasource reference object with uid, type and optional apiVersion
func GetDataSourceRef(ds *DataSourceInfo) map[string]interface{} {
if ds == nil {
return nil
}
ref := map[string]interface{}{
"uid": ds.UID,
"type": ds.Type,
}
if ds.APIVersion != "" {
ref["apiVersion"] = ds.APIVersion
}
return ref
}
// GetDefaultDSInstanceSettings returns the default datasource if one exists
func GetDefaultDSInstanceSettings(datasources []DataSourceInfo) *DataSourceInfo {
for _, ds := range datasources {
if ds.Default {
return &DataSourceInfo{
UID: ds.UID,
Type: ds.Type,
Name: ds.Name,
APIVersion: ds.APIVersion,
}
}
}
return nil
}
// GetInstanceSettings looks up a datasource by name or uid reference
func GetInstanceSettings(nameOrRef interface{}, datasources []DataSourceInfo) *DataSourceInfo {
if nameOrRef == nil || nameOrRef == "default" {
return GetDefaultDSInstanceSettings(datasources)
}
// Check if it's a reference object without UID - should return default
if ref, ok := nameOrRef.(map[string]interface{}); ok {
if _, hasUID := ref["uid"]; !hasUID {
return GetDefaultDSInstanceSettings(datasources)
}
// It's a reference object with UID, search for matching UID
for _, ds := range datasources {
if uid, hasUID := ref["uid"]; hasUID && uid == ds.UID {
return &DataSourceInfo{
UID: ds.UID,
Type: ds.Type,
Name: ds.Name,
APIVersion: ds.APIVersion,
}
}
}
return GetDefaultDSInstanceSettings(datasources)
}
// Check if it's a string
str, ok := nameOrRef.(string)
if !ok {
return GetDefaultDSInstanceSettings(datasources)
}
// Search for matching name or UID
for _, ds := range datasources {
if str == ds.Name || str == ds.UID {
return &DataSourceInfo{
UID: ds.UID,
Type: ds.Type,
Name: ds.Name,
APIVersion: ds.APIVersion,
}
}
}
return GetDefaultDSInstanceSettings(datasources)
}
// MigrateDatasourceNameToRef converts a datasource name/uid string to a reference object
// Matches the frontend migrateDatasourceNameToRef function in DashboardMigrator.ts
// Options:
// - returnDefaultAsNull: if true, returns nil for "default" datasources (used in V33)
// - returnDefaultAsNull: if false, returns reference for "default" datasources (used in V36)
func MigrateDatasourceNameToRef(nameOrRef interface{}, options map[string]bool, datasources []DataSourceInfo) map[string]interface{} {
if options["returnDefaultAsNull"] && (nameOrRef == nil || nameOrRef == "default") {
return nil
}
if dsRef, ok := nameOrRef.(map[string]interface{}); ok {
if _, hasUID := dsRef["uid"]; hasUID {
return dsRef
}
}
ds := GetInstanceSettings(nameOrRef, datasources)
if ds != nil {
return GetDataSourceRef(ds)
}
if dsName, ok := nameOrRef.(string); ok && dsName != "" {
return map[string]interface{}{
"uid": dsName,
}
}
return nil
}
@@ -0,0 +1,442 @@
package schemaversion_test
import (
"testing"
"github.com/grafana/grafana/apps/dashboard/pkg/migration/schemaversion"
"github.com/stretchr/testify/assert"
)
func TestGetDataSourceRef(t *testing.T) {
tests := []struct {
name string
input *schemaversion.DataSourceInfo
expected map[string]interface{}
}{
{
name: "nil datasource should return nil",
input: nil,
expected: nil,
},
{
name: "datasource without apiVersion",
input: &schemaversion.DataSourceInfo{
UID: "test-uid",
Type: "prometheus",
Name: "Test DS",
},
expected: map[string]interface{}{
"uid": "test-uid",
"type": "prometheus",
},
},
{
name: "datasource with apiVersion",
input: &schemaversion.DataSourceInfo{
UID: "test-uid",
Type: "elasticsearch",
Name: "Test ES",
APIVersion: "v2",
},
expected: map[string]interface{}{
"uid": "test-uid",
"type": "elasticsearch",
"apiVersion": "v2",
},
},
{
name: "datasource with empty apiVersion",
input: &schemaversion.DataSourceInfo{
UID: "test-uid",
Type: "prometheus",
Name: "Test",
APIVersion: "",
},
expected: map[string]interface{}{
"uid": "test-uid",
"type": "prometheus",
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := schemaversion.GetDataSourceRef(tt.input)
assert.Equal(t, tt.expected, result)
})
}
}
func TestGetDefaultDSInstanceSettings(t *testing.T) {
tests := []struct {
name string
datasources []schemaversion.DataSourceInfo
expected *schemaversion.DataSourceInfo
}{
{
name: "empty datasources list",
datasources: []schemaversion.DataSourceInfo{},
expected: nil,
},
{
name: "no default datasource",
datasources: []schemaversion.DataSourceInfo{
{UID: "ds1", Type: "prometheus", Name: "DS1", Default: false},
{UID: "ds2", Type: "elasticsearch", Name: "DS2", Default: false},
},
expected: nil,
},
{
name: "single default datasource",
datasources: []schemaversion.DataSourceInfo{
{UID: "ds1", Type: "prometheus", Name: "DS1", Default: false},
{UID: "default-ds", Type: "prometheus", Name: "Default", Default: true, APIVersion: "v1"},
{UID: "ds2", Type: "elasticsearch", Name: "DS2", Default: false},
},
expected: &schemaversion.DataSourceInfo{
UID: "default-ds",
Type: "prometheus",
Name: "Default",
APIVersion: "v1",
},
},
{
name: "multiple default datasources returns first",
datasources: []schemaversion.DataSourceInfo{
{UID: "ds1", Type: "prometheus", Name: "Default1", Default: true, APIVersion: "v1"},
{UID: "ds2", Type: "elasticsearch", Name: "Default2", Default: true, APIVersion: "v2"},
},
expected: &schemaversion.DataSourceInfo{
UID: "ds1",
Type: "prometheus",
Name: "Default1",
APIVersion: "v1",
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := schemaversion.GetDefaultDSInstanceSettings(tt.datasources)
assert.Equal(t, tt.expected, result)
})
}
}
func TestGetInstanceSettings(t *testing.T) {
datasources := []schemaversion.DataSourceInfo{
{UID: "default-ds", Type: "prometheus", Name: "Default", Default: true, APIVersion: "v1"},
{UID: "other-ds", Type: "elasticsearch", Name: "Elasticsearch", Default: false, APIVersion: "v2"},
{UID: "test-uid", Type: "influxdb", Name: "InfluxDB", Default: false},
}
tests := []struct {
name string
nameOrRef interface{}
expected *schemaversion.DataSourceInfo
}{
{
name: "nil should return default",
nameOrRef: nil,
expected: &schemaversion.DataSourceInfo{
UID: "default-ds",
Type: "prometheus",
Name: "Default",
APIVersion: "v1",
},
},
{
name: "default string should return default",
nameOrRef: "default",
expected: &schemaversion.DataSourceInfo{
UID: "default-ds",
Type: "prometheus",
Name: "Default",
APIVersion: "v1",
},
},
{
name: "lookup by UID",
nameOrRef: "other-ds",
expected: &schemaversion.DataSourceInfo{
UID: "other-ds",
Type: "elasticsearch",
Name: "Elasticsearch",
APIVersion: "v2",
},
},
{
name: "lookup by name",
nameOrRef: "Elasticsearch",
expected: &schemaversion.DataSourceInfo{
UID: "other-ds",
Type: "elasticsearch",
Name: "Elasticsearch",
APIVersion: "v2",
},
},
{
name: "lookup by UID without apiVersion",
nameOrRef: "test-uid",
expected: &schemaversion.DataSourceInfo{
UID: "test-uid",
Type: "influxdb",
Name: "InfluxDB",
APIVersion: "",
},
},
{
name: "lookup by reference object with UID",
nameOrRef: map[string]interface{}{
"uid": "other-ds",
},
expected: &schemaversion.DataSourceInfo{
UID: "other-ds",
Type: "elasticsearch",
Name: "Elasticsearch",
APIVersion: "v2",
},
},
{
name: "lookup by reference object without UID",
nameOrRef: map[string]interface{}{
"type": "prometheus",
},
expected: &schemaversion.DataSourceInfo{
UID: "default-ds",
Type: "prometheus",
Name: "Default",
APIVersion: "v1",
},
},
{
name: "unknown datasource should return default",
nameOrRef: "unknown-ds",
expected: &schemaversion.DataSourceInfo{
UID: "default-ds",
Type: "prometheus",
Name: "Default",
APIVersion: "v1",
},
},
{
name: "empty string should return default",
nameOrRef: "",
expected: &schemaversion.DataSourceInfo{
UID: "default-ds",
Type: "prometheus",
Name: "Default",
APIVersion: "v1",
},
},
{
name: "unsupported input type should return default",
nameOrRef: 123,
expected: &schemaversion.DataSourceInfo{
UID: "default-ds",
Type: "prometheus",
Name: "Default",
APIVersion: "v1",
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := schemaversion.GetInstanceSettings(tt.nameOrRef, datasources)
assert.Equal(t, tt.expected, result)
})
}
}
func TestMigrateDatasourceNameToRef(t *testing.T) {
datasources := []schemaversion.DataSourceInfo{
{UID: "default-ds", Type: "prometheus", Name: "Default", Default: true, APIVersion: "v1"},
{UID: "other-ds", Type: "elasticsearch", Name: "Elasticsearch", Default: false, APIVersion: "v2"},
{UID: "test-uid", Type: "influxdb", Name: "InfluxDB", Default: false},
}
t.Run("returnDefaultAsNull: true", func(t *testing.T) {
options := map[string]bool{"returnDefaultAsNull": true}
tests := []struct {
name string
nameOrRef interface{}
expected map[string]interface{}
}{
{
name: "nil should return nil",
nameOrRef: nil,
expected: nil,
},
{
name: "default should return nil",
nameOrRef: "default",
expected: nil,
},
{
name: "existing reference should be preserved",
nameOrRef: map[string]interface{}{
"uid": "existing-uid",
"type": "existing-type",
},
expected: map[string]interface{}{
"uid": "existing-uid",
"type": "existing-type",
},
},
{
name: "lookup by UID",
nameOrRef: "other-ds",
expected: map[string]interface{}{
"uid": "other-ds",
"type": "elasticsearch",
"apiVersion": "v2",
},
},
{
name: "lookup by name",
nameOrRef: "Elasticsearch",
expected: map[string]interface{}{
"uid": "other-ds",
"type": "elasticsearch",
"apiVersion": "v2",
},
},
{
name: "unknown datasource should return default reference",
nameOrRef: "unknown-ds",
expected: map[string]interface{}{
"uid": "default-ds",
"type": "prometheus",
"apiVersion": "v1",
},
},
{
name: "empty string should return default reference",
nameOrRef: "",
expected: map[string]interface{}{
"uid": "default-ds",
"type": "prometheus",
"apiVersion": "v1",
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := schemaversion.MigrateDatasourceNameToRef(tt.nameOrRef, options, datasources)
assert.Equal(t, tt.expected, result)
})
}
})
t.Run("returnDefaultAsNull: false", func(t *testing.T) {
options := map[string]bool{"returnDefaultAsNull": false}
tests := []struct {
name string
nameOrRef interface{}
expected map[string]interface{}
}{
{
name: "nil should return default reference",
nameOrRef: nil,
expected: map[string]interface{}{
"uid": "default-ds",
"type": "prometheus",
"apiVersion": "v1",
},
},
{
name: "default should return default reference",
nameOrRef: "default",
expected: map[string]interface{}{
"uid": "default-ds",
"type": "prometheus",
"apiVersion": "v1",
},
},
{
name: "existing reference should be preserved",
nameOrRef: map[string]interface{}{
"uid": "existing-uid",
"type": "existing-type",
},
expected: map[string]interface{}{
"uid": "existing-uid",
"type": "existing-type",
},
},
{
name: "lookup by UID",
nameOrRef: "other-ds",
expected: map[string]interface{}{
"uid": "other-ds",
"type": "elasticsearch",
"apiVersion": "v2",
},
},
{
name: "unknown datasource should return default reference",
nameOrRef: "unknown-ds",
expected: map[string]interface{}{
"uid": "default-ds",
"type": "prometheus",
"apiVersion": "v1",
},
},
{
name: "empty string should return default reference",
nameOrRef: "",
expected: map[string]interface{}{
"uid": "default-ds",
"type": "prometheus",
"apiVersion": "v1",
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := schemaversion.MigrateDatasourceNameToRef(tt.nameOrRef, options, datasources)
assert.Equal(t, tt.expected, result)
})
}
})
t.Run("edge cases", func(t *testing.T) {
options := map[string]bool{"returnDefaultAsNull": false}
t.Run("reference without uid should lookup default", func(t *testing.T) {
nameOrRef := map[string]interface{}{
"type": "prometheus",
}
result := schemaversion.MigrateDatasourceNameToRef(nameOrRef, options, datasources)
expected := map[string]interface{}{
"uid": "default-ds",
"type": "prometheus",
"apiVersion": "v1",
}
assert.Equal(t, expected, result)
})
t.Run("integer input should return default reference", func(t *testing.T) {
result := schemaversion.MigrateDatasourceNameToRef(123, options, datasources)
expected := map[string]interface{}{
"uid": "default-ds",
"type": "prometheus",
"apiVersion": "v1",
}
assert.Equal(t, expected, result)
})
t.Run("empty datasources list", func(t *testing.T) {
result := schemaversion.MigrateDatasourceNameToRef("any-ds", options, []schemaversion.DataSourceInfo{})
expected := map[string]interface{}{
"uid": "any-ds",
}
assert.Equal(t, expected, result)
})
})
}
@@ -5,7 +5,7 @@ import (
)
const (
MIN_VERSION = 33
MIN_VERSION = 32
LATEST_VERSION = 41
)
@@ -26,6 +26,7 @@ type DataSourceInfoProvider interface {
func GetMigrations(dsInfoProvider DataSourceInfoProvider) map[int]SchemaVersionMigrationFunc {
return map[int]SchemaVersionMigrationFunc{
33: V33(dsInfoProvider),
34: V34,
35: V35,
36: V36(dsInfoProvider),
@@ -0,0 +1,139 @@
package schemaversion
// V33 migrates panel datasource references from string names to UIDs.
//
// This migration addresses datasource references in dashboard panels and their targets
// that use the legacy string format. The migration converts these references to the new
// structured format with uid, type, and apiVersion fields.
//
// The migration works by:
// 1. Identifying datasource references in panel-level datasource fields
// 2. Converting string datasource references to structured reference objects
// 3. Migrating target-level datasource references within each panel
// 4. Handling nested panels in collapsed rows
// 5. Always setting panel datasource (even if migration returns nil)
// 6. Only setting target datasource if migration returns non-nil (preserves originals when nil)
// 7. Using returnDefaultAsNull: true (so "default" and nil become nil for panels, preserved for targets)
//
// Panel Datasource Example - String to Object:
//
// Before migration:
//
// panel: {
// "datasource": "prometheus-uid",
// "targets": [
// { "refId": "A", "datasource": "elasticsearch-name" },
// { "refId": "B", "datasource": null }
// ]
// }
//
// After migration:
//
// panel: {
// "datasource": { "uid": "prometheus-uid", "type": "prometheus", "apiVersion": "v1" },
// "targets": [
// { "refId": "A", "datasource": { "uid": "elasticsearch-uid", "type": "elasticsearch", "apiVersion": "v2" } },
// { "refId": "B", "datasource": null }
// ]
// }
//
// Default Datasource Example - Null Conversion:
//
// Before migration:
//
// panel: {
// "datasource": "default",
// "targets": [
// { "refId": "A", "datasource": "default" }
// ]
// }
//
// After migration:
//
// panel: {
// "datasource": null,
// "targets": [
// { "refId": "A", "datasource": "default" }
// ]
// }
func V33(dsInfo DataSourceInfoProvider) SchemaVersionMigrationFunc {
datasources := dsInfo.GetDataSourceInfo()
return func(dashboard map[string]interface{}) error {
if dashboard == nil {
dashboard = map[string]interface{}{}
}
dashboard["schemaVersion"] = int(33)
migratePanelsV33(dashboard, datasources)
return nil
}
}
// migratePanelsV33 updates datasource references in dashboard panels for V33 migration
func migratePanelsV33(dashboard map[string]interface{}, datasources []DataSourceInfo) {
if dashboard == nil {
return
}
panels, ok := dashboard["panels"].([]interface{})
if !ok {
return
}
for _, panel := range panels {
panelMap, ok := panel.(map[string]interface{})
if !ok {
continue
}
migratePanelDatasourcesV33(panelMap, datasources)
// Handle nested panels in collapsed rows
nestedPanels, hasNested := panelMap["panels"].([]interface{})
if !hasNested {
continue
}
for _, nestedPanel := range nestedPanels {
np, ok := nestedPanel.(map[string]interface{})
if !ok {
continue
}
migratePanelDatasourcesV33(np, datasources)
}
}
}
// migratePanelDatasourcesV33 updates datasource references in a single panel and its targets for V33 migration
func migratePanelDatasourcesV33(panelMap map[string]interface{}, datasources []DataSourceInfo) {
// Handle panel datasource - always set result (even if nil)
if result := MigrateDatasourceNameToRef(panelMap["datasource"], map[string]bool{"returnDefaultAsNull": true}, datasources); result != nil {
panelMap["datasource"] = result
} else {
panelMap["datasource"] = nil
}
// Handle target datasources
targets, hasTargets := panelMap["targets"].([]interface{})
if !hasTargets {
return
}
for _, target := range targets {
targetMap, ok := target.(map[string]interface{})
if !ok {
continue
}
ds, exists := targetMap["datasource"]
if !exists {
continue
}
// Only set target datasource if migration result is not nil
if targetRef := MigrateDatasourceNameToRef(ds, map[string]bool{"returnDefaultAsNull": true}, datasources); targetRef != nil {
targetMap["datasource"] = targetRef
}
// If targetRef is nil, leave target.datasource unchanged (preserves "default" strings, etc.)
}
}
@@ -0,0 +1,428 @@
package schemaversion_test
import (
"testing"
"github.com/grafana/grafana/apps/dashboard/pkg/migration/schemaversion"
"github.com/grafana/grafana/apps/dashboard/pkg/migration/testutil"
)
func TestV33(t *testing.T) {
// Pass the mock provider to V33
migration := schemaversion.V33(testutil.GetTestProvider())
tests := []migrationTestCase{
{
name: "dashboard with no panels",
input: map[string]interface{}{
"schemaVersion": 32,
},
expected: map[string]interface{}{
"schemaVersion": 33,
},
},
{
name: "panel with default datasource should return null",
input: map[string]interface{}{
"schemaVersion": 32,
"panels": []interface{}{
map[string]interface{}{
"datasource": "default",
"targets": []interface{}{
map[string]interface{}{
"datasource": "default",
},
},
},
},
},
expected: map[string]interface{}{
"schemaVersion": 33,
"panels": []interface{}{
map[string]interface{}{
"datasource": nil,
"targets": []interface{}{
map[string]interface{}{
"datasource": "default",
},
},
},
},
},
},
{
name: "panel with null datasource should return null",
input: map[string]interface{}{
"schemaVersion": 32,
"panels": []interface{}{
map[string]interface{}{
"datasource": nil,
"targets": []interface{}{
map[string]interface{}{
"datasource": nil,
},
},
},
},
},
expected: map[string]interface{}{
"schemaVersion": 33,
"panels": []interface{}{
map[string]interface{}{
"datasource": nil,
"targets": []interface{}{
map[string]interface{}{
"datasource": nil,
},
},
},
},
},
},
{
name: "panel with existing datasource reference should be preserved",
input: map[string]interface{}{
"schemaVersion": 32,
"panels": []interface{}{
map[string]interface{}{
"datasource": map[string]interface{}{
"uid": "existing-uid",
"type": "existing-type",
},
"targets": []interface{}{
map[string]interface{}{
"datasource": map[string]interface{}{
"uid": "target-uid",
"type": "target-type",
},
},
},
},
},
},
expected: map[string]interface{}{
"schemaVersion": 33,
"panels": []interface{}{
map[string]interface{}{
"datasource": map[string]interface{}{
"uid": "existing-uid",
"type": "existing-type",
},
"targets": []interface{}{
map[string]interface{}{
"datasource": map[string]interface{}{
"uid": "target-uid",
"type": "target-type",
},
},
},
},
},
},
},
{
name: "panel with datasource by name should be migrated",
input: map[string]interface{}{
"schemaVersion": 32,
"panels": []interface{}{
map[string]interface{}{
"datasource": "Elasticsearch",
"targets": []interface{}{
map[string]interface{}{
"datasource": "Elasticsearch",
},
},
},
},
},
expected: map[string]interface{}{
"schemaVersion": 33,
"panels": []interface{}{
map[string]interface{}{
"datasource": map[string]interface{}{
"type": "elasticsearch",
"uid": "other-ds",
"apiVersion": "v2",
},
"targets": []interface{}{
map[string]interface{}{
"datasource": map[string]interface{}{
"type": "elasticsearch",
"uid": "other-ds",
"apiVersion": "v2",
},
},
},
},
},
},
},
{
name: "panel with datasource by UID should be migrated",
input: map[string]interface{}{
"schemaVersion": 32,
"panels": []interface{}{
map[string]interface{}{
"datasource": "other-ds",
"targets": []interface{}{
map[string]interface{}{
"datasource": "other-ds",
},
},
},
},
},
expected: map[string]interface{}{
"schemaVersion": 33,
"panels": []interface{}{
map[string]interface{}{
"datasource": map[string]interface{}{
"type": "elasticsearch",
"uid": "other-ds",
"apiVersion": "v2",
},
"targets": []interface{}{
map[string]interface{}{
"datasource": map[string]interface{}{
"type": "elasticsearch",
"uid": "other-ds",
"apiVersion": "v2",
},
},
},
},
},
},
},
{
name: "panel with unknown datasource should return default reference",
input: map[string]interface{}{
"schemaVersion": 32,
"panels": []interface{}{
map[string]interface{}{
"datasource": "unknown-datasource",
"targets": []interface{}{
map[string]interface{}{
"datasource": "unknown-datasource",
},
},
},
},
},
expected: map[string]interface{}{
"schemaVersion": 33,
"panels": []interface{}{
map[string]interface{}{
"datasource": map[string]interface{}{
"uid": "default-ds",
"type": "prometheus",
"apiVersion": "v1",
},
"targets": []interface{}{
map[string]interface{}{
"datasource": map[string]interface{}{
"uid": "default-ds",
"type": "prometheus",
"apiVersion": "v1",
},
},
},
},
},
},
},
{
name: "panel with mixed datasources",
input: map[string]interface{}{
"schemaVersion": 32,
"panels": []interface{}{
map[string]interface{}{
"datasource": "Elasticsearch",
"targets": []interface{}{
map[string]interface{}{
"datasource": "default",
},
map[string]interface{}{
"datasource": "other-ds",
},
map[string]interface{}{
"datasource": "unknown-ds",
},
},
},
},
},
expected: map[string]interface{}{
"schemaVersion": 33,
"panels": []interface{}{
map[string]interface{}{
"datasource": map[string]interface{}{
"type": "elasticsearch",
"uid": "other-ds",
"apiVersion": "v2",
},
"targets": []interface{}{
map[string]interface{}{
"datasource": "default",
},
map[string]interface{}{
"datasource": map[string]interface{}{
"type": "elasticsearch",
"uid": "other-ds",
"apiVersion": "v2",
},
},
map[string]interface{}{
"datasource": map[string]interface{}{
"uid": "default-ds",
"type": "prometheus",
"apiVersion": "v1",
},
},
},
},
},
},
},
{
name: "panel without targets should not fail",
input: map[string]interface{}{
"schemaVersion": 32,
"panels": []interface{}{
map[string]interface{}{
"datasource": "Elasticsearch",
},
},
},
expected: map[string]interface{}{
"schemaVersion": 33,
"panels": []interface{}{
map[string]interface{}{
"datasource": map[string]interface{}{
"type": "elasticsearch",
"uid": "other-ds",
"apiVersion": "v2",
},
},
},
},
},
{
name: "nested panels in collapsed rows should be migrated",
input: map[string]interface{}{
"schemaVersion": 32,
"panels": []interface{}{
map[string]interface{}{
"type": "row",
"collapsed": true,
"datasource": "Elasticsearch",
"panels": []interface{}{
map[string]interface{}{
"datasource": "default",
"targets": []interface{}{
map[string]interface{}{
"datasource": "other-ds",
},
},
},
map[string]interface{}{
"datasource": "unknown-ds",
"targets": []interface{}{
map[string]interface{}{
"datasource": "Elasticsearch",
},
},
},
},
},
},
},
expected: map[string]interface{}{
"schemaVersion": 33,
"panels": []interface{}{
map[string]interface{}{
"type": "row",
"collapsed": true,
"datasource": map[string]interface{}{
"type": "elasticsearch",
"uid": "other-ds",
"apiVersion": "v2",
},
"panels": []interface{}{
map[string]interface{}{
"datasource": nil,
"targets": []interface{}{
map[string]interface{}{
"datasource": map[string]interface{}{
"type": "elasticsearch",
"uid": "other-ds",
"apiVersion": "v2",
},
},
},
},
map[string]interface{}{
"datasource": map[string]interface{}{
"uid": "default-ds",
"type": "prometheus",
"apiVersion": "v1",
},
"targets": []interface{}{
map[string]interface{}{
"datasource": map[string]interface{}{
"type": "elasticsearch",
"uid": "other-ds",
"apiVersion": "v2",
},
},
},
},
},
},
},
},
},
{
name: "targets with lowercase default keyword should not be updated",
input: map[string]interface{}{
"schemaVersion": 32,
"panels": []interface{}{
map[string]interface{}{
"datasource": "Elasticsearch",
"targets": []interface{}{
map[string]interface{}{
"datasource": "default",
},
map[string]interface{}{
"datasource": nil,
},
},
},
},
},
expected: map[string]interface{}{
"schemaVersion": 33,
"panels": []interface{}{
map[string]interface{}{
"datasource": map[string]interface{}{
"type": "elasticsearch",
"uid": "other-ds",
"apiVersion": "v2",
},
"targets": []interface{}{
map[string]interface{}{
"datasource": "default",
},
map[string]interface{}{
"datasource": nil,
},
},
},
},
},
},
}
runMigrationTests(t, tests, migration)
}
+95 -148
View File
@@ -3,7 +3,6 @@ package schemaversion
// V36 migrates dashboard datasource references from string names to UIDs.
// This migration converts datasource references in annotations, template variables, and panels
// from the old format (string name or UID) to the new format (object with uid, type, apiVersion).
// This matches the frontend migration in DashboardMigrator.ts.
func V36(dsInfo DataSourceInfoProvider) SchemaVersionMigrationFunc {
datasources := dsInfo.GetDataSourceInfo()
return func(dashboard map[string]interface{}) error {
@@ -17,114 +16,30 @@ func V36(dsInfo DataSourceInfoProvider) SchemaVersionMigrationFunc {
}
}
// getDataSourceRef creates a datasource reference object with uid, type and optional apiVersion
func getDataSourceRef(ds *DataSourceInfo) map[string]interface{} {
if ds == nil {
return nil
}
ref := map[string]interface{}{
"uid": ds.UID,
"type": ds.Type,
}
if ds.APIVersion != "" {
ref["apiVersion"] = ds.APIVersion
}
return ref
}
// getDefaultDSInstanceSettings returns the default datasource if one exists
func getDefaultDSInstanceSettings(datasources []DataSourceInfo) *DataSourceInfo {
for _, ds := range datasources {
if ds.Default {
return &DataSourceInfo{
UID: ds.UID,
Type: ds.Type,
Name: ds.Name,
APIVersion: ds.APIVersion,
}
}
}
return nil
}
// getInstanceSettings looks up a datasource by name or uid reference
func getInstanceSettings(nameOrRef interface{}, datasources []DataSourceInfo) *DataSourceInfo {
if nameOrRef == nil || nameOrRef == "default" {
return getDefaultDSInstanceSettings(datasources)
}
for _, ds := range datasources {
if str, ok := nameOrRef.(string); ok {
if str == ds.Name || str == ds.UID {
return &DataSourceInfo{
UID: ds.UID,
Type: ds.Type,
Name: ds.Name,
APIVersion: ds.APIVersion,
}
}
}
if ref, ok := nameOrRef.(map[string]interface{}); ok {
if uid, hasUID := ref["uid"]; hasUID {
if uid == ds.UID {
return &DataSourceInfo{
UID: ds.UID,
Type: ds.Type,
Name: ds.Name,
APIVersion: ds.APIVersion,
}
}
}
}
}
return nil
}
// migrateDatasourceNameToRef converts a datasource name/uid string to a reference object
// Matches the frontend migrateDatasourceNameToRef function in DashboardMigrator.ts
func migrateDatasourceNameToRef(nameOrRef interface{}, options map[string]bool, datasources []DataSourceInfo) map[string]interface{} {
if options["returnDefaultAsNull"] && (nameOrRef == nil || nameOrRef == "default") {
return nil
}
if dsRef, ok := nameOrRef.(map[string]interface{}); ok {
if _, hasUID := dsRef["uid"]; hasUID {
return dsRef
}
}
ds := getInstanceSettings(nameOrRef, datasources)
if ds != nil {
return getDataSourceRef(ds)
}
if dsName, ok := nameOrRef.(string); ok && dsName != "" {
return map[string]interface{}{
"uid": dsName,
}
}
return nil
}
// migrateAnnotations updates datasource references in dashboard annotations
func migrateAnnotations(dashboard map[string]interface{}, datasources []DataSourceInfo) {
annotations, ok := dashboard["annotations"].(map[string]interface{})
if !ok {
return
}
list, ok := annotations["list"].([]interface{})
if !ok {
return
}
for _, query := range list {
queryMap, ok := query.(map[string]interface{})
if !ok {
continue
}
if ds, exists := queryMap["datasource"]; exists {
queryMap["datasource"] = migrateDatasourceNameToRef(ds, map[string]bool{"returnDefaultAsNull": false}, datasources)
ds, exists := queryMap["datasource"]
if !exists {
continue
}
queryMap["datasource"] = MigrateDatasourceNameToRef(ds, map[string]bool{"returnDefaultAsNull": false}, datasources)
}
}
@@ -134,87 +49,119 @@ func migrateTemplateVariables(dashboard map[string]interface{}, datasources []Da
if !ok {
return
}
list, ok := templating["list"].([]interface{})
if !ok {
return
}
for _, variable := range list {
varMap, ok := variable.(map[string]interface{})
if !ok {
continue
}
if varType, ok := varMap["type"].(string); ok && varType == "query" {
if ds, exists := varMap["datasource"]; exists {
varMap["datasource"] = migrateDatasourceNameToRef(ds, map[string]bool{"returnDefaultAsNull": false}, datasources)
}
varType, ok := varMap["type"].(string)
if !ok || varType != "query" {
continue
}
ds, exists := varMap["datasource"]
if !exists {
continue
}
varMap["datasource"] = MigrateDatasourceNameToRef(ds, map[string]bool{"returnDefaultAsNull": false}, datasources)
}
}
// migratePanels updates datasource references in dashboard panels
func migratePanels(dashboard map[string]interface{}, datasources []DataSourceInfo) {
if panels, ok := dashboard["panels"].([]interface{}); ok {
for _, panel := range panels {
if panelMap, ok := panel.(map[string]interface{}); ok {
migratePanelDatasources(panelMap, datasources)
}
panels, ok := dashboard["panels"].([]interface{})
if !ok {
return
}
for _, panel := range panels {
panelMap, ok := panel.(map[string]interface{})
if !ok {
continue
}
migratePanelDatasources(panelMap, datasources)
}
}
// migratePanelDatasources updates datasource references in a single panel and its targets
func migratePanelDatasources(panelMap map[string]interface{}, datasources []DataSourceInfo) {
if targets, hasTargets := panelMap["targets"].([]interface{}); hasTargets && len(targets) > 0 {
panelDataSourceWasDefault := false
targets, hasTargets := panelMap["targets"].([]interface{})
if !hasTargets || len(targets) == 0 {
return
}
// Handle panel datasource
if ds, exists := panelMap["datasource"]; exists {
if ds == nil {
defaultDS := getDefaultDSInstanceSettings(datasources)
panelMap["datasource"] = getDataSourceRef(defaultDS)
panelDataSourceWasDefault = true
} else {
panelMap["datasource"] = migrateDatasourceNameToRef(ds, map[string]bool{"returnDefaultAsNull": true}, datasources)
panelDataSourceWasDefault := false
// Handle panel datasource
if ds, exists := panelMap["datasource"]; exists {
if ds == nil {
defaultDS := GetDefaultDSInstanceSettings(datasources)
panelMap["datasource"] = GetDataSourceRef(defaultDS)
panelDataSourceWasDefault = true
} else {
panelMap["datasource"] = MigrateDatasourceNameToRef(ds, map[string]bool{"returnDefaultAsNull": true}, datasources)
}
}
// Handle target datasources
for _, target := range targets {
targetMap, ok := target.(map[string]interface{})
if !ok {
continue
}
ds, exists := targetMap["datasource"]
// Check if target datasource is null or has no uid
isNullOrNoUID := !exists || ds == nil
if !isNullOrNoUID {
dsMap, ok := ds.(map[string]interface{})
if ok {
uid, hasUID := dsMap["uid"]
if !hasUID || uid == nil {
isNullOrNoUID = true
}
}
}
// Handle target datasources
for _, target := range targets {
if targetMap, ok := target.(map[string]interface{}); ok {
ds, exists := targetMap["datasource"]
// Check if target datasource is null or has no uid
isNullOrNoUID := !exists || ds == nil
if !isNullOrNoUID {
if dsMap, ok := ds.(map[string]interface{}); ok {
if uid, hasUID := dsMap["uid"]; !hasUID || uid == nil {
isNullOrNoUID = true
}
}
}
if isNullOrNoUID {
// If panel doesn't have mixed datasource, use panel's datasource
if panelDS, ok := panelMap["datasource"].(map[string]interface{}); ok {
if uid, hasUID := panelDS["uid"].(string); hasUID && uid != "-- Mixed --" {
targetMap["datasource"] = panelDS
}
}
} else {
// Migrate existing target datasource
targetDS := migrateDatasourceNameToRef(ds, map[string]bool{"returnDefaultAsNull": false}, datasources)
targetMap["datasource"] = targetDS
}
// Update panel datasource if it was default and target is not an expression
if panelDataSourceWasDefault {
if targetDS, ok := targetMap["datasource"].(map[string]interface{}); ok {
if uid, ok := targetDS["uid"].(string); ok && uid != "__expr__" {
panelMap["datasource"] = targetDS
}
}
}
if isNullOrNoUID {
// If panel doesn't have mixed datasource, use panel's datasource
panelDS, ok := panelMap["datasource"].(map[string]interface{})
if !ok {
continue
}
uid, hasUID := panelDS["uid"].(string)
if hasUID && uid != "-- Mixed --" {
targetMap["datasource"] = panelDS
}
} else {
// Migrate existing target datasource
targetDS := MigrateDatasourceNameToRef(ds, map[string]bool{"returnDefaultAsNull": false}, datasources)
targetMap["datasource"] = targetDS
}
// Update panel datasource if it was default and target is not an expression
if !panelDataSourceWasDefault {
continue
}
targetDS, ok := targetMap["datasource"].(map[string]interface{})
if !ok {
continue
}
uid, ok := targetDS["uid"].(string)
if ok && uid != "__expr__" {
panelMap["datasource"] = targetDS
}
}
}
@@ -0,0 +1,561 @@
{
"annotations": {
"list": [
{
"builtIn": 1,
"datasource": {
"type": "grafana",
"uid": "-- Grafana --"
},
"enable": true,
"hide": true,
"iconColor": "rgba(0, 211, 255, 1)",
"name": "Annotations \u0026 Alerts",
"type": "dashboard"
},
{
"datasource": "Non Default Test Datasource",
"enable": true,
"name": "Test Annotation by Name",
"type": "dashboard"
},
{
"datasource": "non-default-test-ds-uid",
"enable": true,
"name": "Test Annotation by UID",
"type": "dashboard"
},
{
"datasource": "default",
"enable": true,
"name": "Test Default Annotation",
"type": "dashboard"
},
{
"datasource": "non-existing-ds",
"enable": true,
"name": "Test Non-existing Annotation",
"type": "dashboard"
},
{
"name": "CloudWatch Annotation Single Stat",
"enable": true,
"iconColor": "red",
"dimensions": {
"InstanceId": "i-123456"
},
"namespace": "AWS/EC2",
"region": "us-east-1",
"prefixMatching": false,
"statistics": ["Average"]
},
{
"name": "CloudWatch Annotation Multiple Stats",
"enable": true,
"iconColor": "blue",
"dimensions": {
"InstanceId": "i-789012"
},
"namespace": "AWS/RDS",
"region": "us-west-2",
"prefixMatching": false,
"statistics": ["Maximum", "Minimum", "Sum"]
},
{
"datasource": "",
"enable": true,
"name": "Test Empty String Annotation",
"type": "dashboard"
},
{
"datasource": "another-missing-ds",
"enable": true,
"name": "Test Another Non-existing Annotation",
"type": "dashboard"
}
]
},
"editable": true,
"fiscalYearStartMonth": 0,
"graphTooltip": 0,
"links": [],
"panels": [
{
"type": "graph",
"options": {},
"title": "No Legend Config",
"id": 1,
"gridPos": {
"h": 8,
"w": 12,
"x": 0,
"y": 0
}
},
{
"options": {
"legend": true
},
"title": "Boolean Legend True",
"id": 2,
"gridPos": {
"h": 8,
"w": 12,
"x": 12,
"y": 0
}
},
{
"options": {
"legend": false
},
"title": "Boolean Legend False",
"id": 3,
"gridPos": {
"h": 8,
"w": 12,
"x": 0,
"y": 8
}
},
{
"options": {
"legend": {
"displayMode": "hidden"
}
},
"title": "Hidden DisplayMode",
"id": 4,
"gridPos": {
"h": 8,
"w": 12,
"x": 12,
"y": 8
}
},
{
"options": {
"legend": {
"showLegend": false
}
},
"title": "ShowLegend False",
"id": 5,
"gridPos": {
"h": 8,
"w": 12,
"x": 0,
"y": 16
}
},
{
"options": {
"legend": {
"displayMode": "table"
}
},
"title": "Visible Legend",
"id": 6,
"gridPos": {
"h": 8,
"w": 12,
"x": 12,
"y": 16
}
},
{
"datasource": "default",
"title": "Mixed Datasources Panel",
"id": 7,
"gridPos": {
"h": 8,
"w": 12,
"x": 0,
"y": 24
},
"targets": [
{
"datasource": "non-default-test-ds-uid"
},
{
"datasource": "Non Default Test Datasource"
}
]
},
{
"datasource": {
"uid": "-- Mixed --"
},
"title": "Mixed Panel with Mixed Targets",
"id": 8,
"gridPos": {
"h": 8,
"w": 12,
"x": 12,
"y": 24
},
"targets": [
{
"datasource": "non-default-test-ds-uid"
},
{
"datasource": "Non Default Test Datasource"
}
]
},
{
"datasource": "non-existing-ds",
"title": "Non-existing Datasource Panel",
"id": 9,
"gridPos": {
"h": 8,
"w": 12,
"x": 0,
"y": 32
},
"targets": [
{
"datasource": "non-existing-ds"
}
]
},
{
"type": "timeseries",
"title": "Timeseries Panel with Hidden Axes",
"id": 10,
"gridPos": {
"h": 8,
"w": 12,
"x": 0,
"y": 40
},
"fieldConfig": {
"defaults": {
"custom": {
"axisPlacement": "hidden"
}
},
"overrides": []
}
},
{
"type": "timeseries",
"title": "CloudWatch Single Query Multiple Stats",
"id": 11,
"gridPos": {
"h": 8,
"w": 12,
"x": 12,
"y": 40
},
"targets": [
{
"refId": "A",
"dimensions": {
"InstanceId": "i-123456"
},
"namespace": "AWS/EC2",
"region": "us-east-1",
"metricName": "CPUUtilization",
"statistics": ["Average", "Maximum", "Minimum"],
"period": "300",
"alias": "CPU Usage"
}
]
},
{
"type": "timeseries",
"title": "Mixed CloudWatch and Prometheus Queries",
"id": 12,
"gridPos": {
"h": 8,
"w": 12,
"x": 0,
"y": 48
},
"targets": [
{
"refId": "A",
"dimensions": {
"LoadBalancer": "my-load-balancer"
},
"namespace": "AWS/ApplicationELB",
"region": "us-west-2",
"metricName": "RequestCount",
"statistics": ["Sum", "Average"]
},
{
"refId": "B",
"expr": "up",
"datasource": "prometheus"
},
{
"refId": "C",
"dimensions": {
"DBInstanceIdentifier": "my-db"
},
"namespace": "AWS/RDS",
"region": "us-east-1",
"metricName": "DatabaseConnections",
"statistics": ["Maximum"]
}
]
},
{
"type": "row",
"collapsed": true,
"title": "Collapsed Row with CloudWatch",
"id": 13,
"gridPos": {
"h": 1,
"w": 24,
"x": 0,
"y": 56
},
"panels": [
{
"type": "timeseries",
"title": "Nested CloudWatch Panel",
"id": 14,
"gridPos": {
"h": 8,
"w": 12,
"x": 0,
"y": 0
},
"targets": [
{
"refId": "A",
"dimensions": {
"QueueName": "my-queue"
},
"namespace": "AWS/SQS",
"region": "us-east-1",
"metricName": "ApproximateNumberOfMessages",
"statistics": ["Average", "Maximum", "Sum"]
}
]
}
]
},
{
"type": "stat",
"title": "V33: Panel with Null Datasource",
"id": 15,
"datasource": null,
"gridPos": {
"h": 4,
"w": 6,
"x": 0,
"y": 64
},
"targets": [
{
"refId": "A",
"datasource": "non-default-test-ds-uid"
}
]
},
{
"type": "stat",
"title": "V33: Panel with Existing Datasource Reference",
"id": 16,
"datasource": {
"uid": "existing-ref-uid",
"type": "prometheus"
},
"gridPos": {
"h": 4,
"w": 6,
"x": 6,
"y": 64
},
"targets": [
{
"refId": "A",
"datasource": {
"uid": "existing-target-uid",
"type": "elasticsearch"
}
}
]
},
{
"type": "table",
"title": "V33: Panel without Targets",
"id": 17,
"datasource": "Non Default Test Datasource",
"gridPos": {
"h": 4,
"w": 6,
"x": 12,
"y": 64
}
},
{
"type": "table",
"title": "V33: Panel with Empty Targets Array",
"id": 18,
"datasource": "default",
"gridPos": {
"h": 4,
"w": 6,
"x": 18,
"y": 64
},
"targets": []
},
{
"type": "graph",
"title": "V33: Target Datasource Edge Cases",
"id": 19,
"datasource": "non-default-test-ds-uid",
"gridPos": {
"h": 8,
"w": 12,
"x": 0,
"y": 68
},
"targets": [
{
"refId": "A",
"datasource": null
},
{
"refId": "B",
"datasource": "default"
},
{
"refId": "C",
"datasource": "non-existing-ds"
},
{
"refId": "D"
}
]
},
{
"type": "timeseries",
"title": "V33: Mixed Target References",
"id": 20,
"datasource": null,
"gridPos": {
"h": 8,
"w": 12,
"x": 12,
"y": 68
},
"targets": [
{
"refId": "A",
"datasource": {
"uid": "existing-ref",
"type": "prometheus"
}
},
{
"refId": "B",
"datasource": "Non Default Test Datasource"
},
{
"refId": "C",
"datasource": "default"
}
]
},
{
"type": "stat",
"title": "V33: Panel with Empty String Datasource",
"id": 21,
"datasource": "",
"gridPos": {
"h": 4,
"w": 6,
"x": 0,
"y": 76
},
"targets": [
{
"refId": "A",
"datasource": ""
}
]
},
{
"type": "table",
"title": "V33: Panel with Another Non-existing Datasource",
"id": 22,
"datasource": "completely-missing-ds",
"gridPos": {
"h": 4,
"w": 6,
"x": 6,
"y": 76
},
"targets": [
{
"refId": "A",
"datasource": "also-missing-ds"
},
{
"refId": "B",
"datasource": ""
}
]
}
],
"preload": false,
"refresh": true,
"schemaVersion": 32,
"tags": [],
"templating": {
"list": [
{
"type": "query",
"datasource": "default",
"name": "default_var"
},
{
"type": "query",
"datasource": "Non Default Test Datasource",
"name": "es_var_by_name"
},
{
"type": "query",
"datasource": "non-default-test-ds-uid",
"name": "es_var_by_uid"
},
{
"type": "query",
"datasource": null,
"name": "null_var"
},
{
"type": "query",
"datasource": "non-existing-ds",
"name": "non_existing_var"
},
{
"type": "query",
"datasource": "",
"name": "empty_string_var"
},
{
"type": "query",
"datasource": "another-non-existing-ds",
"name": "another_non_existing_var"
}
]
},
"time": {
"from": "now-6h",
"to": "now"
},
"timepicker": {
"time_options": ["5m", "15m", "1h", "6h", "12h", "24h", "2d", "7d", "30d"]
},
"timezone": "utc",
"title": "New dashboard",
"version": 0,
"weekStart": ""
}
@@ -0,0 +1,660 @@
{
"annotations": {
"list": [
{
"builtIn": 1,
"datasource": {
"type": "grafana",
"uid": "-- Grafana --"
},
"enable": true,
"hide": true,
"iconColor": "rgba(0, 211, 255, 1)",
"name": "Annotations \u0026 Alerts",
"type": "dashboard"
},
{
"datasource": "Non Default Test Datasource",
"enable": true,
"name": "Test Annotation by Name",
"type": "dashboard"
},
{
"datasource": "non-default-test-ds-uid",
"enable": true,
"name": "Test Annotation by UID",
"type": "dashboard"
},
{
"datasource": "default",
"enable": true,
"name": "Test Default Annotation",
"type": "dashboard"
},
{
"datasource": "non-existing-ds",
"enable": true,
"name": "Test Non-existing Annotation",
"type": "dashboard"
},
{
"dimensions": {
"InstanceId": "i-123456"
},
"enable": true,
"iconColor": "red",
"name": "CloudWatch Annotation Single Stat",
"namespace": "AWS/EC2",
"prefixMatching": false,
"region": "us-east-1",
"statistics": [
"Average"
]
},
{
"dimensions": {
"InstanceId": "i-789012"
},
"enable": true,
"iconColor": "blue",
"name": "CloudWatch Annotation Multiple Stats",
"namespace": "AWS/RDS",
"prefixMatching": false,
"region": "us-west-2",
"statistics": [
"Maximum",
"Minimum",
"Sum"
]
},
{
"datasource": "",
"enable": true,
"name": "Test Empty String Annotation",
"type": "dashboard"
},
{
"datasource": "another-missing-ds",
"enable": true,
"name": "Test Another Non-existing Annotation",
"type": "dashboard"
}
]
},
"editable": true,
"fiscalYearStartMonth": 0,
"graphTooltip": 0,
"links": [],
"panels": [
{
"datasource": null,
"gridPos": {
"h": 8,
"w": 12,
"x": 0,
"y": 0
},
"id": 1,
"options": {},
"title": "No Legend Config",
"type": "graph"
},
{
"datasource": null,
"gridPos": {
"h": 8,
"w": 12,
"x": 12,
"y": 0
},
"id": 2,
"options": {
"legend": true
},
"title": "Boolean Legend True"
},
{
"datasource": null,
"gridPos": {
"h": 8,
"w": 12,
"x": 0,
"y": 8
},
"id": 3,
"options": {
"legend": false
},
"title": "Boolean Legend False"
},
{
"datasource": null,
"gridPos": {
"h": 8,
"w": 12,
"x": 12,
"y": 8
},
"id": 4,
"options": {
"legend": {
"displayMode": "hidden"
}
},
"title": "Hidden DisplayMode"
},
{
"datasource": null,
"gridPos": {
"h": 8,
"w": 12,
"x": 0,
"y": 16
},
"id": 5,
"options": {
"legend": {
"showLegend": false
}
},
"title": "ShowLegend False"
},
{
"datasource": null,
"gridPos": {
"h": 8,
"w": 12,
"x": 12,
"y": 16
},
"id": 6,
"options": {
"legend": {
"displayMode": "table"
}
},
"title": "Visible Legend"
},
{
"datasource": null,
"gridPos": {
"h": 8,
"w": 12,
"x": 0,
"y": 24
},
"id": 7,
"targets": [
{
"datasource": {
"apiVersion": "1",
"type": "loki",
"uid": "non-default-test-ds-uid"
}
},
{
"datasource": {
"apiVersion": "1",
"type": "loki",
"uid": "non-default-test-ds-uid"
}
}
],
"title": "Mixed Datasources Panel"
},
{
"datasource": {
"uid": "-- Mixed --"
},
"gridPos": {
"h": 8,
"w": 12,
"x": 12,
"y": 24
},
"id": 8,
"targets": [
{
"datasource": {
"apiVersion": "1",
"type": "loki",
"uid": "non-default-test-ds-uid"
}
},
{
"datasource": {
"apiVersion": "1",
"type": "loki",
"uid": "non-default-test-ds-uid"
}
}
],
"title": "Mixed Panel with Mixed Targets"
},
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"gridPos": {
"h": 8,
"w": 12,
"x": 0,
"y": 32
},
"id": 9,
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
}
}
],
"title": "Non-existing Datasource Panel"
},
{
"datasource": null,
"fieldConfig": {
"defaults": {
"custom": {
"axisPlacement": "hidden"
}
},
"overrides": []
},
"gridPos": {
"h": 8,
"w": 12,
"x": 0,
"y": 40
},
"id": 10,
"title": "Timeseries Panel with Hidden Axes",
"type": "timeseries"
},
{
"datasource": null,
"gridPos": {
"h": 8,
"w": 12,
"x": 12,
"y": 40
},
"id": 11,
"targets": [
{
"alias": "CPU Usage",
"dimensions": {
"InstanceId": "i-123456"
},
"metricName": "CPUUtilization",
"namespace": "AWS/EC2",
"period": "300",
"refId": "A",
"region": "us-east-1",
"statistics": [
"Average",
"Maximum",
"Minimum"
]
}
],
"title": "CloudWatch Single Query Multiple Stats",
"type": "timeseries"
},
{
"datasource": null,
"gridPos": {
"h": 8,
"w": 12,
"x": 0,
"y": 48
},
"id": 12,
"targets": [
{
"dimensions": {
"LoadBalancer": "my-load-balancer"
},
"metricName": "RequestCount",
"namespace": "AWS/ApplicationELB",
"refId": "A",
"region": "us-west-2",
"statistics": [
"Sum",
"Average"
]
},
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"expr": "up",
"refId": "B"
},
{
"dimensions": {
"DBInstanceIdentifier": "my-db"
},
"metricName": "DatabaseConnections",
"namespace": "AWS/RDS",
"refId": "C",
"region": "us-east-1",
"statistics": [
"Maximum"
]
}
],
"title": "Mixed CloudWatch and Prometheus Queries",
"type": "timeseries"
},
{
"collapsed": true,
"datasource": null,
"gridPos": {
"h": 1,
"w": 24,
"x": 0,
"y": 56
},
"id": 13,
"panels": [
{
"datasource": null,
"gridPos": {
"h": 8,
"w": 12,
"x": 0,
"y": 0
},
"id": 14,
"targets": [
{
"dimensions": {
"QueueName": "my-queue"
},
"metricName": "ApproximateNumberOfMessages",
"namespace": "AWS/SQS",
"refId": "A",
"region": "us-east-1",
"statistics": [
"Average",
"Maximum",
"Sum"
]
}
],
"title": "Nested CloudWatch Panel",
"type": "timeseries"
}
],
"title": "Collapsed Row with CloudWatch",
"type": "row"
},
{
"datasource": null,
"gridPos": {
"h": 4,
"w": 6,
"x": 0,
"y": 64
},
"id": 15,
"targets": [
{
"datasource": {
"apiVersion": "1",
"type": "loki",
"uid": "non-default-test-ds-uid"
},
"refId": "A"
}
],
"title": "V33: Panel with Null Datasource",
"type": "stat"
},
{
"datasource": {
"type": "prometheus",
"uid": "existing-ref-uid"
},
"gridPos": {
"h": 4,
"w": 6,
"x": 6,
"y": 64
},
"id": 16,
"targets": [
{
"datasource": {
"type": "elasticsearch",
"uid": "existing-target-uid"
},
"refId": "A"
}
],
"title": "V33: Panel with Existing Datasource Reference",
"type": "stat"
},
{
"datasource": {
"apiVersion": "1",
"type": "loki",
"uid": "non-default-test-ds-uid"
},
"gridPos": {
"h": 4,
"w": 6,
"x": 12,
"y": 64
},
"id": 17,
"title": "V33: Panel without Targets",
"type": "table"
},
{
"datasource": null,
"gridPos": {
"h": 4,
"w": 6,
"x": 18,
"y": 64
},
"id": 18,
"targets": [],
"title": "V33: Panel with Empty Targets Array",
"type": "table"
},
{
"datasource": {
"apiVersion": "1",
"type": "loki",
"uid": "non-default-test-ds-uid"
},
"gridPos": {
"h": 8,
"w": 12,
"x": 0,
"y": 68
},
"id": 19,
"targets": [
{
"datasource": null,
"refId": "A"
},
{
"datasource": "default",
"refId": "B"
},
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"refId": "C"
},
{
"refId": "D"
}
],
"title": "V33: Target Datasource Edge Cases",
"type": "graph"
},
{
"datasource": null,
"gridPos": {
"h": 8,
"w": 12,
"x": 12,
"y": 68
},
"id": 20,
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "existing-ref"
},
"refId": "A"
},
{
"datasource": {
"apiVersion": "1",
"type": "loki",
"uid": "non-default-test-ds-uid"
},
"refId": "B"
},
{
"datasource": "default",
"refId": "C"
}
],
"title": "V33: Mixed Target References",
"type": "timeseries"
},
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"gridPos": {
"h": 4,
"w": 6,
"x": 0,
"y": 76
},
"id": 21,
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"refId": "A"
}
],
"title": "V33: Panel with Empty String Datasource",
"type": "stat"
},
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"gridPos": {
"h": 4,
"w": 6,
"x": 6,
"y": 76
},
"id": 22,
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"refId": "A"
},
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"refId": "B"
}
],
"title": "V33: Panel with Another Non-existing Datasource",
"type": "table"
}
],
"preload": false,
"refresh": true,
"schemaVersion": 33,
"tags": [],
"templating": {
"list": [
{
"datasource": "default",
"name": "default_var",
"type": "query"
},
{
"datasource": "Non Default Test Datasource",
"name": "es_var_by_name",
"type": "query"
},
{
"datasource": "non-default-test-ds-uid",
"name": "es_var_by_uid",
"type": "query"
},
{
"datasource": null,
"name": "null_var",
"type": "query"
},
{
"datasource": "non-existing-ds",
"name": "non_existing_var",
"type": "query"
},
{
"datasource": "",
"name": "empty_string_var",
"type": "query"
},
{
"datasource": "another-non-existing-ds",
"name": "another_non_existing_var",
"type": "query"
}
]
},
"time": {
"from": "now-6h",
"to": "now"
},
"timepicker": {
"time_options": [
"5m",
"15m",
"1h",
"6h",
"12h",
"24h",
"2d",
"7d",
"30d"
]
},
"timezone": "utc",
"title": "New dashboard",
"version": 0,
"weekStart": ""
}
@@ -0,0 +1,719 @@
{
"annotations": {
"list": [
{
"builtIn": 1,
"datasource": {
"type": "grafana",
"uid": "-- Grafana --"
},
"enable": true,
"hide": true,
"iconColor": "rgba(0, 211, 255, 1)",
"name": "Annotations \u0026 Alerts",
"type": "dashboard"
},
{
"datasource": "Non Default Test Datasource",
"enable": true,
"name": "Test Annotation by Name",
"type": "dashboard"
},
{
"datasource": "non-default-test-ds-uid",
"enable": true,
"name": "Test Annotation by UID",
"type": "dashboard"
},
{
"datasource": "default",
"enable": true,
"name": "Test Default Annotation",
"type": "dashboard"
},
{
"datasource": "non-existing-ds",
"enable": true,
"name": "Test Non-existing Annotation",
"type": "dashboard"
},
{
"dimensions": {
"InstanceId": "i-123456"
},
"enable": true,
"iconColor": "red",
"name": "CloudWatch Annotation Single Stat",
"namespace": "AWS/EC2",
"prefixMatching": false,
"region": "us-east-1",
"statistic": "Average"
},
{
"dimensions": {
"InstanceId": "i-789012"
},
"enable": true,
"iconColor": "blue",
"name": "CloudWatch Annotation Multiple Stats - Maximum",
"namespace": "AWS/RDS",
"prefixMatching": false,
"region": "us-west-2",
"statistic": "Maximum"
},
{
"datasource": "",
"enable": true,
"name": "Test Empty String Annotation",
"type": "dashboard"
},
{
"datasource": "another-missing-ds",
"enable": true,
"name": "Test Another Non-existing Annotation",
"type": "dashboard"
},
{
"dimensions": {
"InstanceId": "i-789012"
},
"enable": true,
"iconColor": "blue",
"name": "CloudWatch Annotation Multiple Stats - Minimum",
"namespace": "AWS/RDS",
"prefixMatching": false,
"region": "us-west-2",
"statistic": "Minimum"
},
{
"dimensions": {
"InstanceId": "i-789012"
},
"enable": true,
"iconColor": "blue",
"name": "CloudWatch Annotation Multiple Stats - Sum",
"namespace": "AWS/RDS",
"prefixMatching": false,
"region": "us-west-2",
"statistic": "Sum"
}
]
},
"editable": true,
"fiscalYearStartMonth": 0,
"graphTooltip": 0,
"links": [],
"panels": [
{
"datasource": null,
"gridPos": {
"h": 8,
"w": 12,
"x": 0,
"y": 0
},
"id": 1,
"options": {},
"title": "No Legend Config",
"type": "graph"
},
{
"datasource": null,
"gridPos": {
"h": 8,
"w": 12,
"x": 12,
"y": 0
},
"id": 2,
"options": {
"legend": true
},
"title": "Boolean Legend True"
},
{
"datasource": null,
"gridPos": {
"h": 8,
"w": 12,
"x": 0,
"y": 8
},
"id": 3,
"options": {
"legend": false
},
"title": "Boolean Legend False"
},
{
"datasource": null,
"gridPos": {
"h": 8,
"w": 12,
"x": 12,
"y": 8
},
"id": 4,
"options": {
"legend": {
"displayMode": "hidden"
}
},
"title": "Hidden DisplayMode"
},
{
"datasource": null,
"gridPos": {
"h": 8,
"w": 12,
"x": 0,
"y": 16
},
"id": 5,
"options": {
"legend": {
"showLegend": false
}
},
"title": "ShowLegend False"
},
{
"datasource": null,
"gridPos": {
"h": 8,
"w": 12,
"x": 12,
"y": 16
},
"id": 6,
"options": {
"legend": {
"displayMode": "table"
}
},
"title": "Visible Legend"
},
{
"datasource": null,
"gridPos": {
"h": 8,
"w": 12,
"x": 0,
"y": 24
},
"id": 7,
"targets": [
{
"datasource": {
"apiVersion": "1",
"type": "loki",
"uid": "non-default-test-ds-uid"
}
},
{
"datasource": {
"apiVersion": "1",
"type": "loki",
"uid": "non-default-test-ds-uid"
}
}
],
"title": "Mixed Datasources Panel"
},
{
"datasource": {
"uid": "-- Mixed --"
},
"gridPos": {
"h": 8,
"w": 12,
"x": 12,
"y": 24
},
"id": 8,
"targets": [
{
"datasource": {
"apiVersion": "1",
"type": "loki",
"uid": "non-default-test-ds-uid"
}
},
{
"datasource": {
"apiVersion": "1",
"type": "loki",
"uid": "non-default-test-ds-uid"
}
}
],
"title": "Mixed Panel with Mixed Targets"
},
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"gridPos": {
"h": 8,
"w": 12,
"x": 0,
"y": 32
},
"id": 9,
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
}
}
],
"title": "Non-existing Datasource Panel"
},
{
"datasource": null,
"fieldConfig": {
"defaults": {
"custom": {
"axisPlacement": "hidden"
}
},
"overrides": []
},
"gridPos": {
"h": 8,
"w": 12,
"x": 0,
"y": 40
},
"id": 10,
"title": "Timeseries Panel with Hidden Axes",
"type": "timeseries"
},
{
"datasource": null,
"gridPos": {
"h": 8,
"w": 12,
"x": 12,
"y": 40
},
"id": 11,
"targets": [
{
"alias": "CPU Usage",
"dimensions": {
"InstanceId": "i-123456"
},
"metricName": "CPUUtilization",
"namespace": "AWS/EC2",
"period": "300",
"refId": "A",
"region": "us-east-1",
"statistic": "Average"
},
{
"alias": "CPU Usage",
"dimensions": {
"InstanceId": "i-123456"
},
"metricName": "CPUUtilization",
"namespace": "AWS/EC2",
"period": "300",
"refId": "B",
"region": "us-east-1",
"statistic": "Maximum"
},
{
"alias": "CPU Usage",
"dimensions": {
"InstanceId": "i-123456"
},
"metricName": "CPUUtilization",
"namespace": "AWS/EC2",
"period": "300",
"refId": "C",
"region": "us-east-1",
"statistic": "Minimum"
}
],
"title": "CloudWatch Single Query Multiple Stats",
"type": "timeseries"
},
{
"datasource": null,
"gridPos": {
"h": 8,
"w": 12,
"x": 0,
"y": 48
},
"id": 12,
"targets": [
{
"dimensions": {
"LoadBalancer": "my-load-balancer"
},
"metricName": "RequestCount",
"namespace": "AWS/ApplicationELB",
"refId": "A",
"region": "us-west-2",
"statistic": "Sum"
},
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"expr": "up",
"refId": "B"
},
{
"dimensions": {
"DBInstanceIdentifier": "my-db"
},
"metricName": "DatabaseConnections",
"namespace": "AWS/RDS",
"refId": "C",
"region": "us-east-1",
"statistic": "Maximum"
},
{
"dimensions": {
"LoadBalancer": "my-load-balancer"
},
"metricName": "RequestCount",
"namespace": "AWS/ApplicationELB",
"refId": "D",
"region": "us-west-2",
"statistic": "Average"
}
],
"title": "Mixed CloudWatch and Prometheus Queries",
"type": "timeseries"
},
{
"collapsed": true,
"datasource": null,
"gridPos": {
"h": 1,
"w": 24,
"x": 0,
"y": 56
},
"id": 13,
"panels": [
{
"datasource": null,
"gridPos": {
"h": 8,
"w": 12,
"x": 0,
"y": 0
},
"id": 14,
"targets": [
{
"dimensions": {
"QueueName": "my-queue"
},
"metricName": "ApproximateNumberOfMessages",
"namespace": "AWS/SQS",
"refId": "A",
"region": "us-east-1",
"statistic": "Average"
},
{
"dimensions": {
"QueueName": "my-queue"
},
"metricName": "ApproximateNumberOfMessages",
"namespace": "AWS/SQS",
"refId": "B",
"region": "us-east-1",
"statistic": "Maximum"
},
{
"dimensions": {
"QueueName": "my-queue"
},
"metricName": "ApproximateNumberOfMessages",
"namespace": "AWS/SQS",
"refId": "C",
"region": "us-east-1",
"statistic": "Sum"
}
],
"title": "Nested CloudWatch Panel",
"type": "timeseries"
}
],
"title": "Collapsed Row with CloudWatch",
"type": "row"
},
{
"datasource": null,
"gridPos": {
"h": 4,
"w": 6,
"x": 0,
"y": 64
},
"id": 15,
"targets": [
{
"datasource": {
"apiVersion": "1",
"type": "loki",
"uid": "non-default-test-ds-uid"
},
"refId": "A"
}
],
"title": "V33: Panel with Null Datasource",
"type": "stat"
},
{
"datasource": {
"type": "prometheus",
"uid": "existing-ref-uid"
},
"gridPos": {
"h": 4,
"w": 6,
"x": 6,
"y": 64
},
"id": 16,
"targets": [
{
"datasource": {
"type": "elasticsearch",
"uid": "existing-target-uid"
},
"refId": "A"
}
],
"title": "V33: Panel with Existing Datasource Reference",
"type": "stat"
},
{
"datasource": {
"apiVersion": "1",
"type": "loki",
"uid": "non-default-test-ds-uid"
},
"gridPos": {
"h": 4,
"w": 6,
"x": 12,
"y": 64
},
"id": 17,
"title": "V33: Panel without Targets",
"type": "table"
},
{
"datasource": null,
"gridPos": {
"h": 4,
"w": 6,
"x": 18,
"y": 64
},
"id": 18,
"targets": null,
"title": "V33: Panel with Empty Targets Array",
"type": "table"
},
{
"datasource": {
"apiVersion": "1",
"type": "loki",
"uid": "non-default-test-ds-uid"
},
"gridPos": {
"h": 8,
"w": 12,
"x": 0,
"y": 68
},
"id": 19,
"targets": [
{
"datasource": null,
"refId": "A"
},
{
"datasource": "default",
"refId": "B"
},
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"refId": "C"
},
{
"refId": "D"
}
],
"title": "V33: Target Datasource Edge Cases",
"type": "graph"
},
{
"datasource": null,
"gridPos": {
"h": 8,
"w": 12,
"x": 12,
"y": 68
},
"id": 20,
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "existing-ref"
},
"refId": "A"
},
{
"datasource": {
"apiVersion": "1",
"type": "loki",
"uid": "non-default-test-ds-uid"
},
"refId": "B"
},
{
"datasource": "default",
"refId": "C"
}
],
"title": "V33: Mixed Target References",
"type": "timeseries"
},
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"gridPos": {
"h": 4,
"w": 6,
"x": 0,
"y": 76
},
"id": 21,
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"refId": "A"
}
],
"title": "V33: Panel with Empty String Datasource",
"type": "stat"
},
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"gridPos": {
"h": 4,
"w": 6,
"x": 6,
"y": 76
},
"id": 22,
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"refId": "A"
},
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"refId": "B"
}
],
"title": "V33: Panel with Another Non-existing Datasource",
"type": "table"
}
],
"preload": false,
"refresh": true,
"schemaVersion": 34,
"tags": [],
"templating": {
"list": [
{
"datasource": "default",
"name": "default_var",
"type": "query"
},
{
"datasource": "Non Default Test Datasource",
"name": "es_var_by_name",
"type": "query"
},
{
"datasource": "non-default-test-ds-uid",
"name": "es_var_by_uid",
"type": "query"
},
{
"datasource": null,
"name": "null_var",
"type": "query"
},
{
"datasource": "non-existing-ds",
"name": "non_existing_var",
"type": "query"
},
{
"datasource": "",
"name": "empty_string_var",
"type": "query"
},
{
"datasource": "another-non-existing-ds",
"name": "another_non_existing_var",
"type": "query"
}
]
},
"time": {
"from": "now-6h",
"to": "now"
},
"timepicker": {
"time_options": [
"5m",
"15m",
"1h",
"6h",
"12h",
"24h",
"2d",
"7d",
"30d"
]
},
"timezone": "utc",
"title": "New dashboard",
"version": 0,
"weekStart": ""
}
@@ -0,0 +1,732 @@
{
"annotations": {
"list": [
{
"builtIn": 1,
"datasource": {
"type": "grafana",
"uid": "-- Grafana --"
},
"enable": true,
"hide": true,
"iconColor": "rgba(0, 211, 255, 1)",
"name": "Annotations \u0026 Alerts",
"type": "dashboard"
},
{
"datasource": "Non Default Test Datasource",
"enable": true,
"name": "Test Annotation by Name",
"type": "dashboard"
},
{
"datasource": "non-default-test-ds-uid",
"enable": true,
"name": "Test Annotation by UID",
"type": "dashboard"
},
{
"datasource": "default",
"enable": true,
"name": "Test Default Annotation",
"type": "dashboard"
},
{
"datasource": "non-existing-ds",
"enable": true,
"name": "Test Non-existing Annotation",
"type": "dashboard"
},
{
"dimensions": {
"InstanceId": "i-123456"
},
"enable": true,
"iconColor": "red",
"name": "CloudWatch Annotation Single Stat",
"namespace": "AWS/EC2",
"prefixMatching": false,
"region": "us-east-1",
"statistic": "Average"
},
{
"dimensions": {
"InstanceId": "i-789012"
},
"enable": true,
"iconColor": "blue",
"name": "CloudWatch Annotation Multiple Stats - Maximum",
"namespace": "AWS/RDS",
"prefixMatching": false,
"region": "us-west-2",
"statistic": "Maximum"
},
{
"datasource": "",
"enable": true,
"name": "Test Empty String Annotation",
"type": "dashboard"
},
{
"datasource": "another-missing-ds",
"enable": true,
"name": "Test Another Non-existing Annotation",
"type": "dashboard"
},
{
"dimensions": {
"InstanceId": "i-789012"
},
"enable": true,
"iconColor": "blue",
"name": "CloudWatch Annotation Multiple Stats - Minimum",
"namespace": "AWS/RDS",
"prefixMatching": false,
"region": "us-west-2",
"statistic": "Minimum"
},
{
"dimensions": {
"InstanceId": "i-789012"
},
"enable": true,
"iconColor": "blue",
"name": "CloudWatch Annotation Multiple Stats - Sum",
"namespace": "AWS/RDS",
"prefixMatching": false,
"region": "us-west-2",
"statistic": "Sum"
}
]
},
"editable": true,
"fiscalYearStartMonth": 0,
"graphTooltip": 0,
"links": [],
"panels": [
{
"datasource": null,
"gridPos": {
"h": 8,
"w": 12,
"x": 0,
"y": 0
},
"id": 1,
"options": {},
"title": "No Legend Config",
"type": "graph"
},
{
"datasource": null,
"gridPos": {
"h": 8,
"w": 12,
"x": 12,
"y": 0
},
"id": 2,
"options": {
"legend": true
},
"title": "Boolean Legend True"
},
{
"datasource": null,
"gridPos": {
"h": 8,
"w": 12,
"x": 0,
"y": 8
},
"id": 3,
"options": {
"legend": false
},
"title": "Boolean Legend False"
},
{
"datasource": null,
"gridPos": {
"h": 8,
"w": 12,
"x": 12,
"y": 8
},
"id": 4,
"options": {
"legend": {
"displayMode": "hidden"
}
},
"title": "Hidden DisplayMode"
},
{
"datasource": null,
"gridPos": {
"h": 8,
"w": 12,
"x": 0,
"y": 16
},
"id": 5,
"options": {
"legend": {
"showLegend": false
}
},
"title": "ShowLegend False"
},
{
"datasource": null,
"gridPos": {
"h": 8,
"w": 12,
"x": 12,
"y": 16
},
"id": 6,
"options": {
"legend": {
"displayMode": "table"
}
},
"title": "Visible Legend"
},
{
"datasource": null,
"gridPos": {
"h": 8,
"w": 12,
"x": 0,
"y": 24
},
"id": 7,
"targets": [
{
"datasource": {
"apiVersion": "1",
"type": "loki",
"uid": "non-default-test-ds-uid"
}
},
{
"datasource": {
"apiVersion": "1",
"type": "loki",
"uid": "non-default-test-ds-uid"
}
}
],
"title": "Mixed Datasources Panel"
},
{
"datasource": {
"uid": "-- Mixed --"
},
"gridPos": {
"h": 8,
"w": 12,
"x": 12,
"y": 24
},
"id": 8,
"targets": [
{
"datasource": {
"apiVersion": "1",
"type": "loki",
"uid": "non-default-test-ds-uid"
}
},
{
"datasource": {
"apiVersion": "1",
"type": "loki",
"uid": "non-default-test-ds-uid"
}
}
],
"title": "Mixed Panel with Mixed Targets"
},
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"gridPos": {
"h": 8,
"w": 12,
"x": 0,
"y": 32
},
"id": 9,
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
}
}
],
"title": "Non-existing Datasource Panel"
},
{
"datasource": null,
"fieldConfig": {
"defaults": {
"custom": {
"axisPlacement": "hidden"
}
},
"overrides": [
{
"matcher": {
"id": "byType",
"options": "time"
},
"properties": [
{
"id": "custom.axisPlacement",
"value": "auto"
}
]
}
]
},
"gridPos": {
"h": 8,
"w": 12,
"x": 0,
"y": 40
},
"id": 10,
"title": "Timeseries Panel with Hidden Axes",
"type": "timeseries"
},
{
"datasource": null,
"gridPos": {
"h": 8,
"w": 12,
"x": 12,
"y": 40
},
"id": 11,
"targets": [
{
"alias": "CPU Usage",
"dimensions": {
"InstanceId": "i-123456"
},
"metricName": "CPUUtilization",
"namespace": "AWS/EC2",
"period": "300",
"refId": "A",
"region": "us-east-1",
"statistic": "Average"
},
{
"alias": "CPU Usage",
"dimensions": {
"InstanceId": "i-123456"
},
"metricName": "CPUUtilization",
"namespace": "AWS/EC2",
"period": "300",
"refId": "B",
"region": "us-east-1",
"statistic": "Maximum"
},
{
"alias": "CPU Usage",
"dimensions": {
"InstanceId": "i-123456"
},
"metricName": "CPUUtilization",
"namespace": "AWS/EC2",
"period": "300",
"refId": "C",
"region": "us-east-1",
"statistic": "Minimum"
}
],
"title": "CloudWatch Single Query Multiple Stats",
"type": "timeseries"
},
{
"datasource": null,
"gridPos": {
"h": 8,
"w": 12,
"x": 0,
"y": 48
},
"id": 12,
"targets": [
{
"dimensions": {
"LoadBalancer": "my-load-balancer"
},
"metricName": "RequestCount",
"namespace": "AWS/ApplicationELB",
"refId": "A",
"region": "us-west-2",
"statistic": "Sum"
},
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"expr": "up",
"refId": "B"
},
{
"dimensions": {
"DBInstanceIdentifier": "my-db"
},
"metricName": "DatabaseConnections",
"namespace": "AWS/RDS",
"refId": "C",
"region": "us-east-1",
"statistic": "Maximum"
},
{
"dimensions": {
"LoadBalancer": "my-load-balancer"
},
"metricName": "RequestCount",
"namespace": "AWS/ApplicationELB",
"refId": "D",
"region": "us-west-2",
"statistic": "Average"
}
],
"title": "Mixed CloudWatch and Prometheus Queries",
"type": "timeseries"
},
{
"collapsed": true,
"datasource": null,
"gridPos": {
"h": 1,
"w": 24,
"x": 0,
"y": 56
},
"id": 13,
"panels": [
{
"datasource": null,
"gridPos": {
"h": 8,
"w": 12,
"x": 0,
"y": 0
},
"id": 14,
"targets": [
{
"dimensions": {
"QueueName": "my-queue"
},
"metricName": "ApproximateNumberOfMessages",
"namespace": "AWS/SQS",
"refId": "A",
"region": "us-east-1",
"statistic": "Average"
},
{
"dimensions": {
"QueueName": "my-queue"
},
"metricName": "ApproximateNumberOfMessages",
"namespace": "AWS/SQS",
"refId": "B",
"region": "us-east-1",
"statistic": "Maximum"
},
{
"dimensions": {
"QueueName": "my-queue"
},
"metricName": "ApproximateNumberOfMessages",
"namespace": "AWS/SQS",
"refId": "C",
"region": "us-east-1",
"statistic": "Sum"
}
],
"title": "Nested CloudWatch Panel",
"type": "timeseries"
}
],
"title": "Collapsed Row with CloudWatch",
"type": "row"
},
{
"datasource": null,
"gridPos": {
"h": 4,
"w": 6,
"x": 0,
"y": 64
},
"id": 15,
"targets": [
{
"datasource": {
"apiVersion": "1",
"type": "loki",
"uid": "non-default-test-ds-uid"
},
"refId": "A"
}
],
"title": "V33: Panel with Null Datasource",
"type": "stat"
},
{
"datasource": {
"type": "prometheus",
"uid": "existing-ref-uid"
},
"gridPos": {
"h": 4,
"w": 6,
"x": 6,
"y": 64
},
"id": 16,
"targets": [
{
"datasource": {
"type": "elasticsearch",
"uid": "existing-target-uid"
},
"refId": "A"
}
],
"title": "V33: Panel with Existing Datasource Reference",
"type": "stat"
},
{
"datasource": {
"apiVersion": "1",
"type": "loki",
"uid": "non-default-test-ds-uid"
},
"gridPos": {
"h": 4,
"w": 6,
"x": 12,
"y": 64
},
"id": 17,
"title": "V33: Panel without Targets",
"type": "table"
},
{
"datasource": null,
"gridPos": {
"h": 4,
"w": 6,
"x": 18,
"y": 64
},
"id": 18,
"targets": null,
"title": "V33: Panel with Empty Targets Array",
"type": "table"
},
{
"datasource": {
"apiVersion": "1",
"type": "loki",
"uid": "non-default-test-ds-uid"
},
"gridPos": {
"h": 8,
"w": 12,
"x": 0,
"y": 68
},
"id": 19,
"targets": [
{
"datasource": null,
"refId": "A"
},
{
"datasource": "default",
"refId": "B"
},
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"refId": "C"
},
{
"refId": "D"
}
],
"title": "V33: Target Datasource Edge Cases",
"type": "graph"
},
{
"datasource": null,
"gridPos": {
"h": 8,
"w": 12,
"x": 12,
"y": 68
},
"id": 20,
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "existing-ref"
},
"refId": "A"
},
{
"datasource": {
"apiVersion": "1",
"type": "loki",
"uid": "non-default-test-ds-uid"
},
"refId": "B"
},
{
"datasource": "default",
"refId": "C"
}
],
"title": "V33: Mixed Target References",
"type": "timeseries"
},
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"gridPos": {
"h": 4,
"w": 6,
"x": 0,
"y": 76
},
"id": 21,
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"refId": "A"
}
],
"title": "V33: Panel with Empty String Datasource",
"type": "stat"
},
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"gridPos": {
"h": 4,
"w": 6,
"x": 6,
"y": 76
},
"id": 22,
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"refId": "A"
},
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"refId": "B"
}
],
"title": "V33: Panel with Another Non-existing Datasource",
"type": "table"
}
],
"preload": false,
"refresh": true,
"schemaVersion": 35,
"tags": [],
"templating": {
"list": [
{
"datasource": "default",
"name": "default_var",
"type": "query"
},
{
"datasource": "Non Default Test Datasource",
"name": "es_var_by_name",
"type": "query"
},
{
"datasource": "non-default-test-ds-uid",
"name": "es_var_by_uid",
"type": "query"
},
{
"datasource": null,
"name": "null_var",
"type": "query"
},
{
"datasource": "non-existing-ds",
"name": "non_existing_var",
"type": "query"
},
{
"datasource": "",
"name": "empty_string_var",
"type": "query"
},
{
"datasource": "another-non-existing-ds",
"name": "another_non_existing_var",
"type": "query"
}
]
},
"time": {
"from": "now-6h",
"to": "now"
},
"timepicker": {
"time_options": [
"5m",
"15m",
"1h",
"6h",
"12h",
"24h",
"2d",
"7d",
"30d"
]
},
"timezone": "utc",
"title": "New dashboard",
"version": 0,
"weekStart": ""
}
@@ -0,0 +1,831 @@
{
"annotations": {
"list": [
{
"builtIn": 1,
"datasource": {
"type": "grafana",
"uid": "-- Grafana --"
},
"enable": true,
"hide": true,
"iconColor": "rgba(0, 211, 255, 1)",
"name": "Annotations \u0026 Alerts",
"type": "dashboard"
},
{
"datasource": {
"apiVersion": "1",
"type": "loki",
"uid": "non-default-test-ds-uid"
},
"enable": true,
"name": "Test Annotation by Name",
"type": "dashboard"
},
{
"datasource": {
"apiVersion": "1",
"type": "loki",
"uid": "non-default-test-ds-uid"
},
"enable": true,
"name": "Test Annotation by UID",
"type": "dashboard"
},
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"enable": true,
"name": "Test Default Annotation",
"type": "dashboard"
},
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"enable": true,
"name": "Test Non-existing Annotation",
"type": "dashboard"
},
{
"dimensions": {
"InstanceId": "i-123456"
},
"enable": true,
"iconColor": "red",
"name": "CloudWatch Annotation Single Stat",
"namespace": "AWS/EC2",
"prefixMatching": false,
"region": "us-east-1",
"statistic": "Average"
},
{
"dimensions": {
"InstanceId": "i-789012"
},
"enable": true,
"iconColor": "blue",
"name": "CloudWatch Annotation Multiple Stats - Maximum",
"namespace": "AWS/RDS",
"prefixMatching": false,
"region": "us-west-2",
"statistic": "Maximum"
},
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"enable": true,
"name": "Test Empty String Annotation",
"type": "dashboard"
},
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"enable": true,
"name": "Test Another Non-existing Annotation",
"type": "dashboard"
},
{
"dimensions": {
"InstanceId": "i-789012"
},
"enable": true,
"iconColor": "blue",
"name": "CloudWatch Annotation Multiple Stats - Minimum",
"namespace": "AWS/RDS",
"prefixMatching": false,
"region": "us-west-2",
"statistic": "Minimum"
},
{
"dimensions": {
"InstanceId": "i-789012"
},
"enable": true,
"iconColor": "blue",
"name": "CloudWatch Annotation Multiple Stats - Sum",
"namespace": "AWS/RDS",
"prefixMatching": false,
"region": "us-west-2",
"statistic": "Sum"
}
]
},
"editable": true,
"fiscalYearStartMonth": 0,
"graphTooltip": 0,
"links": [],
"panels": [
{
"datasource": null,
"gridPos": {
"h": 8,
"w": 12,
"x": 0,
"y": 0
},
"id": 1,
"options": {},
"title": "No Legend Config",
"type": "graph"
},
{
"datasource": null,
"gridPos": {
"h": 8,
"w": 12,
"x": 12,
"y": 0
},
"id": 2,
"options": {
"legend": true
},
"title": "Boolean Legend True"
},
{
"datasource": null,
"gridPos": {
"h": 8,
"w": 12,
"x": 0,
"y": 8
},
"id": 3,
"options": {
"legend": false
},
"title": "Boolean Legend False"
},
{
"datasource": null,
"gridPos": {
"h": 8,
"w": 12,
"x": 12,
"y": 8
},
"id": 4,
"options": {
"legend": {
"displayMode": "hidden"
}
},
"title": "Hidden DisplayMode"
},
{
"datasource": null,
"gridPos": {
"h": 8,
"w": 12,
"x": 0,
"y": 16
},
"id": 5,
"options": {
"legend": {
"showLegend": false
}
},
"title": "ShowLegend False"
},
{
"datasource": null,
"gridPos": {
"h": 8,
"w": 12,
"x": 12,
"y": 16
},
"id": 6,
"options": {
"legend": {
"displayMode": "table"
}
},
"title": "Visible Legend"
},
{
"datasource": {
"apiVersion": "1",
"type": "loki",
"uid": "non-default-test-ds-uid"
},
"gridPos": {
"h": 8,
"w": 12,
"x": 0,
"y": 24
},
"id": 7,
"targets": [
{
"datasource": {
"apiVersion": "1",
"type": "loki",
"uid": "non-default-test-ds-uid"
}
},
{
"datasource": {
"apiVersion": "1",
"type": "loki",
"uid": "non-default-test-ds-uid"
}
}
],
"title": "Mixed Datasources Panel"
},
{
"datasource": {
"uid": "-- Mixed --"
},
"gridPos": {
"h": 8,
"w": 12,
"x": 12,
"y": 24
},
"id": 8,
"targets": [
{
"datasource": {
"apiVersion": "1",
"type": "loki",
"uid": "non-default-test-ds-uid"
}
},
{
"datasource": {
"apiVersion": "1",
"type": "loki",
"uid": "non-default-test-ds-uid"
}
}
],
"title": "Mixed Panel with Mixed Targets"
},
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"gridPos": {
"h": 8,
"w": 12,
"x": 0,
"y": 32
},
"id": 9,
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
}
}
],
"title": "Non-existing Datasource Panel"
},
{
"datasource": null,
"fieldConfig": {
"defaults": {
"custom": {
"axisPlacement": "hidden"
}
},
"overrides": [
{
"matcher": {
"id": "byType",
"options": "time"
},
"properties": [
{
"id": "custom.axisPlacement",
"value": "auto"
}
]
}
]
},
"gridPos": {
"h": 8,
"w": 12,
"x": 0,
"y": 40
},
"id": 10,
"title": "Timeseries Panel with Hidden Axes",
"type": "timeseries"
},
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"gridPos": {
"h": 8,
"w": 12,
"x": 12,
"y": 40
},
"id": 11,
"targets": [
{
"alias": "CPU Usage",
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"dimensions": {
"InstanceId": "i-123456"
},
"metricName": "CPUUtilization",
"namespace": "AWS/EC2",
"period": "300",
"refId": "A",
"region": "us-east-1",
"statistic": "Average"
},
{
"alias": "CPU Usage",
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"dimensions": {
"InstanceId": "i-123456"
},
"metricName": "CPUUtilization",
"namespace": "AWS/EC2",
"period": "300",
"refId": "B",
"region": "us-east-1",
"statistic": "Maximum"
},
{
"alias": "CPU Usage",
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"dimensions": {
"InstanceId": "i-123456"
},
"metricName": "CPUUtilization",
"namespace": "AWS/EC2",
"period": "300",
"refId": "C",
"region": "us-east-1",
"statistic": "Minimum"
}
],
"title": "CloudWatch Single Query Multiple Stats",
"type": "timeseries"
},
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"gridPos": {
"h": 8,
"w": 12,
"x": 0,
"y": 48
},
"id": 12,
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"dimensions": {
"LoadBalancer": "my-load-balancer"
},
"metricName": "RequestCount",
"namespace": "AWS/ApplicationELB",
"refId": "A",
"region": "us-west-2",
"statistic": "Sum"
},
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"expr": "up",
"refId": "B"
},
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"dimensions": {
"DBInstanceIdentifier": "my-db"
},
"metricName": "DatabaseConnections",
"namespace": "AWS/RDS",
"refId": "C",
"region": "us-east-1",
"statistic": "Maximum"
},
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"dimensions": {
"LoadBalancer": "my-load-balancer"
},
"metricName": "RequestCount",
"namespace": "AWS/ApplicationELB",
"refId": "D",
"region": "us-west-2",
"statistic": "Average"
}
],
"title": "Mixed CloudWatch and Prometheus Queries",
"type": "timeseries"
},
{
"collapsed": true,
"datasource": null,
"gridPos": {
"h": 1,
"w": 24,
"x": 0,
"y": 56
},
"id": 13,
"panels": [
{
"datasource": null,
"gridPos": {
"h": 8,
"w": 12,
"x": 0,
"y": 0
},
"id": 14,
"targets": [
{
"dimensions": {
"QueueName": "my-queue"
},
"metricName": "ApproximateNumberOfMessages",
"namespace": "AWS/SQS",
"refId": "A",
"region": "us-east-1",
"statistic": "Average"
},
{
"dimensions": {
"QueueName": "my-queue"
},
"metricName": "ApproximateNumberOfMessages",
"namespace": "AWS/SQS",
"refId": "B",
"region": "us-east-1",
"statistic": "Maximum"
},
{
"dimensions": {
"QueueName": "my-queue"
},
"metricName": "ApproximateNumberOfMessages",
"namespace": "AWS/SQS",
"refId": "C",
"region": "us-east-1",
"statistic": "Sum"
}
],
"title": "Nested CloudWatch Panel",
"type": "timeseries"
}
],
"title": "Collapsed Row with CloudWatch",
"type": "row"
},
{
"datasource": {
"apiVersion": "1",
"type": "loki",
"uid": "non-default-test-ds-uid"
},
"gridPos": {
"h": 4,
"w": 6,
"x": 0,
"y": 64
},
"id": 15,
"targets": [
{
"datasource": {
"apiVersion": "1",
"type": "loki",
"uid": "non-default-test-ds-uid"
},
"refId": "A"
}
],
"title": "V33: Panel with Null Datasource",
"type": "stat"
},
{
"datasource": {
"type": "prometheus",
"uid": "existing-ref-uid"
},
"gridPos": {
"h": 4,
"w": 6,
"x": 6,
"y": 64
},
"id": 16,
"targets": [
{
"datasource": {
"type": "elasticsearch",
"uid": "existing-target-uid"
},
"refId": "A"
}
],
"title": "V33: Panel with Existing Datasource Reference",
"type": "stat"
},
{
"datasource": {
"apiVersion": "1",
"type": "loki",
"uid": "non-default-test-ds-uid"
},
"gridPos": {
"h": 4,
"w": 6,
"x": 12,
"y": 64
},
"id": 17,
"title": "V33: Panel without Targets",
"type": "table"
},
{
"datasource": null,
"gridPos": {
"h": 4,
"w": 6,
"x": 18,
"y": 64
},
"id": 18,
"targets": null,
"title": "V33: Panel with Empty Targets Array",
"type": "table"
},
{
"datasource": {
"apiVersion": "1",
"type": "loki",
"uid": "non-default-test-ds-uid"
},
"gridPos": {
"h": 8,
"w": 12,
"x": 0,
"y": 68
},
"id": 19,
"targets": [
{
"datasource": {
"apiVersion": "1",
"type": "loki",
"uid": "non-default-test-ds-uid"
},
"refId": "A"
},
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"refId": "B"
},
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"refId": "C"
},
{
"datasource": {
"apiVersion": "1",
"type": "loki",
"uid": "non-default-test-ds-uid"
},
"refId": "D"
}
],
"title": "V33: Target Datasource Edge Cases",
"type": "graph"
},
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"gridPos": {
"h": 8,
"w": 12,
"x": 12,
"y": 68
},
"id": 20,
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "existing-ref"
},
"refId": "A"
},
{
"datasource": {
"apiVersion": "1",
"type": "loki",
"uid": "non-default-test-ds-uid"
},
"refId": "B"
},
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"refId": "C"
}
],
"title": "V33: Mixed Target References",
"type": "timeseries"
},
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"gridPos": {
"h": 4,
"w": 6,
"x": 0,
"y": 76
},
"id": 21,
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"refId": "A"
}
],
"title": "V33: Panel with Empty String Datasource",
"type": "stat"
},
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"gridPos": {
"h": 4,
"w": 6,
"x": 6,
"y": 76
},
"id": 22,
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"refId": "A"
},
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"refId": "B"
}
],
"title": "V33: Panel with Another Non-existing Datasource",
"type": "table"
}
],
"preload": false,
"refresh": true,
"schemaVersion": 36,
"tags": [],
"templating": {
"list": [
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"name": "default_var",
"type": "query"
},
{
"datasource": {
"apiVersion": "1",
"type": "loki",
"uid": "non-default-test-ds-uid"
},
"name": "es_var_by_name",
"type": "query"
},
{
"datasource": {
"apiVersion": "1",
"type": "loki",
"uid": "non-default-test-ds-uid"
},
"name": "es_var_by_uid",
"type": "query"
},
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"name": "null_var",
"type": "query"
},
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"name": "non_existing_var",
"type": "query"
},
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"name": "empty_string_var",
"type": "query"
},
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"name": "another_non_existing_var",
"type": "query"
}
]
},
"time": {
"from": "now-6h",
"to": "now"
},
"timepicker": {
"time_options": [
"5m",
"15m",
"1h",
"6h",
"12h",
"24h",
"2d",
"7d",
"30d"
]
},
"timezone": "utc",
"title": "New dashboard",
"version": 0,
"weekStart": ""
}
@@ -0,0 +1,840 @@
{
"annotations": {
"list": [
{
"builtIn": 1,
"datasource": {
"type": "grafana",
"uid": "-- Grafana --"
},
"enable": true,
"hide": true,
"iconColor": "rgba(0, 211, 255, 1)",
"name": "Annotations \u0026 Alerts",
"type": "dashboard"
},
{
"datasource": {
"apiVersion": "1",
"type": "loki",
"uid": "non-default-test-ds-uid"
},
"enable": true,
"name": "Test Annotation by Name",
"type": "dashboard"
},
{
"datasource": {
"apiVersion": "1",
"type": "loki",
"uid": "non-default-test-ds-uid"
},
"enable": true,
"name": "Test Annotation by UID",
"type": "dashboard"
},
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"enable": true,
"name": "Test Default Annotation",
"type": "dashboard"
},
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"enable": true,
"name": "Test Non-existing Annotation",
"type": "dashboard"
},
{
"dimensions": {
"InstanceId": "i-123456"
},
"enable": true,
"iconColor": "red",
"name": "CloudWatch Annotation Single Stat",
"namespace": "AWS/EC2",
"prefixMatching": false,
"region": "us-east-1",
"statistic": "Average"
},
{
"dimensions": {
"InstanceId": "i-789012"
},
"enable": true,
"iconColor": "blue",
"name": "CloudWatch Annotation Multiple Stats - Maximum",
"namespace": "AWS/RDS",
"prefixMatching": false,
"region": "us-west-2",
"statistic": "Maximum"
},
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"enable": true,
"name": "Test Empty String Annotation",
"type": "dashboard"
},
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"enable": true,
"name": "Test Another Non-existing Annotation",
"type": "dashboard"
},
{
"dimensions": {
"InstanceId": "i-789012"
},
"enable": true,
"iconColor": "blue",
"name": "CloudWatch Annotation Multiple Stats - Minimum",
"namespace": "AWS/RDS",
"prefixMatching": false,
"region": "us-west-2",
"statistic": "Minimum"
},
{
"dimensions": {
"InstanceId": "i-789012"
},
"enable": true,
"iconColor": "blue",
"name": "CloudWatch Annotation Multiple Stats - Sum",
"namespace": "AWS/RDS",
"prefixMatching": false,
"region": "us-west-2",
"statistic": "Sum"
}
]
},
"editable": true,
"fiscalYearStartMonth": 0,
"graphTooltip": 0,
"links": [],
"panels": [
{
"datasource": null,
"gridPos": {
"h": 8,
"w": 12,
"x": 0,
"y": 0
},
"id": 1,
"options": {},
"title": "No Legend Config",
"type": "graph"
},
{
"datasource": null,
"gridPos": {
"h": 8,
"w": 12,
"x": 12,
"y": 0
},
"id": 2,
"options": {
"legend": {
"displayMode": "list",
"showLegend": true
}
},
"title": "Boolean Legend True"
},
{
"datasource": null,
"gridPos": {
"h": 8,
"w": 12,
"x": 0,
"y": 8
},
"id": 3,
"options": {
"legend": {
"displayMode": "list",
"showLegend": false
}
},
"title": "Boolean Legend False"
},
{
"datasource": null,
"gridPos": {
"h": 8,
"w": 12,
"x": 12,
"y": 8
},
"id": 4,
"options": {
"legend": {
"displayMode": "list",
"showLegend": false
}
},
"title": "Hidden DisplayMode"
},
{
"datasource": null,
"gridPos": {
"h": 8,
"w": 12,
"x": 0,
"y": 16
},
"id": 5,
"options": {
"legend": {
"displayMode": "list",
"showLegend": false
}
},
"title": "ShowLegend False"
},
{
"datasource": null,
"gridPos": {
"h": 8,
"w": 12,
"x": 12,
"y": 16
},
"id": 6,
"options": {
"legend": {
"displayMode": "table",
"showLegend": true
}
},
"title": "Visible Legend"
},
{
"datasource": {
"apiVersion": "1",
"type": "loki",
"uid": "non-default-test-ds-uid"
},
"gridPos": {
"h": 8,
"w": 12,
"x": 0,
"y": 24
},
"id": 7,
"targets": [
{
"datasource": {
"apiVersion": "1",
"type": "loki",
"uid": "non-default-test-ds-uid"
}
},
{
"datasource": {
"apiVersion": "1",
"type": "loki",
"uid": "non-default-test-ds-uid"
}
}
],
"title": "Mixed Datasources Panel"
},
{
"datasource": {
"uid": "-- Mixed --"
},
"gridPos": {
"h": 8,
"w": 12,
"x": 12,
"y": 24
},
"id": 8,
"targets": [
{
"datasource": {
"apiVersion": "1",
"type": "loki",
"uid": "non-default-test-ds-uid"
}
},
{
"datasource": {
"apiVersion": "1",
"type": "loki",
"uid": "non-default-test-ds-uid"
}
}
],
"title": "Mixed Panel with Mixed Targets"
},
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"gridPos": {
"h": 8,
"w": 12,
"x": 0,
"y": 32
},
"id": 9,
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
}
}
],
"title": "Non-existing Datasource Panel"
},
{
"datasource": null,
"fieldConfig": {
"defaults": {
"custom": {
"axisPlacement": "hidden"
}
},
"overrides": [
{
"matcher": {
"id": "byType",
"options": "time"
},
"properties": [
{
"id": "custom.axisPlacement",
"value": "auto"
}
]
}
]
},
"gridPos": {
"h": 8,
"w": 12,
"x": 0,
"y": 40
},
"id": 10,
"title": "Timeseries Panel with Hidden Axes",
"type": "timeseries"
},
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"gridPos": {
"h": 8,
"w": 12,
"x": 12,
"y": 40
},
"id": 11,
"targets": [
{
"alias": "CPU Usage",
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"dimensions": {
"InstanceId": "i-123456"
},
"metricName": "CPUUtilization",
"namespace": "AWS/EC2",
"period": "300",
"refId": "A",
"region": "us-east-1",
"statistic": "Average"
},
{
"alias": "CPU Usage",
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"dimensions": {
"InstanceId": "i-123456"
},
"metricName": "CPUUtilization",
"namespace": "AWS/EC2",
"period": "300",
"refId": "B",
"region": "us-east-1",
"statistic": "Maximum"
},
{
"alias": "CPU Usage",
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"dimensions": {
"InstanceId": "i-123456"
},
"metricName": "CPUUtilization",
"namespace": "AWS/EC2",
"period": "300",
"refId": "C",
"region": "us-east-1",
"statistic": "Minimum"
}
],
"title": "CloudWatch Single Query Multiple Stats",
"type": "timeseries"
},
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"gridPos": {
"h": 8,
"w": 12,
"x": 0,
"y": 48
},
"id": 12,
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"dimensions": {
"LoadBalancer": "my-load-balancer"
},
"metricName": "RequestCount",
"namespace": "AWS/ApplicationELB",
"refId": "A",
"region": "us-west-2",
"statistic": "Sum"
},
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"expr": "up",
"refId": "B"
},
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"dimensions": {
"DBInstanceIdentifier": "my-db"
},
"metricName": "DatabaseConnections",
"namespace": "AWS/RDS",
"refId": "C",
"region": "us-east-1",
"statistic": "Maximum"
},
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"dimensions": {
"LoadBalancer": "my-load-balancer"
},
"metricName": "RequestCount",
"namespace": "AWS/ApplicationELB",
"refId": "D",
"region": "us-west-2",
"statistic": "Average"
}
],
"title": "Mixed CloudWatch and Prometheus Queries",
"type": "timeseries"
},
{
"collapsed": true,
"datasource": null,
"gridPos": {
"h": 1,
"w": 24,
"x": 0,
"y": 56
},
"id": 13,
"panels": [
{
"datasource": null,
"gridPos": {
"h": 8,
"w": 12,
"x": 0,
"y": 0
},
"id": 14,
"targets": [
{
"dimensions": {
"QueueName": "my-queue"
},
"metricName": "ApproximateNumberOfMessages",
"namespace": "AWS/SQS",
"refId": "A",
"region": "us-east-1",
"statistic": "Average"
},
{
"dimensions": {
"QueueName": "my-queue"
},
"metricName": "ApproximateNumberOfMessages",
"namespace": "AWS/SQS",
"refId": "B",
"region": "us-east-1",
"statistic": "Maximum"
},
{
"dimensions": {
"QueueName": "my-queue"
},
"metricName": "ApproximateNumberOfMessages",
"namespace": "AWS/SQS",
"refId": "C",
"region": "us-east-1",
"statistic": "Sum"
}
],
"title": "Nested CloudWatch Panel",
"type": "timeseries"
}
],
"title": "Collapsed Row with CloudWatch",
"type": "row"
},
{
"datasource": {
"apiVersion": "1",
"type": "loki",
"uid": "non-default-test-ds-uid"
},
"gridPos": {
"h": 4,
"w": 6,
"x": 0,
"y": 64
},
"id": 15,
"targets": [
{
"datasource": {
"apiVersion": "1",
"type": "loki",
"uid": "non-default-test-ds-uid"
},
"refId": "A"
}
],
"title": "V33: Panel with Null Datasource",
"type": "stat"
},
{
"datasource": {
"type": "prometheus",
"uid": "existing-ref-uid"
},
"gridPos": {
"h": 4,
"w": 6,
"x": 6,
"y": 64
},
"id": 16,
"targets": [
{
"datasource": {
"type": "elasticsearch",
"uid": "existing-target-uid"
},
"refId": "A"
}
],
"title": "V33: Panel with Existing Datasource Reference",
"type": "stat"
},
{
"datasource": {
"apiVersion": "1",
"type": "loki",
"uid": "non-default-test-ds-uid"
},
"gridPos": {
"h": 4,
"w": 6,
"x": 12,
"y": 64
},
"id": 17,
"title": "V33: Panel without Targets",
"type": "table"
},
{
"datasource": null,
"gridPos": {
"h": 4,
"w": 6,
"x": 18,
"y": 64
},
"id": 18,
"targets": null,
"title": "V33: Panel with Empty Targets Array",
"type": "table"
},
{
"datasource": {
"apiVersion": "1",
"type": "loki",
"uid": "non-default-test-ds-uid"
},
"gridPos": {
"h": 8,
"w": 12,
"x": 0,
"y": 68
},
"id": 19,
"targets": [
{
"datasource": {
"apiVersion": "1",
"type": "loki",
"uid": "non-default-test-ds-uid"
},
"refId": "A"
},
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"refId": "B"
},
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"refId": "C"
},
{
"datasource": {
"apiVersion": "1",
"type": "loki",
"uid": "non-default-test-ds-uid"
},
"refId": "D"
}
],
"title": "V33: Target Datasource Edge Cases",
"type": "graph"
},
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"gridPos": {
"h": 8,
"w": 12,
"x": 12,
"y": 68
},
"id": 20,
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "existing-ref"
},
"refId": "A"
},
{
"datasource": {
"apiVersion": "1",
"type": "loki",
"uid": "non-default-test-ds-uid"
},
"refId": "B"
},
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"refId": "C"
}
],
"title": "V33: Mixed Target References",
"type": "timeseries"
},
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"gridPos": {
"h": 4,
"w": 6,
"x": 0,
"y": 76
},
"id": 21,
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"refId": "A"
}
],
"title": "V33: Panel with Empty String Datasource",
"type": "stat"
},
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"gridPos": {
"h": 4,
"w": 6,
"x": 6,
"y": 76
},
"id": 22,
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"refId": "A"
},
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"refId": "B"
}
],
"title": "V33: Panel with Another Non-existing Datasource",
"type": "table"
}
],
"preload": false,
"refresh": true,
"schemaVersion": 37,
"tags": [],
"templating": {
"list": [
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"name": "default_var",
"type": "query"
},
{
"datasource": {
"apiVersion": "1",
"type": "loki",
"uid": "non-default-test-ds-uid"
},
"name": "es_var_by_name",
"type": "query"
},
{
"datasource": {
"apiVersion": "1",
"type": "loki",
"uid": "non-default-test-ds-uid"
},
"name": "es_var_by_uid",
"type": "query"
},
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"name": "null_var",
"type": "query"
},
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"name": "non_existing_var",
"type": "query"
},
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"name": "empty_string_var",
"type": "query"
},
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"name": "another_non_existing_var",
"type": "query"
}
]
},
"time": {
"from": "now-6h",
"to": "now"
},
"timepicker": {
"time_options": [
"5m",
"15m",
"1h",
"6h",
"12h",
"24h",
"2d",
"7d",
"30d"
]
},
"timezone": "utc",
"title": "New dashboard",
"version": 0,
"weekStart": ""
}
@@ -0,0 +1,840 @@
{
"annotations": {
"list": [
{
"builtIn": 1,
"datasource": {
"type": "grafana",
"uid": "-- Grafana --"
},
"enable": true,
"hide": true,
"iconColor": "rgba(0, 211, 255, 1)",
"name": "Annotations \u0026 Alerts",
"type": "dashboard"
},
{
"datasource": {
"apiVersion": "1",
"type": "loki",
"uid": "non-default-test-ds-uid"
},
"enable": true,
"name": "Test Annotation by Name",
"type": "dashboard"
},
{
"datasource": {
"apiVersion": "1",
"type": "loki",
"uid": "non-default-test-ds-uid"
},
"enable": true,
"name": "Test Annotation by UID",
"type": "dashboard"
},
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"enable": true,
"name": "Test Default Annotation",
"type": "dashboard"
},
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"enable": true,
"name": "Test Non-existing Annotation",
"type": "dashboard"
},
{
"dimensions": {
"InstanceId": "i-123456"
},
"enable": true,
"iconColor": "red",
"name": "CloudWatch Annotation Single Stat",
"namespace": "AWS/EC2",
"prefixMatching": false,
"region": "us-east-1",
"statistic": "Average"
},
{
"dimensions": {
"InstanceId": "i-789012"
},
"enable": true,
"iconColor": "blue",
"name": "CloudWatch Annotation Multiple Stats - Maximum",
"namespace": "AWS/RDS",
"prefixMatching": false,
"region": "us-west-2",
"statistic": "Maximum"
},
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"enable": true,
"name": "Test Empty String Annotation",
"type": "dashboard"
},
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"enable": true,
"name": "Test Another Non-existing Annotation",
"type": "dashboard"
},
{
"dimensions": {
"InstanceId": "i-789012"
},
"enable": true,
"iconColor": "blue",
"name": "CloudWatch Annotation Multiple Stats - Minimum",
"namespace": "AWS/RDS",
"prefixMatching": false,
"region": "us-west-2",
"statistic": "Minimum"
},
{
"dimensions": {
"InstanceId": "i-789012"
},
"enable": true,
"iconColor": "blue",
"name": "CloudWatch Annotation Multiple Stats - Sum",
"namespace": "AWS/RDS",
"prefixMatching": false,
"region": "us-west-2",
"statistic": "Sum"
}
]
},
"editable": true,
"fiscalYearStartMonth": 0,
"graphTooltip": 0,
"links": [],
"panels": [
{
"datasource": null,
"gridPos": {
"h": 8,
"w": 12,
"x": 0,
"y": 0
},
"id": 1,
"options": {},
"title": "No Legend Config",
"type": "graph"
},
{
"datasource": null,
"gridPos": {
"h": 8,
"w": 12,
"x": 12,
"y": 0
},
"id": 2,
"options": {
"legend": {
"displayMode": "list",
"showLegend": true
}
},
"title": "Boolean Legend True"
},
{
"datasource": null,
"gridPos": {
"h": 8,
"w": 12,
"x": 0,
"y": 8
},
"id": 3,
"options": {
"legend": {
"displayMode": "list",
"showLegend": false
}
},
"title": "Boolean Legend False"
},
{
"datasource": null,
"gridPos": {
"h": 8,
"w": 12,
"x": 12,
"y": 8
},
"id": 4,
"options": {
"legend": {
"displayMode": "list",
"showLegend": false
}
},
"title": "Hidden DisplayMode"
},
{
"datasource": null,
"gridPos": {
"h": 8,
"w": 12,
"x": 0,
"y": 16
},
"id": 5,
"options": {
"legend": {
"displayMode": "list",
"showLegend": false
}
},
"title": "ShowLegend False"
},
{
"datasource": null,
"gridPos": {
"h": 8,
"w": 12,
"x": 12,
"y": 16
},
"id": 6,
"options": {
"legend": {
"displayMode": "table",
"showLegend": true
}
},
"title": "Visible Legend"
},
{
"datasource": {
"apiVersion": "1",
"type": "loki",
"uid": "non-default-test-ds-uid"
},
"gridPos": {
"h": 8,
"w": 12,
"x": 0,
"y": 24
},
"id": 7,
"targets": [
{
"datasource": {
"apiVersion": "1",
"type": "loki",
"uid": "non-default-test-ds-uid"
}
},
{
"datasource": {
"apiVersion": "1",
"type": "loki",
"uid": "non-default-test-ds-uid"
}
}
],
"title": "Mixed Datasources Panel"
},
{
"datasource": {
"uid": "-- Mixed --"
},
"gridPos": {
"h": 8,
"w": 12,
"x": 12,
"y": 24
},
"id": 8,
"targets": [
{
"datasource": {
"apiVersion": "1",
"type": "loki",
"uid": "non-default-test-ds-uid"
}
},
{
"datasource": {
"apiVersion": "1",
"type": "loki",
"uid": "non-default-test-ds-uid"
}
}
],
"title": "Mixed Panel with Mixed Targets"
},
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"gridPos": {
"h": 8,
"w": 12,
"x": 0,
"y": 32
},
"id": 9,
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
}
}
],
"title": "Non-existing Datasource Panel"
},
{
"datasource": null,
"fieldConfig": {
"defaults": {
"custom": {
"axisPlacement": "hidden"
}
},
"overrides": [
{
"matcher": {
"id": "byType",
"options": "time"
},
"properties": [
{
"id": "custom.axisPlacement",
"value": "auto"
}
]
}
]
},
"gridPos": {
"h": 8,
"w": 12,
"x": 0,
"y": 40
},
"id": 10,
"title": "Timeseries Panel with Hidden Axes",
"type": "timeseries"
},
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"gridPos": {
"h": 8,
"w": 12,
"x": 12,
"y": 40
},
"id": 11,
"targets": [
{
"alias": "CPU Usage",
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"dimensions": {
"InstanceId": "i-123456"
},
"metricName": "CPUUtilization",
"namespace": "AWS/EC2",
"period": "300",
"refId": "A",
"region": "us-east-1",
"statistic": "Average"
},
{
"alias": "CPU Usage",
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"dimensions": {
"InstanceId": "i-123456"
},
"metricName": "CPUUtilization",
"namespace": "AWS/EC2",
"period": "300",
"refId": "B",
"region": "us-east-1",
"statistic": "Maximum"
},
{
"alias": "CPU Usage",
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"dimensions": {
"InstanceId": "i-123456"
},
"metricName": "CPUUtilization",
"namespace": "AWS/EC2",
"period": "300",
"refId": "C",
"region": "us-east-1",
"statistic": "Minimum"
}
],
"title": "CloudWatch Single Query Multiple Stats",
"type": "timeseries"
},
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"gridPos": {
"h": 8,
"w": 12,
"x": 0,
"y": 48
},
"id": 12,
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"dimensions": {
"LoadBalancer": "my-load-balancer"
},
"metricName": "RequestCount",
"namespace": "AWS/ApplicationELB",
"refId": "A",
"region": "us-west-2",
"statistic": "Sum"
},
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"expr": "up",
"refId": "B"
},
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"dimensions": {
"DBInstanceIdentifier": "my-db"
},
"metricName": "DatabaseConnections",
"namespace": "AWS/RDS",
"refId": "C",
"region": "us-east-1",
"statistic": "Maximum"
},
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"dimensions": {
"LoadBalancer": "my-load-balancer"
},
"metricName": "RequestCount",
"namespace": "AWS/ApplicationELB",
"refId": "D",
"region": "us-west-2",
"statistic": "Average"
}
],
"title": "Mixed CloudWatch and Prometheus Queries",
"type": "timeseries"
},
{
"collapsed": true,
"datasource": null,
"gridPos": {
"h": 1,
"w": 24,
"x": 0,
"y": 56
},
"id": 13,
"panels": [
{
"datasource": null,
"gridPos": {
"h": 8,
"w": 12,
"x": 0,
"y": 0
},
"id": 14,
"targets": [
{
"dimensions": {
"QueueName": "my-queue"
},
"metricName": "ApproximateNumberOfMessages",
"namespace": "AWS/SQS",
"refId": "A",
"region": "us-east-1",
"statistic": "Average"
},
{
"dimensions": {
"QueueName": "my-queue"
},
"metricName": "ApproximateNumberOfMessages",
"namespace": "AWS/SQS",
"refId": "B",
"region": "us-east-1",
"statistic": "Maximum"
},
{
"dimensions": {
"QueueName": "my-queue"
},
"metricName": "ApproximateNumberOfMessages",
"namespace": "AWS/SQS",
"refId": "C",
"region": "us-east-1",
"statistic": "Sum"
}
],
"title": "Nested CloudWatch Panel",
"type": "timeseries"
}
],
"title": "Collapsed Row with CloudWatch",
"type": "row"
},
{
"datasource": {
"apiVersion": "1",
"type": "loki",
"uid": "non-default-test-ds-uid"
},
"gridPos": {
"h": 4,
"w": 6,
"x": 0,
"y": 64
},
"id": 15,
"targets": [
{
"datasource": {
"apiVersion": "1",
"type": "loki",
"uid": "non-default-test-ds-uid"
},
"refId": "A"
}
],
"title": "V33: Panel with Null Datasource",
"type": "stat"
},
{
"datasource": {
"type": "prometheus",
"uid": "existing-ref-uid"
},
"gridPos": {
"h": 4,
"w": 6,
"x": 6,
"y": 64
},
"id": 16,
"targets": [
{
"datasource": {
"type": "elasticsearch",
"uid": "existing-target-uid"
},
"refId": "A"
}
],
"title": "V33: Panel with Existing Datasource Reference",
"type": "stat"
},
{
"datasource": {
"apiVersion": "1",
"type": "loki",
"uid": "non-default-test-ds-uid"
},
"gridPos": {
"h": 4,
"w": 6,
"x": 12,
"y": 64
},
"id": 17,
"title": "V33: Panel without Targets",
"type": "table"
},
{
"datasource": null,
"gridPos": {
"h": 4,
"w": 6,
"x": 18,
"y": 64
},
"id": 18,
"targets": null,
"title": "V33: Panel with Empty Targets Array",
"type": "table"
},
{
"datasource": {
"apiVersion": "1",
"type": "loki",
"uid": "non-default-test-ds-uid"
},
"gridPos": {
"h": 8,
"w": 12,
"x": 0,
"y": 68
},
"id": 19,
"targets": [
{
"datasource": {
"apiVersion": "1",
"type": "loki",
"uid": "non-default-test-ds-uid"
},
"refId": "A"
},
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"refId": "B"
},
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"refId": "C"
},
{
"datasource": {
"apiVersion": "1",
"type": "loki",
"uid": "non-default-test-ds-uid"
},
"refId": "D"
}
],
"title": "V33: Target Datasource Edge Cases",
"type": "graph"
},
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"gridPos": {
"h": 8,
"w": 12,
"x": 12,
"y": 68
},
"id": 20,
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "existing-ref"
},
"refId": "A"
},
{
"datasource": {
"apiVersion": "1",
"type": "loki",
"uid": "non-default-test-ds-uid"
},
"refId": "B"
},
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"refId": "C"
}
],
"title": "V33: Mixed Target References",
"type": "timeseries"
},
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"gridPos": {
"h": 4,
"w": 6,
"x": 0,
"y": 76
},
"id": 21,
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"refId": "A"
}
],
"title": "V33: Panel with Empty String Datasource",
"type": "stat"
},
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"gridPos": {
"h": 4,
"w": 6,
"x": 6,
"y": 76
},
"id": 22,
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"refId": "A"
},
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"refId": "B"
}
],
"title": "V33: Panel with Another Non-existing Datasource",
"type": "table"
}
],
"preload": false,
"refresh": true,
"schemaVersion": 38,
"tags": [],
"templating": {
"list": [
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"name": "default_var",
"type": "query"
},
{
"datasource": {
"apiVersion": "1",
"type": "loki",
"uid": "non-default-test-ds-uid"
},
"name": "es_var_by_name",
"type": "query"
},
{
"datasource": {
"apiVersion": "1",
"type": "loki",
"uid": "non-default-test-ds-uid"
},
"name": "es_var_by_uid",
"type": "query"
},
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"name": "null_var",
"type": "query"
},
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"name": "non_existing_var",
"type": "query"
},
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"name": "empty_string_var",
"type": "query"
},
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"name": "another_non_existing_var",
"type": "query"
}
]
},
"time": {
"from": "now-6h",
"to": "now"
},
"timepicker": {
"time_options": [
"5m",
"15m",
"1h",
"6h",
"12h",
"24h",
"2d",
"7d",
"30d"
]
},
"timezone": "utc",
"title": "New dashboard",
"version": 0,
"weekStart": ""
}
@@ -0,0 +1,840 @@
{
"annotations": {
"list": [
{
"builtIn": 1,
"datasource": {
"type": "grafana",
"uid": "-- Grafana --"
},
"enable": true,
"hide": true,
"iconColor": "rgba(0, 211, 255, 1)",
"name": "Annotations \u0026 Alerts",
"type": "dashboard"
},
{
"datasource": {
"apiVersion": "1",
"type": "loki",
"uid": "non-default-test-ds-uid"
},
"enable": true,
"name": "Test Annotation by Name",
"type": "dashboard"
},
{
"datasource": {
"apiVersion": "1",
"type": "loki",
"uid": "non-default-test-ds-uid"
},
"enable": true,
"name": "Test Annotation by UID",
"type": "dashboard"
},
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"enable": true,
"name": "Test Default Annotation",
"type": "dashboard"
},
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"enable": true,
"name": "Test Non-existing Annotation",
"type": "dashboard"
},
{
"dimensions": {
"InstanceId": "i-123456"
},
"enable": true,
"iconColor": "red",
"name": "CloudWatch Annotation Single Stat",
"namespace": "AWS/EC2",
"prefixMatching": false,
"region": "us-east-1",
"statistic": "Average"
},
{
"dimensions": {
"InstanceId": "i-789012"
},
"enable": true,
"iconColor": "blue",
"name": "CloudWatch Annotation Multiple Stats - Maximum",
"namespace": "AWS/RDS",
"prefixMatching": false,
"region": "us-west-2",
"statistic": "Maximum"
},
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"enable": true,
"name": "Test Empty String Annotation",
"type": "dashboard"
},
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"enable": true,
"name": "Test Another Non-existing Annotation",
"type": "dashboard"
},
{
"dimensions": {
"InstanceId": "i-789012"
},
"enable": true,
"iconColor": "blue",
"name": "CloudWatch Annotation Multiple Stats - Minimum",
"namespace": "AWS/RDS",
"prefixMatching": false,
"region": "us-west-2",
"statistic": "Minimum"
},
{
"dimensions": {
"InstanceId": "i-789012"
},
"enable": true,
"iconColor": "blue",
"name": "CloudWatch Annotation Multiple Stats - Sum",
"namespace": "AWS/RDS",
"prefixMatching": false,
"region": "us-west-2",
"statistic": "Sum"
}
]
},
"editable": true,
"fiscalYearStartMonth": 0,
"graphTooltip": 0,
"links": [],
"panels": [
{
"datasource": null,
"gridPos": {
"h": 8,
"w": 12,
"x": 0,
"y": 0
},
"id": 1,
"options": {},
"title": "No Legend Config",
"type": "graph"
},
{
"datasource": null,
"gridPos": {
"h": 8,
"w": 12,
"x": 12,
"y": 0
},
"id": 2,
"options": {
"legend": {
"displayMode": "list",
"showLegend": true
}
},
"title": "Boolean Legend True"
},
{
"datasource": null,
"gridPos": {
"h": 8,
"w": 12,
"x": 0,
"y": 8
},
"id": 3,
"options": {
"legend": {
"displayMode": "list",
"showLegend": false
}
},
"title": "Boolean Legend False"
},
{
"datasource": null,
"gridPos": {
"h": 8,
"w": 12,
"x": 12,
"y": 8
},
"id": 4,
"options": {
"legend": {
"displayMode": "list",
"showLegend": false
}
},
"title": "Hidden DisplayMode"
},
{
"datasource": null,
"gridPos": {
"h": 8,
"w": 12,
"x": 0,
"y": 16
},
"id": 5,
"options": {
"legend": {
"displayMode": "list",
"showLegend": false
}
},
"title": "ShowLegend False"
},
{
"datasource": null,
"gridPos": {
"h": 8,
"w": 12,
"x": 12,
"y": 16
},
"id": 6,
"options": {
"legend": {
"displayMode": "table",
"showLegend": true
}
},
"title": "Visible Legend"
},
{
"datasource": {
"apiVersion": "1",
"type": "loki",
"uid": "non-default-test-ds-uid"
},
"gridPos": {
"h": 8,
"w": 12,
"x": 0,
"y": 24
},
"id": 7,
"targets": [
{
"datasource": {
"apiVersion": "1",
"type": "loki",
"uid": "non-default-test-ds-uid"
}
},
{
"datasource": {
"apiVersion": "1",
"type": "loki",
"uid": "non-default-test-ds-uid"
}
}
],
"title": "Mixed Datasources Panel"
},
{
"datasource": {
"uid": "-- Mixed --"
},
"gridPos": {
"h": 8,
"w": 12,
"x": 12,
"y": 24
},
"id": 8,
"targets": [
{
"datasource": {
"apiVersion": "1",
"type": "loki",
"uid": "non-default-test-ds-uid"
}
},
{
"datasource": {
"apiVersion": "1",
"type": "loki",
"uid": "non-default-test-ds-uid"
}
}
],
"title": "Mixed Panel with Mixed Targets"
},
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"gridPos": {
"h": 8,
"w": 12,
"x": 0,
"y": 32
},
"id": 9,
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
}
}
],
"title": "Non-existing Datasource Panel"
},
{
"datasource": null,
"fieldConfig": {
"defaults": {
"custom": {
"axisPlacement": "hidden"
}
},
"overrides": [
{
"matcher": {
"id": "byType",
"options": "time"
},
"properties": [
{
"id": "custom.axisPlacement",
"value": "auto"
}
]
}
]
},
"gridPos": {
"h": 8,
"w": 12,
"x": 0,
"y": 40
},
"id": 10,
"title": "Timeseries Panel with Hidden Axes",
"type": "timeseries"
},
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"gridPos": {
"h": 8,
"w": 12,
"x": 12,
"y": 40
},
"id": 11,
"targets": [
{
"alias": "CPU Usage",
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"dimensions": {
"InstanceId": "i-123456"
},
"metricName": "CPUUtilization",
"namespace": "AWS/EC2",
"period": "300",
"refId": "A",
"region": "us-east-1",
"statistic": "Average"
},
{
"alias": "CPU Usage",
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"dimensions": {
"InstanceId": "i-123456"
},
"metricName": "CPUUtilization",
"namespace": "AWS/EC2",
"period": "300",
"refId": "B",
"region": "us-east-1",
"statistic": "Maximum"
},
{
"alias": "CPU Usage",
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"dimensions": {
"InstanceId": "i-123456"
},
"metricName": "CPUUtilization",
"namespace": "AWS/EC2",
"period": "300",
"refId": "C",
"region": "us-east-1",
"statistic": "Minimum"
}
],
"title": "CloudWatch Single Query Multiple Stats",
"type": "timeseries"
},
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"gridPos": {
"h": 8,
"w": 12,
"x": 0,
"y": 48
},
"id": 12,
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"dimensions": {
"LoadBalancer": "my-load-balancer"
},
"metricName": "RequestCount",
"namespace": "AWS/ApplicationELB",
"refId": "A",
"region": "us-west-2",
"statistic": "Sum"
},
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"expr": "up",
"refId": "B"
},
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"dimensions": {
"DBInstanceIdentifier": "my-db"
},
"metricName": "DatabaseConnections",
"namespace": "AWS/RDS",
"refId": "C",
"region": "us-east-1",
"statistic": "Maximum"
},
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"dimensions": {
"LoadBalancer": "my-load-balancer"
},
"metricName": "RequestCount",
"namespace": "AWS/ApplicationELB",
"refId": "D",
"region": "us-west-2",
"statistic": "Average"
}
],
"title": "Mixed CloudWatch and Prometheus Queries",
"type": "timeseries"
},
{
"collapsed": true,
"datasource": null,
"gridPos": {
"h": 1,
"w": 24,
"x": 0,
"y": 56
},
"id": 13,
"panels": [
{
"datasource": null,
"gridPos": {
"h": 8,
"w": 12,
"x": 0,
"y": 0
},
"id": 14,
"targets": [
{
"dimensions": {
"QueueName": "my-queue"
},
"metricName": "ApproximateNumberOfMessages",
"namespace": "AWS/SQS",
"refId": "A",
"region": "us-east-1",
"statistic": "Average"
},
{
"dimensions": {
"QueueName": "my-queue"
},
"metricName": "ApproximateNumberOfMessages",
"namespace": "AWS/SQS",
"refId": "B",
"region": "us-east-1",
"statistic": "Maximum"
},
{
"dimensions": {
"QueueName": "my-queue"
},
"metricName": "ApproximateNumberOfMessages",
"namespace": "AWS/SQS",
"refId": "C",
"region": "us-east-1",
"statistic": "Sum"
}
],
"title": "Nested CloudWatch Panel",
"type": "timeseries"
}
],
"title": "Collapsed Row with CloudWatch",
"type": "row"
},
{
"datasource": {
"apiVersion": "1",
"type": "loki",
"uid": "non-default-test-ds-uid"
},
"gridPos": {
"h": 4,
"w": 6,
"x": 0,
"y": 64
},
"id": 15,
"targets": [
{
"datasource": {
"apiVersion": "1",
"type": "loki",
"uid": "non-default-test-ds-uid"
},
"refId": "A"
}
],
"title": "V33: Panel with Null Datasource",
"type": "stat"
},
{
"datasource": {
"type": "prometheus",
"uid": "existing-ref-uid"
},
"gridPos": {
"h": 4,
"w": 6,
"x": 6,
"y": 64
},
"id": 16,
"targets": [
{
"datasource": {
"type": "elasticsearch",
"uid": "existing-target-uid"
},
"refId": "A"
}
],
"title": "V33: Panel with Existing Datasource Reference",
"type": "stat"
},
{
"datasource": {
"apiVersion": "1",
"type": "loki",
"uid": "non-default-test-ds-uid"
},
"gridPos": {
"h": 4,
"w": 6,
"x": 12,
"y": 64
},
"id": 17,
"title": "V33: Panel without Targets",
"type": "table"
},
{
"datasource": null,
"gridPos": {
"h": 4,
"w": 6,
"x": 18,
"y": 64
},
"id": 18,
"targets": null,
"title": "V33: Panel with Empty Targets Array",
"type": "table"
},
{
"datasource": {
"apiVersion": "1",
"type": "loki",
"uid": "non-default-test-ds-uid"
},
"gridPos": {
"h": 8,
"w": 12,
"x": 0,
"y": 68
},
"id": 19,
"targets": [
{
"datasource": {
"apiVersion": "1",
"type": "loki",
"uid": "non-default-test-ds-uid"
},
"refId": "A"
},
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"refId": "B"
},
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"refId": "C"
},
{
"datasource": {
"apiVersion": "1",
"type": "loki",
"uid": "non-default-test-ds-uid"
},
"refId": "D"
}
],
"title": "V33: Target Datasource Edge Cases",
"type": "graph"
},
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"gridPos": {
"h": 8,
"w": 12,
"x": 12,
"y": 68
},
"id": 20,
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "existing-ref"
},
"refId": "A"
},
{
"datasource": {
"apiVersion": "1",
"type": "loki",
"uid": "non-default-test-ds-uid"
},
"refId": "B"
},
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"refId": "C"
}
],
"title": "V33: Mixed Target References",
"type": "timeseries"
},
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"gridPos": {
"h": 4,
"w": 6,
"x": 0,
"y": 76
},
"id": 21,
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"refId": "A"
}
],
"title": "V33: Panel with Empty String Datasource",
"type": "stat"
},
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"gridPos": {
"h": 4,
"w": 6,
"x": 6,
"y": 76
},
"id": 22,
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"refId": "A"
},
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"refId": "B"
}
],
"title": "V33: Panel with Another Non-existing Datasource",
"type": "table"
}
],
"preload": false,
"refresh": true,
"schemaVersion": 39,
"tags": [],
"templating": {
"list": [
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"name": "default_var",
"type": "query"
},
{
"datasource": {
"apiVersion": "1",
"type": "loki",
"uid": "non-default-test-ds-uid"
},
"name": "es_var_by_name",
"type": "query"
},
{
"datasource": {
"apiVersion": "1",
"type": "loki",
"uid": "non-default-test-ds-uid"
},
"name": "es_var_by_uid",
"type": "query"
},
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"name": "null_var",
"type": "query"
},
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"name": "non_existing_var",
"type": "query"
},
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"name": "empty_string_var",
"type": "query"
},
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"name": "another_non_existing_var",
"type": "query"
}
]
},
"time": {
"from": "now-6h",
"to": "now"
},
"timepicker": {
"time_options": [
"5m",
"15m",
"1h",
"6h",
"12h",
"24h",
"2d",
"7d",
"30d"
]
},
"timezone": "utc",
"title": "New dashboard",
"version": 0,
"weekStart": ""
}
@@ -0,0 +1,840 @@
{
"annotations": {
"list": [
{
"builtIn": 1,
"datasource": {
"type": "grafana",
"uid": "-- Grafana --"
},
"enable": true,
"hide": true,
"iconColor": "rgba(0, 211, 255, 1)",
"name": "Annotations \u0026 Alerts",
"type": "dashboard"
},
{
"datasource": {
"apiVersion": "1",
"type": "loki",
"uid": "non-default-test-ds-uid"
},
"enable": true,
"name": "Test Annotation by Name",
"type": "dashboard"
},
{
"datasource": {
"apiVersion": "1",
"type": "loki",
"uid": "non-default-test-ds-uid"
},
"enable": true,
"name": "Test Annotation by UID",
"type": "dashboard"
},
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"enable": true,
"name": "Test Default Annotation",
"type": "dashboard"
},
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"enable": true,
"name": "Test Non-existing Annotation",
"type": "dashboard"
},
{
"dimensions": {
"InstanceId": "i-123456"
},
"enable": true,
"iconColor": "red",
"name": "CloudWatch Annotation Single Stat",
"namespace": "AWS/EC2",
"prefixMatching": false,
"region": "us-east-1",
"statistic": "Average"
},
{
"dimensions": {
"InstanceId": "i-789012"
},
"enable": true,
"iconColor": "blue",
"name": "CloudWatch Annotation Multiple Stats - Maximum",
"namespace": "AWS/RDS",
"prefixMatching": false,
"region": "us-west-2",
"statistic": "Maximum"
},
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"enable": true,
"name": "Test Empty String Annotation",
"type": "dashboard"
},
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"enable": true,
"name": "Test Another Non-existing Annotation",
"type": "dashboard"
},
{
"dimensions": {
"InstanceId": "i-789012"
},
"enable": true,
"iconColor": "blue",
"name": "CloudWatch Annotation Multiple Stats - Minimum",
"namespace": "AWS/RDS",
"prefixMatching": false,
"region": "us-west-2",
"statistic": "Minimum"
},
{
"dimensions": {
"InstanceId": "i-789012"
},
"enable": true,
"iconColor": "blue",
"name": "CloudWatch Annotation Multiple Stats - Sum",
"namespace": "AWS/RDS",
"prefixMatching": false,
"region": "us-west-2",
"statistic": "Sum"
}
]
},
"editable": true,
"fiscalYearStartMonth": 0,
"graphTooltip": 0,
"links": [],
"panels": [
{
"datasource": null,
"gridPos": {
"h": 8,
"w": 12,
"x": 0,
"y": 0
},
"id": 1,
"options": {},
"title": "No Legend Config",
"type": "graph"
},
{
"datasource": null,
"gridPos": {
"h": 8,
"w": 12,
"x": 12,
"y": 0
},
"id": 2,
"options": {
"legend": {
"displayMode": "list",
"showLegend": true
}
},
"title": "Boolean Legend True"
},
{
"datasource": null,
"gridPos": {
"h": 8,
"w": 12,
"x": 0,
"y": 8
},
"id": 3,
"options": {
"legend": {
"displayMode": "list",
"showLegend": false
}
},
"title": "Boolean Legend False"
},
{
"datasource": null,
"gridPos": {
"h": 8,
"w": 12,
"x": 12,
"y": 8
},
"id": 4,
"options": {
"legend": {
"displayMode": "list",
"showLegend": false
}
},
"title": "Hidden DisplayMode"
},
{
"datasource": null,
"gridPos": {
"h": 8,
"w": 12,
"x": 0,
"y": 16
},
"id": 5,
"options": {
"legend": {
"displayMode": "list",
"showLegend": false
}
},
"title": "ShowLegend False"
},
{
"datasource": null,
"gridPos": {
"h": 8,
"w": 12,
"x": 12,
"y": 16
},
"id": 6,
"options": {
"legend": {
"displayMode": "table",
"showLegend": true
}
},
"title": "Visible Legend"
},
{
"datasource": {
"apiVersion": "1",
"type": "loki",
"uid": "non-default-test-ds-uid"
},
"gridPos": {
"h": 8,
"w": 12,
"x": 0,
"y": 24
},
"id": 7,
"targets": [
{
"datasource": {
"apiVersion": "1",
"type": "loki",
"uid": "non-default-test-ds-uid"
}
},
{
"datasource": {
"apiVersion": "1",
"type": "loki",
"uid": "non-default-test-ds-uid"
}
}
],
"title": "Mixed Datasources Panel"
},
{
"datasource": {
"uid": "-- Mixed --"
},
"gridPos": {
"h": 8,
"w": 12,
"x": 12,
"y": 24
},
"id": 8,
"targets": [
{
"datasource": {
"apiVersion": "1",
"type": "loki",
"uid": "non-default-test-ds-uid"
}
},
{
"datasource": {
"apiVersion": "1",
"type": "loki",
"uid": "non-default-test-ds-uid"
}
}
],
"title": "Mixed Panel with Mixed Targets"
},
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"gridPos": {
"h": 8,
"w": 12,
"x": 0,
"y": 32
},
"id": 9,
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
}
}
],
"title": "Non-existing Datasource Panel"
},
{
"datasource": null,
"fieldConfig": {
"defaults": {
"custom": {
"axisPlacement": "hidden"
}
},
"overrides": [
{
"matcher": {
"id": "byType",
"options": "time"
},
"properties": [
{
"id": "custom.axisPlacement",
"value": "auto"
}
]
}
]
},
"gridPos": {
"h": 8,
"w": 12,
"x": 0,
"y": 40
},
"id": 10,
"title": "Timeseries Panel with Hidden Axes",
"type": "timeseries"
},
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"gridPos": {
"h": 8,
"w": 12,
"x": 12,
"y": 40
},
"id": 11,
"targets": [
{
"alias": "CPU Usage",
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"dimensions": {
"InstanceId": "i-123456"
},
"metricName": "CPUUtilization",
"namespace": "AWS/EC2",
"period": "300",
"refId": "A",
"region": "us-east-1",
"statistic": "Average"
},
{
"alias": "CPU Usage",
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"dimensions": {
"InstanceId": "i-123456"
},
"metricName": "CPUUtilization",
"namespace": "AWS/EC2",
"period": "300",
"refId": "B",
"region": "us-east-1",
"statistic": "Maximum"
},
{
"alias": "CPU Usage",
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"dimensions": {
"InstanceId": "i-123456"
},
"metricName": "CPUUtilization",
"namespace": "AWS/EC2",
"period": "300",
"refId": "C",
"region": "us-east-1",
"statistic": "Minimum"
}
],
"title": "CloudWatch Single Query Multiple Stats",
"type": "timeseries"
},
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"gridPos": {
"h": 8,
"w": 12,
"x": 0,
"y": 48
},
"id": 12,
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"dimensions": {
"LoadBalancer": "my-load-balancer"
},
"metricName": "RequestCount",
"namespace": "AWS/ApplicationELB",
"refId": "A",
"region": "us-west-2",
"statistic": "Sum"
},
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"expr": "up",
"refId": "B"
},
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"dimensions": {
"DBInstanceIdentifier": "my-db"
},
"metricName": "DatabaseConnections",
"namespace": "AWS/RDS",
"refId": "C",
"region": "us-east-1",
"statistic": "Maximum"
},
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"dimensions": {
"LoadBalancer": "my-load-balancer"
},
"metricName": "RequestCount",
"namespace": "AWS/ApplicationELB",
"refId": "D",
"region": "us-west-2",
"statistic": "Average"
}
],
"title": "Mixed CloudWatch and Prometheus Queries",
"type": "timeseries"
},
{
"collapsed": true,
"datasource": null,
"gridPos": {
"h": 1,
"w": 24,
"x": 0,
"y": 56
},
"id": 13,
"panels": [
{
"datasource": null,
"gridPos": {
"h": 8,
"w": 12,
"x": 0,
"y": 0
},
"id": 14,
"targets": [
{
"dimensions": {
"QueueName": "my-queue"
},
"metricName": "ApproximateNumberOfMessages",
"namespace": "AWS/SQS",
"refId": "A",
"region": "us-east-1",
"statistic": "Average"
},
{
"dimensions": {
"QueueName": "my-queue"
},
"metricName": "ApproximateNumberOfMessages",
"namespace": "AWS/SQS",
"refId": "B",
"region": "us-east-1",
"statistic": "Maximum"
},
{
"dimensions": {
"QueueName": "my-queue"
},
"metricName": "ApproximateNumberOfMessages",
"namespace": "AWS/SQS",
"refId": "C",
"region": "us-east-1",
"statistic": "Sum"
}
],
"title": "Nested CloudWatch Panel",
"type": "timeseries"
}
],
"title": "Collapsed Row with CloudWatch",
"type": "row"
},
{
"datasource": {
"apiVersion": "1",
"type": "loki",
"uid": "non-default-test-ds-uid"
},
"gridPos": {
"h": 4,
"w": 6,
"x": 0,
"y": 64
},
"id": 15,
"targets": [
{
"datasource": {
"apiVersion": "1",
"type": "loki",
"uid": "non-default-test-ds-uid"
},
"refId": "A"
}
],
"title": "V33: Panel with Null Datasource",
"type": "stat"
},
{
"datasource": {
"type": "prometheus",
"uid": "existing-ref-uid"
},
"gridPos": {
"h": 4,
"w": 6,
"x": 6,
"y": 64
},
"id": 16,
"targets": [
{
"datasource": {
"type": "elasticsearch",
"uid": "existing-target-uid"
},
"refId": "A"
}
],
"title": "V33: Panel with Existing Datasource Reference",
"type": "stat"
},
{
"datasource": {
"apiVersion": "1",
"type": "loki",
"uid": "non-default-test-ds-uid"
},
"gridPos": {
"h": 4,
"w": 6,
"x": 12,
"y": 64
},
"id": 17,
"title": "V33: Panel without Targets",
"type": "table"
},
{
"datasource": null,
"gridPos": {
"h": 4,
"w": 6,
"x": 18,
"y": 64
},
"id": 18,
"targets": null,
"title": "V33: Panel with Empty Targets Array",
"type": "table"
},
{
"datasource": {
"apiVersion": "1",
"type": "loki",
"uid": "non-default-test-ds-uid"
},
"gridPos": {
"h": 8,
"w": 12,
"x": 0,
"y": 68
},
"id": 19,
"targets": [
{
"datasource": {
"apiVersion": "1",
"type": "loki",
"uid": "non-default-test-ds-uid"
},
"refId": "A"
},
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"refId": "B"
},
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"refId": "C"
},
{
"datasource": {
"apiVersion": "1",
"type": "loki",
"uid": "non-default-test-ds-uid"
},
"refId": "D"
}
],
"title": "V33: Target Datasource Edge Cases",
"type": "graph"
},
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"gridPos": {
"h": 8,
"w": 12,
"x": 12,
"y": 68
},
"id": 20,
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "existing-ref"
},
"refId": "A"
},
{
"datasource": {
"apiVersion": "1",
"type": "loki",
"uid": "non-default-test-ds-uid"
},
"refId": "B"
},
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"refId": "C"
}
],
"title": "V33: Mixed Target References",
"type": "timeseries"
},
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"gridPos": {
"h": 4,
"w": 6,
"x": 0,
"y": 76
},
"id": 21,
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"refId": "A"
}
],
"title": "V33: Panel with Empty String Datasource",
"type": "stat"
},
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"gridPos": {
"h": 4,
"w": 6,
"x": 6,
"y": 76
},
"id": 22,
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"refId": "A"
},
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"refId": "B"
}
],
"title": "V33: Panel with Another Non-existing Datasource",
"type": "table"
}
],
"preload": false,
"refresh": "",
"schemaVersion": 40,
"tags": [],
"templating": {
"list": [
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"name": "default_var",
"type": "query"
},
{
"datasource": {
"apiVersion": "1",
"type": "loki",
"uid": "non-default-test-ds-uid"
},
"name": "es_var_by_name",
"type": "query"
},
{
"datasource": {
"apiVersion": "1",
"type": "loki",
"uid": "non-default-test-ds-uid"
},
"name": "es_var_by_uid",
"type": "query"
},
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"name": "null_var",
"type": "query"
},
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"name": "non_existing_var",
"type": "query"
},
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"name": "empty_string_var",
"type": "query"
},
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"name": "another_non_existing_var",
"type": "query"
}
]
},
"time": {
"from": "now-6h",
"to": "now"
},
"timepicker": {
"time_options": [
"5m",
"15m",
"1h",
"6h",
"12h",
"24h",
"2d",
"7d",
"30d"
]
},
"timezone": "utc",
"title": "New dashboard",
"version": 0,
"weekStart": ""
}
@@ -0,0 +1,828 @@
{
"annotations": {
"list": [
{
"builtIn": 1,
"datasource": {
"type": "grafana",
"uid": "-- Grafana --"
},
"enable": true,
"hide": true,
"iconColor": "rgba(0, 211, 255, 1)",
"name": "Annotations \u0026 Alerts",
"type": "dashboard"
},
{
"datasource": {
"apiVersion": "1",
"type": "loki",
"uid": "non-default-test-ds-uid"
},
"enable": true,
"name": "Test Annotation by Name",
"type": "dashboard"
},
{
"datasource": {
"apiVersion": "1",
"type": "loki",
"uid": "non-default-test-ds-uid"
},
"enable": true,
"name": "Test Annotation by UID",
"type": "dashboard"
},
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"enable": true,
"name": "Test Default Annotation",
"type": "dashboard"
},
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"enable": true,
"name": "Test Non-existing Annotation",
"type": "dashboard"
},
{
"dimensions": {
"InstanceId": "i-123456"
},
"enable": true,
"iconColor": "red",
"name": "CloudWatch Annotation Single Stat",
"namespace": "AWS/EC2",
"prefixMatching": false,
"region": "us-east-1",
"statistic": "Average"
},
{
"dimensions": {
"InstanceId": "i-789012"
},
"enable": true,
"iconColor": "blue",
"name": "CloudWatch Annotation Multiple Stats - Maximum",
"namespace": "AWS/RDS",
"prefixMatching": false,
"region": "us-west-2",
"statistic": "Maximum"
},
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"enable": true,
"name": "Test Empty String Annotation",
"type": "dashboard"
},
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"enable": true,
"name": "Test Another Non-existing Annotation",
"type": "dashboard"
},
{
"dimensions": {
"InstanceId": "i-789012"
},
"enable": true,
"iconColor": "blue",
"name": "CloudWatch Annotation Multiple Stats - Minimum",
"namespace": "AWS/RDS",
"prefixMatching": false,
"region": "us-west-2",
"statistic": "Minimum"
},
{
"dimensions": {
"InstanceId": "i-789012"
},
"enable": true,
"iconColor": "blue",
"name": "CloudWatch Annotation Multiple Stats - Sum",
"namespace": "AWS/RDS",
"prefixMatching": false,
"region": "us-west-2",
"statistic": "Sum"
}
]
},
"editable": true,
"fiscalYearStartMonth": 0,
"graphTooltip": 0,
"links": [],
"panels": [
{
"datasource": null,
"gridPos": {
"h": 8,
"w": 12,
"x": 0,
"y": 0
},
"id": 1,
"options": {},
"title": "No Legend Config",
"type": "graph"
},
{
"datasource": null,
"gridPos": {
"h": 8,
"w": 12,
"x": 12,
"y": 0
},
"id": 2,
"options": {
"legend": {
"displayMode": "list",
"showLegend": true
}
},
"title": "Boolean Legend True"
},
{
"datasource": null,
"gridPos": {
"h": 8,
"w": 12,
"x": 0,
"y": 8
},
"id": 3,
"options": {
"legend": {
"displayMode": "list",
"showLegend": false
}
},
"title": "Boolean Legend False"
},
{
"datasource": null,
"gridPos": {
"h": 8,
"w": 12,
"x": 12,
"y": 8
},
"id": 4,
"options": {
"legend": {
"displayMode": "list",
"showLegend": false
}
},
"title": "Hidden DisplayMode"
},
{
"datasource": null,
"gridPos": {
"h": 8,
"w": 12,
"x": 0,
"y": 16
},
"id": 5,
"options": {
"legend": {
"displayMode": "list",
"showLegend": false
}
},
"title": "ShowLegend False"
},
{
"datasource": null,
"gridPos": {
"h": 8,
"w": 12,
"x": 12,
"y": 16
},
"id": 6,
"options": {
"legend": {
"displayMode": "table",
"showLegend": true
}
},
"title": "Visible Legend"
},
{
"datasource": {
"apiVersion": "1",
"type": "loki",
"uid": "non-default-test-ds-uid"
},
"gridPos": {
"h": 8,
"w": 12,
"x": 0,
"y": 24
},
"id": 7,
"targets": [
{
"datasource": {
"apiVersion": "1",
"type": "loki",
"uid": "non-default-test-ds-uid"
}
},
{
"datasource": {
"apiVersion": "1",
"type": "loki",
"uid": "non-default-test-ds-uid"
}
}
],
"title": "Mixed Datasources Panel"
},
{
"datasource": {
"uid": "-- Mixed --"
},
"gridPos": {
"h": 8,
"w": 12,
"x": 12,
"y": 24
},
"id": 8,
"targets": [
{
"datasource": {
"apiVersion": "1",
"type": "loki",
"uid": "non-default-test-ds-uid"
}
},
{
"datasource": {
"apiVersion": "1",
"type": "loki",
"uid": "non-default-test-ds-uid"
}
}
],
"title": "Mixed Panel with Mixed Targets"
},
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"gridPos": {
"h": 8,
"w": 12,
"x": 0,
"y": 32
},
"id": 9,
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
}
}
],
"title": "Non-existing Datasource Panel"
},
{
"datasource": null,
"fieldConfig": {
"defaults": {
"custom": {
"axisPlacement": "hidden"
}
},
"overrides": [
{
"matcher": {
"id": "byType",
"options": "time"
},
"properties": [
{
"id": "custom.axisPlacement",
"value": "auto"
}
]
}
]
},
"gridPos": {
"h": 8,
"w": 12,
"x": 0,
"y": 40
},
"id": 10,
"title": "Timeseries Panel with Hidden Axes",
"type": "timeseries"
},
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"gridPos": {
"h": 8,
"w": 12,
"x": 12,
"y": 40
},
"id": 11,
"targets": [
{
"alias": "CPU Usage",
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"dimensions": {
"InstanceId": "i-123456"
},
"metricName": "CPUUtilization",
"namespace": "AWS/EC2",
"period": "300",
"refId": "A",
"region": "us-east-1",
"statistic": "Average"
},
{
"alias": "CPU Usage",
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"dimensions": {
"InstanceId": "i-123456"
},
"metricName": "CPUUtilization",
"namespace": "AWS/EC2",
"period": "300",
"refId": "B",
"region": "us-east-1",
"statistic": "Maximum"
},
{
"alias": "CPU Usage",
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"dimensions": {
"InstanceId": "i-123456"
},
"metricName": "CPUUtilization",
"namespace": "AWS/EC2",
"period": "300",
"refId": "C",
"region": "us-east-1",
"statistic": "Minimum"
}
],
"title": "CloudWatch Single Query Multiple Stats",
"type": "timeseries"
},
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"gridPos": {
"h": 8,
"w": 12,
"x": 0,
"y": 48
},
"id": 12,
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"dimensions": {
"LoadBalancer": "my-load-balancer"
},
"metricName": "RequestCount",
"namespace": "AWS/ApplicationELB",
"refId": "A",
"region": "us-west-2",
"statistic": "Sum"
},
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"expr": "up",
"refId": "B"
},
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"dimensions": {
"DBInstanceIdentifier": "my-db"
},
"metricName": "DatabaseConnections",
"namespace": "AWS/RDS",
"refId": "C",
"region": "us-east-1",
"statistic": "Maximum"
},
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"dimensions": {
"LoadBalancer": "my-load-balancer"
},
"metricName": "RequestCount",
"namespace": "AWS/ApplicationELB",
"refId": "D",
"region": "us-west-2",
"statistic": "Average"
}
],
"title": "Mixed CloudWatch and Prometheus Queries",
"type": "timeseries"
},
{
"collapsed": true,
"datasource": null,
"gridPos": {
"h": 1,
"w": 24,
"x": 0,
"y": 56
},
"id": 13,
"panels": [
{
"datasource": null,
"gridPos": {
"h": 8,
"w": 12,
"x": 0,
"y": 0
},
"id": 14,
"targets": [
{
"dimensions": {
"QueueName": "my-queue"
},
"metricName": "ApproximateNumberOfMessages",
"namespace": "AWS/SQS",
"refId": "A",
"region": "us-east-1",
"statistic": "Average"
},
{
"dimensions": {
"QueueName": "my-queue"
},
"metricName": "ApproximateNumberOfMessages",
"namespace": "AWS/SQS",
"refId": "B",
"region": "us-east-1",
"statistic": "Maximum"
},
{
"dimensions": {
"QueueName": "my-queue"
},
"metricName": "ApproximateNumberOfMessages",
"namespace": "AWS/SQS",
"refId": "C",
"region": "us-east-1",
"statistic": "Sum"
}
],
"title": "Nested CloudWatch Panel",
"type": "timeseries"
}
],
"title": "Collapsed Row with CloudWatch",
"type": "row"
},
{
"datasource": {
"apiVersion": "1",
"type": "loki",
"uid": "non-default-test-ds-uid"
},
"gridPos": {
"h": 4,
"w": 6,
"x": 0,
"y": 64
},
"id": 15,
"targets": [
{
"datasource": {
"apiVersion": "1",
"type": "loki",
"uid": "non-default-test-ds-uid"
},
"refId": "A"
}
],
"title": "V33: Panel with Null Datasource",
"type": "stat"
},
{
"datasource": {
"type": "prometheus",
"uid": "existing-ref-uid"
},
"gridPos": {
"h": 4,
"w": 6,
"x": 6,
"y": 64
},
"id": 16,
"targets": [
{
"datasource": {
"type": "elasticsearch",
"uid": "existing-target-uid"
},
"refId": "A"
}
],
"title": "V33: Panel with Existing Datasource Reference",
"type": "stat"
},
{
"datasource": {
"apiVersion": "1",
"type": "loki",
"uid": "non-default-test-ds-uid"
},
"gridPos": {
"h": 4,
"w": 6,
"x": 12,
"y": 64
},
"id": 17,
"title": "V33: Panel without Targets",
"type": "table"
},
{
"datasource": null,
"gridPos": {
"h": 4,
"w": 6,
"x": 18,
"y": 64
},
"id": 18,
"targets": null,
"title": "V33: Panel with Empty Targets Array",
"type": "table"
},
{
"datasource": {
"apiVersion": "1",
"type": "loki",
"uid": "non-default-test-ds-uid"
},
"gridPos": {
"h": 8,
"w": 12,
"x": 0,
"y": 68
},
"id": 19,
"targets": [
{
"datasource": {
"apiVersion": "1",
"type": "loki",
"uid": "non-default-test-ds-uid"
},
"refId": "A"
},
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"refId": "B"
},
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"refId": "C"
},
{
"datasource": {
"apiVersion": "1",
"type": "loki",
"uid": "non-default-test-ds-uid"
},
"refId": "D"
}
],
"title": "V33: Target Datasource Edge Cases",
"type": "graph"
},
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"gridPos": {
"h": 8,
"w": 12,
"x": 12,
"y": 68
},
"id": 20,
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "existing-ref"
},
"refId": "A"
},
{
"datasource": {
"apiVersion": "1",
"type": "loki",
"uid": "non-default-test-ds-uid"
},
"refId": "B"
},
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"refId": "C"
}
],
"title": "V33: Mixed Target References",
"type": "timeseries"
},
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"gridPos": {
"h": 4,
"w": 6,
"x": 0,
"y": 76
},
"id": 21,
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"refId": "A"
}
],
"title": "V33: Panel with Empty String Datasource",
"type": "stat"
},
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"gridPos": {
"h": 4,
"w": 6,
"x": 6,
"y": 76
},
"id": 22,
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"refId": "A"
},
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"refId": "B"
}
],
"title": "V33: Panel with Another Non-existing Datasource",
"type": "table"
}
],
"preload": false,
"refresh": "",
"schemaVersion": 41,
"tags": [],
"templating": {
"list": [
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"name": "default_var",
"type": "query"
},
{
"datasource": {
"apiVersion": "1",
"type": "loki",
"uid": "non-default-test-ds-uid"
},
"name": "es_var_by_name",
"type": "query"
},
{
"datasource": {
"apiVersion": "1",
"type": "loki",
"uid": "non-default-test-ds-uid"
},
"name": "es_var_by_uid",
"type": "query"
},
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"name": "null_var",
"type": "query"
},
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"name": "non_existing_var",
"type": "query"
},
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"name": "empty_string_var",
"type": "query"
},
{
"datasource": {
"type": "prometheus",
"uid": "default-ds-uid"
},
"name": "another_non_existing_var",
"type": "query"
}
]
},
"time": {
"from": "now-6h",
"to": "now"
},
"timepicker": {},
"timezone": "utc",
"title": "New dashboard",
"version": 0,
"weekStart": ""
}
@@ -44,7 +44,8 @@
},
{
"datasource": {
"uid": "non-existing-ds"
"type": "prometheus",
"uid": "default-ds-uid"
},
"enable": true,
"name": "Test Non-existing Annotation",
@@ -246,7 +247,8 @@
},
{
"datasource": {
"uid": "non-existing-ds"
"type": "prometheus",
"uid": "default-ds-uid"
},
"gridPos": {
"h": 8,
@@ -258,7 +260,8 @@
"targets": [
{
"datasource": {
"uid": "non-existing-ds"
"type": "prometheus",
"uid": "default-ds-uid"
}
}
],
@@ -366,7 +369,8 @@
},
{
"datasource": {
"uid": "prometheus"
"type": "prometheus",
"uid": "default-ds-uid"
},
"expr": "up",
"refId": "B"
@@ -495,7 +499,8 @@
},
{
"datasource": {
"uid": "non-existing-ds"
"type": "prometheus",
"uid": "default-ds-uid"
},
"name": "non_existing_var",
"type": "query"
@@ -44,7 +44,8 @@
},
{
"datasource": {
"uid": "non-existing-ds"
"type": "prometheus",
"uid": "default-ds-uid"
},
"enable": true,
"name": "Test Non-existing Annotation",
@@ -255,7 +256,8 @@
},
{
"datasource": {
"uid": "non-existing-ds"
"type": "prometheus",
"uid": "default-ds-uid"
},
"gridPos": {
"h": 8,
@@ -267,7 +269,8 @@
"targets": [
{
"datasource": {
"uid": "non-existing-ds"
"type": "prometheus",
"uid": "default-ds-uid"
}
}
],
@@ -375,7 +378,8 @@
},
{
"datasource": {
"uid": "prometheus"
"type": "prometheus",
"uid": "default-ds-uid"
},
"expr": "up",
"refId": "B"
@@ -504,7 +508,8 @@
},
{
"datasource": {
"uid": "non-existing-ds"
"type": "prometheus",
"uid": "default-ds-uid"
},
"name": "non_existing_var",
"type": "query"
@@ -44,7 +44,8 @@
},
{
"datasource": {
"uid": "non-existing-ds"
"type": "prometheus",
"uid": "default-ds-uid"
},
"enable": true,
"name": "Test Non-existing Annotation",
@@ -255,7 +256,8 @@
},
{
"datasource": {
"uid": "non-existing-ds"
"type": "prometheus",
"uid": "default-ds-uid"
},
"gridPos": {
"h": 8,
@@ -267,7 +269,8 @@
"targets": [
{
"datasource": {
"uid": "non-existing-ds"
"type": "prometheus",
"uid": "default-ds-uid"
}
}
],
@@ -375,7 +378,8 @@
},
{
"datasource": {
"uid": "prometheus"
"type": "prometheus",
"uid": "default-ds-uid"
},
"expr": "up",
"refId": "B"
@@ -504,7 +508,8 @@
},
{
"datasource": {
"uid": "non-existing-ds"
"type": "prometheus",
"uid": "default-ds-uid"
},
"name": "non_existing_var",
"type": "query"
@@ -44,7 +44,8 @@
},
{
"datasource": {
"uid": "non-existing-ds"
"type": "prometheus",
"uid": "default-ds-uid"
},
"enable": true,
"name": "Test Non-existing Annotation",
@@ -255,7 +256,8 @@
},
{
"datasource": {
"uid": "non-existing-ds"
"type": "prometheus",
"uid": "default-ds-uid"
},
"gridPos": {
"h": 8,
@@ -267,7 +269,8 @@
"targets": [
{
"datasource": {
"uid": "non-existing-ds"
"type": "prometheus",
"uid": "default-ds-uid"
}
}
],
@@ -375,7 +378,8 @@
},
{
"datasource": {
"uid": "prometheus"
"type": "prometheus",
"uid": "default-ds-uid"
},
"expr": "up",
"refId": "B"
@@ -504,7 +508,8 @@
},
{
"datasource": {
"uid": "non-existing-ds"
"type": "prometheus",
"uid": "default-ds-uid"
},
"name": "non_existing_var",
"type": "query"
@@ -44,7 +44,8 @@
},
{
"datasource": {
"uid": "non-existing-ds"
"type": "prometheus",
"uid": "default-ds-uid"
},
"enable": true,
"name": "Test Non-existing Annotation",
@@ -255,7 +256,8 @@
},
{
"datasource": {
"uid": "non-existing-ds"
"type": "prometheus",
"uid": "default-ds-uid"
},
"gridPos": {
"h": 8,
@@ -267,7 +269,8 @@
"targets": [
{
"datasource": {
"uid": "non-existing-ds"
"type": "prometheus",
"uid": "default-ds-uid"
}
}
],
@@ -375,7 +378,8 @@
},
{
"datasource": {
"uid": "prometheus"
"type": "prometheus",
"uid": "default-ds-uid"
},
"expr": "up",
"refId": "B"
@@ -504,7 +508,8 @@
},
{
"datasource": {
"uid": "non-existing-ds"
"type": "prometheus",
"uid": "default-ds-uid"
},
"name": "non_existing_var",
"type": "query"
@@ -44,7 +44,8 @@
},
{
"datasource": {
"uid": "non-existing-ds"
"type": "prometheus",
"uid": "default-ds-uid"
},
"enable": true,
"name": "Test Non-existing Annotation",
@@ -255,7 +256,8 @@
},
{
"datasource": {
"uid": "non-existing-ds"
"type": "prometheus",
"uid": "default-ds-uid"
},
"gridPos": {
"h": 8,
@@ -267,7 +269,8 @@
"targets": [
{
"datasource": {
"uid": "non-existing-ds"
"type": "prometheus",
"uid": "default-ds-uid"
}
}
],
@@ -375,7 +378,8 @@
},
{
"datasource": {
"uid": "prometheus"
"type": "prometheus",
"uid": "default-ds-uid"
},
"expr": "up",
"refId": "B"
@@ -504,7 +508,8 @@
},
{
"datasource": {
"uid": "non-existing-ds"
"type": "prometheus",
"uid": "default-ds-uid"
},
"name": "non_existing_var",
"type": "query"
@@ -44,7 +44,8 @@
},
{
"datasource": {
"uid": "non-existing-ds"
"type": "prometheus",
"uid": "default-ds-uid"
},
"enable": true,
"name": "Test Non-existing Annotation",
@@ -198,7 +199,8 @@
},
{
"datasource": {
"uid": "non-existing-ds"
"type": "prometheus",
"uid": "default-ds-uid"
},
"gridPos": {
"h": 8,
@@ -210,7 +212,8 @@
"targets": [
{
"datasource": {
"uid": "non-existing-ds"
"type": "prometheus",
"uid": "default-ds-uid"
}
}
],
@@ -291,7 +294,8 @@
},
{
"datasource": {
"uid": "non-existing-ds"
"type": "prometheus",
"uid": "default-ds-uid"
},
"name": "non_existing_var",
"type": "query"
@@ -44,7 +44,8 @@
},
{
"datasource": {
"uid": "non-existing-ds"
"type": "prometheus",
"uid": "default-ds-uid"
},
"enable": true,
"name": "Test Non-existing Annotation",
@@ -207,7 +208,8 @@
},
{
"datasource": {
"uid": "non-existing-ds"
"type": "prometheus",
"uid": "default-ds-uid"
},
"gridPos": {
"h": 8,
@@ -219,7 +221,8 @@
"targets": [
{
"datasource": {
"uid": "non-existing-ds"
"type": "prometheus",
"uid": "default-ds-uid"
}
}
],
@@ -300,7 +303,8 @@
},
{
"datasource": {
"uid": "non-existing-ds"
"type": "prometheus",
"uid": "default-ds-uid"
},
"name": "non_existing_var",
"type": "query"
@@ -44,7 +44,8 @@
},
{
"datasource": {
"uid": "non-existing-ds"
"type": "prometheus",
"uid": "default-ds-uid"
},
"enable": true,
"name": "Test Non-existing Annotation",
@@ -207,7 +208,8 @@
},
{
"datasource": {
"uid": "non-existing-ds"
"type": "prometheus",
"uid": "default-ds-uid"
},
"gridPos": {
"h": 8,
@@ -219,7 +221,8 @@
"targets": [
{
"datasource": {
"uid": "non-existing-ds"
"type": "prometheus",
"uid": "default-ds-uid"
}
}
],
@@ -300,7 +303,8 @@
},
{
"datasource": {
"uid": "non-existing-ds"
"type": "prometheus",
"uid": "default-ds-uid"
},
"name": "non_existing_var",
"type": "query"
@@ -44,7 +44,8 @@
},
{
"datasource": {
"uid": "non-existing-ds"
"type": "prometheus",
"uid": "default-ds-uid"
},
"enable": true,
"name": "Test Non-existing Annotation",
@@ -207,7 +208,8 @@
},
{
"datasource": {
"uid": "non-existing-ds"
"type": "prometheus",
"uid": "default-ds-uid"
},
"gridPos": {
"h": 8,
@@ -219,7 +221,8 @@
"targets": [
{
"datasource": {
"uid": "non-existing-ds"
"type": "prometheus",
"uid": "default-ds-uid"
}
}
],
@@ -300,7 +303,8 @@
},
{
"datasource": {
"uid": "non-existing-ds"
"type": "prometheus",
"uid": "default-ds-uid"
},
"name": "non_existing_var",
"type": "query"
@@ -44,7 +44,8 @@
},
{
"datasource": {
"uid": "non-existing-ds"
"type": "prometheus",
"uid": "default-ds-uid"
},
"enable": true,
"name": "Test Non-existing Annotation",
@@ -207,7 +208,8 @@
},
{
"datasource": {
"uid": "non-existing-ds"
"type": "prometheus",
"uid": "default-ds-uid"
},
"gridPos": {
"h": 8,
@@ -219,7 +221,8 @@
"targets": [
{
"datasource": {
"uid": "non-existing-ds"
"type": "prometheus",
"uid": "default-ds-uid"
}
}
],
@@ -300,7 +303,8 @@
},
{
"datasource": {
"uid": "non-existing-ds"
"type": "prometheus",
"uid": "default-ds-uid"
},
"name": "non_existing_var",
"type": "query"
@@ -44,7 +44,8 @@
},
{
"datasource": {
"uid": "non-existing-ds"
"type": "prometheus",
"uid": "default-ds-uid"
},
"enable": true,
"name": "Test Non-existing Annotation",
@@ -207,7 +208,8 @@
},
{
"datasource": {
"uid": "non-existing-ds"
"type": "prometheus",
"uid": "default-ds-uid"
},
"gridPos": {
"h": 8,
@@ -219,7 +221,8 @@
"targets": [
{
"datasource": {
"uid": "non-existing-ds"
"type": "prometheus",
"uid": "default-ds-uid"
}
}
],
@@ -300,7 +303,8 @@
},
{
"datasource": {
"uid": "non-existing-ds"
"type": "prometheus",
"uid": "default-ds-uid"
},
"name": "non_existing_var",
"type": "query"
@@ -44,7 +44,8 @@
},
{
"datasource": {
"uid": "non-existing-ds"
"type": "prometheus",
"uid": "default-ds-uid"
},
"enable": true,
"name": "Test Non-existing Annotation",
@@ -198,7 +199,8 @@
},
{
"datasource": {
"uid": "non-existing-ds"
"type": "prometheus",
"uid": "default-ds-uid"
},
"gridPos": {
"h": 8,
@@ -210,7 +212,8 @@
"targets": [
{
"datasource": {
"uid": "non-existing-ds"
"type": "prometheus",
"uid": "default-ds-uid"
}
}
],
@@ -259,7 +262,8 @@
},
{
"datasource": {
"uid": "non-existing-ds"
"type": "prometheus",
"uid": "default-ds-uid"
},
"name": "non_existing_var",
"type": "query"
@@ -44,7 +44,8 @@
},
{
"datasource": {
"uid": "non-existing-ds"
"type": "prometheus",
"uid": "default-ds-uid"
},
"enable": true,
"name": "Test Non-existing Annotation",
@@ -207,7 +208,8 @@
},
{
"datasource": {
"uid": "non-existing-ds"
"type": "prometheus",
"uid": "default-ds-uid"
},
"gridPos": {
"h": 8,
@@ -219,7 +221,8 @@
"targets": [
{
"datasource": {
"uid": "non-existing-ds"
"type": "prometheus",
"uid": "default-ds-uid"
}
}
],
@@ -268,7 +271,8 @@
},
{
"datasource": {
"uid": "non-existing-ds"
"type": "prometheus",
"uid": "default-ds-uid"
},
"name": "non_existing_var",
"type": "query"
@@ -44,7 +44,8 @@
},
{
"datasource": {
"uid": "non-existing-ds"
"type": "prometheus",
"uid": "default-ds-uid"
},
"enable": true,
"name": "Test Non-existing Annotation",
@@ -207,7 +208,8 @@
},
{
"datasource": {
"uid": "non-existing-ds"
"type": "prometheus",
"uid": "default-ds-uid"
},
"gridPos": {
"h": 8,
@@ -219,7 +221,8 @@
"targets": [
{
"datasource": {
"uid": "non-existing-ds"
"type": "prometheus",
"uid": "default-ds-uid"
}
}
],
@@ -268,7 +271,8 @@
},
{
"datasource": {
"uid": "non-existing-ds"
"type": "prometheus",
"uid": "default-ds-uid"
},
"name": "non_existing_var",
"type": "query"
@@ -44,7 +44,8 @@
},
{
"datasource": {
"uid": "non-existing-ds"
"type": "prometheus",
"uid": "default-ds-uid"
},
"enable": true,
"name": "Test Non-existing Annotation",
@@ -207,7 +208,8 @@
},
{
"datasource": {
"uid": "non-existing-ds"
"type": "prometheus",
"uid": "default-ds-uid"
},
"gridPos": {
"h": 8,
@@ -219,7 +221,8 @@
"targets": [
{
"datasource": {
"uid": "non-existing-ds"
"type": "prometheus",
"uid": "default-ds-uid"
}
}
],
@@ -268,7 +271,8 @@
},
{
"datasource": {
"uid": "non-existing-ds"
"type": "prometheus",
"uid": "default-ds-uid"
},
"name": "non_existing_var",
"type": "query"
@@ -44,7 +44,8 @@
},
{
"datasource": {
"uid": "non-existing-ds"
"type": "prometheus",
"uid": "default-ds-uid"
},
"enable": true,
"name": "Test Non-existing Annotation",
@@ -207,7 +208,8 @@
},
{
"datasource": {
"uid": "non-existing-ds"
"type": "prometheus",
"uid": "default-ds-uid"
},
"gridPos": {
"h": 8,
@@ -219,7 +221,8 @@
"targets": [
{
"datasource": {
"uid": "non-existing-ds"
"type": "prometheus",
"uid": "default-ds-uid"
}
}
],
@@ -268,7 +271,8 @@
},
{
"datasource": {
"uid": "non-existing-ds"
"type": "prometheus",
"uid": "default-ds-uid"
},
"name": "non_existing_var",
"type": "query"
@@ -44,7 +44,8 @@
},
{
"datasource": {
"uid": "non-existing-ds"
"type": "prometheus",
"uid": "default-ds-uid"
},
"enable": true,
"name": "Test Non-existing Annotation",
@@ -207,7 +208,8 @@
},
{
"datasource": {
"uid": "non-existing-ds"
"type": "prometheus",
"uid": "default-ds-uid"
},
"gridPos": {
"h": 8,
@@ -219,7 +221,8 @@
"targets": [
{
"datasource": {
"uid": "non-existing-ds"
"type": "prometheus",
"uid": "default-ds-uid"
}
}
],
@@ -268,7 +271,8 @@
},
{
"datasource": {
"uid": "non-existing-ds"
"type": "prometheus",
"uid": "default-ds-uid"
},
"name": "non_existing_var",
"type": "query"
+4 -1
View File
@@ -4,6 +4,7 @@ go 1.24.4
require (
github.com/grafana/grafana-app-sdk v0.39.0
github.com/grafana/grafana/pkg/apimachinery v0.0.0-20250514132646-acbc7b54ed9e
k8s.io/apimachinery v0.33.1
k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff
)
@@ -33,11 +34,13 @@ require (
github.com/oasdiff/yaml v0.0.0-20250309154309-f31be36b4037 // indirect
github.com/oasdiff/yaml3 v0.0.0-20250309153720-d2182401db90 // indirect
github.com/perimeterx/marshmallow v1.1.5 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/prometheus/client_golang v1.22.0 // indirect
github.com/prometheus/client_model v0.6.2 // indirect
github.com/prometheus/common v0.64.0 // indirect
github.com/prometheus/procfs v0.16.1 // indirect
github.com/rogpeppe/go-internal v1.14.1 // indirect
github.com/stretchr/objx v0.5.2 // indirect
github.com/stretchr/testify v1.10.0 // indirect
github.com/x448/float16 v0.8.4 // indirect
go.opentelemetry.io/otel v1.36.0 // indirect
go.opentelemetry.io/otel/trace v1.36.0 // indirect
+2
View File
@@ -36,6 +36,8 @@ github.com/grafana/grafana-app-sdk v0.39.0 h1:WC2E9BKXWDX/e2bajdAFjQEyyWf9BFp7Yz
github.com/grafana/grafana-app-sdk v0.39.0/go.mod h1:xRyBQOttgWTc3tGe9pI0upnpEPVhzALf7Mh/61O4zyY=
github.com/grafana/grafana-app-sdk/logging v0.38.2 h1:EdQTRxbbH72zdqJ09Z76zcSjfALJXkpPLgvKEPPnloc=
github.com/grafana/grafana-app-sdk/logging v0.38.2/go.mod h1:Y/bvbDhBiV/tkIle9RW49pgfSPIPSON8Q4qjx3pyqDk=
github.com/grafana/grafana/pkg/apimachinery v0.0.0-20250514132646-acbc7b54ed9e h1:BTKk7LHuG1kmAkucwTA7DuMbKpKvJTKrGdBmUNO4dfQ=
github.com/grafana/grafana/pkg/apimachinery v0.0.0-20250514132646-acbc7b54ed9e/go.mod h1:IA4SOwun8QyST9c5UNs/fN37XL6boXXDvRYFcFwbipg=
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
@@ -0,0 +1,73 @@
package v0alpha1
import (
"fmt"
"time"
"github.com/grafana/grafana/pkg/apimachinery/utils"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
)
const (
GROUP = "iam.grafana.app"
VERSION = "v0alpha1"
APIVERSION = GROUP + "/" + VERSION
)
var CoreRoleInfo = utils.NewResourceInfo(GROUP, VERSION,
"coreroles", "corerole", "CoreRole",
func() runtime.Object { return &CoreRole{} },
func() runtime.Object { return &CoreRoleList{} },
utils.TableColumns{
Definition: []metav1.TableColumnDefinition{
{Name: "Name", Type: "string", Format: "name"},
{Name: "Title", Type: "string", Format: "string", Description: "Core role name"}, // Not sure this is actually needed
{Name: "Created At", Type: "date"},
},
Reader: func(obj any) ([]interface{}, error) {
core, ok := obj.(*CoreRole)
if ok {
if core != nil {
return []interface{}{
core.Name,
core.Spec.Title,
core.CreationTimestamp.UTC().Format(time.RFC3339),
}, nil
}
}
return nil, fmt.Errorf("expected core role")
},
},
)
var (
SchemeBuilder runtime.SchemeBuilder
localSchemeBuilder = &SchemeBuilder
AddToScheme = localSchemeBuilder.AddToScheme
schemeGroupVersion = schema.GroupVersion{Group: GROUP, Version: VERSION}
)
func init() {
localSchemeBuilder.Register(addKnownTypes, addDefaultingFuncs)
}
// Adds the list of known types to the given scheme.
func addKnownTypes(scheme *runtime.Scheme) error {
scheme.AddKnownTypes(schemeGroupVersion,
&CoreRole{},
&CoreRoleList{},
// What is this about?
&metav1.PartialObjectMetadata{},
&metav1.PartialObjectMetadataList{},
)
metav1.AddToGroupVersion(scheme, schemeGroupVersion)
return nil
}
func addDefaultingFuncs(scheme *runtime.Scheme) error {
// return RegisterDefaults(scheme)
return nil
}
+5 -5
View File
@@ -1477,7 +1477,7 @@ enabled = true
# Select which pluggable state history backend to use. Either "annotations", "loki", "prometheus", or "multiple"
# "loki" writes state history to an external Loki instance.
# "prometheus" writes state history as ALERTS metrics to a Prometheus-compatible data source.
# "prometheus" writes state history as GRAFANA_ALERTS metrics to a Prometheus-compatible data source.
# "multiple" allows history to be written to multiple backends at once.
# Defaults to "annotations".
backend =
@@ -1529,15 +1529,15 @@ loki_max_query_length = 721h
loki_max_query_size = 65536
# For "prometheus" only.
# Target datasource UID for writing ALERTS metrics.
# Target datasource UID for writing GRAFANA_ALERTS metrics.
prometheus_target_datasource_uid =
# For "prometheus" only.
# Metric name for the ALERTS metric. Default is "ALERTS".
prometheus_metric_name = ALERTS
# Metric name for the GRAFANA_ALERTS metric. Default is "GRAFANA_ALERTS".
prometheus_metric_name = GRAFANA_ALERTS
# For "prometheus" only.
# Timeout for writing ALERTS metrics to the target datasource. Default is 10s.
# Timeout for writing GRAFANA_ALERTS metrics to the target datasource. Default is 10s.
prometheus_write_timeout = 10s
[unified_alerting.state_history.external_labels]
+5 -5
View File
@@ -1456,7 +1456,7 @@ disabled_labels =
# Select which pluggable state history backend to use. Either "annotations", "loki", "prometheus", or "multiple"
# "loki" writes state history to an external Loki instance.
# "prometheus" writes state history as ALERTS metrics to a Prometheus-compatible data source.
# "prometheus" writes state history as GRAFANA_ALERTS metrics to a Prometheus-compatible data source.
# "multiple" allows history to be written to multiple backends at once.
# Defaults to "annotations".
; backend = "multiple"
@@ -1508,15 +1508,15 @@ disabled_labels =
;loki_max_query_size = 65536
# For "prometheus" only.
# Target datasource UID for writing ALERTS metrics.
# Target datasource UID for writing GRAFANA_ALERTS metrics.
; prometheus_target_datasource_uid = "my-prometheus-uid"
# For "prometheus" only.
# Metric name for the ALERTS metric. Default is "ALERTS".
; prometheus_metric_name = "ALERTS"
# Metric name for the GRAFANA_ALERTS metric. Default is "GRAFANA_ALERTS".
; prometheus_metric_name = "GRAFANA_ALERTS"
# For "prometheus" only.
# Timeout for writing ALERTS metrics to the target datasource. Default is 10s.
# Timeout for writing GRAFANA_ALERTS metrics to the target datasource. Default is 10s.
; prometheus_write_timeout = 10s
[unified_alerting.state_history.external_labels]
@@ -986,6 +986,101 @@
],
"title": "special null + NaN value mapping from data",
"type": "state-timeline"
},
{
"datasource": {
"type": "testdata"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"axisPlacement": "auto",
"fillOpacity": 70,
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
},
"insertNulls": false,
"lineWidth": 0,
"spanNulls": false
},
"fieldMinMax": false,
"mappings": [
{
"options": {
"match": "false",
"result": {
"color": "red",
"index": 0
}
},
"type": "special"
},
{
"options": {
"match": "null+nan",
"result": {
"color": "blue",
"index": 1,
"text": "null + NaN"
}
},
"type": "special"
}
],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": 0
}
]
}
},
"overrides": []
},
"gridPos": {
"h": 9,
"w": 6,
"x": 12,
"y": 18
},
"id": 14,
"options": {
"alignValue": "center",
"legend": {
"displayMode": "list",
"placement": "bottom",
"showLegend": true
},
"mergeValues": true,
"rowHeight": 0.9,
"showValue": "auto",
"tooltip": {
"hideZeros": false,
"mode": "single",
"sort": "none"
}
},
"pluginVersion": "12.1.0-pre",
"targets": [
{
"datasource": {
"type": "testdata",
"uid": "PD8C576611E62080A"
},
"rawFrameContent": "[\n {\n \"schema\": {\n \"refId\": \"A\",\n \"fields\": [\n {\n \"name\": \"time\",\n \"type\": \"time\",\n \"typeInfo\": {\n \"frame\": \"time\",\n \"nullable\": true\n },\n \"config\": {}\n },\n {\n \"name\": \"value\",\n \"type\": \"number\",\n \"typeInfo\": {\n \"frame\": \"int64\",\n \"nullable\": true\n },\n \"config\": {\n \"thresholds\": {\n \"mode\": \"absolute\",\n \"steps\": [\n {\n \"color\": \"green\",\n \"value\": null\n }\n ]\n }\n }\n }\n ]\n },\n \"data\": {\n \"values\": [\n [\n 1674732835000,\n 1674736435000,\n 1674740035000,\n 1674743635000,\n 1674747235000,\n 1674750835000,\n 1674754235000,\n 1674757835000\n ],\n [\n null,\n null,\n false,\n true,\n true,\n false,\n true,\n null\n ]\n ],\n \"entities\": [null, { \"NaN\": [0], \"Undefined\": [1]}]\n }\n }\n]",
"refId": "A",
"scenarioId": "raw_frame"
}
],
"title": "boolean values from data",
"type": "state-timeline"
}
],
"preload": false,
@@ -1008,5 +1103,5 @@
"timezone": "utc",
"title": "StateTimeline - Thresholds & Mappings",
"uid": "Kce7z9TVz",
"version": 13
}
"version": 18
}
File diff suppressed because one or more lines are too long
@@ -81,7 +81,7 @@ Description of provisioning properties:
: Config object
**config.type**
: Correlation type. “query” is the only supported type at the moment
: Correlation type. Valid values are "query" for linking to a data source query and "external" for linking to an external URL.
**config.target**
: [Target query model](#determine-target-query-model-structure)
@@ -22,12 +22,12 @@ Label-Based Access Control (LBAC) allows fine-grained access control to data sou
### Feature availability
LBAC for data sources is currently generally available for `Loki` and in **experimental** for `Prometheus`. Support for additional data sources may be added in future updates.
LBAC for data sources is currently for `Loki` and `Prometheus`. Support for additional data sources may be added in future updates.
| Data source | Grafana Cloud | Grafana Enterprise | Cross-tenant query support |
| ----------- | ------------- | --------------------------------------------------------- | -------------------------- |
| Loki | GA | GA (requires GEL - Grafana Enterprise Logs) | ❌ |
| Prometheus | PublicPreview | PublicPreview (requires GEM - Grafana Enterprise Metrics) | ❌ |
| Data source | Grafana Cloud | Grafana Enterprise | Cross-tenant query support |
| ----------- | ------------- | ---------------------------------------------- | -------------------------- |
| Loki | GA | GA (requires GEL - Grafana Enterprise Logs) | ❌ |
| Prometheus | GA | GA (requires GEM - Grafana Enterprise Metrics) | ❌ |
{{< admonition type="note" >}}
For enterprise this feature requires Grafana Enterprise Metrics (GEM) or Grafana Enterprise Logs (GEL) to function.
@@ -13,9 +13,7 @@ weight: 250
# Configure LBAC for data sources for Loki data source on Grafana Cloud
LBAC for data sources is available in private preview on Grafana Cloud for Loki created with basic authentication. Loki data sources for LBAC for data sources can only be created, provisioning is currently not available.
You cannot configure LBAC rules for Grafana-provisioned data sources from the UI. Alternatively, you can replicate the setting of the provisioned data source in a new data source as described in [LBAC Configuration for New Loki Data Source](https://grafana.com/docs/grafana/latest/administration/data-source-management/teamlbac/configure-teamlbac-for-loki/#task-1-lbac-configuration-for-new-loki-data-source) and then add the LBAC configuration to the new data source.
LBAC for data sources is available on Grafana Cloud using a new Loki data source with basic authentication configured. A new data source can be created as described in [LBAC Configuration for New Loki Data Source](https://grafana.com/docs/grafana/latest/administration/data-source-management/teamlbac/configure-teamlbac-for-loki/#task-1-lbac-configuration-for-new-loki-data-source).
## Before you begin
@@ -16,9 +16,7 @@ weight: 250
## Grafana Cloud
LBAC for data sources is available in private preview on Grafana Cloud for Prometheus created with basic authentication. Prometheus data sources for LBAC for data sources can only be created, provisioning is currently not available.
You cannot configure LBAC rules for Grafana-provisioned data sources from the UI. Alternatively, you can replicate the setting of the provisioned data source in a new data source as described in [LBAC Configuration for New Prometheus Data Source](#task-1-lbac-configuration-for-new-prometheus-data-source) and then add the LBAC configuration to the new data source.
LBAC for data sources is available on Grafana Cloud using a new Prometheus data source with basic authentication configured. A new data source can be created as described in [LBAC Configuration for New Prometheus Data Source](#task-1-lbac-configuration-for-new-prometheus-data-source).
## Before you begin
@@ -49,6 +49,7 @@ To use the Grafana migration assistant, you need:
- [`Admin`](https://grafana.com/docs/grafana-cloud/account-management/authentication-and-permissions/cloud-roles/) access to the Grafana Cloud Stack. To check your access level, go to `https://grafana.com/orgs/<YOUR-ORG-NAME>/members`.
- [Grafana server administrator](https://grafana.com/docs/grafana/<GRAFANA_VERSION>/administration/roles-and-permissions/#grafana-server-administrators) access to your existing Grafana OSS/Enterprise instance. To check your access level, go to `https://<GRAFANA-ONPREM-URL>/admin/users`.
- Internet access from your existing Grafana OSS/Enterprise instance.
- If you are running Grafana in a [highly-available setup](https://grafana.com/docs/grafana/latest/setup-grafana/set-up-for-high-availability/), we recommend scaling Grafana down to one replica to avoid a [known bug](https://github.com/grafana/grafana/issues/107264).
- If your network requires external services to be on an allowlist to allow access, add the following IPs and URLs to your allowlist:
- [Hosted Grafana](https://grafana.com/docs/grafana-cloud/security-and-account-management/allow-list/#hosted-grafana)
- [Hosted Alerts](https://grafana.com/docs/grafana-cloud/security-and-account-management/allow-list/#hosted-alerts)
@@ -148,7 +149,7 @@ After a snapshot is created, a list of resources appears with resource Type and
## Snapshots created by the migration assistant
The migration assistant currently supports a subset of all resources available in Grafana. Refer to [Supported Resources](https://grafana.com/docs/grafana-cloud/account-management/cloud-migration-assistant/#supported-resources) for more details.
The migration assistant currently supports a subset of all resources available in Grafana. Refer to [Supported Resources](#supported-resources) for more details.
When you create a snapshot, the migration assistant makes a copy of all the resources you select and saves them in the snapshot. The snapshot reflects the current state of the resources when the snapshot is built and is stored locally on your instance, ready to be uploaded in the last stage.
@@ -156,7 +157,7 @@ When you create a snapshot, the migration assistant makes a copy of all the reso
In Grafana v11.2 to v11.6, you can't select specific resources to include in the snapshot, such as only dashboards. All supported resources are included by default.
{{< /admonition >}}
Resources saved in the snapshot are strictly limited to the resources stored within an organization. This is important to note if there are multiple organizations used in your Grafana instance. If you want to migrate multiple organizations, refer to [Migrate multiple organizations](https://grafana.com/docs/grafana-cloud/account-management/cloud-migration-assistant/#migrate-multiple-organizations) for more information and guidance.
Resources saved in the snapshot are strictly limited to the resources stored within an organization. This is important to note if there are multiple organizations used in your Grafana instance. If you want to migrate multiple organizations, refer to [Migrate multiple organizations](#migrate-multiple-organizations) for more information and guidance.
## Resource migration details
@@ -54,13 +54,14 @@ The following tables list permissions associated with basic and fixed roles. Thi
## Basic role assignments
| Basic role | UID | Associated fixed roles | Description |
| ------------- | --------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Grafana Admin | `basic_grafana_admin` | fixed:roles:writer`<br>`fixed:users:writer`<br>`fixed:org.users:writer`<br>`fixed:ldap:writer`<br>`fixed:stats:reader`<br>`fixed:settings:writer`<br>`fixed:provisioning:writer`<br>`fixed:organization:maintainer`<br>`fixed:licensing:writer`<br>`fixed:plugins:maintainer`<br>`fixed:authentication.config:writer`<br>`fixed:migrationassistant:migrator` | Default [Grafana server administrator](/docs/grafana/<GRAFANA_VERSION>/administration/roles-and-permissions/#grafana-server-administrators) assignments. |
| Admin | `basic_admin` | All roles assigned to Editor and `fixed:reports:writer` <br>`fixed:datasources:writer`<br>`fixed:organization:writer`<br>`fixed:datasources.permissions:writer`<br>`fixed:teams:writer`<br>`fixed:dashboards:writer`<br>`fixed:dashboards.permissions:writer`<br>`fixed:dashboards.public:writer`<br>`fixed:folders:writer`<br>`fixed:folders.permissions:writer`<br>`fixed:alerting:writer`<br>`fixed:apikeys:writer`<br>`fixed:alerting.provisioning.secrets:reader`<br>`fixed:alerting.provisioning:writer`<br>`fixed:datasources.caching:writer`<br>`fixed:plugins:writer`<br>`fixed:library.panels:writer` | Default [Grafana organization administrator](ref:rbac-basic-roles) assignments. |
| Editor | `basic_editor` | All roles assigned to Viewer and `fixed:datasources:explorer` <br>`fixed:dashboards:creator`<br>`fixed:folders:creator`<br>`fixed:annotations:writer`<br>`fixed:alerting:writer`<br>`fixed:library.panels:creator`<br>`fixed:library.panels:general.writer`<br>`fixed:alerting.provisioning.status:writer` | Default [Editor](ref:rbac-basic-roles) assignments. |
| Viewer | `basic_viewer` | `fixed:datasources.id:reader`<br>`fixed:organization:reader`<br>`fixed:annotations:reader`<br>`fixed:annotations.dashboard:writer`<br>`fixed:alerting:reader`<br>`fixed:plugins.app:reader`<br>`fixed:dashboards.insights:reader`<br>`fixed:datasources.insights:reader`<br>`fixed:library.panels:general.reader`<br>`fixed:folders.general:reader`<br>`fixed:datasources.builtin:reader` | Default [Viewer](ref:rbac-basic-roles) assignments. |
| No Basic Role | n/a | | Default [No Basic Role](ref:rbac-basic-roles) |
| Basic role | UID | Associated fixed roles | Description |
| --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------- |
| Grafana Admin | `basic_grafana_admin` |
| `fixed:authentication.config:writer`<br>`fixed:general.auth.config:writer`<br>`fixed:ldap:writer`<br>`fixed:licensing:writer`<br>`fixed:migrationassistant:migrator`<br>`fixed:org.users:writer`<br>`fixed:organization:maintainer`<br>`fixed:plugins:maintainer`<br>`fixed:provisioning:writer`<br>`fixed:roles:writer`<br>`fixed:settings:reader`<br>`fixed:settings:writer`<br>`fixed:stats:reader`<br>`fixed:support.bundles:writer`<br>`fixed:usagestats:reader`<br>`fixed:users:writer` | Default [Grafana server administrator](/docs/grafana/<GRAFANA_VERSION>/administration/roles-and-permissions/#grafana-server-administrators) assignments. |
| Admin | `basic_admin` | All roles assigned to Editor and `fixed:reports:writer` <br>`fixed:datasources:writer`<br>`fixed:organization:writer`<br>`fixed:datasources.permissions:writer`<br>`fixed:teams:writer`<br>`fixed:dashboards:writer`<br>`fixed:dashboards.permissions:writer`<br>`fixed:dashboards.public:writer`<br>`fixed:folders:writer`<br>`fixed:folders.permissions:writer`<br>`fixed:alerting:writer`<br>`fixed:apikeys:writer`<br>`fixed:alerting.provisioning.secrets:reader`<br>`fixed:alerting.provisioning:writer`<br>`fixed:datasources.caching:writer`<br>`fixed:plugins:writer`<br>`fixed:library.panels:writer` | Default [Grafana organization administrator](ref:rbac-basic-roles) assignments. |
| Editor | `basic_editor` | All roles assigned to Viewer and `fixed:datasources:explorer` <br>`fixed:dashboards:creator`<br>`fixed:folders:creator`<br>`fixed:annotations:writer`<br>`fixed:alerting:writer`<br>`fixed:library.panels:creator`<br>`fixed:library.panels:general.writer`<br>`fixed:alerting.provisioning.status:writer` | Default [Editor](ref:rbac-basic-roles) assignments. |
| Viewer | `basic_viewer` | `fixed:datasources.id:reader`<br>`fixed:organization:reader`<br>`fixed:annotations:reader`<br>`fixed:annotations.dashboard:writer`<br>`fixed:alerting:reader`<br>`fixed:plugins.app:reader`<br>`fixed:dashboards.insights:reader`<br>`fixed:datasources.insights:reader`<br>`fixed:library.panels:general.reader`<br>`fixed:folders.general:reader`<br>`fixed:datasources.builtin:reader` | Default [Viewer](ref:rbac-basic-roles) assignments. |
| No Basic Role | n/a | | Default [No Basic Role](ref:rbac-basic-roles) |
## Fixed role definitions
@@ -92,6 +93,7 @@ To learn how to use the roles API to determine the role UUIDs, refer to [Manage
| `fixed:apikeys:reader` | `fixed_kYZ7UEkwEvGmCCjTrq07cFAVFws` | `apikeys:read` for scope `apikeys:*` | Read all api keys. |
| `fixed:apikeys:writer` | `fixed_anTrcpRkm21NBO1Q2CsX8y0fiCQ` | All permissions from `fixed:apikeys:reader` and <br> `apikeys:create` <br> `apikeys:delete` for scope `apikeys:*` | Read, create, delete all api keys. |
| `fixed:authentication.config:writer` | `fixed_0rYhZ2Qnzs8AdB1nX7gexk3fHDw` | `settings:read` for scope `settings:auth.saml:*` <br> `settings:write` for scope `settings:auth.saml:*` | Read and update authentication and SAML settings. |
| `fixed:general.auth.config:writer` | `fixed_QFxIT_FGtBqbIVJIwx1bLgI5z6c` | `settings:read` for scope `settings:auth:oauth_allow_insecure_email_lookup` <br> `settings:write` for scope `settings:auth:oauth_allow_insecure_email_lookup` | Read and update the Grafana instance's general authentication configuration settings. |
| `fixed:dashboards:creator` | `fixed_ZorKUcEPCM01A1fPakEzGBUyU64` | `dashboards:create`<br>`folders:read` | Create dashboards. |
| `fixed:dashboards:reader` | `fixed_Sgr67JTOhjQGFlzYRahOe45TdWM` | `dashboards:read` | Read all dashboards. |
| `fixed:dashboards:writer` | `fixed_OK2YOQGIoI1G031hVzJB6rAJQAs` | All permissions from `fixed:dashboards:reader` and <br>`dashboards:write`<br>`dashboards:delete`<br>`dashboards:create`<br>`dashboards.permissions:read`<br>`dashboards.permissions:write` | Read, create, update, and delete all dashboards. |
@@ -146,10 +148,13 @@ To learn how to use the roles API to determine the role UUIDs, refer to [Manage
| `fixed:settings:reader` | `fixed_0LaUt1x6PP8hsZzEBhqPQZFUd8Q` | `settings:read` | Read Grafana instance settings. |
| `fixed:settings:writer` | `fixed_joIHDgMrGg790hMhUufVzcU4j44` | All permissions from `fixed:settings:reader` and<br>`settings:write` | Read and update Grafana instance settings. |
| `fixed:stats:reader` | `fixed_OnRCXxZVINWpcKvTF5A1gecJ7pA` | `server.stats:read` | Read Grafana instance statistics. |
| `fixed:support.bundles:reader` | `fixed_gcPjI3PTUJwRx-GJZwDhNa7zbos` | `support.bundles:read` | List and download support bundles. |
| `fixed:support.bundles:writer` | `fixed_dTgCv9Wxrp_WHAhwHYIgeboxKpE` | `support.bundles:read`<br>`support.bundles:create`<br>`support.bundles:delete` | Create, delete, list and download support bundles. |
| `fixed:teams:creator` | `fixed_nzVQoNSDSn0fg1MDgO6XnZX2RZI` | `teams:create`<br>`org.users:read` | Create a team and list organization users (required to manage the created team). |
| `fixed:teams:read` | `fixed_Z8pB0GQlrqRt8IZBCJQxPWvJPgQ` | `teams:read` | List all teams. |
| `fixed:teams:writer` | `fixed_xw1T0579h620MOYi4L96GUs7fZY` | `teams:create`<br>`teams:delete`<br>`teams:read`<br>`teams:write`<br>`teams.permissions:read`<br>`teams.permissions:write` | Create, read, update and delete teams and manage team memberships. |
| `fixed:users:reader` | `fixed_buZastUG3reWyQpPemcWjGqPAd0` | `users:read`<br>`users.quotas:read`<br>`users.authtoken:read`<br>` | Read all users and their information, such as team memberships, authentication tokens, and quotas. |
| `fixed:usagestats:reader` | `fixed_eAM0azEvnWFCJAjNkUKnGL_1-bU` | `server.usagestats.report:read` | View usage statistics report. |
| `fixed:users:reader` | `fixed_buZastUG3reWyQpPemcWjGqPAd0` | `users:read`<br>`users.quotas:read`<br>`users.authtoken:read` | Read all users and their information, such as team memberships, authentication tokens, and quotas. |
| `fixed:users:writer` | `fixed_wjzgHHo_Ux25DJuELn_oiAdB_yM` | All permissions from `fixed:users:reader` and <br>`users:write`<br>`users:create`<br>`users:delete`<br>`users:enable`<br>`users:disable`<br>`users.password:write`<br>`users.permissions:write`<br>`users:logout`<br>`users.authtoken:write`<br>`users.quotas:write` | Read and update all attributes and settings for all users in Grafana: update user information, read user information, create or enable or disable a user, make a user a Grafana administrator, sign out a user, update a user’s authentication token, or update quotas for all users. |
### Alerting roles
+6 -2
View File
@@ -35,13 +35,17 @@ cards:
href: ./configure-notifications/
description: Choose how, when, and where to send your alert notifications.
height: 24
- title: Monitor status
- title: Monitor alerts
href: ./monitor-status/
description: Monitor, respond to, and triage issues within your services.
height: 24
- title: Additional configuration
href: ./set-up/
description: Use advanced configuration options to further tailor your alerting setup. These options can enhance security, scalability, and automation in complex environments.
description: Use advanced configuration to customize your alerting setup and improve security, scalability, and automation in complex environments.
height: 24
- title: Best practices
href: ./best-practices/
description: Get practical guidance for handling common alert issues, and explore examples for creating both basic and advanced alerts.
height: 24
---
+10 -14
View File
@@ -33,16 +33,16 @@ refs:
destination: /docs/grafana/<GRAFANA_VERSION>/alerting/alerting-rules/create-recording-rules/
- pattern: /docs/grafana-cloud/
destination: /docs/grafana-cloud/alerting-and-irm/alerting/alerting-rules/create-recording-rules/
alert-types-comparison-table:
import-to-grafana-managed:
- pattern: /docs/grafana/
destination: /docs/grafana/<GRAFANA_VERSION>/alerting/fundamentals/alert-rules/#comparison-between-alert-rule-types
destination: /docs/grafana/<GRAFANA_VERSION>/alerting/alerting-rules/alerting-migration/
- pattern: /docs/grafana-cloud/
destination: /docs/grafana-cloud/alerting-and-irm/alerting/fundamentals/alert-rules/#comparison-between-alert-rule-types
templating-labels-annotations:
destination: /docs/grafana-cloud/alerting-and-irm/alerting/alerting-rules/alerting-migration/
comparison-ds-grafana-rules:
- pattern: /docs/grafana/
destination: /docs/grafana/<GRAFANA_VERSION>/alerting/fundamentals/templates/
destination: /docs/grafana/<GRAFANA_VERSION>/alerting/alerting-rules/create-data-source-managed-rule/#comparison-with-grafana-managed-rules
- pattern: /docs/grafana-cloud/
destination: /docs/grafana-cloud/alerting-and-irm/alerting/fundamentals/templates/
destination: /docs/grafana-cloud/alerting-and-irm/alerting/alerting-rules/create-data-source-managed-rule/#comparison-with-grafana-managed-rules
---
# Configure alert rules
@@ -53,18 +53,14 @@ An alert rule consists of one or more queries and expressions that select the da
Grafana supports two types of alert rules:
1. Grafana-managed alert rules: These can query multiple data sources.
1. **Grafana-managed alert rules** — the recommended option. They can query backend data sources—including Prometheus-based ones—and offer a [richer feature set](ref:comparison-ds-grafana-rules).
1. **Data source-managed alert rules** — supported for Prometheus-based data sources (such as Mimir, Loki, and Prometheus), with rules stored in the data source itself.
1. Data source-managed alert rules: These can only query Prometheus-based data sources and support horizontal scaling.
We recommend using Grafana-managed alert rules whenever possible, and opting for data source-managed alert rules when horizontal scaling is required. Refer to the [comparison table of alert rule types](ref:alert-types-comparison-table) for a more detailed overview.
You can [convert and import data source-managed rules into Grafana-managed rules](ref:import-to-grafana-managed) to let Grafana Alerting manage them.
Both types of alert rules can be configured in Grafana using the **+ New alert rule** flow. For step-by-step instructions, refer to:
- [Configure Grafana-managed alert rules](ref:configure-grafana-alerts)
- [Configure data source-managed alert rules](ref:configure-ds-alerts)
- [Create and link alert rules to panels](ref:templating-labels-annotations)
Alert rules can also query metrics generated by recording rules. To learn more, refer to:
- [Create recording rules](ref:recording-rules)
In Grafana Alerting, you can also [configure recording rules](ref:recording-rules), which pre-compute queries and save the results as new time series metrics for use in other alert rules or dashboard queries.
@@ -10,7 +10,7 @@ labels:
- oss
title: Import data source-managed rules to Grafana-managed rules
menuTitle: Import to Grafana-managed rules
weight: 450
weight: 300
refs:
configure-grafana-rule_query_offset:
- pattern: /docs/
@@ -18,9 +18,9 @@ labels:
- enterprise
- oss
title: Configure data source-managed alert rules
weight: 200
weight: 400
refs:
shared-configure-prometheus-data-source-alerting:
configure-prometheus-data-source-alerting:
- pattern: /docs/grafana/
destination: /docs/grafana/<GRAFANA_VERSION>/datasources/prometheus/configure/
- pattern: /docs/grafana-cloud/
@@ -29,7 +29,12 @@ refs:
- pattern: /docs/grafana/
destination: /docs/grafana/<GRAFANA_VERSION>/alerting/alerting-rules/create-grafana-managed-rule/
- pattern: /docs/grafana-cloud/
destination: /docs/grafana-cloud/alerting-and-irm/alerting/alerting-rules/create-grafana-managed-rule/notification-policies/
destination: /docs/grafana-cloud/alerting-and-irm/alerting/alerting-rules/create-grafana-managed-rule/
supported-data-sources-grafana-rules:
- pattern: /docs/grafana/
destination: /docs/grafana/<GRAFANA_VERSION>/alerting/alerting-rules/create-grafana-managed-rule/#supported-data-sources
- pattern: /docs/grafana-cloud/
destination: /docs/grafana-cloud/alerting-and-irm/alerting/alerting-rules/create-grafana-managed-rule/#supported-data-sources
notification-policies:
- pattern: /docs/grafana/
destination: /docs/grafana/<GRAFANA_VERSION>/alerting/fundamentals/notifications/notification-policies/
@@ -75,29 +80,113 @@ refs:
destination: /docs/grafana/<GRAFANA_VERSION>/alerting/alerting-rules/link-alert-rules-to-panels/
- pattern: /docs/grafana-cloud/
destination: /docs/grafana-cloud/alerting-and-irm/alerting/alerting-rules/link-alert-rules-to-panels/
import-to-grafana-rules:
- pattern: /docs/grafana/
destination: /docs/grafana/<GRAFANA_VERSION>/alerting/alerting-rules/alerting-migration/
- pattern: /docs/grafana-cloud/
destination: /docs/grafana-cloud/alerting-and-irm/alerting/alerting-rules/alerting-migration/
create-recording-rules:
- pattern: /docs/grafana/
destination: /docs/grafana/<GRAFANA_VERSION>/alerting/alerting-rules/create-recording-rules/
- pattern: /docs/grafana-cloud/
destination: /docs/grafana-cloud/alerting-and-irm/alerting/alerting-rules/create-recording-rules/
rbac:
- pattern: /docs/grafana/
destination: /docs/grafana/<GRAFANA_VERSION>/alerting/set-up/configure-rbac/
- pattern: /docs/grafana-cloud/
destination: /docs/grafana-cloud/alerting-and-irm/alerting/set-up/configure-rbac/
expression-queries:
- pattern: /docs/grafana/
destination: /docs/grafana/<GRAFANA_VERSION>/alerting/fundamentals/alert-rules/queries-conditions/#advanced-options-expressions
- pattern: /docs/grafana-cloud/
destination: /docs/grafana-cloud/alerting-and-irm/alerting/fundamentals/alert-rules/queries-conditions/#advanced-options-expressions
alert-condition:
- pattern: /docs/grafana/
destination: /docs/grafana/<GRAFANA_VERSION>/alerting/fundamentals/alert-rules/queries-conditions/#alert-condition
- pattern: /docs/grafana-cloud/
destination: /docs/grafana-cloud/alerting-and-irm/alerting/fundamentals/alert-rules/queries-conditions/#alert-condition
notification-images:
- pattern: /docs/grafana/
destination: /docs/grafana/<GRAFANA_VERSION>/alerting/configure-notifications/template-notifications/images-in-notifications/
- pattern: /docs/grafana-cloud/
destination: /docs/grafana-cloud/alerting-and-irm/alerting/configure-notifications/template-notifications/images-in-notifications/
view-alert-state-history:
- pattern: /docs/grafana/
destination: /docs/grafana/<GRAFANA_VERSION>/alerting/monitor-status/view-alert-state-history/
- pattern: /docs/grafana-cloud/
destination: /docs/grafana-cloud/alerting-and-irm/alerting/alerting/monitor-status/view-alert-state-history/
view-compare-and-restore-alert-rules-versions:
- pattern: /docs/grafana/
destination: /docs/grafana/<GRAFANA_VERSION>/alerting/monitor-status/view-alert-rules/#view-compare-and-restore-alert-rules-versions
- pattern: /docs/grafana-cloud/
destination: /docs/grafana-cloud/alerting-and-irm/alerting/alerting/monitor-status/view-alert-rules/#view-compare-and-restore-alert-rules-versions
th-provisioning:
- pattern: /docs/grafana/
destination: /docs/grafana/<GRAFANA_VERSION>/alerting/set-up/provision-alerting-resources/terraform-provisioning/
- pattern: /docs/grafana-cloud/
destination: /docs/grafana-cloud/alerting-and-irm/alerting/set-up/provision-alerting-resources/terraform-provisioning/
no-data-error-states:
- pattern: /docs/grafana/
destination: /docs/grafana/<GRAFANA_VERSION>/alerting/fundamentals/alert-rule-evaluation/nodata-and-error-states/
- pattern: /docs/grafana-cloud/
destination: /docs/grafana-cloud/alerting-and-irm/alerting/fundamentals/alert-rule-evaluation/nodata-and-error-states/
stale-alert-instances:
- pattern: /docs/grafana/
destination: /docs/grafana/<GRAFANA_VERSION>/alerting/fundamentals/alert-rule-evaluation/stale-alert-instances/
- pattern: /docs/grafana-cloud/
destination: /docs/grafana-cloud/alerting-and-irm/alerting/fundamentals/alert-rule-evaluation/stale-alert-instances/
---
# Configure data source-managed alert rules
Data source-managed alert rules can only be created using Grafana Mimir or Grafana Loki data sources.
Data source-managed alert rules are alert rules that are stored in the data source, such as in Prometheus or Loki, rather than in Grafana.
The rules are stored within the data source. In a distributed architecture, they can scale horizontally to provide high-availability. For more details, refer to [alert rule types](ref:alert-rules).
In Grafana Alerting, you can:
We recommend using [Grafana-managed alert rules](ref:configure-grafana-managed-rules) whenever possible and opting for data source-managed alert rules when scaling your alerting setup is necessary.
1. Create and edit data source-managed rules for Grafana Mimir and Grafana Loki data sources.
1. View rules from Prometheus data sources when [Manage alerts via Alerting UI](ref:configure-prometheus-data-source-alerting) is enabled. However, you cannot create or edit these rules in Grafana.
1. [Import data source-managed rules](ref:import-to-grafana-rules) from Loki, Mimir, and Prometheus into Grafana-managed rules.
> Rules from a Prometheus data source appear in the **Data source-managed** section of the **Alert rules** page when [Manage alerts via Alerting UI](ref:shared-configure-prometheus-data-source-alerting) is enabled.
>
> However, Grafana can only create and edit data source-managed rules for Mimir and Loki, not for a Prometheus instance.
{{< admonition type="note" >}}
Data source-managed rules are supported for horizontal scalability, but they can introduce more operational complexity than Grafana-managed alert rules.
[//]: <> ({{< docs/shared lookup="alerts/note-prometheus-ds-rules.md" source="grafana" version="<GRAFANA_VERSION>" >}})
We recommend using [Grafana-managed alert rules](ref:configure-grafana-managed-rules) whenever possible, as they provide a richer feature set and better integration with the full Grafana Alerting workflow.
{{< /admonition >}}
## Comparison with Grafana-managed rules
The table below compares Grafana-managed and data source-managed alert rules.
| <div style="width:200px">Feature</div> | <div style="width:200px">Grafana-managed alert rule</div> | <div style="width:200px">Data source-managed alert rule |
| ----------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------- | --------------------------------------------------------------------------------- |
| Supported data sources | All backend data sources enabling the [`alerting` option](ref:rbac) | Only supports creating rules for Mimir and Loki data sources |
| Mix and match data sources | Yes | No |
| Add [expressions](ref:expression-queries) to transform<wbr /> your data and set [alert conditions](ref:alert-condition) | Yes | No |
| [No data and error states](ref:no-data-error-states) | Yes | No |
| [Stale alert instances](ref:stale-alert-instances) | Yes | No |
| [Images in alert notifications](ref:notification-images) | Yes | No |
| [Role-based access control](ref:rbac) | Yes | No |
| [Alert state history](ref:view-alert-state-history) | Yes | No |
| [Alert version history](ref:view-compare-and-restore-alert-rules-versions) | Yes | No |
| [Terraform provisioning](ref:th-provisioning) | Yes | No |
| [Recording rules](ref:create-recording-rules) | Yes | Yes |
| Organization | Organize and manage access with folders | Use namespaces |
| Alert rule evaluation | Alert evaluation is done in Grafana | Alert rule evaluation is done in the data source and allow for horizontal scaling |
| Scaling | Alert rules are stored in the Grafana database. | Alert rules are stored within the data source and allow for horizontal scaling |
The following diagram shows the architecture of a Mimir setup that uses data source-managed alert rules.
{{< figure src="/media/docs/alerting/mimir-managed-alerting-architecture-v2.png" max-width="750px" alt="Data source-managed alerting architecture based on Grafana Mimir" >}}
## Create data source-managed alert rules
To create or edit data source-managed alert rules, follow these instructions.
## Before you begin
### Before you begin
Verify that you have write permission to the Mimir or Loki data source. Otherwise, you cannot create or update data source-managed alert rules.
### Enable the Ruler API
#### Enable the Ruler API
For more information, refer to the [Mimir Ruler API](/docs/mimir/latest/references/http-api/#ruler) or [Loki Ruler API](/docs/loki/latest/api/#ruler).
@@ -105,13 +194,13 @@ For more information, refer to the [Mimir Ruler API](/docs/mimir/latest/referenc
- **Loki** - The `local` rule storage type, default for the Loki data source, supports only viewing of rules. To edit rules, configure one of the other rule storage types.
### Permissions
#### Permissions
Alert rules for Mimir or Loki instances can be edited or deleted by users with **Editor** or **Admin** roles.
If you do not want to manage alert rules for a particular data source, go to its settings and clear the **Manage alerts via Alerting UI** checkbox.
### Provisioning
#### Provisioning
Note that if you delete an alert resource created in the UI, you can no longer retrieve it.
@@ -119,9 +208,11 @@ To backup and manage alert rules, you can [provision alerting resources](ref:sha
[//]: <> ({{< docs/shared lookup="alerts/configure-provisioning-before-begin.md" source="grafana" version="<GRAFANA_VERSION>" >}})
### Set alert rule name
{{< docs/shared lookup="alerts/configure-alert-rule-name.md" source="grafana" version="<GRAFANA_VERSION>" >}}
## Define query and condition
### Define query and condition
Define a query to get the data you want to measure and a condition that needs to be met before an alert rule fires.
@@ -137,7 +228,7 @@ By default, new alert rules are Grafana-managed. To switch to **Data source-mana
1. In the **Rule type** option, select **Data source-managed**.
1. Click **Preview alerts**.
## Set alert evaluation behavior
### Set alert evaluation behavior
Use [alert rule evaluation](ref:alert-rule-evaluation) to determine how frequently an alert rule should be evaluated and how quickly it should change its state.
@@ -154,7 +245,7 @@ Use [alert rule evaluation](ref:alert-rule-evaluation) to determine how frequent
Once a condition is met, the alert goes into the **Pending** state. If the condition remains active for the duration specified, the alert transitions to the **Firing** state, else it reverts to the **Normal** state.
## Configure labels and notifications
### Configure labels and notifications
Add [labels](ref:alert-rule-labels) to your alert rules to set which [notification policy](ref:notification-policies) should handle your firing alert instances.
@@ -164,4 +255,34 @@ All alert rules and instances, irrespective of their labels, match the default n
Add custom labels by selecting existing key-value pairs from the drop down, or add new labels by entering the new key or value.
{{< docs/shared lookup="alerts/configure-notification-message.md" source="grafana" version="<GRAFANA_VERSION>" >}}
### Configure notification message
Use [annotations](ref:shared-annotations) to add information to alert messages that can help respond to the alert.
Annotations are included by default in notification messages, and can use text or [templates](ref:shared-alert-rule-template) to display dynamic data from queries.
Grafana provides several optional annotations.
1. Optional: Add a summary.
Short summary of what happened and why.
1. Optional: Add a description.
Description of what the alert rule does.
1. Optional: Add a Runbook URL.
Webpage where you keep your runbook for the alert
1. Optional: Add a custom annotation.
Add any additional information that could help address the alert.
1. Optional: **Link dashboard and panel**.
[Link the alert rule to a panel](ref:shared-link-alert-rules-to-panels) to facilitate alert investigation.
1. Click **Save rule**.
[//]: <> ({{< docs/shared lookup="alerts/configure-notification-message.md" source="grafana" version="<GRAFANA_VERSION>" >}})
@@ -42,9 +42,9 @@ refs:
destination: /docs/grafana-cloud/alerting-and-irm/alerting/fundamentals/alert-rules/queries-conditions/#recovery-threshold
modify-the-no-data-or-error-state:
- pattern: /docs/grafana/
destination: /docs/grafana/<GRAFANA_VERSION>/alerting/fundamentals/alert-rule-evaluation/state-and-health/#modify-the-no-data-or-error-state
destination: /docs/grafana/<GRAFANA_VERSION>/alerting/fundamentals/alert-rule-evaluation/nodata-and-error-states/#modify-the-no-data-or-error-state
- pattern: /docs/grafana-cloud/
destination: /docs/grafana-cloud/alerting-and-irm/alerting/fundamentals/alert-rule-evaluation/state-and-health/#modify-the-no-data-or-error-state
destination: /docs/grafana-cloud/alerting-and-irm/alerting/fundamentals/alert-rule-evaluation/nodata-and-error-states/#modify-the-no-data-or-error-state
pending-period:
- pattern: /docs/grafana/
destination: /docs/grafana/<GRAFANA_VERSION>/alerting/fundamentals/alert-rule-evaluation/#pending-period
@@ -105,11 +105,6 @@ refs:
destination: /docs/grafana/<GRAFANA_VERSION>/alerting/fundamentals/alert-rules/
- pattern: /docs/grafana-cloud/
destination: /docs/grafana-cloud/alerting-and-irm/alerting/fundamentals/alert-rules/
compatible-data-sources:
- pattern: /docs/grafana/
destination: /docs/grafana/<GRAFANA_VERSION>/alerting/fundamentals/alert-rules/#supported-data-sources
- pattern: /docs/grafana-cloud/
destination: /docs/grafana-cloud/alerting-and-irm/alerting/fundamentals/alert-rules/#supported-data-sources
shared-provision-alerting-resources:
- pattern: /docs/grafana/
destination: /docs/grafana/<GRAFANA_VERSION>/alerting/set-up/provision-alerting-resources/
@@ -130,28 +125,44 @@ refs:
destination: /docs/grafana/<GRAFANA_VERSION>/alerting/alerting-rules/link-alert-rules-to-panels/
- pattern: /docs/grafana-cloud/
destination: /docs/grafana-cloud/alerting-and-irm/alerting/alerting-rules/link-alert-rules-to-panels/
tutorials:
- pattern: /docs/grafana/
destination: /docs/grafana/<GRAFANA_VERSION>/alerting/best-practices/tutorials/
- pattern: /docs/grafana-cloud/
destination: /docs/grafana-cloud/alerting-and-irm/alerting/best-practices/tutorials/
---
# Configure Grafana-managed alert rules
Grafana-managed rules can query data from multiple data sources in a single alert rule.
They're the most flexible [alert rule type](ref:alert-rules).
You can also add expressions to transform your data, set alert conditions, and images in alert notifications.
Grafana-managed alert rules are the default way to create alert rules in Grafana.
Grafana-managed rules inherit their model from Prometheus Alerting and extend it with greater flexibility—such as multi-data source queries, expression-based transformations, advanced alert conditions, images in notifications, custom states, and more.
To create or edit Grafana-managed alert rules, follow the instructions below.
{{< admonition type="tip" >}}
For quick-start tutorials on key alerting features, see [Getting started with Grafana Alerting tutorials](ref:tutorials).
{{< /admonition >}}
## Before you begin
Before you create Grafana-managed alert rules, review the following requirements and options.
### Supported data sources
Grafana-managed alert rules can query backend data sources when the data source's `plugin.json` file sets `{"backend": true, "alerting": true}`.
Before you create an alert rule, verify that the data sources you plan to query are compatible and properly configured.
You can find the public data sources that support alert rules in the [Grafana Plugins directory](/grafana/plugins/data-source-plugins/?features=alerting).
### Alert rule limits in Grafana Cloud
{{< admonition type="note" >}}
In Grafana Cloud, the number of Grafana-managed alert rules you can create depends on your Grafana Cloud plan.
- Free Forever plan: You can create up to 100 free alert rules, with each alert rule having a maximum of 1000 alert instances.
- All paid plans (Pro and Advanced): They have a soft limit of 2000 alert rules and support unlimited alert instances. To increase the limit, open a support ticket from the [Cloud portal](/docs/grafana-cloud/account-management/support/).
{{< /admonition >}}
To create or edit Grafana-managed alert rules, follow the instructions below. For a practical example, check out our [tutorial on getting started with Grafana alerting](http://grafana.com/tutorials/alerting-get-started/).
## Before you begin
Verify that the data sources you plan to query in the alert rule are [compatible with Grafana-managed alert rules](ref:compatible-data-sources) and are properly configured.
### Permissions
Only users with **Edit** permissions for the folder storing the rules can edit or delete Grafana-managed alert rules. Only admins can restore deleted Grafana-managed alert rules.
@@ -174,6 +185,8 @@ After you have created an alert rule, the system defaults to your previous choic
Switching from advanced to default may result in queries and expressions that can't be converted.
In this case, a warning message asks if you want to continue to reset to default settings.
## Set alert rule name
{{< docs/shared lookup="alerts/configure-alert-rule-name.md" source="grafana" version="<GRAFANA_VERSION>" >}}
## Define query and condition
@@ -50,5 +50,5 @@ The evaluation group of the recording rule determines how often the metric is pr
Similar to alert rules, Grafana supports two types of recording rules:
1. [Grafana-managed recording rules](ref:grafana-managed-recording-rules), which can query any Grafana data source supported by alerting.
1. [Grafana-managed recording rules](ref:grafana-managed-recording-rules), which can query any Grafana data source supported by alerting. It's the recommended option.
2. [Data source-managed recording rules](ref:data-source-managed-recording-rules), which can query Prometheus-based data sources like Mimir or Loki.
@@ -28,9 +28,9 @@ refs:
destination: /docs/grafana-cloud/alerting-and-irm/alerting/alerting-rules/create-recording-rules/
alerting-data-sources:
- pattern: /docs/grafana/
destination: /docs/grafana/<GRAFANA_VERSION>/alerting/fundamentals/alert-rules/#supported-data-sources
destination: /docs/grafana/<GRAFANA_VERSION>/alerting/alerting-rules/create-grafana-managed-rule/#supported-data-sources
- pattern: /docs/grafana-cloud/
destination: /docs/grafana-cloud/alerting-and-irm/alerting/fundamentals/alert-rules/#supported-data-sources
destination: /docs/grafana-cloud/alerting-and-irm/alerting/alerting-rules/create-grafana-managed-rule/#supported-data-sources
configure-grafana-min-interval:
- pattern: /docs/
destination: /docs/grafana/<GRAFANA_VERSION>/setup-grafana/configure-grafana/#min_interval
@@ -16,7 +16,7 @@ labels:
- enterprise
- oss
title: Create and link alert rules to panels
weight: 300
weight: 200
refs:
time-series-visualizations:
- pattern: /docs/grafana/
+27 -1
View File
@@ -15,6 +15,32 @@ weight: 170
# Grafana Alerting best practices
This section provides a set of guides and examples of best practices for Grafana Alerting. Here you can learn more about more about how to handle common alert management problems and you can see examples of more advanced usage of Grafana Alerting.
This section provides a set of guides and examples of best practices for Grafana Alerting. Here you can learn more about how to handle common alert management problems and you can see examples of more advanced usage of Grafana Alerting.
{{< section >}}
Designing and configuring an alert management set up that works takes time. Here are some additional tips on how to create an effective alert management set up:
**Which are the key metrics for your business that you want to monitor and alert on?**
- Find events that are important to know about and not so trivial or frequent that recipients ignore them.
- Alerts should only be created for big events that require immediate attention or intervention.
- Consider quality over quantity.
**How do you want to organize your alerts and notifications?**
- Be selective about who you set to receive alerts. Consider sending them to the right teams, whoever is on call, and the specific channels.
- Think carefully about priority and severity levels.
- Automate as far as possible provisioning Alerting resources with the API or Terraform.
**Which information should you include in notifications?**
- Consider who the alert receivers and responders are.
- Share information that helps responders identify and address potential issues.
- Link alerts to dashboards to guide responders on which data to investigate.
**How can you reduce alert fatigue?**
- Avoid noisy, unnecessary alerts by using silences, mute timings, or pausing alert rule evaluation.
- Continually tune your alert rules to review effectiveness. Remove alert rules to avoid duplication or ineffective alerts.
- Continually review your thresholds and evaluation rules.
@@ -28,14 +28,14 @@ refs:
destination: /docs/grafana-cloud/alerting-and-irm/alerting/fundamentals/notifications/
no-data-and-error-alerts:
- pattern: /docs/grafana/
destination: /docs/grafana/<GRAFANA_VERSION>/alerting/fundamentals/alert-rule-evaluation/state-and-health/#no-data-and-error-alerts
destination: /docs/grafana/<GRAFANA_VERSION>/alerting/fundamentals/alert-rule-evaluation/nodata-and-error-states/#no-data-and-error-alerts
- pattern: /docs/grafana-cloud/
destination: /docs/grafana-cloud/alerting-and-irm/alerting/fundamentals/alert-rule-evaluation/state-and-health/#no-data-and-error-alerts
destination: /docs/grafana-cloud/alerting-and-irm/alerting/fundamentals/alert-rule-evaluation/nodata-and-error-states/#no-data-and-error-alerts
configure-nodata-and-error-handling:
- pattern: /docs/grafana/
destination: /docs/grafana/<GRAFANA_VERSION>/alerting/fundamentals/alert-rule-evaluation/state-and-health/#modify-the-no-data-or-error-state
destination: /docs/grafana/<GRAFANA_VERSION>/alerting/fundamentals/alert-rule-evaluation/nodata-and-error-states/#modify-the-no-data-or-error-state
- pattern: /docs/grafana-cloud/
destination: /docs/grafana-cloud/alerting-and-irm/alerting/fundamentals/alert-rule-evaluation/state-and-health/#modify-the-no-data-or-error-state
destination: /docs/grafana-cloud/alerting-and-irm/alerting/fundamentals/alert-rule-evaluation/nodata-and-error-states/#modify-the-no-data-or-error-state
missing-data-guide:
- pattern: /docs/grafana/
destination: /docs/grafana/<GRAFANA_VERSION>/alerting/best-practices/missing-data/
@@ -33,19 +33,24 @@ refs:
destination: /docs/grafana-cloud/alerting-and-irm/alerting/monitor-status/view-alert-state-history/
configure-nodata-and-error-handling:
- pattern: /docs/grafana/
destination: /docs/grafana/<GRAFANA_VERSION>/alerting/fundamentals/alert-rule-evaluation/state-and-health/#modify-the-no-data-or-error-state
destination: /docs/grafana/<GRAFANA_VERSION>/alerting/fundamentals/alert-rule-evaluation/nodata-and-error-states/#modify-the-no-data-or-error-state
- pattern: /docs/grafana-cloud/
destination: /docs/grafana-cloud/alerting-and-irm/alerting/fundamentals/alert-rule-evaluation/state-and-health/#modify-the-no-data-or-error-state
destination: /docs/grafana-cloud/alerting-and-irm/alerting/fundamentals/alert-rule-evaluation/nodata-and-error-states/#modify-the-no-data-or-error-state
stale-alert-instances:
- pattern: /docs/grafana/
destination: /docs/grafana/<GRAFANA_VERSION>/alerting/fundamentals/alert-rule-evaluation/state-and-health/#stale-alert-instances-missingseries
destination: /docs/grafana/<GRAFANA_VERSION>/alerting/fundamentals/alert-rule-evaluation/stale-alert-instances/
- pattern: /docs/grafana-cloud/
destination: /docs/grafana-cloud/alerting-and-irm/alerting/fundamentals/alert-rule-evaluation/state-and-health/#stale-alert-instances-missingseries
destination: /docs/grafana-cloud/alerting-and-irm/alerting/fundamentals/alert-rule-evaluation/stale-alert-instances/
no-data-and-error-alerts:
- pattern: /docs/grafana/
destination: /docs/grafana/<GRAFANA_VERSION>/alerting/fundamentals/alert-rule-evaluation/state-and-health/#no-data-and-error-alerts
destination: /docs/grafana/<GRAFANA_VERSION>/alerting/fundamentals/alert-rule-evaluation/nodata-and-error-states/#no-data-and-error-alerts
- pattern: /docs/grafana-cloud/
destination: /docs/grafana-cloud/alerting-and-irm/alerting/fundamentals/alert-rule-evaluation/state-and-health/#no-data-and-error-alerts
destination: /docs/grafana-cloud/alerting-and-irm/alerting/fundamentals/alert-rule-evaluation/nodata-and-error-states/#no-data-and-error-alerts
grafana-state-reason-annotation:
- pattern: /docs/grafana/
destination: /docs/grafana/<GRAFANA_VERSION>/alerting/fundamentals/alert-rule-evaluation/nodata-and-error-states/#grafana_state_reason-for-troubleshooting
- pattern: /docs/grafana-cloud/
destination: /docs/grafana-cloud/alerting-and-irm/alerting/fundamentals/alert-rule-evaluation/nodata-and-error-states/#grafana_state_reason-for-troubleshooting
---
# Handle missing data in Grafana Alerting
@@ -114,12 +119,18 @@ However, `absent_over_time()` can’t detect which specific series are missing s
If you want to check for missing data per-region or label, you can specify the label in the alert query as follows:
`absent_over_time(http_request_latency_seconds{region="region1"}[5m]) == 1`
`or`
`absent_over_time(http_request_latency_seconds{region="region2"}[5m]) == 1`
```promQL
# Detect missing data in region1
absent_over_time(http_request_latency_seconds{region="region1"}[5m]) == 1
# Detect missing data in region2
absent_over_time(http_request_latency_seconds{region="region2"}[5m]) == 1
```
But this doesn't scale well. It is unreliable to have hard-coded queries for each label set, especially in dynamic cloud environments where instances can appear or disappear at any time.
To detect when a specific target has disappeared, see below **Evict alert instances for missing series** for details on how Grafana handles this case and how to set up detection.
## Manage No Data issues in Grafana alerts
While Prometheus provides functions like `absent_over_time()` to detect missing data, not all data sources — like Graphite, InfluxDB, PostgreSQL, and others — available to Grafana alerts support a similar function.
@@ -168,8 +179,6 @@ If an alert instance becomes stale, you’ll find it in the [alert history](ref:
| 03:00 | 1.4s 🟢 | `(MissingSeries)` <br> `Normal` | 🟢🟢 `region2` was resolved, 📩 notification sent, and instance evicted. |
| 04:00 | 1.4s 🟢 | — | 🟢 No Alerts. `region2` was evicted. |
###
### Why doesn’t MissingSeries match No Data behavior?
In dynamic environments, such as autoscaling groups, ephemeral pods, spot instances, series naturally come and go. **MissingSeries** normally signals infrastructure or deployment changes.
@@ -182,15 +191,50 @@ In environments with frequent scale events, prioritize symptom-based alerts over
### Handle MissingSeries notifications
A stale alert instance triggers a **resolved notification** if it transitions from a firing state (such as `Alerting`, `No Data`, or `Error`) to `Normal`.
A stale alert instance triggers a **resolved notification** if it transitions from a firing state (such as `Alerting`, `No Data`, or `Error`) to `Normal`, and the [`grafana_state_reason` annotation](ref:grafana-state-reason-annotation) is set to **MissingSeries** to indicate that the alert wasn’t resolved by recovery but evicted because the series data went missing.
You can display the `MissingSeries` annotation in notifications to indicate the alert wasn’t resolved by recovery but evicted due to series data going missing.
Recognizing these notifications helps you handle them appropriately. For example:
Review these notifications to confirm whether something broke or if the alert was unnecessary. To reduce noise:
- Display the `grafana_state_reason` annotation to clearly identify **MissingSeries** alerts.
- Or use the `grafana_state_reason` annotation to process these alerts differently.
Also, review these notifications to confirm whether something broke or if the alert was unnecessary. To reduce noise:
- Silence or mute alerts during planned maintenance or rollouts.
- Adjust alert rules to avoid triggering on series you expect to come and go, and use aggregated alerts instead.
### Detect missing series in Prometheus
Previously, an example showed how to detect missing data for a specific label, such as `region`:
```promQL
# Detect missing data in region1
absent_over_time(http_request_latency_seconds{region="region1"}[5m]) == 1
# Detect missing data in region2
absent_over_time(http_request_latency_seconds{region="region2"}[5m]) == 1
```
However, this approach doesn’t scale well because it requires hardcoding all possible `region` values.
As an alternative, you can create an alert rule that detects missing series dynamically using the `present_over_time` function:
```promQL
present_over_time(http_request_latency_seconds{}[24h])
unless
present_over_time(http_request_latency_seconds{}[10m])
```
Or, if you want to group by a label such as region:
```promQL
group(present_over_time(http_request_latency_seconds{}[24h])) by (region)
unless
group(present_over_time(http_request_latency_seconds{}[10m])) by (region)
```
This query finds regions (or other targets) that were present at any time in the past 24 hours but have not been present in the past 10 minutes. The alert rule then triggers an alert instance for each missing region. You can apply the same technique to any label or target dimension.
## Conclusion
Missing data isn’t always a failure. It’s a common scenario in dynamic environments when certain targets stop reporting.
@@ -200,7 +244,10 @@ Grafana Alerting handles distinct scenarios automatically. Here’s how to think
- Understand `DatasourceNoData` and `MissingSeries` notifications, since they don’t behave like regular alerts.
- Use Grafana’s _No Data_ handling options to define what happens when a query returns nothing.
- When _NoData_ is not an issue, consider rewriting the query to always return data — for example, in Prometheus, use `your_metric_query OR on() vector(0)` to return `0` when `your_metric_query` returns nothing.
- Use `absent()` or `absent_over_time()` in Prometheus for fine-grained detection when a metric or label disappears entirely.
- Use `absent_over_time()` or `present_over_time` in Prometheus to detect when a metric or target disappears.
- If data is frequently missing due to scrape delays, use techniques to account for data delays:
- Adjust the **Time Range** query option in Grafana to evaluate slightly behind real time (e.g., set **To** to `now-1m`) to account for late data points.
- In Prometheus, you can use `last_over_time(metric_name[10m])` to pick the most recent sample within a given window.
- Don’t alert on every instance by default. In dynamic environments, it’s better to aggregate and alert on symptoms — unless a missing individual instance directly impacts users.
- If you’re getting too much noise from disappearing data, consider adjusting alerts, using `Keep Last State`, or routing those alerts differently.
- For connectivity issues involving alert query failures, see the sibling guide: [Handling connectivity errors in Grafana Alerting](ref:connectivity-errors-guide).
+35 -59
View File
@@ -10,7 +10,7 @@ labels:
- enterprise
- oss
menuTitle: Introduction
title: Introduction to Alerting
title: Introduction to Grafana Alerting
weight: 100
refs:
notifications:
@@ -58,27 +58,36 @@ refs:
destination: /docs/grafana/<GRAFANA_VERSION>/alerting/fundamentals/notifications/group-alert-notifications/
- pattern: /docs/grafana-cloud/
destination: /docs/grafana-cloud/alerting-and-irm/alerting/fundamentals/notifications/group-alert-notifications/
multi-dimensional-alerts-example:
- pattern: /docs/grafana/
destination: /docs/grafana/<GRAFANA_VERSION>/alerting/best-practices/multi-dimensional-alerts/
- pattern: /docs/grafana-cloud/
destination: /docs/grafana-cloud/alerting-and-irm/alerting/best-practices/multi-dimensional-alerts/
tutorials:
- pattern: /docs/grafana/
destination: /docs/grafana/<GRAFANA_VERSION>/alerting/best-practices/tutorials/
- pattern: /docs/grafana-cloud/
destination: /docs/grafana-cloud/alerting-and-irm/alerting/best-practices/tutorials/
---
# Introduction to Alerting
# Introduction to Grafana Alerting
Whether you’re just starting out or you're a more experienced user of Grafana Alerting, learn more about the fundamentals and available features that help you create, manage, and respond to alerts; and improve your team’s ability to resolve issues quickly.
Grafana Alerting lets you define alert rules across multiple data sources and manage notifications with flexible routing.
Built on the Prometheus alerting model, it integrates with the Grafana stack to provide a scalable and effective alerting setup across a wide range of environments.
{{< admonition type="tip" >}}
For a hands-on introduction, refer to our [tutorial to get started with Grafana Alerting](http://grafana.com/tutorials/alerting-get-started/).
For a hands-on introduction, refer to [Getting started with Grafana Alerting tutorials](ref:tutorials).
{{< /admonition >}}
The following diagram gives you an overview of Grafana Alerting and introduces you to some of the fundamental features that are the principles of how Grafana Alerting works.
<br/>
{{< figure src="/media/docs/alerting/alerting-configure-notifications-v2.png" max-width="750px" alt="How Grafana Alerting works" >}}
## How it works at a glance
- Grafana Alerting periodically queries data sources and evaluates the condition defined in the alert rule
- If the condition is breached, an alert instance fires
- Firing (and resolved) alert instances are sent for notifications, either directly to a contact point or through notification policies for more flexibility
{{< figure src="/media/docs/alerting/alerting-configure-notifications-v2.png" max-width="750px" alt="How Grafana Alerting works" >}}
1. Grafana Alerting periodically evaluates alert rules by executing their data source queries and checking their conditions.
1. Each alert rule can produce multiple alert instances—one per time series or dimension.
1. If a condition is breached, an alert instance fires.
1. Firing (and resolved) alert instances are sent for notifications, either directly to a contact point or through notification policies for more flexibility.
## Fundamentals
@@ -90,9 +99,13 @@ An [alert rule](ref:alert-rules) consists of one or more queries and expressions
In the alert rule, choose the contact point or notification policies to determine how to receive the alert notifications.
### Alert rule evaluation
[Alert rules are frequently evaluated](ref:alert-rule-evaluation) and the state of their alert instances is updated accordingly. Only alert instances that are in a firing or resolved state are sent in notifications.
### Alert instances
Each alert rule can produce multiple alert instances (also known as alerts) - one alert instance for each time series. This is exceptionally powerful as it allows you to observe multiple series in a single expression.
Each alert rule can produce multiple alert instances (also known as alerts) - one alert instance for each time series or dimension. This is allows you to observe multiple resources in a single expression.
```promql
sum by(cpu) (
@@ -102,9 +115,9 @@ sum by(cpu) (
A rule using the PromQL expression above creates as many alert instances as the amount of CPUs after the first evaluation, enabling a single rule to report the status of each CPU.
{{< figure src="/static/img/docs/alerting/unified/multi-dimensional-alert.png" caption="Multiple alert instances from a single alert rule" >}}
{{< figure src="/static/img/docs/alerting/unified/multi-dimensional-alert.png" alt="Multiple alert instances from a single alert rule" >}}
[Alert rules are frequently evaluated](ref:alert-rule-evaluation) and the state of their alert instances is updated accordingly. Only alert instances that are in a firing or resolved state are sent in notifications.
_For a demo, see the [multi-dimensional alerts example](ref:multi-dimensional-alerts-example)._
### Contact points
@@ -118,57 +131,20 @@ In the alert rule, you can choose a contact point to receive the alert notificat
### Notification policies
[Notification policies](ref:notification-policies) is an advanced option to handle alert notifications for larger systems.
[Notification policies](ref:notification-policies) are an advanced option for handling alert notifications by distinct scopes, such as by team or service—ideal for managing large alerting systems.
Notification policies routes alerts to contact points via label matching. Each notification policy consists of a set of label matchers (0 or more) that specify which alert instances (identified by their labels) they handle. Notification policies are defined in a tree structure, where the root of the notification policy tree is the **Default notification policy**, which ensures all alert instances are handled.
Notification policies routes alerts to contact points via label matching. They are defined in a tree structure, where the root of the notification policy tree is the **Default notification policy**, which ensures all alert instances are handled.
{{< figure src="/media/docs/alerting/notification-routing.png" max-width="750px" alt="A diagram displaying how the notification policy tree routes alerts" caption="Routing firing alert instances through notification policies" >}}
<br/>
Each notification policy decides where to send the alert (contact point) and when to send the notification (timing options). Additionally, it can [group multiple firing alert instances into a single notification](ref:group-alert-notifications) to reduce alert noise.
Each notification policy decides where to send the alert (contact point) and when to send the notification (timing options).
{{< figure src="/media/docs/alerting/alerting-notification-policy-diagram-v5.png" max-width="750px" alt="A diagram of the notification policy component" >}}
### Notification grouping
To reduce alert noise, Grafana Alerting [groups related firing alerts into a single notification](ref:group-alert-notifications) by default. You can customize this behavior in the alert rule or notification policy settings.
### Silences and mute timings
[Silences](ref:silences) and [mute timings](ref:mute-timings) allow you to pause notifications without interrupting alert rule evaluation. Use a silence to pause notifications on a one-time basis, such as during a maintenance window; and use mute timings to pause notifications at regular intervals, such as evenings and weekends.
### Architecture
Grafana Alerting is built on the Prometheus model of designing alerting systems. Prometheus-based alerting systems have two main components:
- An alert generator that [evaluates alert rules](ref:alert-rule-evaluation) and sends firing and resolved alerts to the alert receiver.
- An alert receiver (also known as Alertmanager) that receives the alerts and is responsible for sending their [notifications](ref:notifications).
## Design your Alerting system
Monitoring complex IT systems and understanding whether everything is up and running correctly is a difficult task. Setting up an effective alert management system is therefore essential to inform you when things are going wrong before they start to impact your business outcomes.
Designing and configuring an alert management set up that works takes time.
Here are some tips on how to create an effective alert management set up for your business:
**Which are the key metrics for your business that you want to monitor and alert on?**
- Find events that are important to know about and not so trivial or frequent that recipients ignore them.
- Alerts should only be created for big events that require immediate attention or intervention.
- Consider quality over quantity.
**How do you want to organize your alerts and notifications?**
- Be selective about who you set to receive alerts. Consider sending them to the right teams, whoever is on call, and the specific channels.
- Think carefully about priority and severity levels.
- Automate as far as possible provisioning Alerting resources with the API or Terraform.
**Which information should you include in notifications?**
- Consider who the alert receivers and responders are.
- Share information that helps responders identify and address potential issues.
- Link alerts to dashboards to guide responders on which data to investigate.
**How can you reduce alert fatigue?**
- Avoid noisy, unnecessary alerts by using silences, mute timings, or pausing alert rule evaluation.
- Continually tune your alert rules to review effectiveness. Remove alert rules to avoid duplication or ineffective alerts.
- Continually review your thresholds and evaluation rules.
@@ -15,16 +15,26 @@ labels:
title: Alert rule evaluation
weight: 108
refs:
alerts-state-health:
evaluation-within-a-group:
- pattern: /docs/grafana/
destination: /docs/grafana/<GRAFANA_VERSION>/alerting/fundamentals/alert-rule-evaluation/state-and-health/
destination: /docs/grafana/<GRAFANA_VERSION>/alerting/fundamentals/alert-rule-evaluation/evaluation-within-a-group/
- pattern: /docs/grafana-cloud/
destination: /docs/grafana-cloud/alerting-and-irm/alerting/fundamentals/alert-rule-evaluation/state-and-health/
destination: /docs/grafana-cloud/alerting-and-irm/alerting/fundamentals/alert-rule-evaluation/evaluation-within-a-group/
nodata-and-error-states:
- pattern: /docs/grafana/
destination: /docs/grafana/<GRAFANA_VERSION>/alerting/fundamentals/alert-rule-evaluation/nodata-and-error-states/
- pattern: /docs/grafana-cloud/
destination: /docs/grafana-cloud/alerting-and-irm/alerting/fundamentals/alert-rule-evaluation/nodata-and-error-states/
import-ds-rules:
- pattern: /docs/grafana/
destination: /docs/grafana/<GRAFANA_VERSION>/alerting/alerting-rules/alerting-migration/
- pattern: /docs/grafana-cloud/
destination: /docs/grafana-cloud/alerting-and-irm/alerting/alerting-rules/alerting-migration/
notifications:
- pattern: /docs/grafana/
destination: /docs/grafana/<GRAFANA_VERSION>/alerting/fundamentals/notifications/
- pattern: /docs/grafana-cloud/
destination: /docs/grafana-cloud/alerting-and-irm/alerting/fundamentals/notifications/
---
# Alert rule evaluation
@@ -35,80 +45,92 @@ The criteria determining when an alert rule fires are based on three settings:
- [Pending period](#pending-period): how long the condition must be met to start firing.
- [Keep firing for](#pending-period): how long the alert continues to fire after the condition is no longer met.
{{< figure src="/media/docs/alerting/alert-rule-evaluation-2.png" max-width="750px" alt="Set the evaluation behavior of the alert rule in Grafana." caption="Set alert rule evaluation" >}}
{{< figure src="/media/docs/alerting/alert-rule-evaluation-2.png" max-width="750px" alt="Set the evaluation behavior of the alert rule in Grafana." caption="Set alert rule evaluation" >}}
These settings affect how alert instances progress through their lifecycle.
## Alerting lifecycle
Each alert rule can generate one or more alert instances.
An alert instance transitions between these common states based on how long the alert condition remains met or not met.
| State | Description |
| -------------- | --------------------------------------------------------------------------------------------------------------------------------------- |
| **Normal** | The state of an alert when the condition (threshold) is not met. |
| **Pending** | The state of an alert that has breached the threshold but for less than the [pending period](#pending-period). |
| **Alerting** | The state of an alert that has breached the threshold for longer than the [pending period](#pending-period). |
| **Recovering** | The state of a firing alert when the threshold is no longer breached, but for less than the [keep firing for](#keep-firing-for) period. |
{{< figure src="/media/docs/alerting/alert-rule-evaluation-basic-statediagram.png" alt="A diagram of the lifecyle of a firing alert instance." max-width="750px" >}}
If an alert rule changes (except for updates to annotations, the evaluation interval, or other internal fields), its alert instances reset to the **Normal** state, and update accordingly during the next evaluation.
{{< admonition type="note" >}}
To learn about additional alert instance states, see [No Data and Error states](ref:nodata-and-error-states).
{{< /admonition >}}
## Notification routing
Alert instances are routed for [notifications](ref:notifications) in two scenarios:
1. When they transition to the **Alerting** state.
2. When they transition to **Normal** state and marked as `Resolved`, either from the **Alerting** or **Recovering** state.
## Evaluation group
Every alert rule and recording rule is assigned to an evaluation group. You can assign the rule to an existing evaluation group or create a new one.
Every alert rule and recording rule is assigned to an evaluation group.
Each evaluation group contains an **evaluation interval** that determines how frequently the rule is checked. For instance, the evaluation may occur every `10s`, `30s`, `1m`, `10m`, etc.
**Evaluation strategies**
Rules in different groups can be evaluated simultaneously.
- **Grafana-managed** rules within the same group are evaluated concurrently—they are evaluated at different times over the same evaluation interval but display the same evaluation timestamp.
- **Data source-managed** rules within the same group are evaluated sequentially, one after the other—this is useful to ensure that recording rules are evaluated before alert rules.
- **Grafana-managed rules [imported from data source-managed rules](ref:import-ds-rules)** are also evaluated sequentially.
Rules can be evaluated concurrently or sequentially. For details, see [How rules are evaluated within a group](ref:evaluation-within-a-group).
## Pending period
You can set a pending period to prevent unnecessary alerts from temporary issues.
You can set a **Pending period** to prevent unnecessary notifications caused by temporary issues.
The pending period specifies how long the condition must be met before firing, ensuring the condition is consistently met over a consecutive period.
When the alert condition is met, the alert instance enters the **Pending** state. It remains in this state until the condition has been continuously true for the entire **Pending period**.
You can also set the pending period to zero to skip it and have the alert fire immediately once the condition is met.
This ensures the condition breach is stable before the alert transitions to the **Alerting** state and routed for notification.
- **Normal** -> **Pending** -> **Alerting**<sup>\*</sup>
You can also set the **Pending period** to zero to skip the **Pending** state entirely and transition to **Alerting** immediately.
## Keep firing for
You can set a period to keep an alert firing after the threshold is no longer breached. This sets the alert to a Recovering state. In a Recovering state, the alert won’t fire again if the threshold is breached. The Keep firing timer is then reset and the alert transitions back to Alerting state.
You can set a **Keep firing for** period to avoid repeated firing-resolving-firing notifications caused by flapping conditions.
The Keep firing for period helps reduce repeated firing-resolving-firing notification scenarios caused by flapping alerts.
When the alert condition is no longer met during the **Alerting** state, the alert instance enters the **Recovering** state.
- **Alerting** → **Recovering** → **Normal (Resolved)**<sup>\*</sup>
- After the **Keep firing for** period elapses, the alert transitions to the **Normal** state and is marked as **Resolved**.
- If the alert condition is met again, the alert transitions back to the **Alerting** state, and no new notifications are sent.
You can also set the **Keep firing for** period to zero to skip the **Recovering** state entirely.
## Evaluation example
Keep in mind:
- One alert rule can generate multiple alert instances - one for each time series produced by the alert rule's query.
- Alert instances from the same alert rule may be in different states. For instance, only one observed machine might start firing.
- Only **Alerting** and **Resolved** alert instances are routed to manage their notifications.
{{< figure src="/media/docs/alerting/alert-rule-evaluation-overview-statediagram-v2.png" alt="A diagram of the alert instance states and when to route their notifications." max-width="750px" >}}
<!--
Remove ///
stateDiagram-v2
direction LR
Normal --///> Pending
note right of Normal
Route "Resolved" alert instances
for notifications
end note
Pending --///> Alerting
Alerting --///> Normal: Resolved
note right of Alerting
Route "Alerting" alert instances
for notifications
end note
-->
- One alert rule can generate multiple alert instances—one for each series or dimension produced by the rule's query. Alert instances from the same alert rule may be in different states.
- Only alert instances in the **Alerting** and **Normal (Resolved)** state are routed for [notifications](ref:notifications).
Consider an alert rule with an **evaluation interval** set at every 30 seconds and a **pending period** of 90 seconds. The evaluation occurs as follows:
| Time | Condition | Alert instance state | Pending counter |
| ------------------------- | --------- | --------------------- | --------------- |
| 00:30 (first evaluation) | Not met | Normal | - |
| 01:00 (second evaluation) | Breached | Pending | 0s |
| 01:30 (third evaluation) | Breached | Pending | 30s |
| 02:00 (fourth evaluation) | Breached | Pending | 60s |
| 02:30 (fifth evaluation) | Breached | Alerting<sup>\*</sup> | 90s |
| Time | Condition | Alert instance state | Pending counter |
| ------------------------- | --------- | -------------------- | --------------- |
| 00:30 (first evaluation) | Not met | Normal | - |
| 01:00 (second evaluation) | Breached | Pending | 0s |
| 01:30 (third evaluation) | Breached | Pending | 30s |
| 02:00 (fourth evaluation) | Breached | Pending | 60s |
| 02:30 (fifth evaluation) | Breached | Alerting 📩 | 90s |
An alert instance is resolved when it transitions from the `Firing` to the `Normal` state. For instance, in the previous example:
With a **keep firing for** period of 0 seconds, the alert instance transitions immediately from **Alerting** to **Normal**, and marked as `Resolved`:
| Time | Condition | Alert instance state | Pending counter |
| -------------------------- | --------- | ----------------------------- | --------------- |
| 03:00 (sixth evaluation) | Not met | Normal <sup>Resolved \*</sup> | 120s |
| 03:00 (sixth evaluation) | Not met | Normal <sup>Resolved</sup> 📩 | 120s |
| 03:30 (seventh evaluation) | Not met | Normal | 150s |
To learn more about the state changes of alert rules and alert instances, refer to [State and health of alert rules](ref:alerts-state-health).
@@ -0,0 +1,58 @@
---
canonical: https://grafana.com/docs/grafana/latest/alerting/fundamentals/alert-rule-evaluation/alert-rule-state-and-health/
description: An alert instance is considered stale when its series disappears for a number of consecutive evaluation intervals. Learn how Grafana resolves them.
keywords:
- grafana
- alerting
- guide
- state
labels:
products:
- cloud
- enterprise
- oss
title: Alert rule state and health
weight: 130
refs:
example-multi-dimensional-alerts:
- pattern: /docs/grafana/
destination: /docs/grafana/<GRAFANA_VERSION>/alerting/best-practices/multi-dimensional-alerts/
- pattern: /docs/grafana-cloud/
destination: /docs/grafana-cloud/alerting-and-irm/alerting/best-practices/multi-dimensional-alerts/
alert-instance-states:
- pattern: /docs/grafana/
destination: /docs/grafana/<GRAFANA_VERSION>/alerting/fundamentals/alert-rule-evaluation/nodata-and-error-states/#alert-instance-states
- pattern: /docs/grafana-cloud/
destination: /docs/grafana-cloud/alerting-and-irm/alerting/fundamentals/alert-rule-evaluation/nodata-and-error-states/#alert-instance-states
---
# Alert rule state and health
Each alert rule can generate one or more alert instances—one alert instance for each series or dimension, as shown in the [multi-dimensional alert example](ref:example-multi-dimensional-alerts).
Each alert instance of the same alert rule represents a different target and can be in a different state; for example, one alert instance may be **Normal** while another is **Alerting**.
{{< figure src="/media/docs/alerting/alert-rule-example-multiple-alert-instances.png" max-width="750px" alt="Multi dimensional alert rule. The alert rule state and alert rule health are determined by the state of the alert instances." >}}
The alert rule state and alert rule health are determined by the [state of the alert instances](ref:alert-instance-states).
## Alert rule states
An alert rule can be in either of the following states:
| State | Description |
| ----------- | ---------------------------------------------------------------------------------------------------- |
| **Normal** | None of the alert instances returned by the evaluation engine is in a `Pending` or `Alerting` state. |
| **Pending** | At least one alert instances returned by the evaluation engine is `Pending`. |
| **Firing** | At least one alert instances returned by the evaluation engine is `Alerting`. |
## Alert rule health
An alert rule can have one of the following health statuses:
| State | Description |
| ---------------------- | -------------------------------------------------------------------------------------------------------- |
| **Ok** | No error when evaluating the alert rule. |
| **Error** | An error occurred when evaluating the alert rule. |
| **No Data** | The alert rule query returns no data. |
| **{status}, KeepLast** | The rule would have received another status but was configured to keep the last state of the alert rule. |
@@ -0,0 +1,37 @@
---
canonical: https://grafana.com/docs/grafana/latest/alerting/fundamentals/alert-rule-evaluation/evaluation-within-a-group/
description: An alert instance is considered stale when its series disappears for a number of consecutive evaluation intervals. Learn how Grafana resolves them.
keywords:
- grafana
- alerting
- guide
- state
labels:
products:
- cloud
- enterprise
- oss
title: How rules are evaluated within a group
menuTitle: Evaluation within a group
weight: 150
refs:
import-ds-rules:
- pattern: /docs/grafana/
destination: /docs/grafana/<GRAFANA_VERSION>/alerting/alerting-rules/alerting-migration/
- pattern: /docs/grafana-cloud/
destination: /docs/grafana-cloud/alerting-and-irm/alerting/alerting-rules/alerting-migration/
---
# How rules are evaluated within a group
Each evaluation group contains an **evaluation interval** that determines how frequently the rule is evaluated. For instance, the evaluation may occur every `10s`, `30s`, `1m`, `10m`, etc.
Rules in different evaluation groups can be evaluated simultaneously.
Rules within the same evaluation group can be evaluated simultaneously or sequentially, depending on the rule type:
- **Grafana-managed** rules within the same group are evaluated concurrently—they are evaluated at different times over the same evaluation interval but display the same evaluation timestamp.
- **Data source-managed** rules within the same group are evaluated sequentially, one after the other—this is useful to ensure that recording rules are evaluated before alert rules.
- **Grafana-managed rules [imported from data source-managed rules](ref:import-ds-rules)** are also evaluated sequentially.
@@ -1,10 +1,11 @@
---
aliases:
- ../../fundamentals/alert-rule-evaluation/state-and-health/ # /docs/grafana/<GRAFANA_VERSION>/alerting/fundamentals/alert-rule-evaluation/state-and-health/
- ../../fundamentals/alert-rules/state-and-health/ # /docs/grafana/<GRAFANA_VERSION>/alerting/fundamentals/alert-rules/state-and-health/
- ../../fundamentals/state-and-health/ # /docs/grafana/<GRAFANA_VERSION>/alerting/fundamentals/state-and-health/
- ../../unified-alerting/alerting-rules/state-and-health/ # /docs/grafana/<GRAFANA_VERSION>/alerting/unified-alerting/alerting-rules/state-and-health
canonical: https://grafana.com/docs/grafana/latest/alerting/fundamentals/alert-rule-evaluation/state-and-health/
description: Learn about the state and health of alert rules to understand several key status indicators about your alerts
canonical: https://grafana.com/docs/grafana/latest/alerting/fundamentals/alert-rule-evaluation/nodata-and-error-states/
description: Grafana Alerting implements the No Data and Error states to handle these common scenarios when evaluating alert rules.
keywords:
- grafana
- alerting
@@ -16,7 +17,7 @@ labels:
- cloud
- enterprise
- oss
title: State and health of alerts
title: No Data and Error states
weight: 109
refs:
evaluation_timeout:
@@ -67,40 +68,41 @@ refs:
destination: /docs/grafana-cloud/alerting-and-irm/alerting/best-practices/missing-data/
---
# State and health of alerts
# No Data and Error states
There are three key components that help you understand how your alerts behave during their evaluation: [alert instance state](#alert-instance-state), [alert rule state](#alert-rule-state), and [alert rule health](#alert-rule-health). Although related, each component conveys subtly different information.
Grafana Alerting implements the **No Data** and **Error** states to handle common scenarios when evaluating alert rules, and you can modify their behavior.
## Alert instance state
An alert instance can transition to these special states:
An alert instance can be in either of the following states:
- [No Data state](#no-data-state) occurs when the alert rule query runs successfully but returns no data points.
- [Error state](#error-state) occurs when the alert rule fails to evaluate its query or queries successfully.
{{< admonition type="note" >}}
No Data and Error states are supported only for Grafana-managed alert rules.
{{< /admonition >}}
{{< admonition type="tip" >}}
For common examples and practical guidance on handling **Error**, **No Data**, and **stale** alert scenarios, refer to the [Handle connectivity errors](ref:guide-connectivity-errors) and [Handle missing data](ref:guide-missing-data) guides.
{{< /admonition >}}
## Alert instance states
A Grafana-managed alert instance can be in any of the following states, depending on the outcome of the alert rule evaluation:
| State | Description |
| ------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Normal** | The state of an alert when the condition (threshold) is not met. |
| **Pending** | The state of an alert that has breached the threshold but for less than the [pending period](ref:pending-period). |
| **Alerting** | The state of an alert that has breached the threshold for longer than the [pending period](ref:pending-period). |
| **Recovering** | The state of an alert that has been configured to keep [firing for a duration after it is triggered](ref:keep-firing). |
| **Recovering** | The state of a firing alert when the threshold is no longer breached, but for less than the [keep firing for](ref:keep-firing) period. |
| **Error<sup>\*</sup>** | The state of an alert when an error or timeout occurred evaluating the alert rule. <br/> You can customize the behavior of the [Error state](#error-state), which by default triggers a different alert. |
| **No Data<sup>\*</sup>** | The state of an alert whose query returns no data or all values are null. <br/> You can customize the behavior of the [No Data state](#no-data-state), which by default triggers a different alert. |
If an alert rule changes (except for updates to annotations, the evaluation interval, or other internal fields), its alert instances reset to the `Normal` state. The alert instance state then updates accordingly during the next evaluation.
{{< figure src="/media/docs/alerting/alert-state-diagram2.png" caption="Alert instance state diagram" alt="A diagram of the distinct alert instance states and transitions." max-width="750px" >}}
{{< admonition type="note" >}}
`No Data` and `Error` states are supported only for Grafana-managed alert rules.
{{< /admonition >}}
### Notification routing
Alert instances will be routed for [notifications](ref:notifications) when they are in the `Alerting` state or have been `Resolved`, transitioning from `Alerting` to `Normal` state.
{{< figure src="/media/docs/alerting/alert-rule-evaluation-overview-statediagram-v2.png" alt="A diagram of the alert instance states and when to route their notifications." max-width="750px" >}}
### `Error` state
## `Error` state
The **Error** state is triggered when the alert rule fails to evaluate its query or queries successfully.
@@ -108,7 +110,7 @@ This can occur due to evaluation timeouts (default: `30s`) or three repeated fai
When an alert instance enters the **Error** state, Grafana, by default, triggers a new [`DatasourceError` alert](#no-data-and-error-alerts). You can control this behavior based on the desired outcome of your alert rule in [Modify the `No Data` or `Error` state](#modify-the-no-data-or-error-state).
### `No Data` state
## `No Data` state
The **No Data** state occurs when the alert rule query runs successfully but returns no data points at all.
@@ -159,13 +161,9 @@ To minimize the number of **No Data** or **Error** state alerts received, try th
1. To reduce multiple notifications from **Error** alerts, define a [notification policy](ref:notification-policies) to handle all related alerts with `alertname=DatasourceError`, and filter and group errors from the same data source using the `datasource_uid` label.
{{< admonition type="tip" >}}
For common examples and practical guidance on handling **Error**, **No Data**, and **stale** alert scenarios, see the following related guides:
- [Handling connectivity errors](ref:guide-connectivity-errors)
- [Handling missing data](ref:guide-missing-data)
{{< /admonition >}}
{{< admonition type="tip" >}}
For common examples and practical guidance on handling **Error**, **No Data**, and **stale** alert scenarios, refer to the [Handle connectivity errors](ref:guide-connectivity-errors) and [Handle missing data](ref:guide-missing-data) guides.
{{< /admonition >}}
## `grafana_state_reason` for troubleshooting
@@ -183,26 +181,3 @@ The `grafana_state_reason` annotation is included in these situations, providing
- If "no data" or "error" handling transitions to the `Normal` state, the `grafana_state_reason` annotation is included with the value **No Data** or **Error**, respectively.
- If the alert rule is deleted or paused, the `grafana_state_reason` is set to **Paused** or **RuleDeleted**. For some updates, it is set to **Updated**.
- [Stale alert instances](ref:stale-alert-instances) in the `Normal` state include the `grafana_state_reason` annotation with the value **MissingSeries**.
## Alert rule state
The alert rule state is determined by the “worst case” state of the alert instances produced. For example, if one alert instance is `Alerting`, the alert rule state is firing.
An alert rule can be in either of the following states:
| State | Description |
| ----------- | ---------------------------------------------------------------------------------------------------- |
| **Normal** | None of the alert instances returned by the evaluation engine is in a `Pending` or `Alerting` state. |
| **Pending** | At least one alert instances returned by the evaluation engine is `Pending`. |
| **Firing** | At least one alert instances returned by the evaluation engine is `Alerting`. |
## Alert rule health
An alert rule can have one of the following health statuses:
| State | Description |
| ---------------------- | -------------------------------------------------------------------------------------------------------- |
| **Ok** | No error when evaluating an alerting rule. |
| **Error** | An error occurred when evaluating an alerting rule. |
| **No Data** | The absence of data in at least one time series returned during a rule evaluation. |
| **{status}, KeepLast** | The rule would have received another status but was configured to keep the last state of the alert rule. |
@@ -12,13 +12,13 @@ labels:
- enterprise
- oss
title: Stale alert instances
weight: 110
weight: 120
refs:
no-data-state:
- pattern: /docs/grafana/
destination: /docs/grafana/<GRAFANA_VERSION>/alerting/fundamentals/alert-rule-evaluation/state-and-health/#no-data-state
destination: /docs/grafana/<GRAFANA_VERSION>/alerting/fundamentals/alert-rule-evaluation/nodata-and-error-states/#no-data-state
- pattern: /docs/grafana-cloud/
destination: /docs/grafana-cloud/alerting-and-irm/alerting/fundamentals/alert-rule-evaluation/state-and-health/#no-data-state
destination: /docs/grafana-cloud/alerting-and-irm/alerting/fundamentals/alert-rule-evaluation/nodata-and-error-states/#no-data-state
no-data-and-error-handling:
- pattern: /docs/grafana/
destination: /docs/grafana/<GRAFANA_VERSION>/alerting/alerting-rules/create-grafana-managed-rule/#configure-no-data-and-error-handling
@@ -29,6 +29,11 @@ refs:
destination: /docs/grafana/<GRAFANA_VERSION>/alerting/best-practices/missing-data/
- pattern: /docs/grafana-cloud/
destination: /docs/grafana-cloud/alerting-and-irm/alerting/best-practices/missing-data/
grafana-state-reason-annotation:
- pattern: /docs/grafana/
destination: /docs/grafana/<GRAFANA_VERSION>/alerting/fundamentals/alert-rule-evaluation/nodata-and-error-states/#grafana_state_reason-for-troubleshooting
- pattern: /docs/grafana-cloud/
destination: /docs/grafana-cloud/alerting-and-irm/alerting/fundamentals/alert-rule-evaluation/nodata-and-error-states/#grafana_state_reason-for-troubleshooting
---
# Stale alert instances
@@ -60,7 +65,7 @@ The process for handling stale alert instances is as follows:
1. Grafana keeps the previous state of the alert instance for the number of evaluation intervals specified in [Missing series evaluations to resolve](#configure-missing-series-evaluations-to-resolve).
1. If it remains missing after the specified number of evaluation intervals (2 by default), it transitions to the **Normal** state and sets **MissingSeries** in the `grafana_state_reason` annotation.
1. If it remains missing after the specified number of evaluation intervals (2 by default), it transitions to the **Normal** state and sets **MissingSeries** in the [`grafana_state_reason` annotation](ref:grafana-state-reason-annotation).
Stale alert instances in the **Alerting**, **No Data**, or **Error** states transition to the **Normal** state as **Resolved**, and are routed for notifications like other resolved alerts.
@@ -18,11 +18,6 @@ labels:
title: Alert rules
weight: 100
refs:
shared-configure-prometheus-data-source-alerting:
- pattern: /docs/grafana/
destination: /docs/grafana/<GRAFANA_VERSION>/datasources/prometheus/configure/
- pattern: /docs/grafana-cloud/
destination: /docs/grafana-cloud/connect-externally-hosted/data-sources/prometheus/configure/
queries-and-conditions:
- pattern: /docs/grafana/
destination: /docs/grafana/<GRAFANA_VERSION>/alerting/fundamentals/alert-rules/queries-conditions/#data-source-queries
@@ -33,14 +28,11 @@ refs:
destination: /docs/grafana/<GRAFANA_VERSION>/alerting/fundamentals/alert-rules/queries-conditions/#alert-condition
- pattern: /docs/grafana-cloud/
destination: /docs/grafana-cloud/alerting-and-irm/alerting/fundamentals/alert-rules/queries-conditions/#alert-condition
recorded-queries:
- pattern: /docs/
destination: /docs/grafana/<GRAFANA_VERSION>/administration/recorded-queries/
notification-images:
alert-rule-evaluation:
- pattern: /docs/grafana/
destination: /docs/grafana/<GRAFANA_VERSION>/alerting/configure-notifications/template-notifications/images-in-notifications/
destination: /docs/grafana/<GRAFANA_VERSION>/alerting/fundamentals/alert-rules/rule-evaluation/
- pattern: /docs/grafana-cloud/
destination: /docs/grafana-cloud/alerting-and-irm/alerting/configure-notifications/template-notifications/images-in-notifications/
destination: /docs/grafana-cloud/alerting-and-irm/alerting/fundamentals/alert-rules/rule-evaluation/
notifications:
- pattern: /docs/grafana/
destination: /docs/grafana/<GRAFANA_VERSION>/alerting/fundamentals/notifications/
@@ -51,84 +43,50 @@ refs:
destination: /docs/grafana/<GRAFANA_VERSION>/alerting/alerting-rules/create-recording-rules/
- pattern: /docs/grafana-cloud/
destination: /docs/grafana-cloud/alerting-and-irm/alerting/alerting-rules/create-recording-rules/
expression-queries:
configure-grafana-alerts:
- pattern: /docs/grafana/
destination: /docs/grafana/<GRAFANA_VERSION>/alerting/fundamentals/alert-rules/queries-conditions/#advanced-options-expressions
destination: /docs/grafana/<GRAFANA_VERSION>/alerting/alerting-rules/create-grafana-managed-rule/
- pattern: /docs/grafana-cloud/
destination: /docs/grafana-cloud/alerting-and-irm/alerting/fundamentals/alert-rules/queries-conditions/#advanced-options-expressions
alert-rule-evaluation:
destination: /docs/grafana-cloud/alerting-and-irm/alerting/alerting-rules/create-grafana-managed-rule/
comparison-ds-grafana-rules:
- pattern: /docs/grafana/
destination: /docs/grafana/<GRAFANA_VERSION>/alerting/fundamentals/alert-rules/rule-evaluation/
destination: /docs/grafana/<GRAFANA_VERSION>/alerting/alerting-rules/create-data-source-managed-rule/#comparison-with-grafana-managed-rules
- pattern: /docs/grafana-cloud/
destination: /docs/grafana-cloud/alerting-and-irm/alerting/fundamentals/alert-rules/rule-evaluation/
destination: /docs/grafana-cloud/alerting-and-irm/alerting/alerting-rules/create-data-source-managed-rule/#comparison-with-grafana-managed-rules
---
# Alert rules
An alert rule is a set of evaluation criteria for when an alert rule should fire. An alert rule consists of:
- [Queries](ref:queries-and-conditions) that select the dataset to evaluate.
- An [alert condition](ref:alert-condition) (the threshold) that the query must meet or exceed to trigger the alert instance.
- An interval that specifies the frequency of [alert rule evaluation](ref:alert-rule-evaluation) and a duration indicating how long the condition must be met to trigger the alert instance.
- Other customizable options, for example, setting what should happen in the absence of data, notification messages, and more.
1. [Queries](ref:queries-and-conditions) that select the dataset to evaluate.
1. An [alert condition](ref:alert-condition) (the threshold) that the query must meet or exceed to trigger the alert instance.
Grafana supports two different alert rule types: Grafana-managed alert rules and data source-managed alert rules.
{{< figure src="/media/docs/alerting/alerting-query-conditions-default-options.png" max-width="750px" alt="Alert query using the Prometheus query editor and alert condition" >}}
## Grafana-managed alert rules
1. An interval that specifies the frequency of [alert rule evaluation](ref:alert-rule-evaluation) and a duration indicating how long the condition must be met to trigger the alert instance.
1. Other customizable options, including expressions, labels, annotations, error and no data handling, notification routing, and more.
Grafana-managed alert rules are the most flexible alert rule type. They allow you to create alert rules that can act on data from any of the [supported data sources](#supported-data-sources), and use multiple data sources in a single alert rule.
## Alert rule types
{{< figure src="/media/docs/alerting/grafana-managed-alerting-architecture.png" max-width="750px" caption="How Grafana-managed alerting works by default" >}}
Grafana Alerting inherits the Prometheus Alerting model for defining alert rules and supports two alert rule types:
1. Alert rules are created and stored within Grafana.
1. Alert rules can query one or more supported data sources.
1. Alert rules are evaluated by the Alert Rule Evaluation Engine within Grafana.
1. Firing and resolved alert instances are forwarded to [handle their notifications](ref:notifications).
- **Data source-managed alert rules**
### Supported data sources
These alert rules can only query Prometheus-based data sources such as Mimir, Loki, and Prometheus. The rules are stored in the data source.
Grafana-managed alert rules can query backend data sources if Grafana Alerting is enabled by specifying `{"backend": true, "alerting": true}` in the `plugin.json` file.
Grafana Alerting supports this alert rule type for horizontal scalability with these data sources.
Find the public data sources supporting Alerting in the [Grafana Plugins directory](/grafana/plugins/data-source-plugins/?features=alerting).
- **Grafana-managed alert rules**
## Data source-managed alert rules
The recommended alert rule type in Grafana Alerting.
Data source-managed alert rules can only be created using Grafana Mimir or Grafana Loki data sources. Both data source backends can provide high availability and fault tolerance, enabling you to scale your alerting setup.
These alert rules can query a wider range of backend data sources, including multiple data sources in a single alert rule. They support expression-based transformations, advanced alert conditions, images in notifications, handling of error and no data states, and [more](ref:comparison-ds-grafana-rules).
{{< figure src="/media/docs/alerting/mimir-managed-alerting-architecture-v2.png" max-width="750px" caption="Mimir-managed alerting architecture" >}}
1. Alert rules are stored within the Mimir or Loki data source.
1. Alert rules can query only their specific data source.
1. Alert rules are evaluated by the Alert Rule Evaluation Engine within the data source.
1. Firing and resolved alert instances are forwarded to [handle their notifications](ref:notifications).
> Rules from a Prometheus data source appear in the **Data source-managed** section of the **Alert rules** page when [Manage alerts via Alerting UI](ref:shared-configure-prometheus-data-source-alerting) is enabled.
>
> However, Grafana can only create and edit data source-managed rules for Mimir and Loki, not for a Prometheus instance.
[//]: <> ({{< docs/shared lookup="alerts/note-prometheus-ds-rules.md" source="grafana" version="<GRAFANA_VERSION>" >}})
## Comparison between alert rule types
We recommend using Grafana-managed alert rules whenever possible, and opting for data source-managed alert rules when you need to scale your alerting setup.
The table below compares Grafana-managed and data source-managed alert rules.
| <div style="width:200px">Feature</div> | <div style="width:200px">Grafana-managed alert rule</div> | <div style="width:200px">Data source-managed alert rule |
| ----------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------- |
| Create alert rules<wbr /> that query [data sources supporting Alerting](#supported-data-sources) | Yes | Only supports creating rules for Mimir and Loki. |
| Mix and match data sources | Yes | No |
| Add [expressions](ref:expression-queries) to transform<wbr /> your data and set [alert conditions](ref:alert-condition) | Yes | No |
| Use [images in alert notifications](ref:notification-images) | Yes | No |
| Support for [recording rules](#recording-rules) | Yes | Yes |
| Organization | Organize and manage access with folders | Use namespaces |
| Alert rule evaluation and delivery | Alert evaluation is done in Grafana, while delivery can be handled by Grafana or an external Alertmanager. | Alert rule evaluation and alert delivery are distributed. |
| Scaling | Alert rules are stored in the Grafana database, which may experience transient errors. It only scales vertically. | Alert rules are stored within the data source and allow for horizontal scaling. |
You can find the supported public data sources in the [Grafana Plugins directory](/grafana/plugins/data-source-plugins/?features=alerting). For step-by-step instructions, see [Configure Grafana-managed alert rules](ref:configure-grafana-alerts).
## Recording rules
Similar to alert rules, recording rules are evaluated periodically. A recording rule pre-computes frequently used or computationally expensive queries, and saves the results as a new time series metric.
The new recording metric can then be used in alert rules and dashboards to optimize their queries.
For more details, refer to [Create recording rules](ref:create-recording-rules).
The new recording metric can then be used in alert rules and dashboards to optimize their queries. For further details, refer to [Create recording rules](ref:create-recording-rules).
@@ -74,9 +74,10 @@ Start defining your [contact points](ref:contact-points) to specify how to recei
## How it works at a glance
- Grafana alerting periodically [evaluates your alert rules](ref:alert-rule-evaluation) and triggers notifications for firing and resolved alert instances.
- You can configure the alert rule to send notifications to a contact point or route them via Notification Policies for greater flexibility.
- To minimize alert noise, group similar alerts into a single notification by grouping alert labels and notification timings.
- Grafana alerting periodically [evaluates your alert rules](ref:alert-rule-evaluation).
- It triggers notifications for alert instances that are **firing** or **resolved**.
- You can configure an alert rule to send notifications to a **contact point** or route them through **notification policies** for greater flexibility.
- To reduce the number of notifications, you can **group related alerts** into a single notification by using label grouping and notification timings.
## Fundamentals
@@ -130,8 +131,11 @@ Additionally, you can use [silences](ref:silences) and [mute timings](ref:mute-t
## Architecture
Grafana Alerting is based on the Prometheus model for designing alerting systems. Its architecture decouples the alert generator from the alert notification manager (known as the Alertmanager) to enhance scalability and performance.
Grafana Alerting is built on the Prometheus model, which separates two main components for scalability and performance:
- **An alert generator** that evaluates alert rules and sends firing and resolved alerts to the alert receiver.
- **An alert receiver** (also known as Alertmanager) that receives the alerts and is responsible for sending their notifications.
{{< figure src="/media/docs/alerting/alerting-alertmanager-architecture.png" max-width="750px" alt="A diagram with the alert generator and alert manager architecture" >}}
Grafana provides a custom Alertmanager, extending the Prometheus Alertmanager, to manage and deliver alert notifications. If you run a Prometheus or Mimir Alertmanager, you can configure Grafana Alerting to manage them and handle notifications for Grafana-managed alerts. For details, refer to [configure Alertmanagers](ref:configure-alertmanager).
Grafana includes a custom Alertmanager that extends the Prometheus Alertmanager to manage and deliver alert notifications. You can also [configure Grafana Alerting to work with other Alertmanagers](ref:configure-alertmanager).
@@ -14,6 +14,22 @@ labels:
- oss
title: View alert state history
weight: 440
refs:
time-series-visualizations:
- pattern: /docs/grafana/
destination: /docs/grafana/<GRAFANA_VERSION>/panels-visualizations/visualizations/time-series/
- pattern: /docs/grafana-cloud/
destination: /docs/grafana-cloud/visualizations/panels-visualizations/visualizations/time-series/
explore:
- pattern: /docs/grafana/
destination: /docs/grafana/<GRAFANA_VERSION>/explore/
- pattern: /docs/grafana-cloud/
destination: /docs/grafana/<GRAFANA_VERSION>/explore/
meta-monitoring:
- pattern: /docs/grafana/
destination: /docs/grafana/<GRAFANA_VERSION>/alerting/monitor/
- pattern: /docs/grafana-cloud/
destination: /docs/grafana-cloud/alerting-and-irm/alerting/monitor/
---
# View alert state history
@@ -22,17 +38,21 @@ View a history of all alert events generated by your Grafana-managed alert rules
An alert event is displayed each time an alert instance changes its state over a period of time. All alert events are displayed regardless of whether silences or mute timings are set, so you’ll see a complete set of your data history even if you’re not necessarily being notified.
## View from the History page
{{< admonition type="note" >}}
For Grafana Enterprise and OSS users:
The feature is available starting with Grafana 11.2.
To try out the new alert history page, enable the `alertingCentralAlertHistory` feature toggle and configure [Loki annotations](https://grafana.com/docs/grafana/<GRAFANA_VERSION>/alerting/set-up/configure-alert-state-history/).
Users can only see the history and transitions of alert rules they have access to (RBAC).
Grafana OSS and Grafana Enterprise users must [configure alert state history in Loki](/docs/grafana/<GRAFANA_VERSION>/alerting/set-up/configure-alert-state-history/) to view the **History page** and **State history view**.
{{< /admonition >}}
To access the History view, complete the following steps.
## View from the History page
The History page shows the history and state changes of all Grafana-managed alert rules. You can filter by labels and alert states.
Users can only view the history of alert rules they have permission to access (RBAC).
{{< admonition type="note" >}}
Grafana OSS and Grafana Enterprise users must also enable the [`alertingCentralAlertHistory`](/docs/grafana/<GRAFANA_VERSION>/setup-grafana/configure-grafana/feature-toggles/) feature toggle to access this page.
{{< /admonition >}}
To access the History page, complete the following steps.
1. Navigate to **Alerts & IRM** -> **Alerting** -> **History**.
@@ -59,10 +79,6 @@ Use the State history view to get insight into how your individual alert instanc
View information on when a state change occurred, what the previous state was, the current state, any other alert instances that changed their state at the same time as well as what the query value was that triggered the change.
{{< admonition type="note" >}}
Open source users must [configure alert state history](/docs/grafana/latest/alerting/set-up/configure-alert-state-history/) in order to be able to access the view.
{{< /admonition >}}
To access the State history view, complete the following steps.
1. Navigate to **Alerts & IRM** -> **Alerting** -> **Alert rules**.
@@ -84,3 +100,9 @@ To access the State history view, complete the following steps.
The value shown for each instance is for each part of the expression that was evaluated.
1. Click the labels to filter and narrow down the results.
## Explore and query the alert history
You can also use [Grafana Explore](ref:explore) to query the data sources that store alert history.
For details on querying these data sources, refer to [Alerting Meta monitoring](ref:meta-monitoring).
@@ -37,19 +37,19 @@ refs:
destination: /docs/grafana-cloud/alerting-and-irm/alerting/alerting-rules/link-alert-rules-to-panels/
alert-rule-state:
- pattern: /docs/grafana/
destination: /docs/grafana/<GRAFANA_VERSION>/alerting/fundamentals/alert-rule-evaluation/state-and-health/#alert-rule-state
destination: /docs/grafana/<GRAFANA_VERSION>/alerting/fundamentals/alert-rule-evaluation/alert-rule-state-and-health/
- pattern: /docs/grafana-cloud/
destination: /docs/grafana-cloud/alerting-and-irm/alerting/fundamentals/alert-rule-evaluation/state-and-health/#alert-rule-state
destination: /docs/grafana-cloud/alerting-and-irm/alerting/fundamentals/alert-rule-evaluation/alert-rule-state-and-health/
alert-instance-state:
- pattern: /docs/grafana/
destination: /docs/grafana/<GRAFANA_VERSION>/alerting/fundamentals/alert-rule-evaluation/stale-alert-instances/
destination: /docs/grafana/<GRAFANA_VERSION>/alerting/fundamentals/alert-rule-evaluation/nodata-and-error-states/#alert-instance-states
- pattern: /docs/grafana-cloud/
destination: /docs/grafana-cloud/alerting-and-irm/alerting/fundamentals/alert-rule-evaluation/stale-alert-instances/
destination: /docs/grafana-cloud/alerting-and-irm/alerting/fundamentals/alert-rule-evaluation/nodata-and-error-states/#alert-instance-states
alert-rule-health:
- pattern: /docs/grafana/
destination: /docs/grafana/<GRAFANA_VERSION>/alerting/fundamentals/alert-rule-evaluation/state-and-health/
destination: /docs/grafana/<GRAFANA_VERSION>/alerting/fundamentals/alert-rule-evaluation/alert-rule-state-and-health/
- pattern: /docs/grafana-cloud/
destination: /docs/grafana-cloud/alerting-and-irm/alerting/fundamentals/alert-rule-evaluation/state-and-health/
destination: /docs/grafana-cloud/alerting-and-irm/alerting/fundamentals/alert-rule-evaluation/alert-rule-state-and-health/
---
# View alert state

Some files were not shown because too many files have changed in this diff Show More