Compare commits
153 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
47ca5cd934 | ||
|
|
26f2f0dd83 | ||
|
|
591c6e09c8 | ||
|
|
0f304efaa7 | ||
|
|
0f4640dc74 | ||
|
|
c36d51c846 | ||
|
|
177dfd34b8 | ||
|
|
6436e57f1d | ||
|
|
a8adb7230e | ||
|
|
f0744ff99c | ||
|
|
c75ebc758c | ||
|
|
472eea6390 | ||
|
|
ece6a18b3c | ||
|
|
4df84bf181 | ||
|
|
f70b206d79 | ||
|
|
34ab8a31b8 | ||
|
|
02c89fc38c | ||
|
|
7a570bbe9d | ||
|
|
035f50c24a | ||
|
|
a2ad77a3f2 | ||
|
|
af73a40d97 | ||
|
|
cb9f9a0bd7 | ||
|
|
b6b8554c5e | ||
|
|
5ae3e7efee | ||
|
|
af7044122f | ||
|
|
ac761ea9bf | ||
|
|
3fa0b675bb | ||
|
|
4d590fe0f9 | ||
|
|
e2b7d2dc8d | ||
|
|
0a70d1cd1d | ||
|
|
98c432ec37 | ||
|
|
3e9d3b112d | ||
|
|
b19f02fa88 | ||
|
|
65b5a97601 | ||
|
|
97c65479dd | ||
|
|
a430e0d9a9 | ||
|
|
a127b5ddd3 | ||
|
|
baaf271a1b | ||
|
|
0518582e0e | ||
|
|
80a2f701bf | ||
|
|
913d99983f | ||
|
|
438200c50d | ||
|
|
67de1e5a1d | ||
|
|
5cc13f4ba7 | ||
|
|
e0bcceaae2 | ||
|
|
4385eb1840 | ||
|
|
7f7b4dbd51 | ||
|
|
3734293990 | ||
|
|
9c6c097572 | ||
|
|
e651f011b1 | ||
|
|
64485061ba | ||
|
|
917aa0aa49 | ||
|
|
769a3cfdef | ||
|
|
b88de81001 | ||
|
|
6283c154e1 | ||
|
|
4cc18d2f64 | ||
|
|
6e96bc4b7e | ||
|
|
b57fe8e856 | ||
|
|
b74f4eeaa9 | ||
|
|
0ea9b81717 | ||
|
|
a2ee1969f8 | ||
|
|
1425829ac1 | ||
|
|
964be9af97 | ||
|
|
542f5f6dd7 | ||
|
|
aded614ca7 | ||
|
|
dd3c320e90 | ||
|
|
d34be5f037 | ||
|
|
e39fa14ee3 | ||
|
|
7e0d8c3556 | ||
|
|
6a1427d439 | ||
|
|
e83ef7e31a | ||
|
|
9ca226fd56 | ||
|
|
c4d6126ce9 | ||
|
|
47e69207f5 | ||
|
|
29f513371c | ||
|
|
4ce961eae5 | ||
|
|
b72ae45256 | ||
|
|
ea0963ee88 | ||
|
|
a134d3dae4 | ||
|
|
e7b82b8cb5 | ||
|
|
00471e95a7 | ||
|
|
f2e9b7a6b3 | ||
|
|
86a6ebd700 | ||
|
|
de6e27e262 | ||
|
|
b37468701f | ||
|
|
2b8a2ccd2c | ||
|
|
02ce3c5666 | ||
|
|
e800a96c6d | ||
|
|
fa556f1901 | ||
|
|
744b462332 | ||
|
|
0a4e265e7b | ||
|
|
a374ae875c | ||
|
|
3ab18f78f2 | ||
|
|
3946483d31 | ||
|
|
ee9e04992d | ||
|
|
5bc2dd6fee | ||
|
|
fc3716d122 | ||
|
|
7de38a4f8d | ||
|
|
597115e408 | ||
|
|
fd95a8c9bc | ||
|
|
d4428f65c8 | ||
|
|
67479fa240 | ||
|
|
532f010c95 | ||
|
|
c79ccb1c9e | ||
|
|
030520a40d | ||
|
|
79a31f54c6 | ||
|
|
45f1d43a37 | ||
|
|
c004019e9e | ||
|
|
ae73f46517 | ||
|
|
babd6a83ce | ||
|
|
8260205f64 | ||
|
|
829e6ca91b | ||
|
|
649958636f | ||
|
|
ff412e31db | ||
|
|
0a8c3e5130 | ||
|
|
8487fe302c | ||
|
|
4259492898 | ||
|
|
82fa303888 | ||
|
|
1413841850 | ||
|
|
d43336a53c | ||
|
|
67e6326377 | ||
|
|
03f4ed6500 | ||
|
|
974af0c65a | ||
|
|
b44d662ed7 | ||
|
|
2e3815a041 | ||
|
|
fb2ecbbdcd | ||
|
|
f9abe90e4c | ||
|
|
31b1a7bfef | ||
|
|
369882272c | ||
|
|
9f7c35dcaa | ||
|
|
89613f79ac | ||
|
|
e5960a82fb | ||
|
|
894e9fa6de | ||
|
|
013301c903 | ||
|
|
2902e9551b | ||
|
|
0b0260784d | ||
|
|
2867953492 | ||
|
|
aca814c8a5 | ||
|
|
83f0a20004 | ||
|
|
aa43b26e8b | ||
|
|
8bf6b93a1f | ||
|
|
243af55d61 | ||
|
|
14249a28a4 | ||
|
|
f9a9f3bbe9 | ||
|
|
81a800662b | ||
|
|
a45f09112a | ||
|
|
f88972c76f | ||
|
|
551ae2e9de | ||
|
|
64d8179adc | ||
|
|
08c545cf3b | ||
|
|
76ae8351bd | ||
|
|
a9e979b354 | ||
|
|
df7a11ee0f |
@@ -1294,7 +1294,7 @@ This option to group query variable values into groups by tags has been an exper
|
||||
**Deprecation warnings**
|
||||
|
||||
- Scripted dashboards is now deprecated. The feature is not removed but will be in a future release. We hope to address the underlying requirement of dynamic dashboards in a different way. [#24059](https://github.com/grafana/grafana/issues/24059)
|
||||
- The unofficial first version of backend plugins together with usage of [grafana/grafana-plugin-model](https://github.com/grafana/grafana-plugin-model) is now deprecated and support for that will be removed in a future release. Please refer to [backend plugins documentation](https://grafana.com/docs/grafana/latest/developers/plugins/backend/) for information about the new officially supported backend plugins.
|
||||
- The unofficial first version of backend plugins together with usage of [grafana/grafana-plugin-model](https://github.com/grafana/grafana-plugin-model) is now deprecated and support for that will be removed in a future release. Please refer to [backend plugins documentation](/developers/plugin-tools/introduction/backend-plugins) for information about the new officially supported backend plugins.
|
||||
|
||||
## 7.0 Feature highlights
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
aks
|
||||
eror
|
||||
iam
|
||||
wan
|
||||
wan
|
||||
operato
|
||||
|
||||
20
.drone.star
20
.drone.star
@@ -7,8 +7,9 @@
|
||||
This module returns a Drone configuration including pipelines and secrets.
|
||||
"""
|
||||
|
||||
load("scripts/drone/events/pr.star", "pr_pipelines")
|
||||
load("scripts/drone/events/cron.star", "cronjobs")
|
||||
load("scripts/drone/events/main.star", "main_pipelines")
|
||||
load("scripts/drone/events/pr.star", "pr_pipelines")
|
||||
load(
|
||||
"scripts/drone/events/release.star",
|
||||
"integration_test_pipelines",
|
||||
@@ -17,24 +18,21 @@ load(
|
||||
"publish_packages_pipeline",
|
||||
)
|
||||
load(
|
||||
"scripts/drone/rgm.star",
|
||||
"rgm",
|
||||
"scripts/drone/pipelines/ci_images.star",
|
||||
"publish_ci_windows_test_image_pipeline",
|
||||
)
|
||||
load(
|
||||
"scripts/drone/pipelines/publish_images.star",
|
||||
"publish_image_pipelines_public",
|
||||
)
|
||||
load(
|
||||
"scripts/drone/pipelines/ci_images.star",
|
||||
"publish_ci_build_container_image_pipeline",
|
||||
"publish_ci_windows_test_image_pipeline",
|
||||
)
|
||||
load(
|
||||
"scripts/drone/pipelines/windows.star",
|
||||
"windows_test_backend",
|
||||
)
|
||||
load("scripts/drone/version.star", "version_branch_pipelines")
|
||||
load("scripts/drone/events/cron.star", "cronjobs")
|
||||
load(
|
||||
"scripts/drone/rgm.star",
|
||||
"rgm",
|
||||
)
|
||||
load("scripts/drone/vault.star", "secrets")
|
||||
|
||||
def main(_ctx):
|
||||
@@ -50,10 +48,8 @@ def main(_ctx):
|
||||
"event": ["promote"],
|
||||
"target": ["test-windows"],
|
||||
}, "oss", "testing")] +
|
||||
version_branch_pipelines() +
|
||||
integration_test_pipelines() +
|
||||
publish_ci_windows_test_image_pipeline() +
|
||||
publish_ci_build_container_image_pipeline() +
|
||||
cronjobs() +
|
||||
secrets()
|
||||
)
|
||||
|
||||
3175
.drone.yml
3175
.drone.yml
File diff suppressed because it is too large
Load Diff
42
.github/CODEOWNERS
vendored
42
.github/CODEOWNERS
vendored
@@ -34,24 +34,25 @@
|
||||
/devenv/README.md @grafana/docs-grafana
|
||||
|
||||
# Technical documentation
|
||||
/docs/ @Eve832 @jdbaldry
|
||||
/docs/sources/ @Eve832
|
||||
/docs/sources/administration/ @Eve832 @GrafanaWriter
|
||||
/docs/sources/alerting/ @brendamuir
|
||||
/docs/sources/dashboards/ @imatwawana
|
||||
/docs/sources/datasources/ @Eve832 @GrafanaWriter
|
||||
/docs/sources/explore/ @Eve832 @GrafanaWriter
|
||||
/docs/sources/fundamentals @chri2547
|
||||
/docs/sources/getting-started/ @chri2547
|
||||
/docs/sources/introduction/ @chri2547
|
||||
/docs/sources/old-alerting/ @brendamuir
|
||||
/docs/sources/panels-visualizations/ @imatwawana
|
||||
/docs/sources/release-notes/ @Eve832 @GrafanaWriter
|
||||
/docs/sources/setup-grafana/ @chri2547
|
||||
/docs/sources/upgrade-guide/ @chri2547 @imatwawana
|
||||
/docs/sources/whatsnew/ @chri2547 @imatwawana
|
||||
/docs/sources/developers/plugins/ @Eve832 @josmperez @grafana/plugins-platform-frontend @grafana/plugins-platform-backend
|
||||
/docs/sources/developers/plugins/introduction-to-plugin-development/backend/ @Eve832 @grafana/plugins-platform-backend
|
||||
# `make docs` procedure and related workflows are owned @grafana/docs-tooling. Slack #docs.
|
||||
# Documentation sources might have different owners.
|
||||
/docs/ @grafana/docs-tooling
|
||||
/docs/sources/ @Eve832
|
||||
/docs/sources/administration/ @jdbaldry
|
||||
/docs/sources/alerting/ @brendamuir
|
||||
/docs/sources/dashboards/ @imatwawana
|
||||
/docs/sources/datasources/ @lwandz13
|
||||
/docs/sources/explore/ @grafana/explore-squad
|
||||
/docs/sources/fundamentals @chri2547
|
||||
/docs/sources/getting-started/ @chri2547
|
||||
/docs/sources/introduction/ @chri2547
|
||||
/docs/sources/old-alerting/ @brendamuir
|
||||
/docs/sources/panels-visualizations/ @imatwawana
|
||||
/docs/sources/release-notes/ @Eve832 @GrafanaWriter
|
||||
/docs/sources/setup-grafana/ @chri2547
|
||||
/docs/sources/upgrade-guide/ @imatwawana
|
||||
/docs/sources/whatsnew/ @imatwawana
|
||||
/docs/sources/developers/plugins/ @Eve832 @josmperez @grafana/plugins-platform-frontend @grafana/plugins-platform-backend
|
||||
|
||||
# Backend code
|
||||
/go.mod @grafana/backend-platform
|
||||
@@ -359,10 +360,10 @@ lerna.json @grafana/frontend-ops
|
||||
/public/app/features/api-keys/ @grafana/grafana-frontend-platform
|
||||
/public/app/features/canvas/ @grafana/dataviz-squad
|
||||
/public/app/features/commandPalette/ @grafana/grafana-frontend-platform
|
||||
/public/app/features/connections/ @grafana/plugins-platform-frontend
|
||||
/public/app/features/connections/ @grafana/plugins-platform-frontend @mikkancso
|
||||
/public/app/features/correlations/ @grafana/explore-squad
|
||||
/public/app/features/dashboard/ @grafana/dashboards-squad
|
||||
/public/app/features/datasources/ @grafana/plugins-platform-frontend
|
||||
/public/app/features/datasources/ @grafana/plugins-platform-frontend @mikkancso
|
||||
/public/app/features/dimensions/ @grafana/dataviz-squad
|
||||
/public/app/features/dataframe-import/ @grafana/grafana-bi-squad
|
||||
/public/app/features/explore/ @grafana/explore-squad
|
||||
@@ -617,6 +618,7 @@ embed.go @grafana/grafana-as-code
|
||||
/.github/workflows/stale.yml @grafana/grafana-frontend-platform
|
||||
/.github/workflows/update-changelog.yml @grafana/grafana-delivery
|
||||
/.github/workflows/snyk.yml @grafana/security-team
|
||||
/.github/workflows/create-security-patch-from-security-mirror.yml @grafana/grafana-delivery
|
||||
|
||||
# Generated files not requiring owner approval
|
||||
/packages/grafana-data/src/types/featureToggles.gen.ts @grafanabot
|
||||
|
||||
2
.github/workflows/auto-milestone.yml
vendored
2
.github/workflows/auto-milestone.yml
vendored
@@ -2,6 +2,8 @@ name: Auto-milestone
|
||||
on:
|
||||
pull_request:
|
||||
types:
|
||||
- opened
|
||||
- reopened
|
||||
- closed
|
||||
|
||||
jobs:
|
||||
|
||||
5
.github/workflows/bump-version.yml
vendored
5
.github/workflows/bump-version.yml
vendored
@@ -55,6 +55,10 @@ jobs:
|
||||
repository: "grafana/grafana-github-actions"
|
||||
path: ./actions
|
||||
ref: main
|
||||
# Go is required for also updating the schema versions as part of the precommit hook:
|
||||
- uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: '1.20'
|
||||
- uses: actions/setup-node@v3.5.1
|
||||
with:
|
||||
node-version: '16'
|
||||
@@ -71,3 +75,4 @@ jobs:
|
||||
with:
|
||||
token: ${{ steps.generate_token.outputs.token }}
|
||||
metricsWriteAPIKey: ${{ secrets.GRAFANA_MISC_STATS_API_KEY }}
|
||||
precommit_make_target: gen-cue
|
||||
|
||||
2
.github/workflows/codeql-analysis.yml
vendored
2
.github/workflows/codeql-analysis.yml
vendored
@@ -44,7 +44,7 @@ jobs:
|
||||
name: Set go version
|
||||
uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: '1.20.6'
|
||||
go-version: '1.21.5'
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
|
||||
28
.github/workflows/create-security-patch-from-security-mirror.yml
vendored
Normal file
28
.github/workflows/create-security-patch-from-security-mirror.yml
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
# Owned by grafana-delivery-squad
|
||||
# Intended to be dropped into the base repo (Ex: grafana/grafana) for use in the security mirror.
|
||||
name: Create security patch
|
||||
run-name: create-security-patch
|
||||
on:
|
||||
pull_request:
|
||||
types:
|
||||
- opened
|
||||
- reopened
|
||||
- synchronize
|
||||
branches:
|
||||
- "main"
|
||||
- "v*.*.*"
|
||||
|
||||
# This is run before the pull request has been merged, so we'll run against the src branch
|
||||
jobs:
|
||||
trigger_downstream_create_security_patch:
|
||||
concurrency: create-patch-${{ github.ref_name }}
|
||||
uses: grafana/security-patch-actions/.github/workflows/create-patch.yml@main
|
||||
if: github.repository == 'grafana/grafana-security-mirror'
|
||||
with:
|
||||
repo: "${{ github.repository }}"
|
||||
src_ref: "${{ github.head_ref }}" # this is the source branch name, Ex: "feature/newthing"
|
||||
patch_ref: "${{ github.base_ref }}" # this is the target branch name, Ex: "main"
|
||||
patch_repo: "grafana/grafana-security-patches"
|
||||
patch_prefix: "${{ github.event.pull_request.number }}"
|
||||
secrets: inherit
|
||||
|
||||
2
.github/workflows/pr-codeql-analysis-go.yml
vendored
2
.github/workflows/pr-codeql-analysis-go.yml
vendored
@@ -23,7 +23,7 @@ jobs:
|
||||
- name: Set go version
|
||||
uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: '1.20.6'
|
||||
go-version: '1.21.5'
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
|
||||
30
.github/workflows/pr-commands.yml
vendored
30
.github/workflows/pr-commands.yml
vendored
@@ -8,20 +8,44 @@ on:
|
||||
concurrency:
|
||||
group: pr-commands-${{ github.event.number }}
|
||||
jobs:
|
||||
config:
|
||||
runs-on: "ubuntu-latest"
|
||||
outputs:
|
||||
has-secrets: ${{ steps.check.outputs.has-secrets }}
|
||||
steps:
|
||||
- name: "Check for secrets"
|
||||
id: check
|
||||
shell: bash
|
||||
run: |
|
||||
if [ -n "${{ (secrets.GRAFANA_PR_AUTOMATION_APP_ID != '' &&
|
||||
secrets.GRAFANA_PR_AUTOMATION_APP_PEM != '' &&
|
||||
secrets.GRAFANA_MISC_STATS_API_KEY != ''
|
||||
) || '' }}" ]; then
|
||||
echo "has-secrets=1" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
|
||||
main:
|
||||
needs: config
|
||||
if: needs.config.outputs.has-secrets
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout Actions
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
repository: "grafana/grafana-github-actions"
|
||||
path: ./actions
|
||||
ref: main
|
||||
- name: Install Actions
|
||||
run: npm install --production --prefix ./actions
|
||||
- name: "Generate token"
|
||||
id: generate_token
|
||||
uses: tibdex/github-app-token@b62528385c34dbc9f38e5f4225ac829252d1ea92
|
||||
with:
|
||||
app_id: ${{ secrets.GRAFANA_PR_AUTOMATION_APP_ID }}
|
||||
private_key: ${{ secrets.GRAFANA_PR_AUTOMATION_APP_PEM }}
|
||||
- name: Run Commands
|
||||
uses: ./actions/commands
|
||||
with:
|
||||
metricsWriteAPIKey: ${{secrets.GRAFANA_MISC_STATS_API_KEY}}
|
||||
token: ${{secrets.GH_BOT_ACCESS_TOKEN}}
|
||||
configPath: pr-commands
|
||||
token: ${{ steps.generate_token.outputs.token }}
|
||||
configPath: pr-commands
|
||||
@@ -1,11 +1,13 @@
|
||||
# Owned by grafana-delivery-squad
|
||||
# Intended to be dropped into the base repo Ex: grafana/grafana
|
||||
name: Check for security patch conflicts
|
||||
run-name: check-security-patch-conflicts-${{ github.base_ref }}-${{ github.head_ref }}
|
||||
# Intended to be dropped into the base repo Ex: grafana/grafana
|
||||
name: Check for patch conflicts
|
||||
run-name: check-patch-conflicts-${{ github.base_ref }}-${{ github.head_ref }}
|
||||
on:
|
||||
pull_request_target:
|
||||
pull_request:
|
||||
types:
|
||||
- opened
|
||||
- reopened
|
||||
- synchronize
|
||||
branches:
|
||||
- "main"
|
||||
- "v*.*.*"
|
||||
@@ -80,7 +80,7 @@ var config = {
|
||||
{
|
||||
url: '${HOST}/?orgId=1',
|
||||
wait: 500,
|
||||
threshold: 0,
|
||||
threshold: 3,
|
||||
},
|
||||
{
|
||||
url: '${HOST}/d/O6f11TZWk/panel-tests-bar-gauge',
|
||||
@@ -93,7 +93,7 @@ var config = {
|
||||
url: '${HOST}/?orgId=1&search=open',
|
||||
wait: 500,
|
||||
rootElement: '.main-view',
|
||||
threshold: 0,
|
||||
threshold: 3,
|
||||
},
|
||||
{
|
||||
url: '${HOST}/alerting/list',
|
||||
|
||||
4477
CHANGELOG.md
4477
CHANGELOG.md
File diff suppressed because it is too large
Load Diff
@@ -82,6 +82,6 @@ Before we can accept your pull request, you need to [sign our CLA](https://grafa
|
||||
## Where do I go from here?
|
||||
|
||||
- Set up your [development environment](contribute/developer-guide.md).
|
||||
- Learn how to [contribute documentation](contribute/README.md).
|
||||
- Get started [developing plugins](https://grafana.com/docs/grafana/latest/developers/plugins/) for Grafana.
|
||||
- Learn how to [contribute to our documentation](contribute/documentation/README.md).
|
||||
- Get started [developing plugins](https://grafana.com/developers/plugin-tools) for Grafana.
|
||||
- Look through the resources in the [contribute](contribute) folder.
|
||||
|
||||
10
Dockerfile
10
Dockerfile
@@ -1,9 +1,9 @@
|
||||
# syntax=docker/dockerfile:1
|
||||
|
||||
ARG BASE_IMAGE=alpine:3.17
|
||||
ARG JS_IMAGE=node:18-alpine3.17
|
||||
ARG BASE_IMAGE=alpine:3.18.3
|
||||
ARG JS_IMAGE=node:18-alpine3.18
|
||||
ARG JS_PLATFORM=linux/amd64
|
||||
ARG GO_IMAGE=golang:1.20.6-alpine3.17
|
||||
ARG GO_IMAGE=golang:1.21.5-alpine3.18
|
||||
|
||||
ARG GO_SRC=go-builder
|
||||
ARG JS_SRC=js-builder
|
||||
@@ -64,6 +64,7 @@ COPY pkg pkg
|
||||
COPY scripts scripts
|
||||
COPY conf conf
|
||||
COPY .github .github
|
||||
COPY LICENSE ./
|
||||
|
||||
ENV COMMIT_SHA=${COMMIT_SHA}
|
||||
ENV BUILD_BRANCH=${BUILD_BRANCH}
|
||||
@@ -110,7 +111,7 @@ RUN if grep -i -q alpine /etc/issue; then \
|
||||
elif grep -i -q ubuntu /etc/issue; then \
|
||||
DEBIAN_FRONTEND=noninteractive && \
|
||||
apt-get update && \
|
||||
apt-get install -y ca-certificates curl tzdata && \
|
||||
apt-get install -y ca-certificates curl tzdata musl && \
|
||||
apt-get autoremove -y && \
|
||||
rm -rf /var/lib/apt/lists/*; \
|
||||
else \
|
||||
@@ -165,6 +166,7 @@ RUN if [ ! $(getent group "$GF_GID") ]; then \
|
||||
|
||||
COPY --from=go-src /tmp/grafana/bin/grafana* /tmp/grafana/bin/*/grafana* ./bin/
|
||||
COPY --from=js-src /tmp/grafana/public ./public
|
||||
COPY --from=go-src /tmp/grafana/LICENSE ./
|
||||
|
||||
EXPOSE 3000
|
||||
|
||||
|
||||
75
Makefile
75
Makefile
@@ -13,7 +13,6 @@ GO = go
|
||||
GO_FILES ?= ./pkg/...
|
||||
SH_FILES ?= $(shell find ./scripts -name *.sh)
|
||||
GO_BUILD_FLAGS += $(if $(GO_BUILD_DEV),-dev)
|
||||
GO_BUILD_FLAGS += $(if $(GO_BUILD_DEV),-dev)
|
||||
GO_BUILD_FLAGS += $(if $(GO_BUILD_TAGS),-build-tags=$(GO_BUILD_TAGS))
|
||||
|
||||
targets := $(shell echo '$(sources)' | tr "," " ")
|
||||
@@ -37,36 +36,68 @@ node_modules: package.json yarn.lock ## Install node modules.
|
||||
|
||||
##@ Swagger
|
||||
SPEC_TARGET = public/api-spec.json
|
||||
MERGED_SPEC_TARGET := public/api-merged.json
|
||||
ENTERPRISE_SPEC_TARGET = public/api-enterprise-spec.json
|
||||
MERGED_SPEC_TARGET = public/api-merged.json
|
||||
NGALERT_SPEC_TARGET = pkg/services/ngalert/api/tooling/api.json
|
||||
|
||||
$(NGALERT_SPEC_TARGET):
|
||||
+$(MAKE) -C pkg/services/ngalert/api/tooling api.json
|
||||
|
||||
$(MERGED_SPEC_TARGET): $(SPEC_TARGET) $(NGALERT_SPEC_TARGET) $(SWAGGER) ## Merge generated and ngalert API specs
|
||||
$(MERGED_SPEC_TARGET): swagger-oss-gen swagger-enterprise-gen $(NGALERT_SPEC_TARGET) $(SWAGGER) ## Merge generated and ngalert API specs
|
||||
# known conflicts DsPermissionType, AddApiKeyCommand, Json, Duration (identical models referenced by both specs)
|
||||
$(SWAGGER) mixin $(SPEC_TARGET) $(NGALERT_SPEC_TARGET) --ignore-conflicts -o $(MERGED_SPEC_TARGET)
|
||||
$(SWAGGER) mixin $(SPEC_TARGET) $(ENTERPRISE_SPEC_TARGET) $(NGALERT_SPEC_TARGET) --ignore-conflicts -o $(MERGED_SPEC_TARGET)
|
||||
|
||||
$(SPEC_TARGET): $(SWAGGER) ## Generate API Swagger specification
|
||||
swagger-oss-gen: $(SWAGGER) ## Generate API Swagger specification
|
||||
@echo "re-generating swagger for OSS"
|
||||
rm -f $(SPEC_TARGET)
|
||||
SWAGGER_GENERATE_EXTENSION=false $(SWAGGER) generate spec -m -w pkg/server -o $(SPEC_TARGET) \
|
||||
-x "github.com/grafana/grafana/pkg/services/ngalert/api/tooling/definitions" \
|
||||
-x "github.com/prometheus/alertmanager" \
|
||||
-i pkg/api/swagger_tags.json \
|
||||
--exclude-tag=alpha
|
||||
go run pkg/services/ngalert/api/tooling/cmd/clean-swagger/main.go -if $@ -of $@
|
||||
--exclude-tag=alpha \
|
||||
--exclude-tag=enterprise
|
||||
|
||||
swagger-api-spec: gen-go $(SPEC_TARGET) $(MERGED_SPEC_TARGET) validate-api-spec
|
||||
# this file only exists if enterprise is enabled
|
||||
ENTERPRISE_EXT_FILE = pkg/extensions/ext.go
|
||||
ifeq ("$(wildcard $(ENTERPRISE_EXT_FILE))","") ## if enterprise is not enabled
|
||||
swagger-enterprise-gen:
|
||||
@echo "skipping re-generating swagger for enterprise: not enabled"
|
||||
else
|
||||
swagger-enterprise-gen: $(SWAGGER) ## Generate API Swagger specification
|
||||
@echo "re-generating swagger for enterprise"
|
||||
rm -f $(ENTERPRISE_SPEC_TARGET)
|
||||
SWAGGER_GENERATE_EXTENSION=false $(SWAGGER) generate spec -m -w pkg/server -o $(ENTERPRISE_SPEC_TARGET) \
|
||||
-x "github.com/grafana/grafana/pkg/services/ngalert/api/tooling/definitions" \
|
||||
-x "github.com/prometheus/alertmanager" \
|
||||
-i pkg/api/swagger_tags.json \
|
||||
--exclude-tag=alpha \
|
||||
--include-tag=enterprise
|
||||
endif
|
||||
|
||||
validate-api-spec: $(MERGED_SPEC_TARGET) $(SWAGGER) ## Validate API spec
|
||||
swagger-gen: gen-go $(MERGED_SPEC_TARGET) swagger-validate
|
||||
|
||||
swagger-validate: $(MERGED_SPEC_TARGET) $(SWAGGER) ## Validate API spec
|
||||
$(SWAGGER) validate $(<)
|
||||
|
||||
clean-api-spec:
|
||||
swagger-clean:
|
||||
rm -f $(SPEC_TARGET) $(MERGED_SPEC_TARGET) $(OAPI_SPEC_TARGET)
|
||||
|
||||
.PHONY: cleanup-old-git-hooks
|
||||
cleanup-old-git-hooks:
|
||||
./scripts/cleanup-husky.sh
|
||||
|
||||
.PHONY: lefthook-install
|
||||
lefthook-install: cleanup-old-git-hooks $(LEFTHOOK) # install lefthook for pre-commit hooks
|
||||
$(LEFTHOOK) install -f
|
||||
|
||||
.PHONY: lefthook-uninstall
|
||||
lefthook-uninstall: $(LEFTHOOK)
|
||||
$(LEFTHOOK) uninstall
|
||||
|
||||
##@ OpenAPI 3
|
||||
OAPI_SPEC_TARGET = public/openapi3.json
|
||||
|
||||
openapi3-gen: swagger-api-spec ## Generates OpenApi 3 specs from the Swagger 2 already generated
|
||||
openapi3-gen: swagger-gen ## Generates OpenApi 3 specs from the Swagger 2 already generated
|
||||
$(GO) run scripts/openapi3/openapi3conv.go $(MERGED_SPEC_TARGET) $(OAPI_SPEC_TARGET)
|
||||
|
||||
##@ Building
|
||||
@@ -77,7 +108,7 @@ gen-cue: ## Do all CUE/Thema code generation
|
||||
go generate ./public/app/plugins/gen.go
|
||||
go generate ./pkg/kindsys/report.go
|
||||
|
||||
gen-go: $(WIRE) gen-cue
|
||||
gen-go: $(WIRE)
|
||||
@echo "generate go files"
|
||||
$(WIRE) gen -tags $(WIRE_TAGS) ./pkg/server
|
||||
|
||||
@@ -133,6 +164,13 @@ test-go-integration: ## Run integration tests for backend with flags.
|
||||
@echo "test backend integration tests"
|
||||
$(GO) test -count=1 -run "^TestIntegration" -covermode=atomic -timeout=5m $(GO_INTEGRATION_TESTS)
|
||||
|
||||
.PHONY: test-go-integration-alertmanager
|
||||
test-go-integration-alertmanager: ## Run integration tests for the remote alertmanager (config taken from the mimir_backend block).
|
||||
@echo "test remote alertmanager integration tests"
|
||||
$(GO) clean -testcache
|
||||
AM_URL=http://localhost:8080 AM_TENANT_ID=test AM_PASSWORD=test \
|
||||
$(GO) test -count=1 -run "^TestIntegrationRemoteAlertmanager" -covermode=atomic -timeout=5m ./pkg/services/ngalert/notifier/...
|
||||
|
||||
.PHONY: test-go-integration-postgres
|
||||
test-go-integration-postgres: devenv-postgres ## Run integration tests for postgres backend with flags.
|
||||
@echo "test backend integration postgres tests"
|
||||
@@ -191,7 +229,7 @@ build-docker-full: ## Build Docker image for development.
|
||||
--build-arg BINGO=false \
|
||||
--build-arg GO_BUILD_TAGS=$(GO_BUILD_TAGS) \
|
||||
--build-arg WIRE_TAGS=$(WIRE_TAGS) \
|
||||
--build-arg COMMIT_SHA=$$(git rev-parse --short HEAD) \
|
||||
--build-arg COMMIT_SHA=$$(git rev-parse HEAD) \
|
||||
--build-arg BUILD_BRANCH=$$(git rev-parse --abbrev-ref HEAD) \
|
||||
--tag grafana/grafana$(TAG_SUFFIX):dev \
|
||||
$(DOCKER_BUILD_ARGS)
|
||||
@@ -204,10 +242,10 @@ build-docker-full-ubuntu: ## Build Docker image based on Ubuntu for development.
|
||||
--build-arg BINGO=false \
|
||||
--build-arg GO_BUILD_TAGS=$(GO_BUILD_TAGS) \
|
||||
--build-arg WIRE_TAGS=$(WIRE_TAGS) \
|
||||
--build-arg COMMIT_SHA=$$(git rev-parse --short HEAD) \
|
||||
--build-arg COMMIT_SHA=$$(git rev-parse HEAD) \
|
||||
--build-arg BUILD_BRANCH=$$(git rev-parse --abbrev-ref HEAD) \
|
||||
--build-arg BASE_IMAGE=ubuntu:20.04 \
|
||||
--build-arg GO_IMAGE=golang:1.20.6 \
|
||||
--build-arg BASE_IMAGE=ubuntu:22.04 \
|
||||
--build-arg GO_IMAGE=golang:1.21.5 \
|
||||
--tag grafana/grafana$(TAG_SUFFIX):dev-ubuntu \
|
||||
$(DOCKER_BUILD_ARGS)
|
||||
|
||||
@@ -252,6 +290,9 @@ devenv-mysql:
|
||||
protobuf: ## Compile protobuf definitions
|
||||
bash scripts/protobuf-check.sh
|
||||
bash pkg/plugins/backendplugin/pluginextensionv2/generate.sh
|
||||
bash pkg/plugins/backendplugin/secretsmanagerplugin/generate.sh
|
||||
bash pkg/services/store/entity/generate.sh
|
||||
bash pkg/infra/grn/generate.sh
|
||||
|
||||
clean: ## Clean up intermediate build artifacts.
|
||||
@echo "cleaning"
|
||||
@@ -277,7 +318,7 @@ scripts/drone/TAGS: $(shell find scripts/drone -name '*.star')
|
||||
etags --lang none --regex="/def \(\w+\)[^:]+:/\1/" --regex="/\s*\(\w+\) =/\1/" $^ -o $@
|
||||
|
||||
format-drone:
|
||||
buildifier -r scripts/drone
|
||||
buildifier --lint=fix -r scripts/drone
|
||||
|
||||
help: ## Display this help.
|
||||
@awk 'BEGIN {FS = ":.*##"; printf "\nUsage:\n make \033[36m<target>\033[0m\n"} /^[a-zA-Z_-]+:.*?##/ { printf " \033[36m%-15s\033[0m %s\n", $$1, $$2 } /^##@/ { printf "\n\033[1m%s\033[0m\n", substr($$0, 5) } ' $(MAKEFILE_LIST)
|
||||
|
||||
@@ -242,7 +242,7 @@ reporting_distributor = grafana-labs
|
||||
# for new versions of grafana. The check is used
|
||||
# in some UI views to notify that a grafana update exists.
|
||||
# This option does not cause any auto updates, nor send any information
|
||||
# only a GET request to https://raw.githubusercontent.com/grafana/grafana/main/latest.json to get the latest version.
|
||||
# only a GET request to https://grafana.com/api/grafana/versions/stable to get the latest version.
|
||||
check_for_updates = true
|
||||
|
||||
# Set to false to disable all checks to https://grafana.com
|
||||
@@ -556,6 +556,17 @@ azure_auth_enabled = false
|
||||
# Use email lookup in addition to the unique ID provided by the IdP
|
||||
oauth_allow_insecure_email_lookup = false
|
||||
|
||||
# Set to true to include id of identity as a response header
|
||||
id_response_header_enabled = false
|
||||
|
||||
# Prefix used for the id response header, X-Grafana-Identity-Id
|
||||
id_response_header_prefix = X-Grafana
|
||||
|
||||
# List of identity namespaces to add id response headers for, separated by space.
|
||||
# Available namespaces are user, api-key and service-account.
|
||||
# The header value will encode the namespace ("user:<id>", "api-key:<id>", "service-account:<id>")
|
||||
id_response_header_namespaces = user api-key service-account
|
||||
|
||||
#################################### Anonymous Auth ######################
|
||||
[auth.anonymous]
|
||||
# enable anonymous access
|
||||
@@ -806,6 +817,24 @@ managed_identity_enabled = false
|
||||
# Should be set for user-assigned identity and should be empty for system-assigned identity
|
||||
managed_identity_client_id =
|
||||
|
||||
# Specifies whether Azure AD Workload Identity authentication should be enabled in datasources that support it
|
||||
# For more documentation on Azure AD Workload Identity, review this documentation:
|
||||
# https://azure.github.io/azure-workload-identity/docs/
|
||||
# Disabled by default, needs to be explicitly enabled
|
||||
workload_identity_enabled = false
|
||||
|
||||
# Tenant ID of the Azure AD Workload Identity
|
||||
# Allows to override default tenant ID of the Azure AD identity associated with the Kubernetes service account
|
||||
workload_identity_tenant_id =
|
||||
|
||||
# Client ID of the Azure AD Workload Identity
|
||||
# Allows to override default client ID of the Azure AD identity associated with the Kubernetes service account
|
||||
workload_identity_client_id =
|
||||
|
||||
# Custom path to token file for the Azure AD Workload Identity
|
||||
# Allows to set a custom path to the projected service account token file
|
||||
workload_identity_token_file =
|
||||
|
||||
#################################### Role-based Access Control ###########
|
||||
[rbac]
|
||||
# If enabled, cache permissions in a in memory cache
|
||||
@@ -1041,8 +1070,8 @@ execute_alerts = true
|
||||
# The timeout string is a possibly signed sequence of decimal numbers, followed by a unit suffix (ms, s, m, h, d), e.g. 30s or 1m.
|
||||
evaluation_timeout = 30s
|
||||
|
||||
# Number of times we'll attempt to evaluate an alert rule before giving up on that evaluation. This option has a legacy version in the `[alerting]` section that takes precedence.
|
||||
max_attempts = 3
|
||||
# Number of times we'll attempt to evaluate an alert rule before giving up on that evaluation. The default value is 1.
|
||||
max_attempts = 1
|
||||
|
||||
# Minimum interval to enforce between rule evaluations. Rules will be adjusted if they are less than this value or if they are not multiple of the scheduler interval (10s). Higher values can help with resource management as we'll schedule fewer evaluations over time. This option has a legacy version in the `[alerting]` section that takes precedence.
|
||||
# The interval string is a possibly signed sequence of decimal numbers, followed by a unit suffix (ms, s, m, h, d), e.g. 30s or 1m.
|
||||
|
||||
@@ -249,7 +249,7 @@
|
||||
# for new versions of grafana. The check is used
|
||||
# in some UI views to notify that a grafana update exists.
|
||||
# This option does not cause any auto updates, nor send any information
|
||||
# only a GET request to https://raw.githubusercontent.com/grafana/grafana/main/latest.json to get the latest version.
|
||||
# only a GET request to https://grafana.com/api/grafana/versions/stable to get the latest version.
|
||||
;check_for_updates = true
|
||||
|
||||
# Set to false to disable all checks to https://grafana.com
|
||||
@@ -545,6 +545,17 @@
|
||||
# Use email lookup in addition to the unique ID provided by the IdP
|
||||
;oauth_allow_insecure_email_lookup = false
|
||||
|
||||
# Set to true to include id of identity as a response header
|
||||
;id_response_header_enabled = false
|
||||
|
||||
# Prefix used for the id response header, X-Grafana-Identity-Id
|
||||
;id_response_header_prefix = X-Grafana
|
||||
|
||||
# List of identity namespaces to add id response headers for, separated by space.
|
||||
# Available namespaces are user, api-key and service-account.
|
||||
# The header value will encode the namespace ("user:<id>", "api-key:<id>", "service-account:<id>")
|
||||
;id_response_header_namespaces = user api-key service-account
|
||||
|
||||
#################################### Anonymous Auth ######################
|
||||
[auth.anonymous]
|
||||
# enable anonymous access
|
||||
@@ -777,6 +788,24 @@
|
||||
# Should be set for user-assigned identity and should be empty for system-assigned identity
|
||||
;managed_identity_client_id =
|
||||
|
||||
# Specifies whether Azure AD Workload Identity authentication should be enabled in datasources that support it
|
||||
# For more documentation on Azure AD Workload Identity, review this documentation:
|
||||
# https://azure.github.io/azure-workload-identity/docs/
|
||||
# Disabled by default, needs to be explicitly enabled
|
||||
;workload_identity_enabled = false
|
||||
|
||||
# Tenant ID of the Azure AD Workload Identity
|
||||
# Allows to override default tenant ID of the Azure AD identity associated with the Kubernetes service account
|
||||
;workload_identity_tenant_id =
|
||||
|
||||
# Client ID of the Azure AD Workload Identity
|
||||
# Allows to override default client ID of the Azure AD identity associated with the Kubernetes service account
|
||||
;workload_identity_client_id =
|
||||
|
||||
# Custom path to token file for the Azure AD Workload Identity
|
||||
# Allows to set a custom path to the projected service account token file
|
||||
;workload_identity_token_file =
|
||||
|
||||
#################################### Role-based Access Control ###########
|
||||
[rbac]
|
||||
;permission_cache = true
|
||||
@@ -1010,8 +1039,8 @@
|
||||
# The timeout string is a possibly signed sequence of decimal numbers, followed by a unit suffix (ms, s, m, h, d), e.g. 30s or 1m.
|
||||
;evaluation_timeout = 30s
|
||||
|
||||
# Number of times we'll attempt to evaluate an alert rule before giving up on that evaluation. This option has a legacy version in the `[alerting]` section that takes precedence.
|
||||
;max_attempts = 3
|
||||
# Number of times we'll attempt to evaluate an alert rule before giving up on that evaluation. The default value is 1.
|
||||
;max_attempts = 1
|
||||
|
||||
# Minimum interval to enforce between rule evaluations. Rules will be adjusted if they are less than this value or if they are not multiple of the scheduler interval (10s). Higher values can help with resource management as we'll schedule fewer evaluations over time. This option has a legacy version in the `[alerting]` section that takes precedence.
|
||||
# The interval string is a possibly signed sequence of decimal numbers, followed by a unit suffix (ms, s, m, h, d), e.g. 30s or 1m.
|
||||
|
||||
22
contribute/backend/upgrading-go-version.md
Normal file
22
contribute/backend/upgrading-go-version.md
Normal file
@@ -0,0 +1,22 @@
|
||||
# Upgrading Go Version
|
||||
|
||||
Notes on upgrading Go version.
|
||||
|
||||
Example PR: https://github.com/grafana/grafana/pull/79329
|
||||
|
||||
## The main areas that need to change during the upgrade are:
|
||||
|
||||
- https://github.com/grafana/grafana/blob/d8ecea4ed93efb2e4d64a5ee24bc08f3805f413d/scripts/drone/variables.star#L6
|
||||
- https://github.com/grafana/grafana/blob/d8ecea4ed93efb2e4d64a5ee24bc08f3805f413d/Makefile#L264
|
||||
- https://github.com/grafana/grafana/blob/d8ecea4ed93efb2e4d64a5ee24bc08f3805f413d/Dockerfile#L6
|
||||
|
||||
Make sure to run `make drone` so that changes to `.star` files are reflected and `drone.yml` is generated.
|
||||
|
||||
### Additional files to change
|
||||
|
||||
- Take a look in `.github/workflows` folder for what `go` version is being used there in various workflows.
|
||||
- Make sure to create a PR with the corresponding changes in `grafana/grafana-enterprise` repository.
|
||||
|
||||
## Updating the go.mod file
|
||||
|
||||
Please avoid updating the `go.mod` to the newest version unless really necessary. This ensures backwards compatibility and introduces less breaking changes. Always upgrade Go version in the runtime files above first, let them run for a couple of weeks and only then consider updating the `go.mod` file if necessary.
|
||||
@@ -104,7 +104,7 @@ myOldFunction(name: string) {
|
||||
2. Add info in the comment about **when it is going to be removed**
|
||||
3. Add info in the comment about **what should be used instead**
|
||||
4. In case it's a function or a method, use `deprecationWarning(<file name>, <old name>, <new name>)` to raise attention during runtime as well
|
||||
5. Update the [migration guide](../docs/sources/developers/plugins/migration-guide.md) with your instructions
|
||||
5. Update the [migration guide](/developers/plugin-tools/migration-guides/) with your instructions
|
||||
|
||||
### Communicate
|
||||
|
||||
|
||||
@@ -75,7 +75,6 @@ EOF
|
||||
fi
|
||||
|
||||
SOURCES_as_code='as-code-docs'
|
||||
SOURCES_beyla='ebpf-autoinstrument'
|
||||
SOURCES_enterprise_metrics='backend-enterprise'
|
||||
SOURCES_enterprise_metrics_='backend-enterprise'
|
||||
SOURCES_grafana_cloud='website'
|
||||
|
||||
@@ -39,12 +39,16 @@ title: Grafana documentation
|
||||
<img src="/static/img/logos/logo-docker.svg">
|
||||
<h5>Run Docker image</h5>
|
||||
</a>
|
||||
<a href="{{< relref "setup-grafana/installation/kubernetes/" >}}" class="nav-cards__item nav-cards__item--install">
|
||||
<img src="/static/img/logos/logo-kubernetes.svg">
|
||||
<h5>Run on Kubernetes</h5>
|
||||
</a>
|
||||
<a href="https://grafana.com/docs/grafana-cloud/" class="nav-cards__item nav-cards__item--install">
|
||||
<div class="nav-cards__icon fa fa-cloud">
|
||||
</div>
|
||||
<h5>Grafana Cloud</h5>
|
||||
</a>
|
||||
<a href="https://grafana.com/grafana/nightly?edition=oss" class="nav-cards__item nav-cards__item--install">
|
||||
<a href="https://grafana.com/grafana/download/nightly?edition=oss" class="nav-cards__item nav-cards__item--install">
|
||||
<div class="nav-cards__icon fa fa-moon-o">
|
||||
</div>
|
||||
<h5>Nightly builds</h5>
|
||||
|
||||
@@ -135,7 +135,7 @@ Complete the following steps to migrate from API keys to service accounts for AP
|
||||
This action generates a service account token.
|
||||
|
||||
1. Store the ID and secret that the system returns to you.
|
||||
1. Pass the token in the `Authrorization` header, prefixed with `Bearer`.
|
||||
1. Pass the token in the `Authorization` header, prefixed with `Bearer`.
|
||||
|
||||
This action authenticates API requests.
|
||||
|
||||
|
||||
15
docs/sources/administration/back-up-grafana/index.md
Normal file
15
docs/sources/administration/back-up-grafana/index.md
Normal file
@@ -0,0 +1,15 @@
|
||||
---
|
||||
description: Describes how to back up a locally provisioned Grafana instance.
|
||||
keywords:
|
||||
- grafana
|
||||
- backup
|
||||
labels:
|
||||
products:
|
||||
- enterprise
|
||||
- oss
|
||||
title: Back up Grafana
|
||||
weight: 80
|
||||
menuTitle: Back up Grafana
|
||||
---
|
||||
|
||||
{{< docs/shared lookup="back-up/back-up-grafana.md" source="grafana" version="<GRAFANA VERSION>" >}}
|
||||
@@ -14,25 +14,30 @@ weight: 900
|
||||
|
||||
You can create interactive links for Explore visualizations to run queries related to presented data by setting up Correlations.
|
||||
|
||||
A correlation defines how data in one [data source]({{< relref "/docs/grafana/latest/datasources/" >}}) is used to query data in another data source. Some examples:
|
||||
A correlation defines how data in one [data source]({{< relref "../../datasources" >}}) is used to query data in another data source.
|
||||
Some examples:
|
||||
|
||||
- an application name returned in a logs data source can be used to query metrics related to that application in a metrics data source, or
|
||||
- a user name returned by an SQL data source can be used to query logs related to that particular user in a logs data source
|
||||
|
||||
[Explore]({{< relref "/docs/grafana/latest/explore/" >}}) takes user-defined correlations to display links inside the visualizations. You can click on a link to run the related query and see results in [Explore Split View]({{< relref "/docs/grafana/latest/explore/#split-and-compare" >}}).
|
||||
[Explore]({{< relref "../../explore" >}}) takes user-defined correlations to display links inside the visualizations.
|
||||
You can click on a link to run the related query and see results in [Explore Split View]({{< relref "../../explore#split-and-compare" >}}).
|
||||
|
||||
Explore visualizations that currently support showing links based on correlations:
|
||||
|
||||
- [Logs Panel]({{< relref "./use-correlations-in-visualizations#correlations-in-logs-panel">}})
|
||||
- [Table]({{< relref "./use-correlations-in-visualizations#correlations-in-table">}})
|
||||
|
||||
You can configure correlations using [Administration > Correlation page]({{< relref "/docs/grafana/latest/administration/" >}}) or with [provisioning]({{< relref "/docs/grafana/latest/administration/provisioning" >}}).
|
||||
You can configure correlations using the **Administration > Correlation** page in Grafana or with [provisioning]({{< relref "../provisioning" >}}).
|
||||
|
||||
> **Note:** Correlations are available in Grafana 10.0+ as an opt-in beta feature. Modify Grafana [configuration file]({{< relref "/docs/grafana/latest/setup-grafana/configure-grafana/#configuration-file-location" >}}) to enable the `correlations` [feature toggle]({{< relref "/docs/grafana/latest/setup-grafana/configure-grafana/#feature_toggles" >}}) to use it.
|
||||
{{% admonition type="note" %}}
|
||||
Correlations are available in Grafana 10.0+ as an opt-in beta feature.
|
||||
Modify the Grafana [configuration file]({{< relref "../../setup-grafana/configure-grafana#configuration-file-location" >}}) to enable the `correlations` [feature toggle]({{< relref "../../setup-grafana/configure-grafana#feature_toggles" >}}) to use it.
|
||||
{{% /admonition %}}
|
||||
|
||||
## Example of how links work in Explore once set up
|
||||
|
||||
{{< figure src="/static/img/docs/correlations/correlations-in-explore-10-0.gif" caption="Correlations links in Explore" >}}
|
||||
{{< figure src="/static/img/docs/correlations/correlations-in-explore-10-0.gif" alt="Demonstration of following a correlation link in Grafana Explore" caption="Correlations links in Explore" >}}
|
||||
|
||||
See also:
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ weight: 30
|
||||
|
||||
## Before you begin
|
||||
|
||||
Adding access to create correlations for [Viewers and Editors]({{< relref "/docs/grafana/latest/administration/roles-and-permissions/" >}}) is available with [Role-based access control]({{< relref "/docs/grafana/latest/administration/roles-and-permissions/access-control/" >}}).
|
||||
Adding access to create correlations for [Viewers and Editors]({{< relref "../../../administration/roles-and-permissions" >}}) is available with [Role-based access control]({{< relref "../../../administration/roles-and-permissions/access-control" >}}).
|
||||
|
||||
## Add permissions to create correlations
|
||||
|
||||
|
||||
@@ -33,7 +33,9 @@ Learn how to create correlations using the [Administration page]({{< relref "./c
|
||||
|
||||
## Source data source and result field
|
||||
|
||||
Links are shown in Explore visualizations for the results from the correlation’s source data source. A link is assigned to one of the fields from the result provided in the correlation configuration (the results field). Each visualization displays fields with links in a different way ([Correlations in Logs Panel]({{< relref "./use-correlations-in-visualizations#correlations-in-logs-panel">}}) and see [Correlations in Table]({{< relref "./use-correlations-in-visualizations#correlations-in-table">}}))
|
||||
Links are shown in Explore visualizations for the results from the correlation’s source data source.
|
||||
A link is assigned to one of the fields from the result provided in the correlation configuration (the results field).
|
||||
Each visualization displays fields with links in a different way ([Correlations in Logs Panel]({{< relref "./use-correlations-in-visualizations#correlations-in-logs-panel">}}) and see [Correlations in Table]({{< relref "./use-correlations-in-visualizations#correlations-in-table">}})).
|
||||
|
||||
## Target query
|
||||
|
||||
@@ -41,9 +43,11 @@ The target query is run when a link is clicked in the visualization. You can use
|
||||
|
||||
### Correlation Variables
|
||||
|
||||
You can use variables inside the target query to access the source data related to the query. Correlations use [Grafana variable syntax]({{< relref "/docs/grafana/latest/dashboards/variables/variable-syntax" >}}). Variables are filled with values from the source results when the link is clicked. There are two types of variables you can use:
|
||||
You can use variables inside the target query to access the source data related to the query.
|
||||
Correlations use [Grafana variable syntax]({{< relref "../../../dashboards/variables/variable-syntax" >}}).
|
||||
Variables are filled with values from the source results when the link is clicked. There are two types of variables you can use:
|
||||
|
||||
- [field variables]({{< relref "/docs/grafana/latest/panels-visualizations/configure-data-links#field-variables" >}}) (allows to access field values and labels)
|
||||
- [field variables]({{< relref "../../../panels-visualizations/configure-data-links#field-variables" >}}) (allows to access field values and labels)
|
||||
- correlation variables (allows to access field values and transformations)
|
||||
|
||||
Example: If source results contain a field called “employee”, the value of the field can be accessed with:
|
||||
|
||||
@@ -9,11 +9,11 @@ weight: 20
|
||||
|
||||
# Permissions
|
||||
|
||||
Users with [Viewer base role]({{< relref "/docs/grafana/latest/administration/roles-and-permissions/" >}}) or with [datasources:query RBAC role]({{< relref "/docs/grafana/latest/administration/roles-and-permissions/access-control/" >}}) can:
|
||||
Users with [Viewer base role]({{< relref "../../../administration/roles-and-permissions" >}}) or with [datasources:query RBAC role]({{< relref "../../../administration/roles-and-permissions/access-control" >}}) can:
|
||||
|
||||
- Use correlations in Explore’s visualizations
|
||||
- List all available correlations in read-only mode
|
||||
- Use correlations in Explore’s visualizations.
|
||||
- List all available correlations in read-only mode.
|
||||
|
||||
Users with [Admin base role]({{< relref "/docs/grafana/latest/administration/roles-and-permissions/" >}}) or with [datasources:write RBAC role]({{< relref "/docs/grafana/latest/administration/roles-and-permissions/access-control/" >}}) can:
|
||||
Users with [Admin base role]({{< relref "../../../administration/roles-and-permissions" >}}) or with [datasources:write RBAC role]({{< relref "../../../administration/roles-and-permissions/access-control" >}}) can:
|
||||
|
||||
- Add, edit and delete correlations
|
||||
- Add, edit and delete correlations.
|
||||
|
||||
@@ -80,7 +80,7 @@ Description of provisioning properties:
|
||||
: Correlation type. “query” is the only supported type at the moment
|
||||
|
||||
**config.target**
|
||||
: [Target query model]({{< relref "#determine-target-query-model-structure" >}})
|
||||
: [Target query model](#determine-target-query-model-structure)
|
||||
|
||||
**config.field**
|
||||
: Name of the field where link is shown
|
||||
|
||||
@@ -27,7 +27,7 @@ weight: 70
|
||||
1. Open Explore.
|
||||
1. Select a data source that you chose as the source data source of the correlation.
|
||||
1. Run a query that results in data containing fields required to build variables in the target query.
|
||||
1. Links are added to cell rows in the column representing the field with the assigned link ([the results field]({{< relref "/docs/grafana/latest/administration/correlations/correlation-configuration#source-data-source-and-result-field" >}}).
|
||||
1. Links are added to cell rows in the column representing the field with the assigned link ([the results field]({{< relref "../correlation-configuration#source-data-source-and-result-field" >}}).
|
||||
1. Cells containing multiple links accessible with a context menu.
|
||||
|
||||
{{< figure src="/static/img/docs/correlations/correlations-in-table-10-0.png" max-width="600px" caption="Correlations links in table" >}}
|
||||
|
||||
@@ -73,7 +73,7 @@ Instructions below show how to set up a link that can run metrics query for the
|
||||
- Required correlation type (query)
|
||||
- Target query matching test data source model
|
||||
- “App metrics” correlation contains the following configuration:
|
||||
- Alias is set to ${application} variable (note that in provisioning files $ is used to access environment variables so it has to be [escaped]({{< relref "/docs/grafana/latest/administration/provisioning#using-environment-variables" >}})).
|
||||
- Alias is set to ${application} variable (note that in provisioning files $ is used to access environment variables so it has to be [escaped]({{< relref "../../../administration/provisioning#using-environment-variables" >}})).
|
||||
- Regular expression transformation is created to extract values from “msg” field
|
||||
- Regular expression transformation is used to capture the application name from the full name of the service stored in the log line.
|
||||
- The output of the transformation is mapped to a variable called “application”.
|
||||
|
||||
@@ -43,7 +43,7 @@ For links to data source-specific documentation, see [Data sources]({{< relref "
|
||||
You can configure data source permissions to allow or deny certain users the ability to query or edit a data source. Each data source’s configuration includes a Permissions tab where you can restrict data source permissions to specific users, teams, or roles.
|
||||
|
||||
{{% admonition type="note" %}}
|
||||
Available in [Grafana Enterprise]({{< relref "../../introduction/grafana-enterprise/" >}}) and [Grafana Cloud Pro and Advanced](/docs/grafana-cloud).
|
||||
Available in [Grafana Enterprise]({{< relref "../../introduction/grafana-enterprise/" >}}) and [Grafana Cloud](/docs/grafana-cloud).
|
||||
{{% /admonition %}}
|
||||
|
||||
By default, data sources in an organization can be queried by any user in that organization. For example, a user with the `Viewer` role can issue any possible query to a data source, not just queries that exist on dashboards to which they have access. Additionally, by default, data sources can be edited by the user who created the data source, as well as users with the `Admin` role.
|
||||
@@ -89,12 +89,12 @@ You can assign data source permissions to users, teams, and roles which will all
|
||||
|
||||
When you enable query and resource caching, Grafana temporarily stores the results of data source queries and resource requests. When you or another user submit the same query or resource request again, the results will come back from the cache instead of from the data source.
|
||||
|
||||
When using Grafana, a query pertains to a request for data frames to be modified or displayed. A resource relates to any HTTP requests made by a plugin, such as the Amazon Timestream plugin requesting a list of available databases from AWS. For more information on data source queries and resources, please see the developers page on [backend plugins]({{< relref "../../developers/plugins/introduction-to-plugin-development/backend/" >}}).
|
||||
When using Grafana, a query pertains to a request for data frames to be modified or displayed. A resource relates to any HTTP requests made by a plugin, such as the Amazon Timestream plugin requesting a list of available databases from AWS. For more information on data source queries and resources, please see the developers page on [backend plugins](/developers/plugin-tools/introduction/backend-plugins).
|
||||
|
||||
The caching feature works for **all** backend data sources. You can enable the cache globally in Grafana's [configuration]({{< relref "../../setup-grafana/configure-grafana/enterprise-configuration/#caching" >}}), and configure a cache duration (also called Time to Live, or TTL) for each data source individually.
|
||||
|
||||
{{% admonition type="note" %}}
|
||||
Available in [Grafana Enterprise]({{< relref "../../introduction/grafana-enterprise/" >}}) and [Grafana Cloud Pro and Advanced](/docs/grafana-cloud/).
|
||||
Available in [Grafana Enterprise]({{< relref "../../introduction/grafana-enterprise/" >}}) and [Grafana Cloud](/docs/grafana-cloud/).
|
||||
{{% /admonition %}}
|
||||
|
||||
The following cache backend options are available: in-memory, Redis, and Memcached.
|
||||
@@ -124,7 +124,7 @@ Query caching works for Grafana's [built-in data sources]({{< relref "../../data
|
||||
To verify that a data source works with query caching, follow the [instructions below](#enable-and-configure-query-caching) to **Enable and Configure query caching**. If caching is enabled in Grafana but the Caching tab is not visible for the given data source, then query caching is not available for that data source.
|
||||
|
||||
{{% admonition type="note" %}}
|
||||
Some data sources, such as Elasticsearch, Prometheus, and Loki, cache queries themselves, so Grafana _query_ caching does not significantly improve performance. However, _resource_ caching may help. See the developers page on [plugin resources]({{< relref "../../developers/plugins/introduction-to-plugin-development/backend/#resources" >}}) for details.
|
||||
Some data sources, such as Elasticsearch, Prometheus, and Loki, cache queries themselves, so Grafana _query_ caching does not significantly improve performance. However, _resource_ caching may help. See the developers page on [plugin resources](/developers/plugin-tools/introduction/backend-plugins) for details.
|
||||
{{% /admonition %}}
|
||||
|
||||
### Enable and configure query caching
|
||||
@@ -195,4 +195,4 @@ For more documentation on a specific data source plugin's features, including it
|
||||
|
||||
### Create a data source plugin
|
||||
|
||||
To build your own data source plugin, refer to the ["Build a data source plugin"](/tutorials/build-a-data-source-plugin/) tutorial and our documentation about [building a plugin](/developers/plugins/).
|
||||
To build your own data source plugin, refer to the ["Build a data source plugin"](/developers/plugin-tools/tutorials/build-a-data-source-plugin) tutorial and our documentation about [building a plugin](/developers/plugin-tools).
|
||||
|
||||
@@ -17,7 +17,7 @@ weight: 600
|
||||
|
||||
Besides the wide range of visualizations and data sources that are available immediately after you install Grafana, you can extend your Grafana experience with _plugins_.
|
||||
|
||||
You can [install]({{< relref "#install-a-plugin" >}}) one of the plugins built by the Grafana community, or [build one yourself]({{< relref "../../developers/plugins/" >}}).
|
||||
You can [install](#install-a-plugin) one of the plugins built by the Grafana community, or [build one yourself](/developers/plugin-tools).
|
||||
|
||||
Grafana supports three types of plugins: [panels](/grafana/plugins?type=panel), [data sources](/plugins?type=datasource), and [apps](/grafana/plugins?type=app).
|
||||
|
||||
@@ -100,6 +100,7 @@ To browse for available plugins:
|
||||
To install a plugin:
|
||||
|
||||
1. In Grafana, click **Administration > Plugins** in the side navigation menu to view installed plugins.
|
||||
1. Click the **All** filter to browse all available plugins.
|
||||
1. Browse and find a plugin.
|
||||
1. Click on the plugin logo.
|
||||
1. Click **Install**.
|
||||
@@ -171,7 +172,7 @@ Grafana also writes an error message to the server log:
|
||||
WARN[05-26|12:00:00] Some plugin scanning errors were found errors="plugin '<plugin id>' is unsigned, plugin '<plugin id>' has an invalid signature"
|
||||
```
|
||||
|
||||
If you are a plugin developer and want to know how to sign your plugin, refer to [Sign a plugin]({{< relref "../../developers/plugins/publish-a-plugin/sign-a-plugin.md" >}}).
|
||||
If you are a plugin developer and want to know how to sign your plugin, refer to [Sign a plugin](/developers/plugin-tools/publish-a-plugin/sign-a-plugin).
|
||||
|
||||
| Signature status | Description |
|
||||
| ------------------ | ------------------------------------------------------------------------------- |
|
||||
|
||||
@@ -62,7 +62,7 @@ Currently we do not provide any scripts/manifests for configuring Grafana. Rathe
|
||||
| Tool | Project |
|
||||
| --------- | -------------------------------------------------------------------------------------------------------------- |
|
||||
| Puppet | [https://forge.puppet.com/puppet/grafana](https://forge.puppet.com/puppet/grafana) |
|
||||
| Ansible | [https://github.com/cloudalchemy/ansible-grafana](https://github.com/cloudalchemy/ansible-grafana) |
|
||||
| Ansible | [https://github.com/grafana/grafana-ansible-collection](https://github.com/grafana/grafana-ansible-collection) |
|
||||
| Chef | [https://github.com/sous-chefs/chef-grafana](https://github.com/sous-chefs/chef-grafana) |
|
||||
| Saltstack | [https://github.com/salt-formulas/salt-formula-grafana](https://github.com/salt-formulas/salt-formula-grafana) |
|
||||
| Jsonnet | [https://github.com/grafana/grafonnet-lib/](https://github.com/grafana/grafonnet-lib/) |
|
||||
@@ -370,8 +370,8 @@ By default, Grafana deletes dashboards in the database if the file is removed. Y
|
||||
|
||||
{{% admonition type="note" %}}
|
||||
Provisioning allows you to overwrite existing dashboards
|
||||
which leads to problems if you re-use settings that are supposed to be unique.
|
||||
Be careful not to re-use the same `title` multiple times within a folder
|
||||
which leads to problems if you reuse settings that are supposed to be unique.
|
||||
Be careful not to reuse the same `title` multiple times within a folder
|
||||
or `uid` within the same installation as this will cause weird behaviors.
|
||||
{{% /admonition %}}
|
||||
|
||||
|
||||
@@ -101,7 +101,7 @@ The following tables list permissions associated with basic and fixed roles.
|
||||
|
||||
### Alerting roles
|
||||
|
||||
If alerting is [enabled]({{< relref "../../../../alerting/set-up/migrating-alerts/opt-out/" >}}), you can use predefined roles to manage user access to alert rules, alert instances, and alert notification settings and create custom roles to limit user access to alert rules in a folder.
|
||||
If alerting is [enabled]({{< relref "../../../../alerting/set-up/migrating-alerts" >}}), you can use predefined roles to manage user access to alert rules, alert instances, and alert notification settings and create custom roles to limit user access to alert rules in a folder.
|
||||
|
||||
Access to Grafana alert rules is an intersection of many permissions:
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ filters = accesscontrol:debug accesscontrol.evaluator:debug dashboard.permission
|
||||
## Enable audit logging
|
||||
|
||||
{{% admonition type="note" %}}
|
||||
Available in [Grafana Enterprise]({{< relref "../../introduction/grafana-enterprise/" >}}) version 7.3 and later, and [Grafana Cloud](/docs/grafana-cloud).
|
||||
Available in [Grafana Enterprise]({{< relref "../../../../introduction/grafana-enterprise/" >}}) version 7.3 and later, and [Grafana Cloud](/docs/grafana-cloud).
|
||||
{{% /admonition %}}
|
||||
|
||||
You can enable auditing in the Grafana configuration file.
|
||||
|
||||
@@ -54,10 +54,12 @@ Before Grafana v8.2, the configuration of the embedded Alertmanager was shared a
|
||||
|
||||
## Add new nested policy
|
||||
|
||||
To create a new notification policy, you need to follow its tree structure. New policies created on the trunk of the tree (default policy), are the tree branches. And, subsequently, each branch can bear their own child policies. This is why you will always be adding a new **nested** policy under either the default policy, or under a already nested policy.
|
||||
|
||||
1. In the left-side menu, click **Alerts & IRM** and then **Alerting**.
|
||||
1. Click **Notification policies**.
|
||||
1. From the **Choose Alertmanager** dropdown, select an Alertmanager. By default, the **Grafana Alertmanager** is selected.
|
||||
1. To add a top level specific policy, go to the Specific routing section and click **+New specific policy**.
|
||||
1. To add a top level specific policy, go to the Specific routing section (either to the default policy, or to another existing policy in which you would like to add a new nested policy) and click **+New nested policy**.
|
||||
1. In the Matching labels section, add one or more rules for matching alert labels.
|
||||
1. In the **Contact point** dropdown, select the contact point to send notification to if alert matches only this specific policy and not any of the nested policies.
|
||||
1. Optionally, enable **Continue matching subsequent sibling nodes** to continue matching sibling policies even after the alert matched the current policy. When this option is enabled, you can get more than one notification for one alert.
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
---
|
||||
aliases:
|
||||
- ../../contact-points/create-contact-point/
|
||||
- ../../contact-points/delete-contact-point/
|
||||
- ../../contact-points/edit-contact-point/
|
||||
- ../../contact-points/test-contact-point/
|
||||
- ../create-contact-point/
|
||||
- alerting/manage-notifications/manage-contact-points/
|
||||
- ../contact-points/ # /docs/grafana/<GRAFANA_VERSION>/alerting/contact-points/
|
||||
- ../contact-points/create-contact-point/ # /docs/grafana/<GRAFANA_VERSION>/alerting/contact-points/create-contact-point/
|
||||
- ../contact-points/delete-contact-point/ # /docs/grafana/<GRAFANA_VERSION>/alerting/contact-points/delete-contact-point/
|
||||
- ../contact-points/edit-contact-point/ # /docs/grafana/<GRAFANA_VERSION>/alerting/contact-points/edit-contact-point/
|
||||
- ../contact-points/test-contact-point/ # /docs/grafana/<GRAFANA_VERSION>/alerting/contact-points/test-contact-point/
|
||||
- ../manage-notifications/manage-contact-points/ # /docs/grafana/<GRAFANA_VERSION>/alerting/manage-notifications/manage-contact-points/
|
||||
- create-contact-point/ # /docs/grafana/<GRAFANA_VERSION>/alerting/alerting-rules/create-contact-point/
|
||||
canonical: https://grafana.com/docs/grafana/latest/alerting/alerting-rules/manage-contact-points/
|
||||
description: How to manage your contact points
|
||||
keywords:
|
||||
|
||||
@@ -59,11 +59,11 @@ Alert summary:
|
||||
|
||||
You can use any of the following built-in template options to embed custom templates.
|
||||
|
||||
| Name | Notes |
|
||||
| ----------------------- | ------------------------------------------------------------- |
|
||||
| `default.title` | Displays high-level status information. |
|
||||
| `default.message` | Provides a formatted summary of firing and resolved alerts. |
|
||||
| `teams.default.message` | Similar to `default.messsage`, formatted for Microsoft Teams. |
|
||||
| Name | Notes |
|
||||
| ----------------------- | ------------------------------------------------------------ |
|
||||
| `default.title` | Displays high-level status information. |
|
||||
| `default.message` | Provides a formatted summary of firing and resolved alerts. |
|
||||
| `teams.default.message` | Similar to `default.message`, formatted for Microsoft Teams. |
|
||||
|
||||
### HTML in notification templates
|
||||
|
||||
|
||||
@@ -97,6 +97,27 @@ Checks if any time series data matches the alert condition.
|
||||
Classic condition expression queries always produce one alert instance only, no matter how many time series meet the condition.
|
||||
Classic conditions exist mainly for compatibility reasons and should be avoided if possible.
|
||||
|
||||
## Aggregations
|
||||
|
||||
Grafana Alerting provides the following aggregation functions to enable you to further refine your query.
|
||||
|
||||
These functions are available for **Reduce** and **Classic condition** expressions only.
|
||||
|
||||
| Function | Expression | What it does |
|
||||
| ---------------- | ---------------- | ------------------------------------------------------------------------------- |
|
||||
| avg | Reduce / Classic | Displays the average of the values |
|
||||
| min | Reduce / Classic | Displays the lowest value |
|
||||
| max | Reduce / Classic | Displays the highest value |
|
||||
| sum | Reduce / Classic | Displays the sum of all values |
|
||||
| count | Reduce / Classic | Counts the number of values in the result |
|
||||
| last | Reduce / Classic | Displays the last value |
|
||||
| median | Reduce / Classic | Displays the median value |
|
||||
| diff | Classic | Displays the difference between the newest and oldest value |
|
||||
| diff_abs | Classic | Displays the absolute value of diff |
|
||||
| percent_diff | Classic | Displays the percentage value of the difference between newest and oldest value |
|
||||
| percent_diff_abs | Classic | Displays the absolute value of percent_diff |
|
||||
| count_non_null | Classic | Displays a count of values in the result set that aren't `null` |
|
||||
|
||||
## Alert condition
|
||||
|
||||
An alert condition is the query or expression that determines whether the alert will fire or not depending on the value it yields. There can be only one condition which will determine the triggering of the alert.
|
||||
|
||||
@@ -22,9 +22,11 @@ To do this, you need to make sure that your alert rule is in the right evaluatio
|
||||
|
||||
## Evaluation group
|
||||
|
||||
Every alert rule is part of an evaluation group. Each evaluation group contains an evaluation interval that determines how frequently the alert rule is checked. Alert rules within the same group are evaluated one after the other, while alert rules in different groups can be evaluated simultaneously.
|
||||
Every alert rule is part of an evaluation group. Each evaluation group contains an evaluation interval that determines how frequently the alert rule is checked.
|
||||
|
||||
This feature is especially useful for Prometheus/Mimir rules when you want to ensure that recording rules are evaluated before any alert rules.
|
||||
**Data-source managed** alert rules within the same group are evaluated one after the other, while alert rules in different groups can be evaluated simultaneously. This feature is especially useful when you want to ensure that recording rules are evaluated before any alert rules.
|
||||
|
||||
**Grafana-managed** alert rules are evaluated at the same time, regardless of alert rule group. The default evaluation interval is set at 10 seconds, which means that Grafana-managed alert rules are evaluated every 10 seconds to the closest 10-second window on the clock, for example, 10:00:00, 10:00:10, 10:00:20, and so on. You can also configure your own evaluation interval, if required.
|
||||
|
||||
**Note:**
|
||||
|
||||
@@ -45,7 +47,7 @@ Evaluation will occur as follows:
|
||||
[00:30] First evaluation - condition not met.
|
||||
|
||||
[01:00] Second evaluation - condition breached.
|
||||
Pending counter starts. **Alert stars pending.**
|
||||
Pending counter starts. **Alert starts pending.**
|
||||
|
||||
[01:30] Third evaluation - condition breached. Pending counter = 30s. **Pending state.**
|
||||
|
||||
|
||||
@@ -50,3 +50,15 @@ then:
|
||||
- A label matcher defined as `id=~[0-9]+` matches this alert rule.
|
||||
- A label matcher defined as `baz!~[0-9]+` matches this alert rule.
|
||||
- Two label matchers defined as `foo=bar` and `id=~[0-9]+` match this alert rule.
|
||||
|
||||
## Exclude labels
|
||||
|
||||
You can also write label matchers to exclude labels.
|
||||
|
||||
Here is an example that shows how to exclude the label `Team`. You can choose between any of the values below to exclude labels.
|
||||
|
||||
| Label | Operator | Value |
|
||||
| ------ | -------- | ----- |
|
||||
| `team` | `=` | `""` |
|
||||
| `team` | `!~` | `.+` |
|
||||
| `team` | `=~` | `^$` |
|
||||
|
||||
@@ -103,15 +103,35 @@ If you were to print the value of the expression with RefID `B` in the summary o
|
||||
The summary will contain just the value:
|
||||
|
||||
```
|
||||
api has an over 5% of responses with 5xx errors: 6.789%
|
||||
api has over 5% of responses with 5xx errors: 6.78912%
|
||||
```
|
||||
|
||||
However, while `{{ $values.B }}` prints the number 6.789, it is actually a string as you are printing the object that contains both the labels and value for RefID B, not the floating point value of B. To use the floating point value of RefID B you must use the `Value` field from `$values.B`. If you were to humanize the floating point value in the summary of an alert:
|
||||
However, while `{{ $values.B }}` prints the number 6.78912, it is actually a string as you are printing the object that contains both the labels and value for RefID B, not the floating point value of B. To use the floating point value of RefID B you must use the `Value` field from `$values.B`.
|
||||
|
||||
If you were to print the humanized floating point value in the summary of an alert:
|
||||
|
||||
```
|
||||
{{ $labels.service }} has over 5% of responses with 5xx errors: {{ humanize $values.B.Value }}%
|
||||
```
|
||||
|
||||
The summary will contain the humanized value:
|
||||
|
||||
```
|
||||
api has over 5% of responses with 5xx errors: 6.789%
|
||||
```
|
||||
|
||||
You can also compare the floating point value using the `eq`, `ne`, `lt`, `le`, `gt` and `ge` comparison operators:
|
||||
|
||||
```
|
||||
{{ if gt $values.B.Value 50.0 -}}
|
||||
Critical 5xx error rate
|
||||
{{ else -}}
|
||||
Elevated 5xx error rate
|
||||
{{ end }}
|
||||
```
|
||||
|
||||
When using comparison operators with `$values` make sure to compare it to a floating point number such as `50.0` and not an integer such as `50`. Go templates do not support implicit type coercion, and comparing a floating point number to an integer will break your template.
|
||||
|
||||
### No data, execution errors and timeouts
|
||||
|
||||
If the query in your alert rule returns no data, or fails because of a datasource error or timeout, then any Threshold, Reduce or Math expressions that use that query will also return no data or an error. When this happens these expression will be absent from `$values`. It is good practice to check that a RefID is present before using it as otherwise your template will break should your query return no data or an error. You can do this using an if statement:
|
||||
|
||||
@@ -6,11 +6,11 @@ labels:
|
||||
- cloud
|
||||
- enterprise
|
||||
- oss
|
||||
title: Data sources
|
||||
title: Data sources and Grafana Alerting
|
||||
weight: 100
|
||||
---
|
||||
|
||||
# Data sources
|
||||
# Data sources and Grafana Alerting
|
||||
|
||||
There are a number of data sources that are compatible with Grafana Alerting. Each data source is supported by a plugin. You can use one of the built-in data sources listed below, use [external data source plugins](/grafana/plugins/?type=datasource), or create your own data source plugin.
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ Grafana managed alerts query the following backend data sources that have alerti
|
||||
|
||||
- built-in data sources or those developed and maintained by Grafana: `Graphite`, `Prometheus`, `Loki`, `InfluxDB`, `Elasticsearch`,
|
||||
`Google Cloud Monitoring`, `Cloudwatch`, `Azure Monitor`, `MySQL`, `PostgreSQL`, `MSSQL`, `OpenTSDB`, `Oracle`, and `Azure Monitor`
|
||||
- community developed backend data sources with alerting enabled (`backend` and `alerting` properties are set in the [plugin.json]({{< relref "../../developers/plugins/metadata" >}}))
|
||||
- community developed backend data sources with alerting enabled (`backend` and `alerting` properties are set in the [plugin.json](/developers/plugin-tools/reference-plugin-json))
|
||||
|
||||
### Metrics from the alerting engine
|
||||
|
||||
@@ -106,3 +106,8 @@ When this query is used as the **condition** in an alert rule, then the non-zero
|
||||
| {Host=web1,disk=/etc} | Alerting |
|
||||
| {Host=web2,disk=/var} | Alerting |
|
||||
| {Host=web3,disk=/var} | Normal |
|
||||
|
||||
{{% docs/reference %}}
|
||||
[set-up-grafana-monitoring]: "/docs/grafana/ -> /docs/grafana/<GRAFANA VERSION>/setup-grafana/set-up-grafana-monitoring"
|
||||
[set-up-grafana-monitoring]: "/docs/grafana-cloud/ -> /docs/grafana/<GRAFANA VERSION>/setup-grafana/set-up-grafana-monitoring"
|
||||
{{% /docs/reference %}}
|
||||
|
||||
@@ -71,4 +71,4 @@ To remove a silence, complete the following steps.
|
||||
|
||||
## Useful links
|
||||
|
||||
[Aggregation operators](/docs/prometheus/latest/querying/operators/#aggregation-operators)
|
||||
[Aggregation operators](https://prometheus.io/docs/prometheus/latest/querying/operators/#aggregation-operators)
|
||||
|
||||
@@ -25,15 +25,17 @@ Notification templates are not tied to specific contact point integrations, such
|
||||
|
||||
You can use notification templates to:
|
||||
|
||||
- Add, remove, or re-order information in the notification including the summary, description, labels and annotations, values, and links
|
||||
- Format text in bold and italic, and add or remove line breaks
|
||||
- Customize the subject of an email or the title of a message.
|
||||
- Add, change or remove text in notifications. For example, to select or omit certain labels, annotations and links.
|
||||
- Format text in bold and italic, and add or remove line breaks.
|
||||
|
||||
You cannot use notification templates to:
|
||||
|
||||
- Change how images are included in notifications, such as the number of images in each notification or where in the notification inline images are shown
|
||||
- Change the design of notifications in instant messaging services such as Slack and Microsoft Teams
|
||||
- Change the data in webhook notifications, including the structure of the JSON request or sending data in other formats such as XML
|
||||
- Add or remove HTTP headers in webhook notifications other than those in the contact point configuration
|
||||
- Add HTML and CSS to email notifications to change their visual appearance.
|
||||
- Change the design of notifications in instant messaging services such as Slack and Microsoft Teams. For example, to add or remove custom blocks with Slack Block Kit or adaptive cards with Microsoft Teams.
|
||||
- Choose the number and size of images, or where in the notification images are shown.
|
||||
- Customize the data in webhooks, including the fields or structure of the JSON data or send the data in other formats such as XML.
|
||||
- Add or remove HTTP headers in webhooks other than those in the contact point configuration.
|
||||
|
||||
[Using Go's templating language]({{< relref "./using-go-templating-language" >}})
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ weight: 400
|
||||
| GeneratorURL | `string` | A link to Grafana, or the Alertmanager if using an external Alertmanager | `{{ .GeneratorURL }}` |
|
||||
| SilenceURL | `string` | A link to silence the alert | `{{ .SilenceURL }}` |
|
||||
| DashboardURL | `string` | A link to the Grafana Dashboard if the alert has a Dashboard UID annotation | `{{ .DashboardURL }}` |
|
||||
| PanelURL | `string` | A link to the panel if the alert has a Panel ID annotation | `{{ .PanelID }}` |
|
||||
| PanelURL | `string` | A link to the panel if the alert has a Panel ID annotation | `{{ .PanelURL }}` |
|
||||
| Fingerprint | `string` | A unique string that identifies the alert | `{{ .Fingerprint }}` |
|
||||
| ValueString | `string` | A string that contains the labels and value of each reduced expression in the alert. | `{{ .ValueString }}` |
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ keywords:
|
||||
- alert state history
|
||||
labels:
|
||||
products:
|
||||
- cloud
|
||||
- oss
|
||||
title: Configure Alert State History
|
||||
weight: 600
|
||||
---
|
||||
|
||||
@@ -33,7 +33,7 @@ Since gossiping of notifications and silences uses both TCP and UDP port `9094`,
|
||||
|
||||
1. In your custom configuration file ($WORKING_DIR/conf/custom.ini), go to the `[unified_alerting]` section.
|
||||
2. Set `[ha_peers]` to the number of hosts for each Grafana instance in the cluster (using a format of host:port), for example, `ha_peers=10.0.0.5:9094,10.0.0.6:9094,10.0.0.7:9094`.
|
||||
You must have at least one (1) Grafana instance added to the [`[ha_peer]` section.
|
||||
You must have at least one (1) Grafana instance added to the `ha_peers` section.
|
||||
3. Set `[ha_listen_address]` to the instance IP address using a format of `host:port` (or the [Pod's](https://kubernetes.io/docs/concepts/workloads/pods/) IP in the case of using Kubernetes).
|
||||
By default, it is set to listen to all interfaces (`0.0.0.0`).
|
||||
4. Set `[ha_peer_timeout]` in the `[unified_alerting]` section of the custom.ini to specify the time to wait for an instance to send a notification via the Alertmanager. The default value is 15s, but it may increase if Grafana servers are located in different geographic regions or if the network latency between them is high
|
||||
|
||||
@@ -4,17 +4,17 @@ aliases:
|
||||
- /docs/grafana-cloud/how-do-i/grafana-cloud-alerting/alertmanager/
|
||||
- /docs/grafana-cloud/legacy-alerting/grafana-cloud-alerting/alertmanager/
|
||||
canonical: https://grafana.com/docs/grafana/latest/alerting/set-up/migrating-alerts/legacy-alerting/grafana-cloud-alerting/alertmanager/
|
||||
description: Alertmanager
|
||||
description: Alertmanager (legacy)
|
||||
labels:
|
||||
products:
|
||||
- cloud
|
||||
- enterprise
|
||||
- oss
|
||||
title: Alertmanager
|
||||
title: Alertmanager (legacy)
|
||||
weight: 500
|
||||
---
|
||||
|
||||
# Alertmanager
|
||||
# Alertmanager (legacy)
|
||||
|
||||
Grafana Cloud Alerting allows you to edit and view configuration for your Alertmanager directly inside of Grafana. See the official [Alertmanager documentation](https://prometheus.io/docs/alerting/latest/configuration/) to learn how to configure.
|
||||
|
||||
|
||||
@@ -72,7 +72,7 @@ groups:
|
||||
# <string, required> which query should be used for the condition
|
||||
condition: A
|
||||
# <list, required> list of query objects that should be executed on each
|
||||
# evaluation - should be obtained trough the API
|
||||
# evaluation - should be obtained through the API
|
||||
data:
|
||||
- refId: A
|
||||
datasourceUid: '__expr__'
|
||||
@@ -589,7 +589,7 @@ apiVersion: 1
|
||||
# List of templates to import or update
|
||||
templates:
|
||||
# <int> organization ID, default = 1
|
||||
- orgID: 1
|
||||
- orgId: 1
|
||||
# <string, required> name of the template, must be unique
|
||||
name: my_first_template
|
||||
# <string, required> content of the the template
|
||||
|
||||
@@ -115,7 +115,7 @@ You cannot edit resources provisioned via Terraform from the UI. This ensures th
|
||||
|
||||
**Note:**
|
||||
|
||||
You can re-use the same templates across many contact points. In the example above, a shared template ie embedded using the statement `{{ template “Alert Instance Template” . }}`
|
||||
You can reuse the same templates across many contact points. In the example above, a shared template ie embedded using the statement `{{ template “Alert Instance Template” . }}`
|
||||
|
||||
This fragment can then be managed separately in Terraform:
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ weight: 200
|
||||
Usage insights enables you to have a better understanding of how your Grafana instance is used.
|
||||
|
||||
{{% admonition type="note" %}}
|
||||
Available in [Grafana Enterprise]({{< relref "../../introduction/grafana-enterprise/" >}}) and [Grafana Cloud Pro and Advanced](/docs/grafana-cloud/). Grafana Cloud insights logs include additional fields with their own dashboards. Read more in the [Grafana Cloud documentation](/docs/grafana-cloud/usage-insights/).
|
||||
Available in [Grafana Enterprise]({{< relref "../../introduction/grafana-enterprise/" >}}) and [Grafana Cloud](/docs/grafana-cloud/). Grafana Cloud insights logs include additional fields with their own dashboards. Read more in the [Grafana Cloud documentation](/docs/grafana-cloud/usage-insights/).
|
||||
{{% /admonition %}}
|
||||
|
||||
The usage insights feature collects a number of aggregated data and stores them in the database:
|
||||
@@ -95,10 +95,23 @@ To change _recent_ to something other than the past 10 minutes, edit the [config
|
||||
|
||||
```ini
|
||||
[analytics.views]
|
||||
# Set age for recent active users
|
||||
|
||||
# Set age for recent active users to 10 minutes
|
||||
recent_users_age = 10m
|
||||
```
|
||||
|
||||
To disable the presence indicator, edit the [configuration][] file as follows:
|
||||
|
||||
```ini
|
||||
[analytics.views]
|
||||
|
||||
|
||||
# Disables the presence indicator
|
||||
recent_users_age = 0
|
||||
```
|
||||
|
||||
The dashboard won't show any avatars and thus no recent user activity.
|
||||
|
||||
## Sort dashboards by using insights data
|
||||
|
||||
In the search view, you can use insights data to help you find most-used, broken, and unused dashboards.
|
||||
|
||||
@@ -116,6 +116,8 @@ To add a new annotation query to a dashboard, take the following steps:
|
||||
|
||||
After you add an annotation, they will still be visible. This is due to the built-in annotation query that exists on all dashboards. This annotation query will fetch all annotation events that originate from the current dashboard, which are stored in Grafana, and show them on the panel where they were created. This includes alert state history annotations.
|
||||
|
||||
By default, the built-in annotation query uses the `-- Grafana --` special data source, and manual annotations are only supported using this data source. You can use another data source in the built-in annotation query, but you'll only be able to create automated annotations using the query editor for that data source.
|
||||
|
||||
To add annotations directly to the dashboard, this query must be enabled.
|
||||
|
||||
To confirm if the built-in query is enabled, take the following steps:
|
||||
@@ -133,7 +135,7 @@ You can stop annotations from being fetched and drawn by taking the following st
|
||||
1. Find and click the **Annotations & Alerts (Built-in)** query to open it.
|
||||
1. Click the **Enabled** toggle to turn it off.
|
||||
|
||||
When you copy a dashboard using the **Save As** feature it will get a new dashboard id, so annotations created on source dashboard will no longer be visible on the copy. You can still show them if you add a new **Annotation Query** and filter by tags. However, this only works if the annotations on the source dashboard had tags to filter by.
|
||||
When you copy a dashboard using the **Save As** feature it will get a new dashboard id, so annotations created on the source dashboard will no longer be visible on the copy. You can still show them if you add a new **Annotation Query** and filter by tags. However, this only works if the annotations on the source dashboard had tags to filter by.
|
||||
|
||||
Following are some query options specific to the built-in annotation query.
|
||||
|
||||
|
||||
@@ -243,6 +243,6 @@ Usage of the above mentioned fields in the templating section is explained below
|
||||
| **name** | name of variable |
|
||||
| **options** | array of variable text/value pairs available for selection on dashboard |
|
||||
| **query** | data source query used to fetch values for a variable |
|
||||
| **refresh** | |
|
||||
| **regex** | |
|
||||
| **refresh** | configures when to refresh a variable |
|
||||
| **regex** | extracts part of a series name or metric node segment |
|
||||
| **type** | type of variable, i.e. `custom`, `query` or `interval` |
|
||||
|
||||
@@ -21,7 +21,7 @@ weight: 85
|
||||
|
||||
# Create and manage reports
|
||||
|
||||
Reporting enables you to automatically generate PDFs from any of your dashboards and have Grafana email them to interested parties on a schedule. This is available in Grafana Cloud Pro and Advanced and in Grafana Enterprise.
|
||||
Reporting enables you to automatically generate PDFs from any of your dashboards and have Grafana email them to interested parties on a schedule. This is available in Grafana Cloud and in Grafana Enterprise.
|
||||
|
||||
> If you have [Role-based access control]({{< relref "../../administration/roles-and-permissions/access-control/" >}}) enabled, for some actions you would need to have relevant permissions.
|
||||
> Refer to specific guides to understand what permissions are required.
|
||||
@@ -76,7 +76,7 @@ Only organization administrators can create reports by default. You can customiz
|
||||
### Save as draft
|
||||
|
||||
{{% admonition type="note" %}}
|
||||
Available in [Grafana Enterprise]({{< relref "../../introduction/grafana-enterprise/" >}}) version 9.1.0 and later and [Grafana Cloud Pro and Advanced](/docs/grafana-cloud/).
|
||||
Available in [Grafana Enterprise]({{< relref "../../introduction/grafana-enterprise/" >}}) version 9.1.0 and later and [Grafana Cloud](/docs/grafana-cloud/).
|
||||
{{% /admonition %}}
|
||||
|
||||
You can save a report as a draft at any point during the report creation or update process. You can save a report as a draft even if it's missing required fields. Also, the report won't be sent according to its schedule while it's a draft.
|
||||
@@ -84,7 +84,7 @@ You can save a report as a draft at any point during the report creation or upda
|
||||
### Choose template variables
|
||||
|
||||
{{% admonition type="note" %}}
|
||||
Available in [Grafana Enterprise]({{< relref "../../introduction/grafana-enterprise/" >}}) version 7.5 and later behind the `reportVariables` feature flag, Grafana Enterprise version 8.0 and later without a feature flag, and [Grafana Cloud Pro and Advanced](/docs/grafana-cloud/).
|
||||
Available in [Grafana Enterprise]({{< relref "../../introduction/grafana-enterprise/" >}}) version 7.5 and later behind the `reportVariables` feature flag, Grafana Enterprise version 8.0 and later without a feature flag, and [Grafana Cloud](/docs/grafana-cloud/).
|
||||
{{% /admonition %}}
|
||||
|
||||
You can configure report-specific template variables for the dashboard on the report page. The variables that you select will override the variables from the dashboard, and they are used when rendering a PDF file of the report. For detailed information about using template variables, refer to the [Templates and variables]({{< relref "../variables/" >}}) section.
|
||||
@@ -96,7 +96,7 @@ The query variables saved with a report might become of date if the results of t
|
||||
### Render a report with panels or rows set to repeat by a variable
|
||||
|
||||
{{% admonition type="note" %}}
|
||||
Available in [Grafana Enterprise]({{< relref "../../introduction/grafana-enterprise/" >}}) version 8.0 and later, and [Grafana Cloud Pro and Advanced](/docs/grafana-cloud/).
|
||||
Available in [Grafana Enterprise]({{< relref "../../introduction/grafana-enterprise/" >}}) version 8.0 and later, and [Grafana Cloud](/docs/grafana-cloud/).
|
||||
{{% /admonition %}}
|
||||
|
||||
You can include dynamic dashboards with panels or rows, set to repeat by a variable, into reports. For detailed information about setting up repeating panels or rows in dashboards, refer to [Repeat panels or rows]({{< relref "../../panels-visualizations/configure-panel-options/#configure-repeating-rows-or-panels" >}}).
|
||||
@@ -113,7 +113,7 @@ You can include dynamic dashboards with panels or rows, set to repeat by a varia
|
||||
### Report time range
|
||||
|
||||
{{% admonition type="note" %}}
|
||||
You can set custom report time ranges in [Grafana Enterprise]({{< relref "../../introduction/grafana-enterprise/" >}}) 7.2+ and [Grafana Cloud Pro and Advanced](/docs/grafana-cloud/).
|
||||
You can set custom report time ranges in [Grafana Enterprise]({{< relref "../../introduction/grafana-enterprise/" >}}) 7.2+ and [Grafana Cloud](/docs/grafana-cloud/).
|
||||
{{% /admonition %}}
|
||||
|
||||
By default, reports use the saved time range of the dashboard. You can change the time range of the report by:
|
||||
@@ -139,7 +139,7 @@ If the time zone is set differently between your Grafana server and its remote i
|
||||
### CSV export
|
||||
|
||||
{{% admonition type="note" %}}
|
||||
Available in [Grafana Enterprise]({{< relref "../../introduction/grafana-enterprise/" >}}) 8+ with the [Grafana image renderer plugin](/grafana/plugins/grafana-image-renderer) v3.0+, and [Grafana Cloud Pro and Advanced](/docs/grafana-cloud/).
|
||||
Available in [Grafana Enterprise]({{< relref "../../introduction/grafana-enterprise/" >}}) 8+ with the [Grafana image renderer plugin](/grafana/plugins/grafana-image-renderer) v3.0+, and [Grafana Cloud](/docs/grafana-cloud/).
|
||||
{{% /admonition %}}
|
||||
|
||||
You can attach a CSV file to the report email for each table panel on the selected dashboard, along with the PDF report. By default, CSVs larger than 10Mb are not sent which keeps email servers from rejecting the email. You can increase or decrease this limit in the [reporting configuration]({{< relref "#rendering-configuration" >}}).
|
||||
@@ -153,7 +153,7 @@ A background job runs every 10 minutes and removes temporary CSV files. You can
|
||||
### Scheduling
|
||||
|
||||
{{% admonition type="note" %}}
|
||||
Available in [Grafana Enterprise]({{< relref "../../introduction/grafana-enterprise/" >}}) version 8.0 and later, and [Grafana Cloud Pro and Advanced](/docs/grafana-cloud/).
|
||||
Available in [Grafana Enterprise]({{< relref "../../introduction/grafana-enterprise/" >}}) version 8.0 and later, and [Grafana Cloud](/docs/grafana-cloud/).
|
||||
The scheduler was significantly changed in Grafana Enterprise version 8.1.
|
||||
{{% /admonition %}}
|
||||
|
||||
@@ -176,7 +176,7 @@ When you schedule a report with a monthly frequency, and set the start date betw
|
||||
#### Send a test email
|
||||
|
||||
{{% admonition type="note" %}}
|
||||
Available in [Grafana Enterprise]({{< relref "../../introduction/grafana-enterprise/" >}}) version 7.0 and later, and [Grafana Cloud Pro and Advanced](/docs/grafana-cloud/).
|
||||
Available in [Grafana Enterprise]({{< relref "../../introduction/grafana-enterprise/" >}}) version 7.0 and later, and [Grafana Cloud](/docs/grafana-cloud/).
|
||||
{{% /admonition %}}
|
||||
|
||||
1. In the report, click **Send test email**.
|
||||
@@ -189,7 +189,7 @@ The last saved version of the report will be sent to selected emails. You can us
|
||||
### Pause a report
|
||||
|
||||
{{% admonition type="note" %}}
|
||||
Available in [Grafana Enterprise]({{< relref "../../introduction/grafana-enterprise/" >}}) version 8.0 and later, and [Grafana Cloud Pro and Advanced](/docs/grafana-cloud/).
|
||||
Available in [Grafana Enterprise]({{< relref "../../introduction/grafana-enterprise/" >}}) version 8.0 and later, and [Grafana Cloud](/docs/grafana-cloud/).
|
||||
{{% /admonition %}}
|
||||
|
||||
You can pause sending reports from the report list view by clicking the pause icon. The report will not be sent according to its schedule until it is resumed by clicking the resume button on the report row.
|
||||
@@ -197,7 +197,7 @@ You can pause sending reports from the report list view by clicking the pause ic
|
||||
### Add multiple dashboards to a report
|
||||
|
||||
{{% admonition type="note" %}}
|
||||
Available in [Grafana Enterprise]({{< relref "../../introduction/grafana-enterprise/" >}}) version 9.0 and later, and [Grafana Cloud Pro and Advanced](/docs/grafana-cloud/).
|
||||
Available in [Grafana Enterprise]({{< relref "../../introduction/grafana-enterprise/" >}}) version 9.0 and later, and [Grafana Cloud](/docs/grafana-cloud/).
|
||||
{{% /admonition %}}
|
||||
|
||||
You can add more than one dashboard to a report. Additional dashboards will be rendered as new pages in the same PDF file, or additional images if you chose to embed images in your report email. You cannot add the same dashboard to a report multiple times.
|
||||
@@ -205,7 +205,7 @@ You can add more than one dashboard to a report. Additional dashboards will be r
|
||||
### Embed a dashboard as an image into a report
|
||||
|
||||
{{% admonition type="note" %}}
|
||||
Available in [Grafana Enterprise]({{< relref "../../introduction/grafana-enterprise/" >}}) version 9.0 and later, and [Grafana Cloud Pro and Advanced](/docs/grafana-cloud/).
|
||||
Available in [Grafana Enterprise]({{< relref "../../introduction/grafana-enterprise/" >}}) version 9.0 and later, and [Grafana Cloud](/docs/grafana-cloud/).
|
||||
{{% /admonition %}}
|
||||
|
||||
You can send a report email with an image of the dashboard embedded in the email instead of attached as a PDF. In this case, the email recipients can see the dashboard at a glance instead of having to open the PDF.
|
||||
@@ -215,7 +215,7 @@ You can send a report email with an image of the dashboard embedded in the email
|
||||
You can generate and save PDF files of any dashboard.
|
||||
|
||||
{{% admonition type="note" %}}
|
||||
Available in [Grafana Enterprise]({{< relref "../../introduction/grafana-enterprise/" >}}) version 6.7 and later, and [Grafana Cloud Pro and Advanced](/docs/grafana-cloud/).
|
||||
Available in [Grafana Enterprise]({{< relref "../../introduction/grafana-enterprise/" >}}) version 6.7 and later, and [Grafana Cloud](/docs/grafana-cloud/).
|
||||
{{% /admonition %}}
|
||||
|
||||
1. In the dashboard that you want to export as PDF, click the **Share dashboard** icon.
|
||||
@@ -263,7 +263,7 @@ font_italic = DejaVuSansCondensed-Oblique.ttf
|
||||
|
||||
## Report settings
|
||||
|
||||
> **Note:** Available in [Grafana Enterprise]({{< relref "../../introduction/grafana-enterprise/" >}}) version 7.2 and later, and [Grafana Cloud Pro and Advanced](/docs/grafana-cloud/).
|
||||
> **Note:** Available in [Grafana Enterprise]({{< relref "../../introduction/grafana-enterprise/" >}}) version 7.2 and later, and [Grafana Cloud](/docs/grafana-cloud/).
|
||||
|
||||
You can configure organization-wide report settings in the **Settings** under **Dashboards > Reporting**. Settings are applied to all the reports for current organization.
|
||||
|
||||
|
||||
@@ -54,7 +54,7 @@ If you are using Docker, use an environment variable to enable public dashboards
|
||||
|
||||
{{% admonition type="note" %}}
|
||||
|
||||
For Grafana Cloud (Pro and Advanced only), contact support to have the feature enabled.
|
||||
For Grafana Cloud, contact support to have the feature enabled.
|
||||
|
||||
{{% /admonition %}}
|
||||
|
||||
@@ -88,7 +88,7 @@ The link no longer works. You must create a new public URL, as in [Make a dashbo
|
||||
|
||||
{{% admonition type="note" %}}
|
||||
|
||||
Available in [private preview](/docs/release-life-cycle/) in [Grafana Cloud Pro and Advanced](/docs/grafana-cloud). This feature will have a cost by active users after being promoted into general availability.
|
||||
Available in [private preview](/docs/release-life-cycle/) in [Grafana Cloud](/docs/grafana-cloud). This feature will have a cost by active users after being promoted into general availability.
|
||||
|
||||
Please contact support to have the feature enabled.
|
||||
|
||||
@@ -150,7 +150,7 @@ If a Grafana user has read access to the parent dashboard, they can view the pub
|
||||
## Assess public dashboard usage
|
||||
|
||||
{{% admonition type="note" %}}
|
||||
Available in [Grafana Enterprise]({{< relref "../../introduction/grafana-enterprise/" >}}) and [Grafana Cloud Pro and Advanced](/docs/grafana-cloud).
|
||||
Available in [Grafana Enterprise]({{< relref "../../introduction/grafana-enterprise/" >}}) and [Grafana Cloud](/docs/grafana-cloud).
|
||||
{{% /admonition %}}
|
||||
|
||||
You can check usage analytics about your public dashboard by clicking the insights icon in the dashboard header:
|
||||
|
||||
@@ -58,6 +58,8 @@ Folders help you organize and group dashboards, which is useful when you have ma
|
||||
1. On the Dashboards page, click **New** and select **New folder** in the dropdown.
|
||||
1. Enter a unique name and click **Create**.
|
||||
|
||||
Do not use the name of a folder that has already been provisioned (such as "General") and avoid special characters (except underscores and hyphens).
|
||||
|
||||
When you save a dashboard, you can either select a folder for the dashboard to be saved in or create a new folder.
|
||||
|
||||
## Manage dashboards
|
||||
|
||||
@@ -66,7 +66,7 @@ The following table lists the types of variables shipped with Grafana.
|
||||
You must enter general options for any type of variable that you create.
|
||||
|
||||
1. Navigate to the dashboard you want to make a variable for and click the **Dashboard settings** (gear) icon at the top of the page.
|
||||
1. On the **Variables** tab, click **New**.
|
||||
1. On the **Variables** tab, click **New variable**.
|
||||
1. Enter a **Name** for the variable.
|
||||
1. In the **Type** list, select **Query**.
|
||||
1. (Optional) In **Label**, enter the display name of the variable dropdown.
|
||||
@@ -94,9 +94,10 @@ Query expressions are different for each data source. For more information, refe
|
||||
1. In the **Data source** list, select the target data source for the query. For more information about data sources, refer to [Add a data source]({{< relref "../../../administration/data-source-management#add-a-data-source" >}}).
|
||||
1. In the **Refresh** list, select when the variable should update options.
|
||||
- **On Dashboard Load:** Queries the data source every time the dashboard loads. This slows down dashboard loading, because the variable query needs to be completed before dashboard can be initialized.
|
||||
- **On Time Range Change:** Queries the data source when the dashboard time range changes. Only use this option if your variable options query contains a time range filter or is dependent on the dashboard time range.
|
||||
- **On Time Range Change:** Queries the data source every time the dashboard loads and when the dashboard time range changes. Use this option if your variable options query contains a time range filter or is dependent on the dashboard time range.
|
||||
1. In the **Query** field, enter a query.
|
||||
- The query field varies according to your data source. Some data sources have custom query editors.
|
||||
- Make sure that the query returns values named `__text` and `__value` as appropriate in your query syntax. For example, in SQL, you can use a query such as `SELECT hostname AS __text, id AS __value FROM MyTable`. Queries for other languages will vary depending on syntax.
|
||||
- If you need more room in a single input field query editor, then hover your cursor over the lines in the lower right corner of the field and drag downward to expand.
|
||||
1. (Optional) In the **Regex** field, type a regex expression to filter or capture specific parts of the names returned by your data source query. To see examples, refer to [Filter variables with regex]({{< relref "#filter-variables-with-regex" >}}).
|
||||
1. In the **Sort** list, select the sort order for values to be displayed in the dropdown list. The default option, **Disabled**, means that the order of options returned by your data source query will be used.
|
||||
@@ -343,7 +344,7 @@ Extremely complex linked templated dashboards are possible, 5 or 10 levels deep.
|
||||
The following Grafana Play dashboards contain fairly simple chained variables, only two layers deep. To view the variables and their settings, click **Dashboard settings** (gear icon) and then click **Variables**. Both examples are expanded in the following section.
|
||||
|
||||
- [Graphite Templated Nested](https://play.grafana.org/d/000000056/graphite-templated-nested?orgId=1&var-app=country&var-server=All&var-interval=1h)
|
||||
- [InfluxDB Templated](https://play.grafana.org/d/000000002/influxdb-templated?orgId=1)
|
||||
- [InfluxDB Templated](https://play.grafana.org/d/e7bad3ef-db0c-4bbd-8245-b85c0b2ca2b9/influx-2-73a-hourly-electric-grid-monitor-for-us?orgId=1&refresh=1m)
|
||||
|
||||
### Examples explained
|
||||
|
||||
@@ -417,7 +418,7 @@ apps.fakesite.web_server_01.cpu.*
|
||||
|
||||
#### InfluxDB example
|
||||
|
||||
In this example, you have several data centers. Each data center has a different subset of hosts. It is based on the [InfluxDB Templated](https://play.grafana.org/d/000000002/influxdb-templated?orgId=1) dashboard.
|
||||
In this example, you have several data centers. Each data center has a different subset of hosts. It is based on the [InfluxDB Templated](https://play.grafana.org/d/e7bad3ef-db0c-4bbd-8245-b85c0b2ca2b9/influx-2-73a-hourly-electric-grid-monitor-for-us?orgId=1&refresh=1m) dashboard.
|
||||
|
||||
In this example, when the user changes the value of the `datacenter` variable, it changes the dropdown options returned by the `host` variable. The `host` variable uses the **Multi-value** option and **Include all option**, allowing users to select some or all options presented at any time. The `datacenter` does not use either option, so you can only select one data center at a time.
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ title: Data sources
|
||||
weight: 60
|
||||
---
|
||||
|
||||
# Data sources
|
||||
# Grafana data sources
|
||||
|
||||
Grafana comes with built-in support for many _data sources_.
|
||||
If you need other data sources, you can also install one of the many data source plugins.
|
||||
@@ -29,7 +29,8 @@ After you add and configure a data source, you can use it as an input for many o
|
||||
This documentation describes how to manage data sources in general,
|
||||
and how to configure or query the built-in data sources.
|
||||
For other data sources, refer to the list of [datasource plugins](/grafana/plugins/).
|
||||
To develop a custom plugin, refer to [Build a plugin][build-a-plugin].
|
||||
|
||||
To develop a custom plugin, refer to [Build a plugin](/developers/plugin-tools).
|
||||
|
||||
## Manage data sources
|
||||
|
||||
@@ -52,7 +53,7 @@ For example, this video demonstrates the visual Prometheus query builder:
|
||||
|
||||
{{< vimeo 720004179 >}}
|
||||
|
||||
For general information about querying in Grafana, and common options and user interface elements across all query editors, refer to [Query and transform data]({{< relref "../panels-visualizations/query-transform-data/" >}}).
|
||||
For general information about querying in Grafana, and common options and user interface elements across all query editors, refer to [Query and transform data][query-transform-data] .
|
||||
|
||||
## Special data sources
|
||||
|
||||
@@ -92,9 +93,6 @@ These built-in core data sources are also included in the Grafana documentation:
|
||||
[alerts]: "/docs/grafana/ -> /docs/grafana/<GRAFANA VERSION>/alerting"
|
||||
[alerts]: "/docs/grafana-cloud/ -> /docs/grafana/<GRAFANA VERSION>/alerting"
|
||||
|
||||
[build-a-plugin]: "/docs/grafana/ -> /docs/grafana/<GRAFANA VERSION>/developers/plugins"
|
||||
[build-a-plugin]: "/docs/grafana-cloud/ -> /docs/grafana/<GRAFANA VERSION>/developers/plugins"
|
||||
|
||||
[data-source-management]: "/docs/grafana/ -> /docs/grafana/<GRAFANA VERSION>/administration/data-source-management"
|
||||
[data-source-management]: "/docs/grafana-cloud/ -> /docs/grafana/<GRAFANA VERSION>/administration/data-source-management"
|
||||
|
||||
|
||||
@@ -63,6 +63,9 @@ For more information, refer to [Azure documentation for role assignments](https:
|
||||
If you host Grafana in Azure, such as in App Service or Azure Virtual Machines, you can configure the Azure Monitor data source to use Managed Identity for secure authentication without entering credentials into Grafana.
|
||||
For details, refer to [Configuring using Managed Identity](#configuring-using-managed-identity).
|
||||
|
||||
You can configure the Azure Monitor data source to use Workload Identity for secure authentication without entering credentials into Grafana if you host Grafana in a Kubernetes environment, such as AKS, and require access to Azure resources.
|
||||
For details, refer to [Configuring using Workload Identity](#configuring-using-workload-identity).
|
||||
|
||||
| Name | Description |
|
||||
| --------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| **Authentication** | Enables Managed Identity. Selecting Managed Identity hides many of the other fields. For details, see [Configuring using Managed Identity](#configuring-using-managed-identity). |
|
||||
@@ -114,6 +117,21 @@ datasources:
|
||||
version: 1
|
||||
```
|
||||
|
||||
**Workload Identity:**
|
||||
|
||||
```yaml
|
||||
apiVersion: 1 # config file version
|
||||
|
||||
datasources:
|
||||
- name: Azure Monitor
|
||||
type: grafana-azure-monitor-datasource
|
||||
access: proxy
|
||||
jsonData:
|
||||
azureAuthType: workloadidentity
|
||||
subscriptionId: <subscription-id> # Optional, default subscription
|
||||
version: 1
|
||||
```
|
||||
|
||||
#### Supported cloud names
|
||||
|
||||
| Azure Cloud | `cloudName` Value |
|
||||
@@ -124,8 +142,8 @@ datasources:
|
||||
|
||||
### Configure Managed Identity
|
||||
|
||||
If you host Grafana in Azure, such as an App Service or with Azure Virtual Machines, and have managed identity enabled on your VM, you can use managed identity to configure Azure Monitor in Grafana.
|
||||
This lets you securely authenticate data sources without manually configuring credentials via Azure AD App Registrations for each.
|
||||
You can use managed identity to configure Azure Monitor in Grafana if you host Grafana in Azure (such as an App Service or with Azure Virtual Machines) and have managed identity enabled on your VM.
|
||||
This lets you securely authenticate data sources without manually configuring credentials via Azure AD App Registrations.
|
||||
For details on Azure managed identities, refer to the [Azure documentation](https://docs.microsoft.com/en-us/azure/active-directory/managed-identities-azure-resources/overview).
|
||||
|
||||
**To enable managed identity for Grafana:**
|
||||
@@ -141,7 +159,46 @@ For details on Azure managed identities, refer to the [Azure documentation](http
|
||||
|
||||
This hides the directory ID, application ID, and client secret fields, and the data source uses managed identity to authenticate to Azure Monitor Metrics and Logs, and Azure Resource Graph.
|
||||
|
||||
{{< figure src="/media/docs/grafana/data-sources/screenshot-managed-identity.png" max-width="800px" class="docs-image--no-shadow" caption="Azure Monitor Metrics screenshot showing Dimensions" >}}
|
||||
{{< figure src="/media/docs/grafana/data-sources/screenshot-managed-identity-2.png" max-width="800px" class="docs-image--no-shadow" caption="Azure Monitor screenshot showing Managed Identity authentication" >}}
|
||||
|
||||
3. You can set the `managed_identity_client_id` field in the `[azure]` section of the [Grafana server configuration][configure-grafana-azure] to allow a user-assigned managed identity to be used instead of the default system-assigned identity.
|
||||
|
||||
```ini
|
||||
[azure]
|
||||
managed_identity_enabled = true
|
||||
managed_identity_client_id = USER_ASSIGNED_IDENTITY_CLIENT_ID
|
||||
```
|
||||
|
||||
### Configure Workload Identity
|
||||
|
||||
You can use workload identity to configure Azure Monitor in Grafana if you host Grafana in a Kubernetes environment, such as AKS, in conjunction with managed identities.
|
||||
This lets you securely authenticate data sources without manually configuring credentials via Azure AD App Registrations.
|
||||
For details on workload identity, refer to the [Azure workload identity documentation](https://azure.github.io/azure-workload-identity/docs/).
|
||||
|
||||
**To enable workload identity for Grafana:**
|
||||
|
||||
1. Set the `workload_identity_enabled` flag in the `[azure]` section of the [Grafana server configuration][configure-grafana-azure].
|
||||
|
||||
```ini
|
||||
[azure]
|
||||
workload_identity_enabled = true
|
||||
```
|
||||
|
||||
2. In the Azure Monitor data source configuration, set **Authentication** to **Workload Identity**.
|
||||
|
||||
This hides the directory ID, application ID, and client secret fields, and the data source uses workload identity to authenticate to Azure Monitor Metrics and Logs, and Azure Resource Graph.
|
||||
|
||||
{{< figure src="/media/docs/grafana/data-sources/screenshot-workload-identity.png" max-width="800px" class="docs-image--no-shadow" caption="Azure Monitor screenshot showing Workload Identity authentication" >}}
|
||||
|
||||
3. There are additional configuration variables that can control the authentication method.`workload_identity_tenant_id` represents the Azure AD tenant that contains the managed identity, `workload_identity_client_id` represents the client ID of the managed identity if it differs from the default client ID, `workload_identity_token_file` represents the path to the token file. Refer to the [documentation](https://azure.github.io/azure-workload-identity/docs/) for more information on what values these variables should use, if any.
|
||||
|
||||
```ini
|
||||
[azure]
|
||||
workload_identity_enabled = true
|
||||
workload_identity_tenant_id = IDENTITY_TENANT_ID
|
||||
workload_identity_client_id = IDENTITY_CLIENT_ID
|
||||
workload_identity_token_file = TOKEN_FILE_PATH
|
||||
```
|
||||
|
||||
## Query the data source
|
||||
|
||||
|
||||
@@ -139,7 +139,7 @@ The default values for "cloud monitoring auto" are:
|
||||
|
||||
The other automatic option is "grafana auto", which automatically sets the Group By time depending on the time range chosen and width of the time series panel.
|
||||
|
||||
For more information about "grafana auto", refer to [Interval variable]({{< relref "../../../dashboards/variables/add-template-variables/#add-an-interval-variable" >}}).
|
||||
For more information about "grafana auto", refer to [Interval variable][add-template-variables-add-interval-variable].
|
||||
|
||||
You can also choose fixed time intervals to group by, like `1h` or `1d`.
|
||||
|
||||
@@ -305,6 +305,9 @@ Example result: `monitoring.googleapis.com/uptime_check/http_status has this val
|
||||
| `{{resource.label.xxx}}` | Returns the resource label value. | `{{resource.label.zone}}` | `us-east1-b` |
|
||||
|
||||
{{% docs/reference %}}
|
||||
[add-template-variables-add-interval-variable]: "/docs/grafana/ -> /docs/grafana/<GRAFANA VERSION>/dashboards/variables/add-template-variables#add-an-interval-variable"
|
||||
[add-template-variables-add-interval-variable]: "/docs/grafana-cloud/ -> /docs/grafana/<GRAFANA VERSION>/dashboards/variables/add-template-variables#add-an-interval-variable"
|
||||
|
||||
[annotate-visualizations]: "/docs/grafana/ -> /docs/grafana/<GRAFANA VERSION>/dashboards/build-dashboards/annotate-visualizations"
|
||||
[annotate-visualizations]: "/docs/grafana-cloud/ -> /docs/grafana/<GRAFANA VERSION>/dashboards/build-dashboards/annotate-visualizations"
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ weight: 700
|
||||
|
||||
# InfluxDB data source
|
||||
|
||||
{{< docs/shared "influxdb/intro.md" >}}
|
||||
{{< docs/shared lookup="influxdb/intro.md" source="grafana" version="<GRAFANA VERSION>" >}}
|
||||
|
||||
Grafana includes built-in support for InfluxDB.
|
||||
This topic explains options, variables, querying, and other features specific to the InfluxDB data source, which include its [feature-rich code editor for queries and visual query builder]({{< relref "./query-editor" >}}).
|
||||
|
||||
@@ -43,7 +43,7 @@ SHOW TAG VALUES WITH KEY = "hostname"
|
||||
|
||||
### Chain or nest variables
|
||||
|
||||
You can also create nested variables, sometimes called [chained variables]({{< relref "../../../dashboards/variables/add-template-variables#chained-variables" >}}).
|
||||
You can also create nested variables, sometimes called [chained variables][add-template-variables-chained-variables].
|
||||
|
||||
For example, if you had a variable called `region`, you could have the `hosts` variable show only hosts from the selected region with a query like:
|
||||
|
||||
@@ -90,6 +90,9 @@ When you enable the **Multi-value** or **Include all value** options, Grafana co
|
||||
To view an example templated dashboard, refer to [InfluxDB Templated Dashboard](https://play.grafana.org/dashboard/db/influxdb-templated).
|
||||
|
||||
{{% docs/reference %}}
|
||||
[add-template-variables-chained-variables]: "/docs/grafana/ -> /docs/grafana/<GRAFANA VERSION>/dashboards/variables/add-template-variables#chained-variables"
|
||||
[add-template-variables-chained-variables]: "/docs/grafana-cloud/ -> /docs/grafana/<GRAFANA VERSION>/dashboards/variables/add-template-variables#chained-variables"
|
||||
|
||||
[add-template-variables-add-ad-hoc-filters]: "/docs/grafana/ -> /docs/grafana/<GRAFANA VERSION>/dashboards/variables/add-template-variables#add-ad-hoc-filters"
|
||||
[add-template-variables-add-ad-hoc-filters]: "/docs/grafana-cloud/ -> /docs/grafana/<GRAFANA VERSION>/dashboards/variables/add-template-variables#add-ad-hoc-filters"
|
||||
|
||||
|
||||
@@ -225,7 +225,7 @@ The resulting table panel:
|
||||
If you set the **Format** setting in the query editor to **Time series**, then the query must have a column named `time` that returns either a SQL datetime or any numeric datatype representing Unix epoch in seconds.
|
||||
Result sets of time series queries must also be sorted by time for panels to properly visualize the result.
|
||||
|
||||
A time series query result is returned in a [wide data frame format][data-frames-wide-format].
|
||||
A time series query result is returned in a [wide data frame format](/developers/plugin-tools/introduction/data-frames#wide-format).
|
||||
Any column except time or of type string transforms into value fields in the data frame query result.
|
||||
Any string column transforms into field labels in the data frame query result.
|
||||
|
||||
@@ -550,9 +550,6 @@ EXEC dbo.sp_test_datetime @from, @to
|
||||
[configure-standard-options-display-name]: "/docs/grafana/ -> /docs/grafana/<GRAFANA VERSION>/panels-visualizations/configure-standard-options#display-name"
|
||||
[configure-standard-options-display-name]: "/docs/grafana-cloud/ -> /docs/grafana/<GRAFANA VERSION>/panels-visualizations/configure-standard-options#display-name"
|
||||
|
||||
[data-frames-wide-format]: "/docs/grafana/ -> /docs/grafana/<GRAFANA VERSION>/developers/plugins/introduction-to-plugin-development/data-frames#wide-format"
|
||||
[data-frames-wide-format]: "/docs/grafana-cloud/ -> /docs/grafana/<GRAFANA VERSION>/developers/plugins/introduction-to-plugin-development/data-frames#wide-format"
|
||||
|
||||
[query-transform-data]: "/docs/grafana/ -> /docs/grafana/<GRAFANA VERSION>/panels-visualizations/query-transform-data"
|
||||
[query-transform-data]: "/docs/grafana-cloud/ -> /docs/grafana/<GRAFANA VERSION>/panels-visualizations/query-transform-data"
|
||||
|
||||
|
||||
@@ -119,7 +119,7 @@ datasources:
|
||||
password: ${GRAFANA_MYSQL_PASSWORD}
|
||||
```
|
||||
|
||||
##### Using TLS Verificaiton
|
||||
##### Using TLS verification
|
||||
|
||||
```yaml
|
||||
apiVersion: 1
|
||||
@@ -280,7 +280,7 @@ The resulting table panel:
|
||||
|
||||
If you set Format as to _Time series_, then the query must have a column named time that returns either a SQL datetime or any numeric datatype representing Unix epoch in seconds. In addition, result sets of time series queries must be sorted by time for panels to properly visualize the result.
|
||||
|
||||
A time series query result is returned in a [wide data frame format][data-frames-wide-format]. Any column except time or of type string transforms into value fields in the data frame query result. Any string column transforms into field labels in the data frame query result.
|
||||
A time series query result is returned in a [wide data frame format](/developers/plugin-tools/introduction/data-frames#wide-format). Any column except time or of type string transforms into value fields in the data frame query result. Any string column transforms into field labels in the data frame query result.
|
||||
|
||||
> For backward compatibility, there's an exception to the above rule for queries that return three columns including a string column named metric. Instead of transforming the metric column into field labels, it becomes the field name, and then the series name is formatted as the value of the metric column. See the example with the metric column below.
|
||||
|
||||
@@ -467,7 +467,7 @@ ORDER BY atimestamp ASC
|
||||
|
||||
#### Disabling Quoting for Multi-value Variables
|
||||
|
||||
Grafana automatically creates a quoted, comma-separated string for multi-value variables. For example: if `server01` and `server02` are selected then it will be formatted as: `'server01', 'server02'`. Do disable quoting, use the csv formatting option for variables:
|
||||
Grafana automatically creates a quoted, comma-separated string for multi-value variables. For example: if `server01` and `server02` are selected then it will be formatted as: `'server01', 'server02'`. To disable quoting, use the csv formatting option for variables:
|
||||
|
||||
`${servers:csv}`
|
||||
|
||||
@@ -546,9 +546,6 @@ Time series queries should work in alerting conditions. Table formatted queries
|
||||
[configure-standard-options-display-name]: "/docs/grafana/ -> /docs/grafana/<GRAFANA VERSION>/panels-visualizations/configure-standard-options#display-name"
|
||||
[configure-standard-options-display-name]: "/docs/grafana-cloud/ -> /docs/grafana/<GRAFANA VERSION>/panels-visualizations/configure-standard-options#display-name"
|
||||
|
||||
[data-frames-wide-format]: "/docs/grafana/ -> /docs/grafana/<GRAFANA VERSION>/developers/plugins/introduction-to-plugin-development/data-frames#wide-format"
|
||||
[data-frames-wide-format]: "/docs/grafana-cloud/ -> /docs/grafana/<GRAFANA VERSION>/developers/plugins/introduction-to-plugin-development/data-frames#wide-format"
|
||||
|
||||
[data-source-management]: "/docs/grafana/ -> /docs/grafana/<GRAFANA VERSION>/administration/data-source-management"
|
||||
[data-source-management]: "/docs/grafana-cloud/ -> /docs/grafana/<GRAFANA VERSION>/administration/data-source-management"
|
||||
|
||||
|
||||
@@ -232,7 +232,7 @@ The resulting table panel:
|
||||
|
||||
If you set Format as to _Time series_, then the query must have a column named time that returns either a SQL datetime or any numeric datatype representing Unix epoch in seconds. In addition, result sets of time series queries must be sorted by time for panels to properly visualize the result.
|
||||
|
||||
A time series query result is returned in a [wide data frame format][data-frames-wide-format]. Any column except time or of type string transforms into value fields in the data frame query result. Any string column transforms into field labels in the data frame query result.
|
||||
A time series query result is returned in a [wide data frame format](https://grafana.com/developers/plugin-tools/introduction/data-frames#wide-format). Any column except time or of type string transforms into value fields in the data frame query result. Any string column transforms into field labels in the data frame query result.
|
||||
|
||||
> For backward compatibility, there's an exception to the above rule for queries that return three columns including a string column named metric. Instead of transforming the metric column into field labels, it becomes the field name, and then the series name is formatted as the value of the metric column. See the example with the metric column below.
|
||||
|
||||
@@ -492,9 +492,6 @@ conditions.
|
||||
[configure-standard-options-display-name]: "/docs/grafana/ -> /docs/grafana/<GRAFANA VERSION>/panels-visualizations/configure-standard-options#display-name"
|
||||
[configure-standard-options-display-name]: "/docs/grafana-cloud/ -> /docs/grafana/<GRAFANA VERSION>/panels-visualizations/configure-standard-options#display-name"
|
||||
|
||||
[data-frames-wide-format]: "/docs/grafana/ -> /docs/grafana/<GRAFANA VERSION>/developers/plugins/introduction-to-plugin-development/data-frames#wide-format"
|
||||
[data-frames-wide-format]: "/docs/grafana-cloud/ -> /docs/grafana/<GRAFANA VERSION>/developers/plugins/introduction-to-plugin-development/data-frames#wide-format"
|
||||
|
||||
[data-source-management]: "/docs/grafana/ -> /docs/grafana/<GRAFANA VERSION>/administration/data-source-management"
|
||||
[data-source-management]: "/docs/grafana-cloud/ -> /docs/grafana/<GRAFANA VERSION>/administration/data-source-management"
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ For instructions on how to add a data source to Grafana, refer to the [administr
|
||||
Only users with the organization `administrator` role can add data sources and edit existing data sources.
|
||||
Administrators can also [configure the data source via YAML](#provision-the-data-source) with Grafana's provisioning system.
|
||||
|
||||
Once you've added the Prometheus data source, you can [configure it][configure-prometheus-data-source] so that your Grafana instance's users can create queries in its [query editor]({{< relref "./query-editor" >}}) when they [build dashboards][build-dashboards], use [Explore][explore], and [annotate visualizations]({{< relref "./query-editor#apply-annotations" >}}).
|
||||
Once you've added the Prometheus data source, you can [configure it][configure-prometheus-data-source] so that your Grafana instance's users can create queries in its [query editor]({{< relref "./query-editor" >}}) when they [build dashboards][build-dashboards], use [Explore][explore], and [annotate visualizations][annotate visualizations].
|
||||
|
||||
The following guides will help you get started with the Prometheus data source:
|
||||
|
||||
@@ -189,4 +189,8 @@ Increasing the duration of the `incrementalQueryOverlapWindow` will increase the
|
||||
|
||||
[set-up-grafana-monitoring]: "/docs/grafana/ -> /docs/grafana/<GRAFANA VERSION>/setup-grafana/set-up-grafana-monitoring"
|
||||
[set-up-grafana-monitoring]: "/docs/grafana-cloud/ -> /docs/grafana/<GRAFANA VERSION>/setup-grafana/set-up-grafana-monitoring"
|
||||
|
||||
[annotate visualizations]: "/docs/grafana/ -> /docs/grafana/<GRAFANA_VERSION>/dashboards/build-dashboards/annotate-visualizations"
|
||||
[annotate visualizations]: "/docs/grafana-cloud/ -> /docs/grafana-cloud/visualizations/dashboards/build-dashboards/annotate-visualizations"
|
||||
|
||||
{{% /docs/reference %}}
|
||||
|
||||
@@ -80,7 +80,7 @@ You can create TraceQL queries using the Query editor or using Search query tab
|
||||
|
||||
[//]: # 'Include content for preview of Search tab featuring TraceQL query builder'
|
||||
|
||||
{{< docs/shared source="grafana" lookup="datasources/tempo-search-traceql.md" leveloffset="+1" >}}
|
||||
{{< docs/shared source="grafana" lookup="datasources/tempo-search-traceql.md" leveloffset="+1" version="<GRAFANA VERSION>" >}}
|
||||
|
||||
## Query Loki for traces
|
||||
|
||||
|
||||
@@ -11,7 +11,14 @@ weight: 190
|
||||
|
||||
# Developers
|
||||
|
||||
This section includes the following Grafana developer documentation:
|
||||
Go to the [Grafana developer portal](/developers) to access the following documentation:
|
||||
|
||||
- [Grafana plugin development](/developers/plugin-tools)
|
||||
- [Grafana design system](https://developers.grafana.com)
|
||||
- [Grafana Scenes](/developers/scenes)
|
||||
- [Grafana data plane](/developers/dataplane)
|
||||
|
||||
This section of our documentation contains additional resources:
|
||||
|
||||
{{< section >}}
|
||||
|
||||
@@ -19,4 +26,3 @@ You might also find the following resources to be helpful:
|
||||
|
||||
- [Grafana Tutorials:](https://grafana.com/tutorials/) Step-by-step guides that help you make the most of Grafana.
|
||||
- [Grafana Community Forums:](https://community.grafana.com) Get technical support for open source Grafana, Loki, and Tempo.
|
||||
- [Grafana design system:](https://developers.grafana.com) Library of reusable Grafana components and guidelines that help you with contribution and plugin development.
|
||||
|
||||
@@ -40,7 +40,7 @@ We encourage you to locate the repository of the corresponding plugin and create
|
||||
|
||||
### Links
|
||||
|
||||
- [Migrate Angular to React]({{< relref "../plugins/migration-guide/angular-react/" >}})
|
||||
- [Migrate Angular to React](/developers/plugin-tools/migration-guides/migrate-angularjs-to-react)
|
||||
- [Build a panel plugin](https://grafana.com/tutorials/build-a-panel-plugin/)
|
||||
- [Build a data source plugin](https://grafana.com/tutorials/build-a-data-source-plugin/)
|
||||
- [List of current Angular plugins]({{< relref "./angular-plugins/" >}})
|
||||
|
||||
@@ -42,7 +42,7 @@ Plugins were updated to include signatures in 2021, so whilst a plugin may show
|
||||
|
||||
We are greatly appreciative of the developers who have contributed plugins to the Grafana ecosystem, your work has helped support millions of users to gain insights into their data. A plugin being listed below is no reflection on its quality, and is purely to help users understand the impact of the removal of Angular support in Grafana.
|
||||
|
||||
Guidance on migrating a plugin to React can be found in our [migration guide]({{< relref "../plugins/migration-guide/angular-react/" >}}). If you would like to add any specific migration guidance for your plugin here or update our assessment, please open a PR by clicking the `Suggest an edit` button at the bottom of this page.
|
||||
Guidance on migrating a plugin to React can be found in our [migration guide](/developers/plugin-tools/migration-guides/migrate-angularjs-to-react). If you would like to add any specific migration guidance for your plugin here or update our assessment, please open a PR by clicking the `Suggest an edit` button at the bottom of this page.
|
||||
|
||||
# Current AngularJS based plugins
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ title: RBAC HTTP API
|
||||
|
||||
# RBAC API
|
||||
|
||||
> Role-based access control API is only available in Grafana Enterprise. Read more about [Grafana Enterprise]({{< relref "/docs/grafana/latest/introduction/grafana-enterprise" >}}).
|
||||
> Role-based access control API is only available in Grafana Cloud or Grafana Enterprise. Read more about [Grafana Enterprise]({{< relref "/docs/grafana/latest/introduction/grafana-enterprise" >}}).
|
||||
|
||||
The API can be used to create, update, delete, get, and list roles.
|
||||
|
||||
@@ -533,7 +533,7 @@ Content-Type: application/json; charset=UTF-8
|
||||
For example, if a user does not have required permissions for creating users, they won't be able to unassign a role which will allow to do that. This is done to prevent escalation of privileges.
|
||||
|
||||
| Action | Scope |
|
||||
| ------------------ | ------------------------- |
|
||||
| ------------------ | ------------------------- |
|
||||
| users.roles:remove | permissions:type:delegate |
|
||||
|
||||
#### Query parameters
|
||||
|
||||
@@ -34,14 +34,14 @@ Returns a list of all library elements the authenticated user has permission to
|
||||
|
||||
Query parameters:
|
||||
|
||||
- **searchString** – Part of the name or description searched for.
|
||||
- **kind** – Kind of element to search for. Use `1` for library panels or `2` for library variables.
|
||||
- **sortDirection** – Sort order of elements. Use `alpha-asc` for ascending and `alpha-desc` for descending sort order.
|
||||
- **typeFilter** – A comma separated list of types to filter the elements by.
|
||||
- **excludeUid** – Element UID to exclude from search results.
|
||||
- **folderFilter** – A comma separated list of folder ID(s) to filter the elements by.
|
||||
- **perPage** – The number of results per page; default is 100.
|
||||
- **page** – The page for a set of records, given that only `perPage` records are returned at a time. Numbering starts at `1`.
|
||||
- `searchString`: Part of the name or description searched for.
|
||||
- `kind`: Kind of element to search for. Use `1` for library panels or `2` for library variables.
|
||||
- `sortDirection`: Sort order of elements. Use `alpha-asc` for ascending and `alpha-desc` for descending sort order.
|
||||
- `typeFilter`: A comma separated list of types to filter the elements by.
|
||||
- `excludeUid`: Element UID to exclude from search results.
|
||||
- `folderFilter`: A comma separated list of folder IDs to filter the elements by.
|
||||
- `perPage`: The number of results per page; default is 100.
|
||||
- `page`: The page for a set of records, given that only `perPage` records are returned at a time. Numbering starts at `1`.
|
||||
|
||||
**Example Request**:
|
||||
|
||||
@@ -102,8 +102,8 @@ Content-Type: application/json
|
||||
|
||||
**Example Request**:
|
||||
|
||||
```http
|
||||
GET /api/library-elements/name/API docs Example HTTP/1.1
|
||||
```http
|
||||
GET /api/library-elements/name/API docs Example HTTP/1.1
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
|
||||
@@ -161,9 +161,9 @@ Content-Type: application/json
|
||||
JSON Body schema:
|
||||
|
||||
- `folderId`: ID of the folder where the library element is stored. It is deprecated since Grafana v9
|
||||
- **folderUid** – Optional, the UID of the folder where the library element is stored, empty string when it is General folder
|
||||
- **name** – Optional, the name of the library element.
|
||||
- **model** – The JSON model for the library element.
|
||||
- `folderUid`: Optional, the UID of the folder where the library element is stored, empty string when it is General folder
|
||||
- `name`: Optional, the name of the library element.
|
||||
- `model`: The JSON model for the library element.
|
||||
- `kind`: Kind of element to create, Use `1` for library panels or `2` for library variables.
|
||||
- `uid`: Optional, the [unique identifier](/http_api/library_element/#identifier-id-vs-unique-identifier-uid).
|
||||
|
||||
@@ -223,9 +223,9 @@ Content-Type: application/json
|
||||
```http
|
||||
HTTP/1.1 200
|
||||
Content-Type: application/json
|
||||
|
||||
```
|
||||
|
||||
|
||||
```
|
||||
|
||||
Status Codes:
|
||||
|
||||
- `200`: Updated
|
||||
@@ -269,9 +269,9 @@ Content-Type: application/json
|
||||
- `401`: Unauthorized
|
||||
- `400`: Bad request
|
||||
- `403`: Access denied
|
||||
- **404** – Library element not found
|
||||
- **401** – Unauthorized
|
||||
- **404** – Library element not found
|
||||
- `404`: Library element not found
|
||||
- `401`: Unauthorized
|
||||
- `404`: Library element not found
|
||||
|
||||
## Create library element
|
||||
|
||||
@@ -281,12 +281,12 @@ Creates a new library element.
|
||||
|
||||
JSON Body schema:
|
||||
|
||||
- **folderId** – ID of the folder where the library element is stored. It is deprecated since Grafana v9
|
||||
- **folderUid** – Optional, the UID of the folder where the library element is stored, empty string when it is General folder
|
||||
- **name** – Optional, the name of the library element.
|
||||
- **model** – The JSON model for the library element.
|
||||
- **kind** – Kind of element to create, Use `1` for library panels or `2` for library variables.
|
||||
- **uid** – Optional, the [unique identifier](/http_api/library_element/#identifier-id-vs-unique-identifier-uid).
|
||||
- `folderId`: ID of the folder where the library element is stored. It is deprecated since Grafana v9
|
||||
- `folderUid`: Optional, the UID of the folder where the library element is stored, empty string when it is General folder
|
||||
- `name`: Optional, the name of the library element.
|
||||
- `model`: The JSON model for the library element.
|
||||
- `kind`: Kind of element to create, Use `1` for library panels or `2` for library variables.
|
||||
- `uid`: Optional, the [unique identifier](/http_api/library_element/#identifier-id-vs-unique-identifier-uid).
|
||||
|
||||
**Example Request**:
|
||||
|
||||
@@ -347,10 +347,10 @@ Content-Type: application/json
|
||||
|
||||
Status Codes:
|
||||
|
||||
- **200** – Created
|
||||
- **400** – Errors (for example, name or UID already exists, invalid JSON, missing or invalid fields, and so on).
|
||||
- **401** – Unauthorized
|
||||
- **403** – Access denied
|
||||
- `200`: Created
|
||||
- `400`: Errors (for example, name or UID already exists, invalid JSON, missing or invalid fields, and so on).
|
||||
- `401`: Unauthorized
|
||||
- `403`: Access denied
|
||||
|
||||
## Update library element
|
||||
|
||||
@@ -360,13 +360,13 @@ Updates an existing library element identified by uid.
|
||||
|
||||
JSON Body schema:
|
||||
|
||||
- **folderId** – ID of the folder where the library element is stored. It is deprecated since Grafana v9
|
||||
- **folderUid** – UID of the folder where the library element is stored, empty string when it is General folder.
|
||||
- **name** – Name of the library element.
|
||||
- **model** – The JSON model for the library element.
|
||||
- **kind** – Kind of element to create. Use `1` for library panels or `2` for library variables.
|
||||
- **version** – Version of the library element you are updating.
|
||||
- **uid** – Optional, the [unique identifier](/http_api/library_element/#identifier-id-vs-unique-identifier-uid).
|
||||
- `folderId`: ID of the folder where the library element is stored. It is deprecated since Grafana v9
|
||||
- `folderUid`: UID of the folder where the library element is stored, empty string when it is General folder.
|
||||
- `name`: Name of the library element.
|
||||
- `model`: The JSON model for the library element.
|
||||
- `kind`: Kind of element to create. Use `1` for library panels or `2` for library variables.
|
||||
- `version`: Version of the library element you are updating.
|
||||
- `uid`: Optional, the [unique identifier](/http_api/library_element/#identifier-id-vs-unique-identifier-uid).
|
||||
|
||||
**Example Request**:
|
||||
|
||||
@@ -428,12 +428,12 @@ Content-Type: application/json
|
||||
|
||||
Status Codes:
|
||||
|
||||
- **200** – Updated
|
||||
- **400** – Errors (for example, name or UID already exists, invalid JSON, missing or invalid fields, and so on).
|
||||
- **401** – Unauthorized
|
||||
- **403** – Access denied
|
||||
- **404** – Library element not found
|
||||
- **412** – Version mismatch
|
||||
- `200`: Updated
|
||||
- `400`: Errors (for example, name or UID already exists, invalid JSON, missing or invalid fields, and so on).
|
||||
- `401`: Unauthorized
|
||||
- `403`: Access denied
|
||||
- `404`: Library element not found
|
||||
- `412`: Version mismatch
|
||||
|
||||
## Delete library element
|
||||
|
||||
@@ -469,8 +469,8 @@ Content-Type: application/json
|
||||
|
||||
Status Codes:
|
||||
|
||||
- **200** – Deleted
|
||||
- **401** – Unauthorized
|
||||
- **400** – Bad request
|
||||
- **403** – Access denied
|
||||
- **404** – Library element not found
|
||||
- `200`: Deleted
|
||||
- `401`: Unauthorized
|
||||
- `400`: Bad request
|
||||
- `403`: Access denied
|
||||
- `404`: Library element not found
|
||||
|
||||
@@ -1,58 +0,0 @@
|
||||
---
|
||||
aliases:
|
||||
- ../plugins/developing/
|
||||
description: Resources for creating Grafana plugins
|
||||
labels:
|
||||
products:
|
||||
- enterprise
|
||||
- oss
|
||||
menuTitle: Plugin developer's guide
|
||||
title: Grafana plugin developer's guide
|
||||
weight: 200
|
||||
---
|
||||
|
||||
# Grafana plugin developer's guide
|
||||
|
||||
You can extend Grafana's built-in capabilities with plugins. Plugins enable Grafana to accomplish specialized tasks, custom-tailored to your requirements. By making a plugin for your organization, you can connect Grafana to other data sources, ticketing tools, and CI/CD tooling.
|
||||
|
||||
You can create plugins for private use or contribute them to the open source community by publishing to the [Grafana plugin catalog](/grafana/plugins/). This catalog has hundreds of other community and commercial plugins.
|
||||
|
||||
If you are a Grafana plugin developer or want to become one, this plugin developer's guide contains the tutorials and reference materials to help you get started.
|
||||
|
||||
## Plugin basics
|
||||
|
||||
You can create several types of plugins, including:
|
||||
|
||||
- **Panel plugins** - Visualize data and navigate between dashboards.
|
||||
- **Data source plugins** - Link to new databases or other sources of data.
|
||||
- **App plugins** - Create rich applications for custom out-of-the-box experiences.
|
||||
|
||||
> **Note:** To learn more about the types of plugins you can build, refer to the [Plugin management]({{< relref "../../administration/plugin-management" >}}) documentation.
|
||||
|
||||
## Contents of this developer's guide
|
||||
|
||||
The following topics are covered in this guide:
|
||||
|
||||
- **[Introduction to plugin development]({{< relref "./introduction-to-plugin-development" >}})** - Learn the fundamentals of Grafana plugin development: backend development, data frames, error handling, and more.
|
||||
- **[Get started with plugins]({{< relref "./get-started-with-plugins" >}})** - Start developing Grafana plugins with the [create-plugin](https://www.npmjs.com/package/@grafana/create-plugin) tool.
|
||||
- **[Create a Grafana plugin]({{< relref "./create-a-grafana-plugin/_index.md" >}})** - If you're familiar with plugin creation, use the tutorials for creating panel plugins, data source plugins, and more to deepen your knowledge.
|
||||
- **[Publish a Grafana plugin]({{< relref "./publish-a-plugin" >}})** - Learn about publishing a plugin to the Grafana plugin catalog, including publishing criteria, packaging, and deployment.
|
||||
- **[Work with legacy plugins]({{< relref "./legacy" >}})** - Learn how to upgrade from a previous version of a Grafana plugin, rewrite an old Angular plugin in React, or update to a newer version.
|
||||
- **[Migrate a plugin]({{< relref "./migration-guide" >}})** - Consult these documents if you need to work with a plugin written in deprecated technology.
|
||||
- **[Reference]({{< relref "./metadata.md" >}})** - Description of the `plugin.json` schema and plugin metadata.
|
||||
|
||||
## Go further
|
||||
|
||||
Learn more about additional tools and see plugin type examples.
|
||||
|
||||
### User interface creation
|
||||
|
||||
Explore the many UI components in our [Grafana UI library](https://developers.grafana.com/ui).
|
||||
|
||||
### Plugin examples
|
||||
|
||||
Grafana Labs provides a number of best practice example plugins for different use cases to help you quickly get started. Browse our [plugin examples](https://github.com/grafana/grafana-plugin-examples).
|
||||
|
||||
### SDK
|
||||
|
||||
Learn more about [Grafana Plugin SDK for Go]({{< relref "./introduction-to-plugin-development/backend/grafana-plugin-sdk-for-go" >}}).
|
||||
@@ -1,22 +0,0 @@
|
||||
---
|
||||
description: An index of how-to topics for Grafana plugin development.
|
||||
keywords:
|
||||
- grafana
|
||||
- plugins
|
||||
- plugin
|
||||
- documentation
|
||||
labels:
|
||||
products:
|
||||
- enterprise
|
||||
- oss
|
||||
menuTitle: Create a plugin
|
||||
title: Create a Grafana plugin
|
||||
weight: 300
|
||||
---
|
||||
|
||||
# Create a Grafana plugin
|
||||
|
||||
This section contains how-to topics for developing and extending Grafana plugins with more advanced capabilities.
|
||||
|
||||
- [Develop a plugin]({{< relref "./develop-a-plugin" >}})
|
||||
- [Extend a plugin]({{< relref "./extend-a-plugin" >}})
|
||||
@@ -1,32 +0,0 @@
|
||||
---
|
||||
description: An index of how-to topics for Grafana plugin development.
|
||||
keywords:
|
||||
- grafana
|
||||
- plugins
|
||||
- plugin
|
||||
- development
|
||||
- documentation
|
||||
labels:
|
||||
products:
|
||||
- enterprise
|
||||
- oss
|
||||
menuTitle: Develop a plugin
|
||||
title: Develop a Grafana plugin
|
||||
weight: 100
|
||||
---
|
||||
|
||||
# Develop a Grafana plugin
|
||||
|
||||
This section contains how-to topics for developing Grafana plugins:
|
||||
|
||||
- [Build a panel plugin]({{< relref "./build-a-panel-plugin.md" >}})
|
||||
- [Build a panel plugin with d3.js]({{< relref "./build-a-panel-plugin-with-d3.md" >}})
|
||||
- [Build a data source plugin]({{< relref "./build-a-data-source-plugin.md" >}})
|
||||
- [Build a data source backend plugin]({{< relref "./build-a-data-source-backend-plugin.md" >}})
|
||||
- [Build a logs data source plugin]({{< relref "./build-a-logs-data-source-plugin.md" >}})
|
||||
- [Build a streaming data source plugin]({{< relref "./build-a-streaming-data-source-plugin.md" >}})
|
||||
- [Work with data frames]({{< relref "./working-with-data-frames.md" >}})
|
||||
|
||||
Additional resources:
|
||||
|
||||
- [Build a Grafana plugin with the create-plugin tool](https://grafana.github.io/plugin-tools/docs/get-started/).
|
||||
@@ -1,188 +0,0 @@
|
||||
---
|
||||
description: Create a backend for your data source plugin.
|
||||
keywords:
|
||||
- grafana
|
||||
- plugins
|
||||
- plugin
|
||||
- backend
|
||||
- backend data source
|
||||
- datasource
|
||||
labels:
|
||||
products:
|
||||
- enterprise
|
||||
- oss
|
||||
title: Build a data source backend plugin
|
||||
weight: 400
|
||||
---
|
||||
|
||||
## Introduction
|
||||
|
||||
Grafana supports a wide range of data sources, including Prometheus, MySQL, and even Datadog. There's a good chance you can already visualize metrics from the systems you have set up. In some cases, though, you already have an in-house metrics solution that you’d like to add to your Grafana dashboards. This tutorial teaches you to build a support for your data source.
|
||||
|
||||
For more information about backend plugins, refer to the documentation on [Backend plugins](/docs/grafana/latest/developers/plugins/backend/).
|
||||
|
||||
In this tutorial, you'll:
|
||||
|
||||
- Build a backend for your data source
|
||||
- Implement a health check for your data source
|
||||
- Enable Grafana Alerting for your data source
|
||||
|
||||
{{% class "prerequisite-section" %}}
|
||||
|
||||
#### Prerequisites
|
||||
|
||||
- Knowledge about how data sources are implemented in the frontend.
|
||||
- Grafana 7.0
|
||||
- Go ([Version](https://github.com/grafana/plugin-tools/blob/main/packages/create-plugin/templates/backend/go.mod#L3))
|
||||
- [Mage](https://magefile.org/)
|
||||
- [LTS](https://nodejs.dev/en/about/releases/) version of Node.js
|
||||
- yarn
|
||||
{{% /class %}}
|
||||
|
||||
## Set up your environment
|
||||
|
||||
{{< docs/shared lookup="tutorials/set-up-environment.md" source="grafana" version="latest" >}}
|
||||
|
||||
## Create a new plugin
|
||||
|
||||
To build a backend for your data source plugin, Grafana requires a binary that it can execute when it loads the plugin during start-up. In this guide, we will build a binary using the [Grafana plugin SDK for Go]({{< relref "../../introduction-to-plugin-development/backend/grafana-plugin-sdk-for-go" >}}).
|
||||
|
||||
The easiest way to get started is to use the Grafana [create-plugin tool](https://www.npmjs.com/package/@grafana/create-plugin). Navigate to the plugin folder that you configured in step 1 and type:
|
||||
|
||||
```
|
||||
npx @grafana/create-plugin@latest
|
||||
```
|
||||
|
||||
Follow the steps and select **datasource** as your plugin type and answer **yes** when prompted to create a backend for your plugin.
|
||||
|
||||
```bash
|
||||
cd my-plugin
|
||||
```
|
||||
|
||||
Install frontend dependencies and build frontend parts of the plugin to _dist_ directory:
|
||||
|
||||
```bash
|
||||
yarn install
|
||||
yarn build
|
||||
```
|
||||
|
||||
Run the following to update [Grafana plugin SDK for Go]({{< relref "../../introduction-to-plugin-development/backend/grafana-plugin-sdk-for-go" >}}) dependency to the latest minor version:
|
||||
|
||||
```bash
|
||||
go get -u github.com/grafana/grafana-plugin-sdk-go
|
||||
go mod tidy
|
||||
```
|
||||
|
||||
Build backend plugin binaries for Linux, Windows and Darwin to _dist_ directory:
|
||||
|
||||
```bash
|
||||
mage -v
|
||||
```
|
||||
|
||||
Now, let's verify that the plugin you've built so far can be used in Grafana when creating a new data source:
|
||||
|
||||
1. Restart your Grafana instance.
|
||||
1. Open Grafana in your web browser.
|
||||
1. Navigate via the side-menu to **Configuration** -> **Data Sources**.
|
||||
1. Click **Add data source**.
|
||||
1. Find your newly created plugin and select it.
|
||||
1. Enter a name and then click **Save & Test** (ignore any errors reported for now).
|
||||
|
||||
You now have a new data source instance of your plugin that is ready to use in a dashboard:
|
||||
|
||||
1. Navigate via the side-menu to **Create** -> **Dashboard**.
|
||||
1. Click **Add new panel**.
|
||||
1. In the query tab, select the data source you just created.
|
||||
1. A line graph is rendered with one series consisting of two data points.
|
||||
1. Save the dashboard.
|
||||
|
||||
### Troubleshooting
|
||||
|
||||
#### Grafana doesn't load my plugin
|
||||
|
||||
By default, Grafana requires backend plugins to be signed. To load unsigned backend plugins, you need to
|
||||
configure Grafana to [allow unsigned plugins](/docs/grafana/latest/plugins/plugin-signature-verification/#allow-unsigned-plugins).
|
||||
For more information, refer to [Plugin signature verification](/docs/grafana/latest/plugins/plugin-signature-verification/#backend-plugins).
|
||||
|
||||
## Anatomy of a backend plugin
|
||||
|
||||
The folders and files used to build the backend for the data source are:
|
||||
|
||||
| file/folder | description |
|
||||
| ------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `Magefile.go` | It’s not a requirement to use mage build files, but we strongly recommend using it so that you can use the build targets provided by the plugin SDK. |
|
||||
| `/go.mod ` | Go modules dependencies, [reference](https://golang.org/cmd/go/#hdr-The_go_mod_file) |
|
||||
| `/src/plugin.json` | A JSON file describing the backend plugin |
|
||||
| `/pkg/main.go` | Starting point of the plugin binary. |
|
||||
|
||||
#### plugin.json
|
||||
|
||||
The [plugin.json](/docs/grafana/latest/developers/plugins/metadata/) file is required for all plugins. When building a backend plugin these properties are important:
|
||||
|
||||
| property | description |
|
||||
| ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| backend | Should be set to `true` for backend plugins. This tells Grafana that it should start a binary when loading the plugin. |
|
||||
| executable | This is the name of the executable that Grafana expects to start, see [plugin.json reference](/docs/grafana/latest/developers/plugins/metadata/) for details. |
|
||||
| alerting | Should be set to `true` if your backend datasource supports alerting. |
|
||||
|
||||
In the next step we will look at the query endpoint!
|
||||
|
||||
## Implement data queries
|
||||
|
||||
We begin by opening the file `/pkg/plugin/plugin.go`. In this file you will see the `SampleDatasource` struct which implements the [backend.QueryDataHandler](https://pkg.go.dev/github.com/grafana/grafana-plugin-sdk-go/backend?tab=doc#QueryDataHandler) interface. The `QueryData` method on this struct is where the data fetching happens for a data source plugin.
|
||||
|
||||
Each request contains multiple queries to reduce traffic between Grafana and plugins. So you need to loop over the slice of queries, process each query, and then return the results of all queries.
|
||||
|
||||
In the tutorial we have extracted a method named `query` to take care of each query model. Since each plugin has their own unique query model, Grafana sends it to the backend plugin as JSON. Therefore the plugin needs to `Unmarshal` the query model into something easier to work with.
|
||||
|
||||
As you can see the sample only returns static numbers. Try to extend the plugin to return other types of data.
|
||||
|
||||
You can read more about how to [build data frames in our docs](/docs/grafana/latest/developers/plugins/data-frames/).
|
||||
|
||||
## Add support for health checks
|
||||
|
||||
Implementing the health check handler allows Grafana to verify that a data source has been configured correctly.
|
||||
|
||||
When editing a data source in Grafana's UI, you can **Save & Test** to verify that it works as expected.
|
||||
|
||||
In this sample data source, there is a 50% chance that the health check will be successful. Make sure to return appropriate error messages to the users, informing them about what is misconfigured in the data source.
|
||||
|
||||
Open `/pkg/plugin/plugin.go`. In this file you'll see that the `SampleDatasource` struct also implements the [backend.CheckHealthHandler](https://pkg.go.dev/github.com/grafana/grafana-plugin-sdk-go/backend?tab=doc#CheckHealthHandler) interface. Navigate to the `CheckHealth` method to see how the health check for this sample plugin is implemented.
|
||||
|
||||
## Add authentication
|
||||
|
||||
Implementing authentication allows your plugin to access protected resources like databases or APIs. To learn more about how to authenticate using a backend plugin, refer to [our documentation]({{< relref "../extend-a-plugin/add-authentication-for-data-source-plugins/#authenticate-using-a-backend-plugin" >}}).
|
||||
|
||||
## Enable Grafana Alerting
|
||||
|
||||
1. Open _src/plugin.json_.
|
||||
1. Add the top level `backend` property with a value of `true` to specify that your plugin supports Grafana Alerting, e.g.
|
||||
```json
|
||||
{
|
||||
...
|
||||
"backend": true,
|
||||
"executable": "gpx_simple_datasource_backend",
|
||||
"alerting": true,
|
||||
"info": {
|
||||
...
|
||||
}
|
||||
```
|
||||
1. Rebuild frontend parts of the plugin to _dist_ directory:
|
||||
|
||||
```bash
|
||||
yarn build
|
||||
```
|
||||
|
||||
1. Restart your Grafana instance.
|
||||
1. Open Grafana in your web browser.
|
||||
1. Open the dashboard you created earlier in the _Create a new plugin_ step.
|
||||
1. Edit the existing panel.
|
||||
1. Click on the _Alert_ tab.
|
||||
1. Click on _Create Alert_ button.
|
||||
1. Edit condition and specify _IS ABOVE 10_. Change _Evaluate every_ to _10s_ and clear the _For_ field to make the alert rule evaluate quickly.
|
||||
1. Save the dashboard.
|
||||
1. After some time the alert rule evaluates and transitions into _Alerting_ state.
|
||||
|
||||
## Summary
|
||||
|
||||
In this tutorial you created a backend for your data source plugin.
|
||||
@@ -1,380 +0,0 @@
|
||||
---
|
||||
description: Create a plugin to add support for your own data sources.
|
||||
keywords:
|
||||
- grafana
|
||||
- plugins
|
||||
- plugin
|
||||
- data source
|
||||
- datasource
|
||||
labels:
|
||||
products:
|
||||
- enterprise
|
||||
- oss
|
||||
title: Build a data source plugin
|
||||
weight: 300
|
||||
---
|
||||
|
||||
## Introduction
|
||||
|
||||
Grafana supports a wide range of data sources, including Prometheus, MySQL, and even Datadog. There's a good chance you can already visualize metrics from the systems you have set up. In some cases, though, you already have an in-house metrics solution that you’d like to add to your Grafana dashboards. This tutorial teaches you to build a support for your data source.
|
||||
|
||||
In this tutorial, you'll:
|
||||
|
||||
- Build a data source to visualize a sine wave
|
||||
- Construct queries using the query editor
|
||||
- Configure your data source using the config editor
|
||||
|
||||
{{% class "prerequisite-section" %}}
|
||||
|
||||
### Prerequisites
|
||||
|
||||
- Grafana >=7.0
|
||||
- [LTS](https://nodejs.dev/en/about/releases/) version of Node.js
|
||||
- yarn
|
||||
{{% /class %}}
|
||||
|
||||
## Set up your environment
|
||||
|
||||
{{< docs/shared lookup="tutorials/set-up-environment.md" source="grafana" version="latest" >}}
|
||||
|
||||
## Create a new plugin
|
||||
|
||||
{{< docs/shared lookup="tutorials/create-plugin.md" source="grafana" version="latest" >}}
|
||||
|
||||
To learn how to create a backend data source plugin, see [Build a data source backend plugin]({{< relref "./build-a-data-source-backend-plugin.md" >}})
|
||||
|
||||
## Anatomy of a plugin
|
||||
|
||||
{{< docs/shared lookup="tutorials/plugin-anatomy.md" source="grafana" version="latest" >}}
|
||||
|
||||
## Data source plugins
|
||||
|
||||
A data source in Grafana must extend the `DataSourceApi` interface, which requires you to define two methods: `query` and `testDatasource`.
|
||||
|
||||
### The `query` method
|
||||
|
||||
The `query` method is the heart of any data source plugin. It accepts a query from the user, retrieves the data from an external database, and returns the data in a format that Grafana recognizes.
|
||||
|
||||
```
|
||||
async query(options: DataQueryRequest<MyQuery>): Promise<DataQueryResponse>
|
||||
```
|
||||
|
||||
The `options` object contains the queries, or _targets_, that the user made, along with context information, like the current time interval. Use this information to query an external database.
|
||||
|
||||
> The term _target_ originates from Graphite, and the earlier days of Grafana when Graphite was the only supported data source. As Grafana gained support for more data sources, the term "target" became synonymous with any type of query.
|
||||
|
||||
### Test your data source
|
||||
|
||||
`testDatasource` implements a health check for your data source. For example, Grafana calls this method whenever the user clicks the **Save & Test** button, after changing the connection settings.
|
||||
|
||||
```
|
||||
async testDatasource()
|
||||
```
|
||||
|
||||
## Data frames
|
||||
|
||||
Nowadays there are countless different databases, each with their own ways of querying data. To be able to support all the different data formats, Grafana consolidates the data into a unified data structure called _data frames_.
|
||||
|
||||
Let's see how to create and return a data frame from the `query` method. In this step, you'll change the code in the starter plugin to return a [sine wave](https://en.wikipedia.org/wiki/Sine_wave).
|
||||
|
||||
1. In the current `query` method, remove the code inside the `map` function.
|
||||
|
||||
The `query` method now look like this:
|
||||
|
||||
```ts
|
||||
async query(options: DataQueryRequest<MyQuery>): Promise<DataQueryResponse> {
|
||||
const { range } = options;
|
||||
const from = range!.from.valueOf();
|
||||
const to = range!.to.valueOf();
|
||||
|
||||
const data = options.targets.map(target => {
|
||||
// Your code goes here.
|
||||
});
|
||||
|
||||
return { data };
|
||||
}
|
||||
```
|
||||
|
||||
1. In the `map` function, use the `lodash/defaults` package to set default values for query properties that haven't been set:
|
||||
|
||||
```ts
|
||||
const query = defaults(target, defaultQuery);
|
||||
```
|
||||
|
||||
1. Create a default query at the top of datasource.ts:
|
||||
|
||||
```ts
|
||||
export const defaultQuery: Partial<MyQuery> = {
|
||||
constant: 6.5,
|
||||
};
|
||||
```
|
||||
|
||||
1. Create a data frame with a time field and a number field:
|
||||
|
||||
```ts
|
||||
const frame = new MutableDataFrame({
|
||||
refId: query.refId,
|
||||
fields: [
|
||||
{ name: 'time', type: FieldType.time },
|
||||
{ name: 'value', type: FieldType.number },
|
||||
],
|
||||
});
|
||||
```
|
||||
|
||||
`refId` needs to be set to tell Grafana which query that generated this date frame.
|
||||
|
||||
Next, we'll add the actual values to the data frame. Don't worry about the math used to calculate the values.
|
||||
|
||||
1. Create a couple of helper variables:
|
||||
|
||||
```ts
|
||||
// duration of the time range, in milliseconds.
|
||||
const duration = to - from;
|
||||
|
||||
// step determines how close in time (ms) the points will be to each other.
|
||||
const step = duration / 1000;
|
||||
```
|
||||
|
||||
1. Add the values to the data frame:
|
||||
|
||||
```ts
|
||||
for (let t = 0; t < duration; t += step) {
|
||||
frame.add({ time: from + t, value: Math.sin((2 * Math.PI * t) / duration) });
|
||||
}
|
||||
```
|
||||
|
||||
The `frame.add()` accepts an object where the keys corresponds to the name of each field in the data frame.
|
||||
|
||||
1. Return the data frame:
|
||||
|
||||
```ts
|
||||
return frame;
|
||||
```
|
||||
|
||||
1. Rebuild the plugin and try it out.
|
||||
|
||||
Your data source is now sending data frames that Grafana can visualize. Next, we'll look at how you can control the frequency of the sine wave by defining a _query_.
|
||||
|
||||
> In this example, we're generating timestamps from the current time range. This means that you'll get the same graph no matter what time range you're using. In practice, you'd instead use the timestamps returned by your database.
|
||||
|
||||
## Define a query
|
||||
|
||||
Most data sources offer a way to query specific data. MySQL and PostgreSQL use SQL, while Prometheus has its own query language, called _PromQL_. No matter what query language your databases are using, Grafana lets you build support for it.
|
||||
|
||||
Add support for custom queries to your data source, by implementing your own _query editor_, a React component that enables users to build their own queries, through a user-friendly graphical interface.
|
||||
|
||||
A query editor can be as simple as a text field where the user edits the raw query text, or it can provide a more user-friendly form with drop-down menus and switches, that later gets converted into the raw query text before it gets sent off to the database.
|
||||
|
||||
### Define the query model
|
||||
|
||||
The first step in designing your query editor is to define its _query model_. The query model defines the user input to your data source.
|
||||
|
||||
We want to be able to control the frequency of the sine wave, so let's add another property.
|
||||
|
||||
1. Add a new number property called `frequency` to the query model:
|
||||
|
||||
**src/types.ts**
|
||||
|
||||
```ts
|
||||
export interface MyQuery extends DataQuery {
|
||||
queryText?: string;
|
||||
constant: number;
|
||||
frequency: number;
|
||||
}
|
||||
```
|
||||
|
||||
1. Set a default value to the new `frequency` property:
|
||||
|
||||
```ts
|
||||
export const defaultQuery: Partial<MyQuery> = {
|
||||
constant: 6.5,
|
||||
frequency: 1.0,
|
||||
};
|
||||
```
|
||||
|
||||
### Bind the model to a form
|
||||
|
||||
Now that you've defined the query model you wish to support, the next step is to bind the model to a form. The `FormField` is a text field component from `grafana/ui` that lets you register a listener which will be invoked whenever the form field value changes.
|
||||
|
||||
1. Define the `frequency` from the `query` object and add a new form field to the query editor to control the new frequency property in the `render` method.
|
||||
|
||||
**QueryEditor.tsx**
|
||||
|
||||
```ts
|
||||
const { queryText, constant, frequency } = query;
|
||||
```
|
||||
|
||||
```ts
|
||||
<InlineField label="Frequency" labelWidth={16}>
|
||||
<Input onChange={onFrequencyChange} value={frequency} />
|
||||
</InlineField>
|
||||
```
|
||||
|
||||
1. Add a event listener for the new property.
|
||||
|
||||
```ts
|
||||
const onFrequencyChange = (event: ChangeEvent<HTMLInputElement>) => {
|
||||
onChange({ ...query, frequency: parseFloat(event.target.value) });
|
||||
// executes the query
|
||||
onRunQuery();
|
||||
};
|
||||
```
|
||||
|
||||
The registered listener, `onFrequencyChange`, calls `onChange` to update the current query with the value from the form field.
|
||||
|
||||
`onRunQuery();` tells Grafana to run the query after each change. For fast queries, this is recommended to provide a more responsive experience.
|
||||
|
||||
### Use the property
|
||||
|
||||
The new query model is now ready to use in our `query` method.
|
||||
|
||||
1. In the `query` method, use the `frequency` property to adjust our equation.
|
||||
|
||||
```ts
|
||||
frame.add({ time: from + t, value: Math.sin((2 * Math.PI * query.frequency * t) / duration) });
|
||||
```
|
||||
|
||||
## Configure your data source
|
||||
|
||||
To access a specific data source, you often need to configure things like hostname, credentials, or authentication method. A _config editor_ lets your users configure your data source plugin to fit their needs.
|
||||
|
||||
The config editor looks similar to the query editor, in that it defines a model and binds it to a form.
|
||||
|
||||
Since we're not actually connecting to an external database in our sine wave example, we don't really need many options. To show you how you can add an option however, we're going to add the _wave resolution_ as an option.
|
||||
|
||||
The resolution controls how close in time the data points are to each other. A higher resolution means more points closer together, at the cost of more data being processed.
|
||||
|
||||
### Define the options model
|
||||
|
||||
1. Add a new number property called `resolution` to the options model.
|
||||
|
||||
**types.ts**
|
||||
|
||||
```ts
|
||||
export interface MyDataSourceOptions extends DataSourceJsonData {
|
||||
path?: string;
|
||||
resolution?: number;
|
||||
}
|
||||
```
|
||||
|
||||
### Bind the model to a form
|
||||
|
||||
Just like query editor, the form field in the config editor calls the registered listener whenever the value changes.
|
||||
|
||||
1. Add a new form field to the query editor to control the new resolution option.
|
||||
|
||||
**ConfigEditor.tsx**
|
||||
|
||||
```ts
|
||||
<InlineField label="Resolution" labelWidth={12}>
|
||||
<Input onChange={onResolutionChange} value={jsonData.resolution || ''} placeholder="Enter a number" width={40} />
|
||||
</InlineField>
|
||||
```
|
||||
|
||||
1. Add a event listener for the new option.
|
||||
|
||||
```ts
|
||||
const onResolutionChange = (event: ChangeEvent<HTMLInputElement>) => {
|
||||
const jsonData = {
|
||||
...options.jsonData,
|
||||
resolution: parseFloat(event.target.value),
|
||||
};
|
||||
onOptionsChange({ ...options, jsonData });
|
||||
};
|
||||
```
|
||||
|
||||
The `onResolutionChange` listener calls `onOptionsChange` to update the current options with the value from the form field.
|
||||
|
||||
### Use the option
|
||||
|
||||
1. Create a property called `resolution` to the `DataSource` class.
|
||||
|
||||
```ts
|
||||
export class DataSource extends DataSourceApi<MyQuery, MyDataSourceOptions> {
|
||||
resolution: number;
|
||||
|
||||
constructor(instanceSettings: DataSourceInstanceSettings<MyDataSourceOptions>) {
|
||||
super(instanceSettings);
|
||||
|
||||
this.resolution = instanceSettings.jsonData.resolution || 1000.0;
|
||||
}
|
||||
|
||||
// ...
|
||||
```
|
||||
|
||||
1. In the `query` method, use the `resolution` property to calculate the step size.
|
||||
|
||||
**src/datasource.ts**
|
||||
|
||||
```ts
|
||||
const step = duration / this.resolution;
|
||||
```
|
||||
|
||||
## Get data from an external API
|
||||
|
||||
So far, you've generated the data returned by the data source. A more realistic use case would be to fetch data from an external API.
|
||||
|
||||
While you can use something like [axios](https://github.com/axios/axios) or the [Fetch API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) to make requests, we recommend using the [`getBackendSrv` function](https://github.com/grafana/grafana/blob/main/packages/grafana-runtime/src/services/backendSrv.ts) from the [`grafana-runtime` package](https://github.com/grafana/grafana/tree/main/packages/grafana-runtime).
|
||||
|
||||
The main advantage of `getBackendSrv` is that it proxies requests through the Grafana server rather making the request from the browser. This is strongly recommended when making authenticated requests to an external API. For more information on authenticating external requests, refer to [Add authentication for data source plugins]({{< relref "../extend-a-plugin/add-authentication-for-data-source-plugins.md" >}}).
|
||||
|
||||
1. Import `getBackendSrv`.
|
||||
|
||||
**src/datasource.ts**
|
||||
|
||||
```ts
|
||||
import { getBackendSrv } from '@grafana/runtime';
|
||||
```
|
||||
|
||||
1. Create a helper method `doRequest` and use the `datasourceRequest` method to make a request to your API. Replace `https://api.example.com/metrics` to point to your own API endpoint.
|
||||
|
||||
```ts
|
||||
async doRequest(query: MyQuery) {
|
||||
const result = await getBackendSrv().datasourceRequest({
|
||||
method: "GET",
|
||||
url: "https://api.example.com/metrics",
|
||||
params: query,
|
||||
})
|
||||
|
||||
return result;
|
||||
}
|
||||
```
|
||||
|
||||
1. Make a request for each query. `Promises.all` waits for all requests to finish before returning the data.
|
||||
|
||||
```ts
|
||||
async query(options: DataQueryRequest<MyQuery>): Promise<DataQueryResponse> {
|
||||
const promises = options.targets.map((query) =>
|
||||
this.doRequest(query).then((response) => {
|
||||
const frame = new MutableDataFrame({
|
||||
refId: query.refId,
|
||||
fields: [
|
||||
{ name: "Time", type: FieldType.time },
|
||||
{ name: "Value", type: FieldType.number },
|
||||
],
|
||||
});
|
||||
|
||||
response.data.forEach((point: any) => {
|
||||
frame.appendRow([point.time, point.value]);
|
||||
});
|
||||
|
||||
return frame;
|
||||
})
|
||||
);
|
||||
|
||||
return Promise.all(promises).then((data) => ({ data }));
|
||||
}
|
||||
```
|
||||
|
||||
## Summary
|
||||
|
||||
In this tutorial you built a complete data source plugin for Grafana that uses a query editor to control what data to visualize. You've added a data source option, commonly used to set connection options and more.
|
||||
|
||||
### Learn more
|
||||
|
||||
Learn how you can improve your plugin even further, by reading our advanced guides:
|
||||
|
||||
- [Add support for variables](/docs/grafana/latest/developers/plugins/add-support-for-variables/)
|
||||
- [Add support for annotations](/docs/grafana/latest/developers/plugins/add-support-for-annotations/)
|
||||
- [Add support for Explore queries](/docs/grafana/latest/developers/plugins/add-support-for-explore-queries/)
|
||||
- [Build a logs data source](/docs/grafana/latest/developers/plugins/build-a-logs-data-source-plugin/)
|
||||
@@ -1,163 +0,0 @@
|
||||
---
|
||||
aliases:
|
||||
- ../../../plugins/build-a-logs-data-source-plugin/
|
||||
description: How to build a logs data source plugin.
|
||||
keywords:
|
||||
- grafana
|
||||
- plugins
|
||||
- plugin
|
||||
- logs
|
||||
- logs data source
|
||||
- datasource
|
||||
labels:
|
||||
products:
|
||||
- enterprise
|
||||
- oss
|
||||
title: Build a logs data source plugin
|
||||
weight: 500
|
||||
---
|
||||
|
||||
# Build a logs data source plugin
|
||||
|
||||
Grafana data source plugins support metrics, logs, and other data types. The steps to build a logs data source plugin are largely the same as for a metrics data source, but there are a few differences which we will explain in this guide.
|
||||
|
||||
## Before you begin
|
||||
|
||||
This guide assumes that you're already familiar with how to [Build a data source plugin]({{< relref "./build-a-data-source-plugin" >}}) for metrics. We recommend that you review this material before continuing.
|
||||
|
||||
## Add logs support to your data source
|
||||
|
||||
To add logs support to an existing data source, you need to:
|
||||
|
||||
1. Enable logs support
|
||||
1. Construct the log data
|
||||
|
||||
When these steps are done, then you can improve the user experience with one or more [optional features](#enhance-your-logs-data-source-plugin-with-optional-features).
|
||||
|
||||
### Step 1: Enable logs support
|
||||
|
||||
Tell Grafana that your data source plugin can return log data, by adding `"logs": true` to the [plugin.json]({{< relref "../../metadata.md" >}}) file.
|
||||
|
||||
```json
|
||||
{
|
||||
"logs": true
|
||||
}
|
||||
```
|
||||
|
||||
### Step 2: Construct the log data
|
||||
|
||||
As it does with metrics data, Grafana expects your plugin to return log data as a [data frame]({{< relref "../../introduction-to-plugin-development/data-frames.md" >}}).
|
||||
|
||||
To return log data, return a data frame with at least one time field and one text field from the data source's `query` method.
|
||||
|
||||
**Example:**
|
||||
|
||||
```ts
|
||||
const frame = new MutableDataFrame({
|
||||
refId: query.refId,
|
||||
fields: [
|
||||
{ name: 'time', type: FieldType.time },
|
||||
{ name: 'content', type: FieldType.string },
|
||||
],
|
||||
});
|
||||
|
||||
frame.add({ time: 1589189388597, content: 'user registered' });
|
||||
frame.add({ time: 1589189406480, content: 'user logged in' });
|
||||
```
|
||||
|
||||
That's all you need to start returning log data from your data source. Go ahead and try it out in [Explore]({{< relref "../../../../explore" >}}) or by adding a [Logs panel]({{< relref "../../../../panels-visualizations/visualizations/logs" >}}).
|
||||
|
||||
Congratulations, you just wrote your first logs data source plugin! Next, let's look at a couple of features that can further improve the experience for the user.
|
||||
|
||||
## Enhance your logs data source plugin with optional features
|
||||
|
||||
Add visualization type hints, labels, and other optional features to logs.
|
||||
|
||||
### Add a preferred visualization type hint to the data frame
|
||||
|
||||
To make sure Grafana recognizes data as logs and shows logs visualization automatically in Explore, set `meta.preferredVisualisationType` to `'logs'` in the returned data frame. See [Selecting preferred visualization section]({{< relref "../extend-a-plugin/add-support-for-explore-queries#select-a-preferred-visualization-type" >}})
|
||||
|
||||
**Example:**
|
||||
|
||||
```ts
|
||||
const frame = new MutableDataFrame({
|
||||
refId: query.refId,
|
||||
meta: {
|
||||
preferredVisualisationType: 'logs',
|
||||
},
|
||||
fields: [
|
||||
{ name: 'time', type: FieldType.time },
|
||||
{ name: 'content', type: FieldType.string },
|
||||
],
|
||||
});
|
||||
```
|
||||
|
||||
### Add labels to your logs
|
||||
|
||||
Many log systems let you query logs based on metadata, or _labels_, to help filter log lines.
|
||||
|
||||
Add labels to a stream of logs by setting the `labels` property on the Field.
|
||||
|
||||
**Example**:
|
||||
|
||||
```ts
|
||||
const frame = new MutableDataFrame({
|
||||
refId: query.refId,
|
||||
fields: [
|
||||
{ name: 'time', type: FieldType.time },
|
||||
{ name: 'content', type: FieldType.string, labels: { filename: 'file.txt' } },
|
||||
],
|
||||
});
|
||||
|
||||
frame.add({ time: 1589189388597, content: 'user registered' });
|
||||
frame.add({ time: 1589189406480, content: 'user logged in' });
|
||||
```
|
||||
|
||||
### Extract detected fields from your logs
|
||||
|
||||
Add additional information about each log line by supplying more data frame fields.
|
||||
|
||||
If a data frame has more than one text field, then Grafana assumes the first field in the data frame to be the actual log line. Grafana treats subsequent text fields as detected fields.
|
||||
|
||||
Any number of custom fields can be added to your data frame; Grafana comes with two dedicated fields: `levels` and `id`.
|
||||
|
||||
#### Levels
|
||||
|
||||
To set the level for each log line, add a `level` field.
|
||||
|
||||
**Example:**
|
||||
|
||||
```ts
|
||||
const frame = new MutableDataFrame({
|
||||
refId: query.refId,
|
||||
fields: [
|
||||
{ name: 'time', type: FieldType.time },
|
||||
{ name: 'content', type: FieldType.string, labels: { filename: 'file.txt' } },
|
||||
{ name: 'level', type: FieldType.string },
|
||||
],
|
||||
});
|
||||
|
||||
frame.add({ time: 1589189388597, content: 'user registered', level: 'info' });
|
||||
frame.add({ time: 1589189406480, content: 'unknown error', level: 'error' });
|
||||
```
|
||||
|
||||
#### 'id' for assigning unique identifiers to log lines
|
||||
|
||||
By default, Grafana offers basic support for deduplicating log lines. You can improve the support by adding an `id` field to explicitly assign identifiers to each log line.
|
||||
|
||||
**Example:**
|
||||
|
||||
```ts
|
||||
const frame = new MutableDataFrame({
|
||||
refId: query.refId,
|
||||
fields: [
|
||||
{ name: 'time', type: FieldType.time },
|
||||
{ name: 'content', type: FieldType.string, labels: { filename: 'file.txt' } },
|
||||
{ name: 'level', type: FieldType.string },
|
||||
{ name: 'id', type: FieldType.string },
|
||||
],
|
||||
});
|
||||
|
||||
frame.add({ time: 1589189388597, content: 'user registered', level: 'info', id: 'd3b07384d113edec49eaa6238ad5ff00' });
|
||||
frame.add({ time: 1589189406480, content: 'unknown error', level: 'error', id: 'c157a79031e1c40f85931829bc5fc552' });
|
||||
```
|
||||
@@ -1,240 +0,0 @@
|
||||
---
|
||||
description: how to use D3.js in your panel plugins.
|
||||
keywords:
|
||||
- grafana
|
||||
- plugins
|
||||
- plugin
|
||||
- d3js
|
||||
- d3
|
||||
- panel
|
||||
- panel plugin
|
||||
labels:
|
||||
products:
|
||||
- enterprise
|
||||
- oss
|
||||
title: Build a panel plugin with D3.js
|
||||
weight: 200
|
||||
---
|
||||
|
||||
## Introduction
|
||||
|
||||
Panels are the building blocks of Grafana, and allow you to visualize data in different ways. This tutorial gives you a hands-on walkthrough of creating your own panel using [D3.js](https://d3js.org/).
|
||||
|
||||
For more information about panels, refer to the documentation on [Panels](/docs/grafana/latest/features/panels/panels/).
|
||||
|
||||
In this tutorial, you'll:
|
||||
|
||||
- Build a simple panel plugin to visualize a bar chart.
|
||||
- Learn how to use D3.js to build a panel using data-driven transformations.
|
||||
|
||||
{{% class "prerequisite-section" %}}
|
||||
|
||||
### Prerequisites
|
||||
|
||||
- Grafana 7.0
|
||||
- [LTS](https://nodejs.dev/en/about/releases/) version of Node.js
|
||||
- yarn
|
||||
{{% /class %}}
|
||||
|
||||
## Set up your environment
|
||||
|
||||
{{< docs/shared lookup="tutorials/set-up-environment.md" source="grafana" version="latest" >}}
|
||||
|
||||
## Create a new plugin
|
||||
|
||||
{{< docs/shared lookup="tutorials/create-plugin.md" source="grafana" version="latest" >}}
|
||||
|
||||
## Data-driven documents
|
||||
|
||||
[D3.js](https://d3js.org/) is a JavaScript library for manipulating documents based on data. It lets you transform arbitrary data into HTML, and is commonly used for creating visualizations.
|
||||
|
||||
Wait a minute. Manipulating documents based on data? That's sounds an awful lot like React. In fact, much of what you can accomplish with D3 you can already do with React. So before we start looking at D3, let's see how you can create an SVG from data, using only React.
|
||||
|
||||
In **SimplePanel.tsx**, change `SimplePanel` to return an `svg` with a `rect` element.
|
||||
|
||||
```ts
|
||||
export const SimplePanel = ({ options, data, width, height }: Props) => {
|
||||
const theme = useTheme();
|
||||
|
||||
return (
|
||||
<svg width={width} height={height}>
|
||||
<rect x={0} y={0} width={10} height={10} fill={theme.palette.greenBase} />
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
One single rectangle might not be very exciting, so let's see how you can create rectangles from data.
|
||||
|
||||
1. Create some data that we can visualize.
|
||||
|
||||
```ts
|
||||
const values = [4, 8, 15, 16, 23, 42];
|
||||
```
|
||||
|
||||
1. Calculate the height of each bar based on the height of the panel.
|
||||
|
||||
```ts
|
||||
const barHeight = height / values.length;
|
||||
```
|
||||
|
||||
1. Inside a SVG group, `g`, create a `rect` element for every value in the dataset. Each rectangle uses the value as its width.
|
||||
|
||||
```ts
|
||||
return (
|
||||
<svg width={width} height={height}>
|
||||
<g>
|
||||
{values.map((value, i) => (
|
||||
<rect x={0} y={i * barHeight} width={value} height={barHeight - 1} fill={theme.palette.greenBase} />
|
||||
))}
|
||||
</g>
|
||||
</svg>
|
||||
);
|
||||
```
|
||||
|
||||
1. Rebuild the plugin and reload your browser to see the changes you've made.
|
||||
|
||||
As you can see, React is perfectly capable of dynamically creating HTML elements. In fact, creating elements using React is often faster than creating them using D3.
|
||||
|
||||
So why would you use even use D3? In the next step, we'll see how you can take advantage of D3's data transformations.
|
||||
|
||||
## Transform data using D3.js
|
||||
|
||||
In this step, you'll see how you can transform data using D3 before rendering it using React.
|
||||
|
||||
D3 is already bundled with Grafana, and you can access it by importing the `d3` package. However, we're going to need the type definitions while developing.
|
||||
|
||||
1. Install the D3 type definitions:
|
||||
|
||||
```bash
|
||||
yarn add --dev @types/d3
|
||||
```
|
||||
|
||||
1. Import `d3` in **SimplePanel.tsx**.
|
||||
|
||||
```ts
|
||||
import * as d3 from 'd3';
|
||||
```
|
||||
|
||||
In the previous step, we had to define the width of each bar in pixels. Instead, let's use _scales_ from the D3 library to make the width of each bar depend on the width of the panel.
|
||||
|
||||
Scales are functions that map a range of values to another range of values. In this case, we want to map the values in our datasets to a position within our panel.
|
||||
|
||||
1. Create a scale to map a value between 0 and the maximum value in the dataset, to a value between 0 and the width of the panel. We'll be using this to calculate the width of the bar.
|
||||
|
||||
```ts
|
||||
const scale = d3
|
||||
.scaleLinear()
|
||||
.domain([0, d3.max(values) || 0.0])
|
||||
.range([0, width]);
|
||||
```
|
||||
|
||||
1. Pass the value to the scale function to calculate the width of the bar in pixels.
|
||||
|
||||
```ts
|
||||
return (
|
||||
<svg width={width} height={height}>
|
||||
<g>
|
||||
{values.map((value, i) => (
|
||||
<rect x={0} y={i * barHeight} width={scale(value)} height={barHeight - 1} fill={theme.palette.greenBase} />
|
||||
))}
|
||||
</g>
|
||||
</svg>
|
||||
);
|
||||
```
|
||||
|
||||
As you can see, even if we're using React to render the actual elements, the D3 library contains useful tools that you can use to transform your data before rendering it.
|
||||
|
||||
## Add an axis
|
||||
|
||||
Another useful tool in the D3 toolbox is the ability to generate _axes_. Adding axes to our chart makes it easier for the user to understand the differences between each bar.
|
||||
|
||||
Let's see how you can use D3 to add a horizontal axis to your bar chart.
|
||||
|
||||
1. Create a D3 axis. Notice that by using the same scale as before, we make sure that the bar width aligns with the ticks on the axis.
|
||||
|
||||
```ts
|
||||
const axis = d3.axisBottom(scale);
|
||||
```
|
||||
|
||||
1. Generate the axis. While D3 needs to generate the elements for the axis, we can encapsulate it by generating them within an anonymous function which we pass as a `ref` to a group element `g`.
|
||||
|
||||
```ts
|
||||
<g
|
||||
ref={(node) => {
|
||||
d3.select(node).call(axis as any);
|
||||
}}
|
||||
/>
|
||||
```
|
||||
|
||||
By default, the axis renders at the top of the SVG element. We'd like to move it to the bottom, but to do that, we first need to make room for it by decreasing the height of each bar.
|
||||
|
||||
1. Calculate the new bar height based on the padded height.
|
||||
|
||||
```ts
|
||||
const padding = 20;
|
||||
const chartHeight = height - padding;
|
||||
const barHeight = chartHeight / values.length;
|
||||
```
|
||||
|
||||
1. Translate the axis by adding a transform to the `g` element.
|
||||
|
||||
```ts
|
||||
<g
|
||||
transform={`translate(0, ${chartHeight})`}
|
||||
ref={(node) => {
|
||||
d3.select(node).call(axis as any);
|
||||
}}
|
||||
/>
|
||||
```
|
||||
|
||||
Congrats! You've created a simple and responsive bar chart.
|
||||
|
||||
## Complete example
|
||||
|
||||
```ts
|
||||
import React from 'react';
|
||||
import { PanelProps } from '@grafana/data';
|
||||
import { SimpleOptions } from 'types';
|
||||
import { useTheme } from '@grafana/ui';
|
||||
import * as d3 from 'd3';
|
||||
|
||||
interface Props extends PanelProps<SimpleOptions> {}
|
||||
|
||||
export const SimplePanel = ({ options, data, width, height }: Props) => {
|
||||
const theme = useTheme();
|
||||
|
||||
const values = [4, 8, 15, 16, 23, 42];
|
||||
|
||||
const scale = d3
|
||||
.scaleLinear()
|
||||
.domain([0, d3.max(values) || 0.0])
|
||||
.range([0, width]);
|
||||
|
||||
const axis = d3.axisBottom(scale);
|
||||
|
||||
const padding = 20;
|
||||
const chartHeight = height - padding;
|
||||
const barHeight = chartHeight / values.length;
|
||||
|
||||
return (
|
||||
<svg width={width} height={height}>
|
||||
<g>
|
||||
{values.map((value, i) => (
|
||||
<rect x={0} y={i * barHeight} width={scale(value)} height={barHeight - 1} fill={theme.palette.greenBase} />
|
||||
))}
|
||||
</g>
|
||||
<g
|
||||
transform={`translate(0, ${chartHeight})`}
|
||||
ref={(node) => {
|
||||
d3.select(node).call(axis as any);
|
||||
}}
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
## Summary
|
||||
|
||||
In this tutorial you built a panel plugin with D3.js.
|
||||
@@ -1,264 +0,0 @@
|
||||
---
|
||||
description: Learn how to create a custom visualization for your dashboards.
|
||||
keywords:
|
||||
- grafana
|
||||
- plugins
|
||||
- plugin
|
||||
- visualization
|
||||
- custom visualization
|
||||
- dashboard
|
||||
- dashboards
|
||||
labels:
|
||||
products:
|
||||
- enterprise
|
||||
- oss
|
||||
title: Build a panel plugin
|
||||
weight: 100
|
||||
---
|
||||
|
||||
## Introduction
|
||||
|
||||
Panels are the building blocks of Grafana. They allow you to visualize data in different ways. While Grafana has several types of panels already built-in, you can also build your own panel, to add support for other visualizations.
|
||||
|
||||
For more information about panels, refer to the documentation on [Panels](/docs/grafana/latest/panels/).
|
||||
|
||||
{{% class "prerequisite-section" %}}
|
||||
|
||||
### Prerequisites
|
||||
|
||||
- Grafana >=7.0
|
||||
- [LTS](https://nodejs.dev/en/about/releases/) version of Node.js
|
||||
- yarn
|
||||
{{% /class %}}
|
||||
|
||||
## Set up your environment
|
||||
|
||||
{{< docs/shared lookup="tutorials/set-up-environment.md" source="grafana" version="latest" >}}
|
||||
|
||||
## Create a new plugin
|
||||
|
||||
{{< docs/shared lookup="tutorials/create-plugin.md" source="grafana" version="latest" >}}
|
||||
|
||||
## Anatomy of a plugin
|
||||
|
||||
{{< docs/shared lookup="tutorials/plugin-anatomy.md" source="grafana" version="latest" >}}
|
||||
|
||||
## Panel plugins
|
||||
|
||||
Since Grafana 6.x, panels are [ReactJS components](https://reactjs.org/docs/components-and-props.html).
|
||||
|
||||
Prior to Grafana 6.0, plugins were written in [AngularJS](https://angular.io/). Even though we still support plugins written in AngularJS, we highly recommend that you write new plugins using ReactJS.
|
||||
|
||||
### Panel properties
|
||||
|
||||
The [PanelProps](https://github.com/grafana/grafana/blob/747b546c260f9a448e2cb56319f796d0301f4bb9/packages/grafana-data/src/types/panel.ts#L27-L40) interface exposes runtime information about the panel, such as panel dimensions, and the current time range.
|
||||
|
||||
You can access the panel properties through `props`, as seen in your plugin.
|
||||
|
||||
**src/SimplePanel.tsx**
|
||||
|
||||
```js
|
||||
const { options, data, width, height } = props;
|
||||
```
|
||||
|
||||
### Development workflow
|
||||
|
||||
Next, you'll learn the basic workflow of making a change to your panel, building it, and reloading Grafana to reflect the changes you made.
|
||||
|
||||
First, you need to add your panel to a dashboard:
|
||||
|
||||
1. Open Grafana in your browser.
|
||||
1. Create a new dashboard, and add a new panel.
|
||||
1. Select your panel from the list of visualization types.
|
||||
1. Save the dashboard.
|
||||
|
||||
Now that you can view your panel, try making a change to the panel plugin:
|
||||
|
||||
1. In `SimplePanel.tsx`, change the fill color of the circle.
|
||||
1. Run `yarn dev` to build the plugin.
|
||||
1. In the browser, reload Grafana with the new changes.
|
||||
|
||||
## Add panel options
|
||||
|
||||
Sometimes you want to offer the users of your panel an option to configure the behavior of your plugin. By configuring _panel options_ for your plugin, your panel will be able to accept user input.
|
||||
|
||||
In the previous step, you changed the fill color of the circle in the code. Let's change the code so that the plugin user can configure the color from the panel editor.
|
||||
|
||||
#### Add an option
|
||||
|
||||
Panel options are defined in a _panel options object_. `SimpleOptions` is an interface that describes the options object.
|
||||
|
||||
1. In `types.ts`, add a `CircleColor` type to hold the colors the users can choose from:
|
||||
|
||||
```
|
||||
type CircleColor = 'red' | 'green' | 'blue';
|
||||
```
|
||||
|
||||
1. In the `SimpleOptions` interface, add a new option called `color`:
|
||||
|
||||
```
|
||||
color: CircleColor;
|
||||
```
|
||||
|
||||
Here's the updated options definition:
|
||||
|
||||
**src/types.ts**
|
||||
|
||||
```ts
|
||||
type SeriesSize = 'sm' | 'md' | 'lg';
|
||||
type CircleColor = 'red' | 'green' | 'blue';
|
||||
|
||||
// interface defining panel options type
|
||||
export interface SimpleOptions {
|
||||
text: string;
|
||||
showSeriesCount: boolean;
|
||||
seriesCountSize: SeriesSize;
|
||||
color: CircleColor;
|
||||
}
|
||||
```
|
||||
|
||||
#### Add an option control
|
||||
|
||||
To change the option from the panel editor, you need to bind the `color` option to an _option control_.
|
||||
|
||||
Grafana supports a range of option controls, such as text inputs, switches, and radio groups.
|
||||
|
||||
Let's create a radio control and bind it to the `color` option.
|
||||
|
||||
1. In `src/module.ts`, add the control at the end of the builder:
|
||||
|
||||
```ts
|
||||
.addRadio({
|
||||
path: 'color',
|
||||
name: 'Circle color',
|
||||
defaultValue: 'red',
|
||||
settings: {
|
||||
options: [
|
||||
{
|
||||
value: 'red',
|
||||
label: 'Red',
|
||||
},
|
||||
{
|
||||
value: 'green',
|
||||
label: 'Green',
|
||||
},
|
||||
{
|
||||
value: 'blue',
|
||||
label: 'Blue',
|
||||
},
|
||||
],
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
The `path` is used to bind the control to an option. You can bind a control to nested option by specifying the full path within a options object, for example `colors.background`.
|
||||
|
||||
Grafana builds an options editor for you and displays it in the panel editor sidebar in the **Display** section.
|
||||
|
||||
#### Use the new option
|
||||
|
||||
You're almost done. You've added a new option and a corresponding control to change the value. But the plugin isn't using the option yet. Let's change that.
|
||||
|
||||
1. To convert option value to the colors used by the current theme, add a `switch` statement right before the `return` statement in `SimplePanel.tsx`.
|
||||
|
||||
**src/SimplePanel.tsx**
|
||||
|
||||
```ts
|
||||
let color: string;
|
||||
switch (options.color) {
|
||||
case 'red':
|
||||
color = theme.palette.redBase;
|
||||
break;
|
||||
case 'green':
|
||||
color = theme.palette.greenBase;
|
||||
break;
|
||||
case 'blue':
|
||||
color = theme.palette.blue95;
|
||||
break;
|
||||
}
|
||||
```
|
||||
|
||||
1. Configure the circle to use the color.
|
||||
|
||||
```ts
|
||||
<g>
|
||||
<circle style={{ fill: color }} r={100} />
|
||||
</g>
|
||||
```
|
||||
|
||||
Now, when you change the color in the panel editor, the fill color of the circle changes as well.
|
||||
|
||||
## Create dynamic panels using data frames
|
||||
|
||||
Most panels visualize dynamic data from a Grafana data source. In this step, you'll create one circle per series, each with a radius equal to the last value in the series.
|
||||
|
||||
> To use data from queries in your panel, you need to set up a data source. If you don't have one available, you can use the [TestData](/docs/grafana/latest/features/datasources/testdata) data source while developing.
|
||||
|
||||
The results from a data source query within your panel are available in the `data` property inside your panel component.
|
||||
|
||||
```ts
|
||||
const { data } = props;
|
||||
```
|
||||
|
||||
`data.series` contains the series returned from a data source query. Each series is represented as a data structure called _data frame_. A data frame resembles a table, where data is stored by columns, or _fields_, instead of rows. Every value in a field share the same data type, such as string, number, or time.
|
||||
|
||||
Here's an example of a data frame with a time field, `Time`, and a number field, `Value`:
|
||||
|
||||
| Time | Value |
|
||||
| ------------- | ----- |
|
||||
| 1589189388597 | 32.4 |
|
||||
| 1589189406480 | 27.2 |
|
||||
| 1589189513721 | 15.0 |
|
||||
|
||||
Let's see how you can retrieve data from a data frame and use it in your visualization.
|
||||
|
||||
1. Get the last value of each field of type `number`, by adding the following to `SimplePanel.tsx`, before the `return` statement:
|
||||
|
||||
```ts
|
||||
const radii = data.series
|
||||
.map((series) => series.fields.find((field) => field.type === 'number'))
|
||||
.map((field) => field?.values.get(field.values.length - 1));
|
||||
```
|
||||
|
||||
`radii` will contain the last values in each of the series that are returned from a data source query. You'll use these to set the radius for each circle.
|
||||
|
||||
1. Change the `svg` element to the following:
|
||||
|
||||
```ts
|
||||
<svg
|
||||
className={styles.svg}
|
||||
width={width}
|
||||
height={height}
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlnsXlink="http://www.w3.org/1999/xlink"
|
||||
viewBox={`0 -${height / 2} ${width} ${height}`}
|
||||
>
|
||||
<g fill={color}>
|
||||
{radii.map((radius, index) => {
|
||||
const step = width / radii.length;
|
||||
return <circle r={radius} transform={`translate(${index * step + step / 2}, 0)`} />;
|
||||
})}
|
||||
</g>
|
||||
</svg>
|
||||
```
|
||||
|
||||
Note how we're creating a `<circle>` element for each value in `radii`:
|
||||
|
||||
```ts
|
||||
{
|
||||
radii.map((radius, index) => {
|
||||
const step = width / radii.length;
|
||||
return <circle r={radius} transform={`translate(${index * step + step / 2}, 0)`} />;
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
We use the `transform` here to distribute the circle horizontally within the panel.
|
||||
|
||||
1. Rebuild your plugin and try it out by adding multiple queries to the panel. Refresh the dashboard.
|
||||
|
||||
If you want to know more about data frames, check out our introduction to [Data frames](/docs/grafana/latest/developers/plugins/data-frames/).
|
||||
|
||||
## Summary
|
||||
|
||||
In this tutorial you learned how to create a custom visualization for your dashboards.
|
||||
@@ -1,166 +0,0 @@
|
||||
---
|
||||
aliases:
|
||||
- ../../../plugins/build-a-streaming-data-source-plugin/
|
||||
description: How to build a streaming data source plugin.
|
||||
keywords:
|
||||
- grafana
|
||||
- plugins
|
||||
- plugin
|
||||
- streaming
|
||||
- streaming data source
|
||||
- datasource
|
||||
labels:
|
||||
products:
|
||||
- enterprise
|
||||
- oss
|
||||
title: Build a streaming data source plugin
|
||||
weight: 600
|
||||
---
|
||||
|
||||
# Build a streaming data source plugin
|
||||
|
||||
In Grafana, you can set your dashboards to automatically refresh at a certain interval, no matter what data source you use. Unfortunately, this means that your queries are requesting all the data to be sent again, regardless of whether the data has actually changed. Adding streaming to a plugin helps reduce queries so your dashboard is only updated when new data becomes available.
|
||||
|
||||
## Before you begin
|
||||
|
||||
This guide assumes that you're already familiar with how to [Build a data source plugin]({{< relref "./build-a-data-source-plugin" >}})
|
||||
|
||||
Grafana uses [RxJS](https://rxjs.dev/) to continuously send data from a data source to a panel visualization.
|
||||
|
||||
> **Note:** To learn more about RxJs, refer to the [RxJS documentation](https://rxjs.dev/guide/overview).
|
||||
|
||||
## Add streaming to your data source
|
||||
|
||||
Enable streaming for your data source plugin to update your dashboard when new data becomes available.
|
||||
|
||||
For example, a streaming data source plugin can connect to a websocket, or subscribe to a message bus, and update the visualization whenever a new message is available.
|
||||
|
||||
### Step 1: Edit the `plugin.json` file
|
||||
|
||||
Enable streaming for your data source in the `plugin.json` file.
|
||||
|
||||
```json
|
||||
{
|
||||
"streaming": true
|
||||
}
|
||||
```
|
||||
|
||||
### Step 2: Change the signature of the `query` method
|
||||
|
||||
Modify the signature of the `query` method to return an `Observable` from the `rxjs` package. Make sure you remove the `async` keyword.
|
||||
|
||||
```ts
|
||||
import { Observable } from 'rxjs';
|
||||
```
|
||||
|
||||
```ts
|
||||
query(options: DataQueryRequest<MyQuery>): Observable<DataQueryResponse> {
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
### Step 3: Create an `Observable` instance for each query
|
||||
|
||||
Create an `Observable` instance for each query, and then combine them all using the `merge` function from the `rxjs` package.
|
||||
|
||||
```ts
|
||||
import { Observable, merge } from 'rxjs';
|
||||
```
|
||||
|
||||
```ts
|
||||
const observables = options.targets.map((target) => {
|
||||
return new Observable<DataQueryResponse>((subscriber) => {
|
||||
// ...
|
||||
});
|
||||
});
|
||||
|
||||
return merge(...observables);
|
||||
```
|
||||
|
||||
### Step 4: Create a `CircularDataFrame` instance
|
||||
|
||||
In the `subscribe` function, create a `CircularDataFrame` instance.
|
||||
|
||||
```ts
|
||||
import { CircularDataFrame } from '@grafana/data';
|
||||
```
|
||||
|
||||
```ts
|
||||
const frame = new CircularDataFrame({
|
||||
append: 'tail',
|
||||
capacity: 1000,
|
||||
});
|
||||
|
||||
frame.refId = query.refId;
|
||||
frame.addField({ name: 'time', type: FieldType.time });
|
||||
frame.addField({ name: 'value', type: FieldType.number });
|
||||
```
|
||||
|
||||
Circular data frames have a limited capacity. When a circular data frame reaches its capacity, the oldest data point is removed.
|
||||
|
||||
### Step 5: Send the updated data frame
|
||||
|
||||
Use `subscriber.next()` to send the updated data frame whenever you receive new updates.
|
||||
|
||||
```ts
|
||||
import { LoadingState } from '@grafana/data';
|
||||
```
|
||||
|
||||
```ts
|
||||
const intervalId = setInterval(() => {
|
||||
frame.add({ time: Date.now(), value: Math.random() });
|
||||
|
||||
subscriber.next({
|
||||
data: [frame],
|
||||
key: query.refId,
|
||||
state: LoadingState.Streaming,
|
||||
});
|
||||
}, 500);
|
||||
|
||||
return () => {
|
||||
clearInterval(intervalId);
|
||||
};
|
||||
```
|
||||
|
||||
> **Note:** In practice, you'd call `subscriber.next` as soon as you receive new data from a websocket or a message bus. In the example above, data is being received every 500 milliseconds.
|
||||
|
||||
### Example code for final `query` method
|
||||
|
||||
```ts
|
||||
query(options: DataQueryRequest<MyQuery>): Observable<DataQueryResponse> {
|
||||
const streams = options.targets.map(target => {
|
||||
const query = defaults(target, defaultQuery);
|
||||
|
||||
return new Observable<DataQueryResponse>(subscriber => {
|
||||
const frame = new CircularDataFrame({
|
||||
append: 'tail',
|
||||
capacity: 1000,
|
||||
});
|
||||
|
||||
frame.refId = query.refId;
|
||||
frame.addField({ name: 'time', type: FieldType.time });
|
||||
frame.addField({ name: 'value', type: FieldType.number });
|
||||
|
||||
const intervalId = setInterval(() => {
|
||||
frame.add({ time: Date.now(), value: Math.random() });
|
||||
|
||||
subscriber.next({
|
||||
data: [frame],
|
||||
key: query.refId,
|
||||
state: LoadingState.Streaming,
|
||||
});
|
||||
}, 100);
|
||||
|
||||
return () => {
|
||||
clearInterval(intervalId);
|
||||
};
|
||||
});
|
||||
});
|
||||
|
||||
return merge(...streams);
|
||||
}
|
||||
```
|
||||
|
||||
One limitation with this example is that the panel visualization is cleared every time you update the dashboard. If you have access to historical data, you can add it, or _backfill_ it, to the data frame before the first call to `subscriber.next()`.
|
||||
|
||||
For another example of a streaming plugin, refer to the [streaming websocket example](https://github.com/grafana/grafana-plugin-examples/tree/main/examples/datasource-streaming-websocket) on GitHub.
|
||||
@@ -1,211 +0,0 @@
|
||||
---
|
||||
description: Learn at how to create an app for Grafana.
|
||||
draft: true
|
||||
keywords:
|
||||
- grafana
|
||||
- plugins
|
||||
- plugin
|
||||
- app
|
||||
- app plugin
|
||||
labels:
|
||||
products:
|
||||
- enterprise
|
||||
- oss
|
||||
title: Build an app plugin
|
||||
weight: 700
|
||||
---
|
||||
|
||||
## Introduction
|
||||
|
||||
App plugins are Grafana plugins that can bundle data source and panel plugins within one package. They also let you create _custom pages_ within Grafana. Custom pages enable the plugin author to include things like documentation, sign-up forms, or to control other services over HTTP.
|
||||
|
||||
Data source and panel plugins will show up like normal plugins. The app pages will be available in the main menu.
|
||||
|
||||
{{% class "prerequisite-section" %}}
|
||||
|
||||
### Prerequisites
|
||||
|
||||
- Grafana 7.0
|
||||
- [LTS](https://nodejs.dev/en/about/releases/) version of Node.js
|
||||
- yarn
|
||||
{{% /class %}}
|
||||
|
||||
## Set up your environment
|
||||
|
||||
{{< docs/shared lookup="tutorials/set-up-environment.md" source="grafana" version="latest" >}}
|
||||
|
||||
## Create a new plugin
|
||||
|
||||
{{< docs/shared lookup="tutorials/create-plugin.md" source="grafana" version="latest" >}}
|
||||
|
||||
## Anatomy of a plugin
|
||||
|
||||
{{< docs/shared lookup="tutorials/plugin-anatomy.md" source="grafana" version="latest" >}}
|
||||
|
||||
## App plugins
|
||||
|
||||
App plugins let you bundle resources such as dashboards, panels, and data sources into a single plugin.
|
||||
|
||||
Any resource you want to include needs to be added to the `includes` property in the `plugin.json` file. To add a resource to your app plugin, you need to include it to the `plugin.json`.
|
||||
|
||||
Plugins that are included in an app plugin are available like any other plugin.
|
||||
|
||||
Dashboards and pages can be added to the app menu by setting `addToNav` to `true`.
|
||||
|
||||
By setting `"defaultNav": true`, users can navigate to the dashboard by clicking the app icon in the side menu.
|
||||
|
||||
## Add a custom page
|
||||
|
||||
App plugins let you extend the Grafana user interface through the use of _custom pages_.
|
||||
|
||||
Any requests sent to `/a/<plugin-id>`, e.g. `/a/myorgid-simple-app/`, are routed to the _root page_ of the app plugin. The root page is a React component that returns the content for a given route.
|
||||
|
||||
While you're free to implement your own routing, in this tutorial you'll use a tab-based navigation page that you can use by calling `onNavChange`.
|
||||
|
||||
Let's add a tab for managing server instances.
|
||||
|
||||
1. In the `src/pages` directory, add a new file called `Instances.tsx`. This component contains the content for the new tab.
|
||||
|
||||
```ts
|
||||
import { AppRootProps } from '@grafana/data';
|
||||
import React from 'react';
|
||||
|
||||
export const Instances = ({ query, path, meta }: AppRootProps) => {
|
||||
return <p>Hello</p>;
|
||||
};
|
||||
```
|
||||
|
||||
1. Register the page by adding it to the `pages` array in `src/pages/index.ts`.
|
||||
|
||||
**index.ts**
|
||||
|
||||
```ts
|
||||
import { Instances } from './Instances';
|
||||
```
|
||||
|
||||
```ts
|
||||
{
|
||||
component: Instances,
|
||||
icon: 'file-alt',
|
||||
id: 'instances',
|
||||
text: 'Instances',
|
||||
}
|
||||
```
|
||||
|
||||
1. Add the page to the app menu, by including it in `plugin.json`. This will be the main view of the app, so we'll set `defaultNav` to let users quickly get to it by clicking the app icon in the side menu.
|
||||
|
||||
**plugin.json**
|
||||
|
||||
```json
|
||||
"includes": [
|
||||
{
|
||||
"type": "page",
|
||||
"name": "Instances",
|
||||
"path": "/a/myorgid-simple-app?tab=instances",
|
||||
"role": "Viewer",
|
||||
"addToNav": true,
|
||||
"defaultNav": true
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
> **Note:** While `page` includes typically reference pages created by the app, you can set `path` to any URL, internal or external. Try setting `path` to `https://grafana.com`.
|
||||
|
||||
## Configure the app
|
||||
|
||||
Let's add a new configuration page where users are able to configure default zone and regions for any instances they create.
|
||||
|
||||
1. In `module.ts`, add new configuration page using the `addConfigPage` method. `body` is the React component that renders the page content.
|
||||
|
||||
**module.ts**
|
||||
|
||||
```ts
|
||||
.addConfigPage({
|
||||
title: 'Defaults',
|
||||
icon: 'fa fa-info',
|
||||
body: DefaultsConfigPage,
|
||||
id: 'defaults',
|
||||
})
|
||||
```
|
||||
|
||||
## Add a dashboard
|
||||
|
||||
#### Include a dashboard in your app
|
||||
|
||||
1. In `src/`, create a new directory called `dashboards`.
|
||||
1. Create a file called `overview.json` in the `dashboards` directory.
|
||||
1. Copy the JSON definition for the dashboard you want to include and paste it into `overview.json`. If you don't have one available, you can find a sample dashboard at the end of this step.
|
||||
1. In `plugin.json`, add the following object to the `includes` property.
|
||||
|
||||
- The `name` of the dashboard needs to be the same as the `title` in the dashboard JSON model.
|
||||
- `path` points out the file that contains the dashboard definition, relative to the `plugin.json` file.
|
||||
|
||||
```json
|
||||
"includes": [
|
||||
{
|
||||
"type": "dashboard",
|
||||
"name": "System overview",
|
||||
"path": "dashboards/overview.json",
|
||||
"addToNav": true
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
1. Save and restart Grafana to load the new changes.
|
||||
|
||||
## Bundle a plugin
|
||||
|
||||
An app plugin can contain panel and data source plugins that get installed along with the app plugin.
|
||||
|
||||
In this step, you'll add a data source to your app plugin. You can add panel plugins the same way by changing `datasource` to `panel`.
|
||||
|
||||
1. In `src/`, create a new directory called `datasources`.
|
||||
1. Create a new data source using Grafana create-plugin tool in a temporary directory.
|
||||
|
||||
```bash
|
||||
mkdir tmp
|
||||
cd tmp
|
||||
npx @grafana/create-plugin@latest
|
||||
```
|
||||
|
||||
1. Move the `src` directory in the data source plugin to `src/datasources`, and rename it to `my-datasource`.
|
||||
|
||||
```bash
|
||||
mv ./my-datasource/src ../src/datasources/my-datasource
|
||||
```
|
||||
|
||||
Any bundled plugins are built along with the app plugin. Grafana looks for any subdirectory containing a `plugin.json` file and attempts to load a plugin in that directory.
|
||||
|
||||
To let users know that your plugin bundles other plugins, you can optionally display it on the plugin configuration page. This is not done automatically, so you need to add it to the `plugin.json`.
|
||||
|
||||
1. Include the data source in the `plugin.json`. The `name` property is only used for displaying in the Grafana UI.
|
||||
|
||||
```json
|
||||
"includes": [
|
||||
{
|
||||
"type": "datasource",
|
||||
"name": "My data source"
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
#### Include external plugins
|
||||
|
||||
If you want to let users know that your app requires an existing plugin, you can add it as a dependency in `plugin.json`. Note that they'll still need to install it themselves.
|
||||
|
||||
```json
|
||||
"dependencies": {
|
||||
"plugins": [
|
||||
{
|
||||
"type": "panel",
|
||||
"name": "Clock Panel",
|
||||
"id": "grafana-clock-panel",
|
||||
"version": "^2.1.3"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## Summary
|
||||
|
||||
In this tutorial you learned how to create an app plugin.
|
||||
@@ -1,135 +0,0 @@
|
||||
---
|
||||
aliases:
|
||||
- ../../../plugins/working-with-data-frames/
|
||||
description: How to work with data frames.
|
||||
keywords:
|
||||
- grafana
|
||||
- plugins
|
||||
- plugin
|
||||
- data frames
|
||||
- dataframes
|
||||
labels:
|
||||
products:
|
||||
- enterprise
|
||||
- oss
|
||||
title: Work with data frames
|
||||
weight: 900
|
||||
---
|
||||
|
||||
# Work with data frames
|
||||
|
||||
The [data frame]({{< relref "../../introduction-to-plugin-development/data-frames" >}}) is a columnar data structure that allows for efficient querying of large amounts of data. Since data frames are a central concept when developing plugins for Grafana, in this guide we'll look at some ways you can use them.
|
||||
|
||||
The `DataFrame` interface contains a `name` and an array of `fields` where each field contains the name, type, and the values for the field.
|
||||
|
||||
> **Note:** If you want to migrate an existing plugin to use the data frame format, refer to [Migrate to data frames]({{< relref "../../migration-guide/v6.x-v7.x#migrate-to-data-frames" >}}).
|
||||
|
||||
## Create a data frame
|
||||
|
||||
If you build a data source plugin, then you'll most likely want to convert a response from an external API to a data frame. Let's look at how to do this.
|
||||
|
||||
Let's start with creating a simple data frame that represents a time series. The easiest way to create a data frame is to use the `toDataFrame` function.
|
||||
|
||||
```ts
|
||||
// Need to be of the same length.
|
||||
const timeValues = [1599471973065, 1599471975729];
|
||||
const numberValues = [12.3, 28.6];
|
||||
|
||||
// Create data frame from values.
|
||||
const frame = toDataFrame({
|
||||
name: 'http_requests_total',
|
||||
fields: [
|
||||
{ name: 'Time', type: FieldType.time, values: timeValues },
|
||||
{ name: 'Value', type: FieldType.number, values: numberValues },
|
||||
],
|
||||
});
|
||||
```
|
||||
|
||||
> **Note:** Data frames representing time series contain at least a `time` field and a `number` field. By convention, built-in plugins use `Time` and `Value` as field names for data frames containing time series data.
|
||||
|
||||
As you can see from the example, to create data frames like this, your data must already be stored as columnar data. If you already have the records in the form of an array of objects, then you can pass it to `toDataFrame`. In this case, `toDataFrame` tries to guess the schema based on the types and names of the objects in the array. To create complex data frames this way, be sure to verify that you get the schema you expect.
|
||||
|
||||
```ts
|
||||
const series = [
|
||||
{ Time: 1599471973065, Value: 12.3 },
|
||||
{ Time: 1599471975729, Value: 28.6 },
|
||||
];
|
||||
|
||||
const frame = toDataFrame(series);
|
||||
frame.name = 'http_requests_total';
|
||||
```
|
||||
|
||||
## Read values from a data frame
|
||||
|
||||
When you're building a panel plugin, the data frames returned by the data source are available from the `data` prop in your panel component.
|
||||
|
||||
```ts
|
||||
function SimplePanel({ data: Props }) {
|
||||
const frame = data.series[0];
|
||||
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
Before you start reading the data, think about what data you expect. For example, to visualize a time series you need at least one time field and one number field.
|
||||
|
||||
```ts
|
||||
const timeField = frame.fields.find((field) => field.type === FieldType.time);
|
||||
const valueField = frame.fields.find((field) => field.type === FieldType.number);
|
||||
```
|
||||
|
||||
Other types of visualizations might need multiple dimensions. For example, a bubble chart that uses three numeric fields: the X-axis, Y-axis, and one for the radius of each bubble. In this case, instead of hard coding the field names, we recommend that you let the user choose the field to use for each dimension.
|
||||
|
||||
```ts
|
||||
const x = frame.fields.find((field) => field.name === xField);
|
||||
const y = frame.fields.find((field) => field.name === yField);
|
||||
const size = frame.fields.find((field) => field.name === sizeField);
|
||||
|
||||
for (let i = 0; i < frame.length; i++) {
|
||||
const row = [x?.values[i], y?.values[i], size?.values[i]];
|
||||
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
Alternatively, you can use the `DataFrameView`, which gives you an array of objects that contain a property for each field in the frame.
|
||||
|
||||
```ts
|
||||
const view = new DataFrameView(frame);
|
||||
|
||||
view.forEach((row) => {
|
||||
console.log(row[options.xField], row[options.yField], row[options.sizeField]);
|
||||
});
|
||||
```
|
||||
|
||||
## Display values from a data frame
|
||||
|
||||
Field options let the user control how Grafana displays the data in a data frame.
|
||||
|
||||
To apply the field options to a value, use the `display` method on the corresponding field. The result contains information such as the color and suffix to use when display the value.
|
||||
|
||||
```ts
|
||||
const valueField = frame.fields.find((field) => field.type === FieldType.number);
|
||||
|
||||
return (
|
||||
<div>
|
||||
{valueField
|
||||
? valueField.values.map((value) => {
|
||||
const displayValue = valueField.display!(value);
|
||||
return (
|
||||
<p style={{ color: displayValue.color }}>
|
||||
{displayValue.text} {displayValue.suffix ? displayValue.suffix : ''}
|
||||
</p>
|
||||
);
|
||||
})
|
||||
: null}
|
||||
</div>
|
||||
);
|
||||
```
|
||||
|
||||
To apply field options to the name of a field, use `getFieldDisplayName`.
|
||||
|
||||
```ts
|
||||
const valueField = frame.fields.find((field) => field.type === FieldType.number);
|
||||
const valueFieldName = getFieldDisplayName(valueField, frame);
|
||||
```
|
||||
@@ -1,38 +0,0 @@
|
||||
---
|
||||
description: An index of how-to topics for extending or enhancing Grafana plugins.
|
||||
keywords:
|
||||
- grafana
|
||||
- plugins
|
||||
- plugin
|
||||
- development
|
||||
- extension
|
||||
- documentation
|
||||
labels:
|
||||
products:
|
||||
- enterprise
|
||||
- oss
|
||||
menuTitle: Extend a plugin
|
||||
title: Extend a Grafana plugin
|
||||
weight: 200
|
||||
---
|
||||
|
||||
# Extend a Grafana plugin
|
||||
|
||||
This section contains how-to topics for extending or enhancing Grafana plugins:
|
||||
|
||||
- [Enable annotations]({{< relref "./add-support-for-annotations.md" >}})
|
||||
- [Add anonymous usage reporting]({{< relref "./add-anonymous-usage-reporting.md" >}})
|
||||
- [Add authentication for a data source plugin]({{< relref "./add-authentication-for-data-source-plugins.md" >}})
|
||||
- [Add distributed tracing for backend plugins]({{< relref "./add-distributed-tracing-for-backend-plugins.md" >}})
|
||||
- [Add features to Explore queries]({{< relref "./add-support-for-explore-queries.md" >}})
|
||||
- [Add query editor help]({{< relref "./add-query-editor-help.md" >}})
|
||||
- [Add support for variables]({{< relref "./add-support-for-variables.md" >}})
|
||||
- [Build a custom panel option editor]({{< relref "./custom-panel-option-editors.md" >}})
|
||||
- [Use extensions to add links to app plugins]({{< relref "./extend-the-grafana-ui-with-links.md" >}})
|
||||
- [Work with cross-plugin links]({{< relref "./cross-plugin-linking.md" >}})
|
||||
|
||||
Additional resources:
|
||||
|
||||
- [Automate development with CI](https://grafana.github.io/plugin-tools/docs/development/ci)
|
||||
- [Create nested plugins](https://grafana.github.io/plugin-tools/docs/advanced-usage/nested-plugins)
|
||||
- [Extend configurations](https://grafana.github.io/plugin-tools/docs/advanced-usage/advanced-configuration)
|
||||
@@ -1,180 +0,0 @@
|
||||
---
|
||||
aliases:
|
||||
- ../../../plugins/add-anonymous-usage-reporting/
|
||||
description: How to add anonymous usage tracking to your Grafana plugin.
|
||||
keywords:
|
||||
- grafana
|
||||
- plugins
|
||||
- plugin
|
||||
- anonymous usage
|
||||
- reporting
|
||||
labels:
|
||||
products:
|
||||
- enterprise
|
||||
- oss
|
||||
title: Add anonymous usage reporting
|
||||
weight: 200
|
||||
---
|
||||
|
||||
# Add anonymous usage reporting
|
||||
|
||||
Add anonymous usage tracking to your plugin to send [reporting events]({{< relref "../../../../setup-grafana/configure-grafana#reporting_enabled" >}}) that describe how your plugin is being used to a tracking system configured by your Grafana server administrator.
|
||||
|
||||
## Event reporting
|
||||
|
||||
In this section, we show an example of tracking usage data from a query editor and receiving a report back from the analytics service.
|
||||
|
||||
### Sample query editor
|
||||
|
||||
Let's say you have a `QueryEditor` that looks similar to the example below. It has a `CodeEditor` field where you can write your query and a query type selector so you can select the kind of query result that you expect to return:
|
||||
|
||||
```ts
|
||||
import React, { ReactElement } from 'react';
|
||||
import { InlineFieldRow, InlineField, Select, CodeEditor } from '@grafana/ui';
|
||||
import type { EditorProps } from './types';
|
||||
|
||||
export function QueryEditor(props: EditorProps): ReactElement {
|
||||
const { datasource, query, onChange, onRunQuery } = props;
|
||||
const queryType = { value: query.value ?? 'timeseries' };
|
||||
const queryTypes = [
|
||||
{
|
||||
label: 'Timeseries',
|
||||
value: 'timeseries',
|
||||
},
|
||||
{
|
||||
label: 'Table',
|
||||
value: 'table',
|
||||
},
|
||||
];
|
||||
|
||||
const onChangeQueryType = (type: string) => {
|
||||
onChange({
|
||||
...query,
|
||||
queryType: type,
|
||||
});
|
||||
runQuery();
|
||||
};
|
||||
|
||||
const onChangeRawQuery = (rawQuery: string) => {
|
||||
onChange({
|
||||
...query,
|
||||
rawQuery: type,
|
||||
});
|
||||
runQuery();
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<div>
|
||||
<CodeEditor
|
||||
height="200px"
|
||||
showLineNumbers={true}
|
||||
language="sql"
|
||||
onBlur={onChangeRawQuery}
|
||||
value={query.rawQuery}
|
||||
/>
|
||||
</div>
|
||||
<InlineFieldRow>
|
||||
<InlineField label="Query type" grow>
|
||||
<Select options={queryTypes} onChange={onChangeQueryType} value={queryType} />
|
||||
</InlineField>
|
||||
</InlineFieldRow>
|
||||
</>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### Track usage with `usePluginInteractionReporter`
|
||||
|
||||
Let's say that you want to track how the usage looks between time series and table queries.
|
||||
|
||||
What you want to do is to add the `usePluginInteractionReporter` to fetch a report function that takes two arguments:
|
||||
|
||||
- Required: An event name that begins with `grafana_plugin_`. It is used to identify the interaction being made.
|
||||
- Optional: Attached contextual data. In our example, that is the query type.
|
||||
|
||||
```ts
|
||||
import React, { ReactElement } from 'react';
|
||||
import { InlineFieldRow, InlineField, Select, CodeEditor } from '@grafana/ui';
|
||||
import { usePluginInteractionReporter } from '@grafana/runtime';
|
||||
import type { EditorProps } from './types';
|
||||
|
||||
export function QueryEditor(props: EditorProps): ReactElement {
|
||||
const { datasource, query, onChange, onRunQuery } = props;
|
||||
const report = usePluginInteractionReporter();
|
||||
|
||||
const queryType = { value: query.value ?? 'timeseries' };
|
||||
const queryTypes = [
|
||||
{
|
||||
label: 'Timeseries',
|
||||
value: 'timeseries',
|
||||
},
|
||||
{
|
||||
label: 'Table',
|
||||
value: 'table',
|
||||
},
|
||||
];
|
||||
|
||||
const onChangeQueryType = (type: string) => {
|
||||
onChange({
|
||||
...query,
|
||||
queryType: type,
|
||||
});
|
||||
runQuery();
|
||||
};
|
||||
|
||||
const onChangeRawQuery = (rawQuery: string) => {
|
||||
onChange({
|
||||
...query,
|
||||
rawQuery: type,
|
||||
});
|
||||
|
||||
report('grafana_plugin_executed_query', {
|
||||
query_type: queryType.value,
|
||||
});
|
||||
|
||||
runQuery();
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<div>
|
||||
<CodeEditor
|
||||
height="200px"
|
||||
showLineNumbers={true}
|
||||
language="sql"
|
||||
onBlur={onChangeRawQuery}
|
||||
value={query.rawQuery}
|
||||
/>
|
||||
</div>
|
||||
<InlineFieldRow>
|
||||
<InlineField label="Query type" grow>
|
||||
<Select options={queryTypes} onChange={onChangeQueryType} value={queryType} />
|
||||
</InlineField>
|
||||
</InlineFieldRow>
|
||||
</>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### Data returned from the analytics service
|
||||
|
||||
When you use `usePluginInteractionReporter`, the report function that is handed back to you automatically attaches contextual data about the plugin you are tracking to the events.
|
||||
|
||||
In our example, the following information is sent to the analytics service configured by the Grafana server administrator:
|
||||
|
||||
```ts
|
||||
{
|
||||
type: 'interaction',
|
||||
payload: {
|
||||
interactionName: 'grafana_plugin_executed_query',
|
||||
grafana_version: '9.2.1',
|
||||
plugin_type: 'datasource',
|
||||
plugin_version: '1.0.0',
|
||||
plugin_id: 'grafana-example-datasource',
|
||||
plugin_name: 'Example',
|
||||
datasource_uid: 'qeSI8VV7z', // will only be added for datasources
|
||||
query_type: 'timeseries'
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -1,472 +0,0 @@
|
||||
---
|
||||
aliases:
|
||||
- ../../plugins/developing/auth-for-datasources/
|
||||
- /docs/grafana/next/developers/plugins/authentication/
|
||||
description: How to add authentication for data source plugins.
|
||||
keywords:
|
||||
- grafana
|
||||
- plugins
|
||||
- plugin
|
||||
- authentication
|
||||
- data source
|
||||
- datasource
|
||||
labels:
|
||||
products:
|
||||
- enterprise
|
||||
- oss
|
||||
title: Add authentication for data source plugins
|
||||
weight: 300
|
||||
---
|
||||
|
||||
# Add authentication for data source plugins
|
||||
|
||||
Grafana plugins can perform authenticated requests against a third-party API by using the _data source proxy_ or through a custom a _backend plugin_.
|
||||
|
||||
## Choose an authentication method
|
||||
|
||||
Configure your data source plugin to authenticate against a third-party API in one of either of two ways:
|
||||
|
||||
- Use the [_data source proxy_](#authenticate-using-the-data-source-proxy) method, or
|
||||
- Build a [_backend plugin_](#authenticate-using-a-backend-plugin).
|
||||
|
||||
| Case | Use |
|
||||
| ----------------------------------------------------------------------------------------------- | ------------------------------- |
|
||||
| Do you need to authenticate your plugin using Basic Auth or API keys? | Use the data source proxy. |
|
||||
| Does your API support OAuth 2.0 using client credentials? | Use the data source proxy. |
|
||||
| Does your API use a custom authentication method that isn't supported by the data source proxy? | Use a backend plugin. |
|
||||
| Does your API communicate over a protocol other than HTTP? | Build and use a backend plugin. |
|
||||
| Does your plugin require alerting support? | Build and use a backend plugin. |
|
||||
|
||||
## Encrypt data source configuration
|
||||
|
||||
Data source plugins have two ways of storing custom configuration: `jsonData` and `secureJsonData`.
|
||||
|
||||
Users with the Viewer role can access data source configuration such as the contents of `jsonData` in cleartext. If you've enabled anonymous access, anyone who can access Grafana in their browser can see the contents of `jsonData`.
|
||||
|
||||
Users of [Grafana Enterprise](/products/enterprise/grafana/) can restrict access to data sources to specific users and teams. For more information, refer to [Data source permissions](/docs/grafana/latest/enterprise/datasource_permissions).
|
||||
|
||||
> **Important:** Do not use `jsonData` with sensitive data such as password, tokens, and API keys. If you need to store sensitive information, use `secureJsonData` instead.
|
||||
|
||||
> **Note:** You can see the settings that the current user has access to by entering `window.grafanaBootData` in the developer console of your browser.
|
||||
|
||||
### Store configuration in `secureJsonData`
|
||||
|
||||
If you need to store sensitive information, use `secureJsonData` instead of `jsonData`. Whenever the user saves the data source configuration, the secrets in `secureJsonData` are sent to the Grafana server and encrypted before they're stored.
|
||||
|
||||
Once you have encrypted the secure configuration, it can no longer be accessed from the browser. The only way to access secrets after they've been saved is by using the [_data source proxy_](#authenticate-using-the-data-source-proxy).
|
||||
|
||||
### Add secret configuration to your data source plugin
|
||||
|
||||
To demonstrate how you can add secrets to a data source plugin, let's add support for configuring an API key.
|
||||
|
||||
1. Create a new interface in `types.ts` to hold the API key:
|
||||
```ts
|
||||
export interface MySecureJsonData {
|
||||
apiKey?: string;
|
||||
}
|
||||
```
|
||||
1. Add type information to your `secureJsonData` object by updating the props for your `ConfigEditor` to accept the interface as a second type parameter. Access the value of the secret from the `options` prop inside your `ConfigEditor`:
|
||||
|
||||
```ts
|
||||
interface Props extends DataSourcePluginOptionsEditorProps<MyDataSourceOptions, MySecureJsonData> {}
|
||||
```
|
||||
|
||||
```ts
|
||||
const { secureJsonData, secureJsonFields } = options;
|
||||
const { apiKey } = secureJsonData;
|
||||
```
|
||||
|
||||
> **Note:** You can do this until the user saves the configuration; when the user saves the configuration, Grafana clears the value. After that, you can use `secureJsonFields` to determine whether the property has been configured.
|
||||
|
||||
1. To securely update the secret in your plugin's configuration editor, update the `secureJsonData` object using the `onOptionsChange` prop:
|
||||
|
||||
```ts
|
||||
const onAPIKeyChange = (event: ChangeEvent<HTMLInputElement>) => {
|
||||
onOptionsChange({
|
||||
...options,
|
||||
secureJsonData: {
|
||||
apiKey: event.target.value,
|
||||
},
|
||||
});
|
||||
};
|
||||
```
|
||||
|
||||
1. Define a component that can accept user input:
|
||||
|
||||
```ts
|
||||
<Input
|
||||
type="password"
|
||||
placeholder={secureJsonFields?.apiKey ? 'configured' : ''}
|
||||
value={secureJsonData.apiKey ?? ''}
|
||||
onChange={onAPIKeyChange}
|
||||
/>
|
||||
```
|
||||
|
||||
1. Optional: If you want the user to be able to reset the API key, then you need to set the property to `false` in the `secureJsonFields` object:
|
||||
|
||||
```ts
|
||||
const onResetAPIKey = () => {
|
||||
onOptionsChange({
|
||||
...options,
|
||||
secureJsonFields: {
|
||||
...options.secureJsonFields,
|
||||
apiKey: false,
|
||||
},
|
||||
secureJsonData: {
|
||||
...options.secureJsonData,
|
||||
apiKey: '',
|
||||
},
|
||||
});
|
||||
};
|
||||
```
|
||||
|
||||
Now that users can configure secrets, the next step is to see how we can add them to our requests.
|
||||
|
||||
## Authenticate using the data source proxy
|
||||
|
||||
Once the user has saved the configuration for a data source, the secret data source configuration will no longer be available in the browser. Encrypted secrets can only be accessed on the server. So how do you add them to your request?
|
||||
|
||||
The Grafana server comes with a proxy that lets you define templates for your requests: _proxy routes_. Grafana sends the proxy route to the server, decrypts the secrets along with other configuration, and adds them to the request before sending it.
|
||||
|
||||
> **Note:** Be sure not to confuse the data source proxy with the [auth proxy]({{< relref "../../../../setup-grafana/configure-security/configure-authentication/auth-proxy/index.md" >}}). The data source proxy is used to authenticate a data source, while the auth proxy is used to log into Grafana itself.
|
||||
|
||||
### Add a proxy route to your plugin
|
||||
|
||||
To forward requests through the Grafana proxy, you need to configure one or more _proxy routes_. A proxy route is a template for any outgoing request that is handled by the proxy. You can configure proxy routes in the [plugin.json]({{< relref "../../metadata.md" >}}) file.
|
||||
|
||||
1. Add the route to `plugin.json`:
|
||||
|
||||
```json
|
||||
"routes": [
|
||||
{
|
||||
"path": "example",
|
||||
"url": "https://api.example.com"
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
> **Note:** You need to restart the Grafana server every time you make a change to your `plugin.json` file.
|
||||
|
||||
1. In the `DataSource`, extract the proxy URL from `instanceSettings` to a class property called `url`:
|
||||
|
||||
```ts
|
||||
export class DataSource extends DataSourceApi<MyQuery, MyDataSourceOptions> {
|
||||
url?: string;
|
||||
|
||||
constructor(instanceSettings: DataSourceInstanceSettings<MyDataSourceOptions>) {
|
||||
super(instanceSettings);
|
||||
|
||||
this.url = instanceSettings.url;
|
||||
}
|
||||
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
1. In the `query` method, make a request using `BackendSrv`. The first section of the URL path needs to match the `path` of your proxy route. The data source proxy replaces `this.url + routePath` with the `url` of the route. Based on our example, the URL for the request would be `https://api.example.com/v1/users`:
|
||||
|
||||
```ts
|
||||
import { getBackendSrv } from '@grafana/runtime';
|
||||
```
|
||||
|
||||
```ts
|
||||
const routePath = '/example';
|
||||
|
||||
getBackendSrv().datasourceRequest({
|
||||
url: this.url + routePath + '/v1/users',
|
||||
method: 'GET',
|
||||
});
|
||||
```
|
||||
|
||||
### Add a dynamic proxy route to your plugin
|
||||
|
||||
Grafana sends the proxy route to the server, where the data source proxy decrypts any sensitive data and interpolates the template variables with the decrypted data before making the request.
|
||||
|
||||
To add user-defined configuration to your routes:
|
||||
|
||||
- Use `.JsonData` for configuration stored in `jsonData`. For example, where `projectId` is the name of a property in the `jsonData` object:
|
||||
|
||||
```json
|
||||
"routes": [
|
||||
{
|
||||
"path": "example",
|
||||
"url": "https://api.example.com/projects/{{ .JsonData.projectId }}"
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
- Use `.SecureJsonData` for sensitive data stored in `secureJsonData`. For example, where `password` is the name of a property in the `secureJsonData` object:
|
||||
|
||||
```json
|
||||
"routes": [
|
||||
{
|
||||
"path": "example",
|
||||
"url": "https://{{ .JsonData.username }}:{{ .SecureJsonData.password }}@api.example.com"
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
In addition to adding the URL to the proxy route, you can also add headers, URL parameters, and a request body.
|
||||
|
||||
#### Add HTTP headers to a proxy route
|
||||
|
||||
Here's an example of adding `name` and `content` as HTTP headers:
|
||||
|
||||
```json
|
||||
"routes": [
|
||||
{
|
||||
"path": "example",
|
||||
"url": "https://api.example.com",
|
||||
"headers": [
|
||||
{
|
||||
"name": "Authorization",
|
||||
"content": "Bearer {{ .SecureJsonData.apiToken }}"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
#### Add URL parameters to a proxy route
|
||||
|
||||
Here's an example of adding `name` and `content` as URL parameters:
|
||||
|
||||
```json
|
||||
"routes": [
|
||||
{
|
||||
"path": "example",
|
||||
"url": "http://api.example.com",
|
||||
"urlParams": [
|
||||
{
|
||||
"name": "apiKey",
|
||||
"content": "{{ .SecureJsonData.apiKey }}"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
#### Add a request body to a proxy route
|
||||
|
||||
Here's an example of adding `username` and `password` to the request body:
|
||||
|
||||
```json
|
||||
"routes": [
|
||||
{
|
||||
"path": "example",
|
||||
"url": "http://api.example.com",
|
||||
"body": {
|
||||
"username": "{{ .JsonData.username }}",
|
||||
"password": "{{ .SecureJsonData.password }}"
|
||||
}
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
### Add an OAuth 2.0 proxy route to your plugin
|
||||
|
||||
Since your request to each route is made server-side with OAuth 2.0 authentication, only machine-to-machine requests are supported. In order words, if you need to use a different grant than client credentials, you need to implement it yourself.
|
||||
|
||||
To authenticate using OAuth 2.0, add a `tokenAuth` object to the proxy route definition. If necessary, Grafana performs a request to the URL defined in `tokenAuth` to retrieve a token before making the request to the URL in your proxy route. Grafana automatically renews the token when it expires.
|
||||
|
||||
Any parameters defined in `tokenAuth.params` are encoded as `application/x-www-form-urlencoded` and sent to the token URL.
|
||||
|
||||
```json
|
||||
{
|
||||
"routes": [
|
||||
{
|
||||
"path": "api",
|
||||
"url": "https://api.example.com/v1",
|
||||
"tokenAuth": {
|
||||
"url": "https://api.example.com/v1/oauth/token",
|
||||
"params": {
|
||||
"grant_type": "client_credentials",
|
||||
"client_id": "{{ .SecureJsonData.clientId }}",
|
||||
"client_secret": "{{ .SecureJsonData.clientSecret }}"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## Authenticate using a backend plugin
|
||||
|
||||
While the data source proxy supports the most common authentication methods for HTTP APIs, using proxy routes has a few limitations:
|
||||
|
||||
- Proxy routes only support HTTP or HTTPS.
|
||||
- Proxy routes don't support custom token authentication.
|
||||
|
||||
If any of these limitations apply to your plugin, you need to add a [backend plugin]({{< relref "../../introduction-to-plugin-development/backend" >}}). Because backend plugins run on the server, they can access decrypted secrets, which makes it easier to implement custom authentication methods.
|
||||
|
||||
The decrypted secrets are available from the `DecryptedSecureJSONData` field in the instance settings.
|
||||
|
||||
```go
|
||||
func (ds *dataSource) QueryData(ctx context.Context, req *backend.QueryDataRequest) (*backend.QueryDataResponse, error) {
|
||||
instanceSettings := req.PluginContext.DataSourceInstanceSettings
|
||||
|
||||
if apiKey, exists := settings.DecryptedSecureJSONData["apiKey"]; exists {
|
||||
// Use the decrypted API key.
|
||||
}
|
||||
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
## Forward OAuth identity for the logged-in user
|
||||
|
||||
If your data source uses the same OAuth provider as Grafana itself, for example using [Generic OAuth Authentication]({{< relref "../../../../setup-grafana/configure-security/configure-authentication/generic-oauth" >}}), then your data source plugin can reuse the access token for the logged-in Grafana user.
|
||||
|
||||
To allow Grafana to pass the access token to the plugin, update the data source configuration and set the `jsonData.oauthPassThru` property to `true`. The [DataSourceHttpSettings](https://developers.grafana.com/ui/latest/index.html?path=/story/data-source-datasourcehttpsettings--basic) settings provide a toggle, the **Forward OAuth Identity** option, for this. You can also build an appropriate toggle to set `jsonData.oauthPassThru` in your data source configuration page UI.
|
||||
|
||||
When configured, Grafana can forward authorization HTTP headers such as `Authorization` or `X-ID-Token` to a backend data source. This information is available across the `QueryData`, `CallResource` and `CheckHealth` requests.
|
||||
|
||||
To get Grafana to forward the headers, create a HTTP client using the [Grafana plugin SDK for Go](https://pkg.go.dev/github.com/grafana/grafana-plugin-sdk-go/backend/httpclient) and set the `ForwardHTTPHeaders` option to `true` (by default, it's set to `false`). This package exposes request information which can be subsequently forwarded downstream and/or used directly within the plugin.
|
||||
|
||||
```go
|
||||
func NewDatasource(settings backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) {
|
||||
opts, err := settings.HTTPClientOptions()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("http client options: %w", err)
|
||||
}
|
||||
|
||||
// Important: Reuse the same client for each query to avoid using all available connections on a host.
|
||||
|
||||
opts.ForwardHTTPHeaders = true
|
||||
|
||||
cl, err := httpclient.New(opts)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("httpclient new: %w", err)
|
||||
}
|
||||
return &Datasource{
|
||||
httpClient: cl,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (ds *dataSource) QueryData(ctx context.Context, req *backend.QueryDataRequest) (*backend.QueryDataResponse, error) {
|
||||
// Necessary to keep the Context, since the injected middleware is configured there
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, "https://some-url", nil)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("new request with context: %w", err)
|
||||
}
|
||||
// Authorization header will be automatically injected if oauthPassThru is configured
|
||||
resp, err := ds.httpClient.Do(req)
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
You can see a full working plugin example here: [datasource-http-backend](https://github.com/grafana/grafana-plugin-examples/tree/main/examples/datasource-http-backend).
|
||||
|
||||
### Extract a header from an HTTP request
|
||||
|
||||
If you need to access the HTTP header information directly, you can also extract that information from the request:
|
||||
|
||||
```go
|
||||
func (ds *dataSource) CheckHealth(ctx context.Context, req *backend.CheckHealthRequest) (*backend.CheckHealthResult, error) {
|
||||
token := strings.Fields(req.GetHTTPHeader(backend.OAuthIdentityTokenHeaderName))
|
||||
var (
|
||||
tokenType = token[0]
|
||||
accessToken = token[1]
|
||||
)
|
||||
idToken := req.GetHTTPHeader(backend.OAuthIdentityIDTokenHeaderName) // present if user's token includes an ID token
|
||||
|
||||
// ...
|
||||
return &backend.CheckHealthResult{Status: backend.HealthStatusOk}, nil
|
||||
}
|
||||
|
||||
func (ds *dataSource) QueryData(ctx context.Context, req *backend.QueryDataRequest) (*backend.QueryDataResponse, error) {
|
||||
token := strings.Fields(req.GetHTTPHeader(backend.OAuthIdentityTokenHeaderName))
|
||||
var (
|
||||
tokenType = token[0]
|
||||
accessToken = token[1]
|
||||
)
|
||||
idToken := req.GetHTTPHeader(backend.OAuthIdentityIDTokenHeaderName)
|
||||
|
||||
for _, q := range req.Queries {
|
||||
// ...
|
||||
}
|
||||
}
|
||||
|
||||
func (ds *dataSource) CallResource(ctx context.Context, req *backend.CallResourceRequest, sender backend.CallResourceResponseSender) error {
|
||||
token := req.GetHTTPHeader(backend.OAuthIdentityTokenHeaderName)
|
||||
idToken := req.GetHTTPHeader(backend.OAuthIdentityIDTokenHeaderName)
|
||||
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
## Work with cookies
|
||||
|
||||
### Forward cookies for the logged-in user
|
||||
|
||||
Your data source plugin can forward cookies for the logged-in Grafana user to the data source. Use the [DataSourceHttpSettings](https://developers.grafana.com/ui/latest/index.html?path=/story/data-source-datasourcehttpsettings--basic) component on the data source's configuration page. It provides the **Allowed cookies** option, where you can specify the cookie names.
|
||||
|
||||
When configured, as with [authorization headers](#forward-oauth-identity-for-the-logged-in-user), these cookies are automatically injected if you use the SDK HTTP client.
|
||||
|
||||
### Extract cookies for the logged-in user
|
||||
|
||||
You can also extract the cookies in the `QueryData`, `CallResource` and `CheckHealth` requests if required.
|
||||
|
||||
**`QueryData`**
|
||||
|
||||
```go
|
||||
func (ds *dataSource) QueryData(ctx context.Context, req *backend.QueryDataRequest) (*backend.QueryDataResponse, error) {
|
||||
cookies:= req.GetHTTPHeader(backend.CookiesHeaderName)
|
||||
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
**`CallResource`**
|
||||
|
||||
```go
|
||||
func (ds *dataSource) CallResource(ctx context.Context, req *backend.CallResourceRequest, sender backend.CallResourceResponseSender) error {
|
||||
cookies:= req.GetHTTPHeader(backend.CookiesHeaderName)
|
||||
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
**`CheckHealth`**
|
||||
|
||||
```go
|
||||
func (ds *dataSource) CheckHealth(ctx context.Context, req *backend.CheckHealthRequest) (*backend.CheckHealthResult, error) {
|
||||
cookies:= req.GetHTTPHeader(backend.CookiesHeaderName)
|
||||
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
## Forward user header for the logged-in user
|
||||
|
||||
When `send_user_header` is enabled, Grafana passes the user header to the plugin using the `X-Grafana-User` header. You can forward this header as well as [authorization headers](#forward-oauth-identity-for-the-logged-in-user) or [configured cookies](#forward-cookies-for-the-logged-in-user).
|
||||
|
||||
**`QueryData`**
|
||||
|
||||
```go
|
||||
func (ds *dataSource) QueryData(ctx context.Context, req *backend.QueryDataRequest) (*backend.QueryDataResponse, error) {
|
||||
u := req.GetHTTPHeader("X-Grafana-User")
|
||||
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
**`CallResource`**
|
||||
|
||||
```go
|
||||
func (ds *dataSource) CallResource(ctx context.Context, req *backend.CallResourceRequest, sender backend.CallResourceResponseSender) error {
|
||||
u := req.GetHTTPHeader("X-Grafana-User")
|
||||
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
**`CheckHealth`**
|
||||
|
||||
```go
|
||||
func (ds *dataSource) CheckHealth(ctx context.Context, req *backend.CheckHealthRequest) (*backend.CheckHealthResult, error) {
|
||||
u := req.GetHTTPHeader("X-Grafana-User")
|
||||
|
||||
// ...
|
||||
}
|
||||
```
|
||||
@@ -1,126 +0,0 @@
|
||||
---
|
||||
aliases:
|
||||
- ../../../plugins/add-distributed-tracing-for-backend-plugins/
|
||||
description: How to add distributed tracing for backend plugins.
|
||||
keywords:
|
||||
- grafana
|
||||
- plugins
|
||||
- plugin
|
||||
- distributed tracing
|
||||
- tracing
|
||||
- backend
|
||||
- back-end
|
||||
labels:
|
||||
products:
|
||||
- enterprise
|
||||
- oss
|
||||
title: Add distributed tracing for backend plugins
|
||||
weight: 350
|
||||
---
|
||||
|
||||
# Add distributed tracing for backend plugins
|
||||
|
||||
> **Note:** This feature requires at least Grafana 9.5.0, and your plugin needs to be built at least with grafana-plugins-sdk-go v0.157.0. If you run a plugin with tracing features on an older version of Grafana, tracing is disabled.
|
||||
|
||||
Distributed tracing allows backend plugin developers to create custom spans in their plugins, and send them to the same endpoint and with the same propagation format as the main Grafana instance. The tracing context is also propagated from the Grafana instance to the plugin, so the plugin's spans will be correlated to the correct trace.
|
||||
|
||||
## Plugin configuration
|
||||
|
||||
Plugin tracing must be enabled manually on a per-plugin basis, by specifying `tracing = true` in the plugin's config section:
|
||||
|
||||
```ini
|
||||
[plugin.myorg-myplugin-datasource]
|
||||
tracing = true
|
||||
```
|
||||
|
||||
## OpenTelemetry configuration in Grafana
|
||||
|
||||
Grafana supports [OpenTelemetry](https://opentelemetry.io/) for distributed tracing. If Grafana is configured to use a deprecated tracing system (Jaeger or OpenTracing), then tracing is disabled in the plugin provided by the SDK and configured when calling `datasource.Manage | app.Manage`.
|
||||
|
||||
OpenTelemetry must be enabled and configured for the Grafana instance. Please refer to the [Grafana configuration documentation](
|
||||
{{< relref "../../../../setup-grafana/configure-grafana#tracingopentelemetry" >}}) for more information.
|
||||
|
||||
Refer to the [OpenTelemetry Go SDK](https://pkg.go.dev/go.opentelemetry.io/otel) for in-depth documentation about all the features provided by OpenTelemetry.
|
||||
|
||||
> **Note:** If tracing is disabled in Grafana, `backend.DefaultTracer()` returns a no-op tracer.
|
||||
|
||||
## Implement tracing in your plugin
|
||||
|
||||
> **Note:** Make sure you are using at least grafana-plugin-sdk-go v0.157.0. You can update with `go get -u github.com/grafana/grafana-plugin-sdk-go`.
|
||||
|
||||
### Configure a global tracer
|
||||
|
||||
When OpenTelemetry tracing is enabled on the main Grafana instance and tracing is enabled for a plugin, the OpenTelemetry endpoint address and propagation format is passed to the plugin during startup. These parameters are used to configure a global tracer.
|
||||
|
||||
1. Use `datasource.Manage` or `app.Manage` to run your plugin to automatically configure the global tracer. Specify any custom attributes for the default tracer using `CustomAttributes`:
|
||||
|
||||
```go
|
||||
func main() {
|
||||
if err := datasource.Manage("MY_PLUGIN_ID", plugin.NewDatasource, datasource.ManageOpts{
|
||||
TracingOpts: tracing.Opts{
|
||||
// Optional custom attributes attached to the tracer's resource.
|
||||
// The tracer will already have some SDK and runtime ones pre-populated.
|
||||
CustomAttributes: []attribute.KeyValue{
|
||||
attribute.String("my_plugin.my_attribute", "custom value"),
|
||||
},
|
||||
},
|
||||
}); err != nil {
|
||||
log.DefaultLogger.Error(err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
1. Once you have configured tracing, use the global tracer like this:
|
||||
|
||||
```go
|
||||
tracing.DefaultTracer()
|
||||
```
|
||||
|
||||
This returns an [OpenTelemetry `trace.Tracer`](https://pkg.go.dev/go.opentelemetry.io/otel/trace#Tracer) for creating spans.
|
||||
|
||||
**Example:**
|
||||
|
||||
```go
|
||||
func (d *Datasource) query(ctx context.Context, pCtx backend.PluginContext, query backend.DataQuery) (backend.DataResponse, error) {
|
||||
ctx, span := tracing.DefaultTracer().Start(
|
||||
ctx,
|
||||
"query processing",
|
||||
trace.WithAttributes(
|
||||
attribute.String("query.ref_id", query.RefID),
|
||||
attribute.String("query.type", query.QueryType),
|
||||
attribute.Int64("query.max_data_points", query.MaxDataPoints),
|
||||
attribute.Int64("query.interval_ms", query.Interval.Milliseconds()),
|
||||
attribute.Int64("query.time_range.from", query.TimeRange.From.Unix()),
|
||||
attribute.Int64("query.time_range.to", query.TimeRange.To.Unix()),
|
||||
),
|
||||
)
|
||||
defer span.End()
|
||||
log.DefaultLogger.Debug("query", "traceID", trace.SpanContextFromContext(ctx).TraceID())
|
||||
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
### Tracing gRPC calls
|
||||
|
||||
When tracing is enabled, a new span is created automatically for each gRPC call (`QueryData`, `CheckHealth`, etc.), both on Grafana's side and on the plugin's side. The plugin SDK also injects the trace context into the `context.Context` that is passed to those methods.
|
||||
|
||||
You can retrieve the [trace.SpanContext](https://pkg.go.dev/go.opentelemetry.io/otel/trace#SpanContext) with `tracing.SpanContextFromContext` by passing the original `context.Context` to it:
|
||||
|
||||
```go
|
||||
func (d *Datasource) query(ctx context.Context, pCtx backend.PluginContext, query backend.DataQuery) (backend.DataResponse, error) {
|
||||
spanCtx := trace.SpanContextFromContext(ctx)
|
||||
traceID := spanCtx.TraceID()
|
||||
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
### Tracing HTTP requests
|
||||
|
||||
When tracing is enabled, a `TracingMiddleware` is also added to the default middleware stack to all HTTP clients created using the `httpclient.New` or `httpclient.NewProvider`, unless you specify custom middleware. This middleware creates spans for each outgoing HTTP request and provides some useful attributes and events related to the request's lifecycle.
|
||||
|
||||
## Plugin example
|
||||
|
||||
Refer to the [datasource-http-backend plugin example](https://github.com/grafana/grafana-plugin-examples/tree/main/examples/datasource-http-backend) for a complete example of a plugin with full distributed tracing support.
|
||||
@@ -1,88 +0,0 @@
|
||||
---
|
||||
aliases:
|
||||
- ../../../plugins/add-query-editor-help/
|
||||
description: How to add a help component to query editors in Grafana.
|
||||
keywords:
|
||||
- grafana
|
||||
- plugins
|
||||
- plugin
|
||||
- queries
|
||||
- query editor
|
||||
- query editor help
|
||||
labels:
|
||||
products:
|
||||
- enterprise
|
||||
- oss
|
||||
title: Add query editor help
|
||||
weight: 500
|
||||
---
|
||||
|
||||
# Add query editor help
|
||||
|
||||
Query editors support the addition of a help component to display examples of potential queries. When the user clicks on one of the examples, the query editor is automatically updated. This helps the user to make faster queries.
|
||||
|
||||
1. In the `src` directory of your plugin, create a file `QueryEditorHelp.tsx` with the following content:
|
||||
|
||||
```ts
|
||||
import React from 'react';
|
||||
import { QueryEditorHelpProps } from '@grafana/data';
|
||||
|
||||
export default (props: QueryEditorHelpProps) => {
|
||||
return <h2>My cheat sheet</h2>;
|
||||
};
|
||||
```
|
||||
|
||||
1. Configure the plugin to use `QueryEditorHelp`:
|
||||
|
||||
```ts
|
||||
import QueryEditorHelp from './QueryEditorHelp';
|
||||
```
|
||||
|
||||
```ts
|
||||
export const plugin = new DataSourcePlugin<DataSource, MyQuery, MyDataSourceOptions>(DataSource)
|
||||
.setConfigEditor(ConfigEditor)
|
||||
.setQueryEditor(QueryEditor)
|
||||
.setQueryEditorHelp(QueryEditorHelp);
|
||||
```
|
||||
|
||||
1. Create a few examples of potential queries:
|
||||
|
||||
```ts
|
||||
import React from 'react';
|
||||
import { QueryEditorHelpProps, DataQuery } from '@grafana/data';
|
||||
|
||||
const examples = [
|
||||
{
|
||||
title: 'Addition',
|
||||
expression: '1 + 2',
|
||||
label: 'Add two integers',
|
||||
},
|
||||
{
|
||||
title: 'Subtraction',
|
||||
expression: '2 - 1',
|
||||
label: 'Subtract an integer from another',
|
||||
},
|
||||
];
|
||||
|
||||
export default (props: QueryEditorHelpProps) => {
|
||||
return (
|
||||
<div>
|
||||
<h2>Cheat Sheet</h2>
|
||||
{examples.map((item, index) => (
|
||||
<div className="cheat-sheet-item" key={index}>
|
||||
<div className="cheat-sheet-item__title">{item.title}</div>
|
||||
{item.expression ? (
|
||||
<div
|
||||
className="cheat-sheet-item__example"
|
||||
onClick={(e) => props.onClickExample({ refId: 'A', queryText: item.expression } as DataQuery)}
|
||||
>
|
||||
<code>{item.expression}</code>
|
||||
</div>
|
||||
) : null}
|
||||
<div className="cheat-sheet-item__label">{item.label}</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
```
|
||||
@@ -1,44 +0,0 @@
|
||||
---
|
||||
aliases:
|
||||
- ../../../plugins/add-support-for-annotations/
|
||||
description: Add support for annotations in your plugin.
|
||||
keywords:
|
||||
- grafana
|
||||
- plugins
|
||||
- plugin
|
||||
- annotations
|
||||
labels:
|
||||
products:
|
||||
- enterprise
|
||||
- oss
|
||||
menuTitle: Enable annotations
|
||||
title: Enable annotations
|
||||
weight: 100
|
||||
---
|
||||
|
||||
# Enable annotations
|
||||
|
||||
You can add support to your plugin for annotations that will insert information into Grafana alerts. This guide explains how to add support for [annotations]({{< relref "../../../../dashboards/build-dashboards/annotate-visualizations#querying-other-data-sources" >}}) to a data source plugin.
|
||||
|
||||
## Support annotations in your data source plugin
|
||||
|
||||
To enable annotations, simply add two lines of code to your plugin. Grafana uses your default query editor for editing annotation queries.
|
||||
|
||||
1. Add `"annotations": true` to the [plugin.json]({{< relref "../../metadata.md" >}}) file to let Grafana know that your plugin supports annotations.
|
||||
|
||||
**In `plugin.json`:**
|
||||
|
||||
```json
|
||||
{
|
||||
"annotations": true
|
||||
}
|
||||
```
|
||||
|
||||
2. In `datasource.ts`, override the `annotations` property from `DataSourceApi` (or `DataSourceWithBackend` for backend data sources). For the default behavior, set `annotations` to an empty object.
|
||||
|
||||
**In `datasource.ts`:**
|
||||
|
||||
```ts
|
||||
annotations: {
|
||||
}
|
||||
```
|
||||
@@ -1,85 +0,0 @@
|
||||
---
|
||||
aliases:
|
||||
- ../../../plugins/add-support-for-explore-queries/
|
||||
description: Add features to Explore queries.
|
||||
keywords:
|
||||
- grafana
|
||||
- plugins
|
||||
- plugin
|
||||
- queries
|
||||
- explore queries
|
||||
- explore
|
||||
labels:
|
||||
products:
|
||||
- enterprise
|
||||
- oss
|
||||
title: Add features to Explore queries
|
||||
weight: 400
|
||||
---
|
||||
|
||||
# Add features to Explore queries
|
||||
|
||||
[Explore]({{< relref "../../../../explore" >}}) allows users can make ad-hoc queries without the use of a dashboard. This is useful when they want to troubleshoot or learn more about the data.
|
||||
|
||||
Your data source supports Explore by default and uses the existing query editor for the data source. This guide explains how to extend functionality for Explore queries in a data source plugin.
|
||||
|
||||
## Add an Explore-specific query editor
|
||||
|
||||
To extend Explore functionality for your data source, define an Explore-specific query editor.
|
||||
|
||||
1. Create a file `ExploreQueryEditor.tsx` in the `src` directory of your plugin, with content similar to this:
|
||||
|
||||
```ts
|
||||
import React from 'react';
|
||||
|
||||
import { QueryEditorProps } from '@grafana/data';
|
||||
import { QueryField } from '@grafana/ui';
|
||||
import { DataSource } from './DataSource';
|
||||
import { MyQuery, MyDataSourceOptions } from './types';
|
||||
|
||||
type Props = QueryEditorProps<DataSource, MyQuery, MyDataSourceOptions>;
|
||||
|
||||
export default (props: Props) => {
|
||||
return <h2>My Explore-specific query editor</h2>;
|
||||
};
|
||||
```
|
||||
|
||||
1. Modify your base query editor in `QueryEditor.tsx` to render the Explore-specific query editor. For example:
|
||||
|
||||
```ts
|
||||
// [...]
|
||||
import { CoreApp } from '@grafana/data';
|
||||
import ExploreQueryEditor from './ExploreQueryEditor';
|
||||
|
||||
type Props = QueryEditorProps<DataSource, MyQuery, MyDataSourceOptions>;
|
||||
|
||||
export default (props: Props) => {
|
||||
const { app } = props;
|
||||
|
||||
switch (app) {
|
||||
case CoreApp.Explore:
|
||||
return <ExploreQueryEditor {...props} />;
|
||||
default:
|
||||
return <div>My base query editor</div>;
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
## Select a preferred visualization type
|
||||
|
||||
By default, Explore should select an appropriate and useful visualization for your data. It can figure out whether the returned data is time series data or logs or something else, and creates the right type of visualization.
|
||||
|
||||
However, if you want a custom visualization, you can add a hint to your returned data frame by setting the `meta' attribute to `preferredVisualisationType`.
|
||||
|
||||
Construct a data frame with specific metadata like this:
|
||||
|
||||
```
|
||||
const firstResult = new MutableDataFrame({
|
||||
fields: [...],
|
||||
meta: {
|
||||
preferredVisualisationType: 'logs',
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
For possible options, refer to [PreferredVisualisationType](https://github.com/grafana/grafana/blob/main/packages/grafana-data/src/types/data.ts#L25).
|
||||
@@ -1,212 +0,0 @@
|
||||
---
|
||||
aliases:
|
||||
- ../../../plugins/add-support-for-variables/
|
||||
description: Add support for variables.
|
||||
keywords:
|
||||
- grafana
|
||||
- plugins
|
||||
- plugin
|
||||
- queries
|
||||
- variables
|
||||
labels:
|
||||
products:
|
||||
- enterprise
|
||||
- oss
|
||||
title: Add support for variables
|
||||
weight: 600
|
||||
---
|
||||
|
||||
# Add support for variables
|
||||
|
||||
Variables are placeholders for values, and you can use them to create templated queries, and dashboard or panel links. For more information on variables, refer to [Templates and variables]({{< relref "../../../../dashboards/variables" >}}).
|
||||
|
||||
In this guide, you'll see how you can turn a query string like this:
|
||||
|
||||
```sql
|
||||
SELECT * FROM services WHERE id = "$service"
|
||||
```
|
||||
|
||||
into
|
||||
|
||||
```sql
|
||||
SELECT * FROM services WHERE id = "auth-api"
|
||||
```
|
||||
|
||||
Grafana provides a couple of helper functions to interpolate variables in a string template. Let's see how you can use them in your plugin.
|
||||
|
||||
## Interpolate variables in panel plugins
|
||||
|
||||
For panels, the `replaceVariables` function is available in the `PanelProps`.
|
||||
|
||||
Add `replaceVariables` to the argument list, and pass a user-defined template string to it:
|
||||
|
||||
```ts
|
||||
export function SimplePanel({ options, data, width, height, replaceVariables }: Props) {
|
||||
const query = replaceVariables('Now displaying $service');
|
||||
|
||||
return <div>{query}</div>;
|
||||
}
|
||||
```
|
||||
|
||||
## Interpolate variables in data source plugins
|
||||
|
||||
For data sources, you need to use the `getTemplateSrv`, which returns an instance of `TemplateSrv`.
|
||||
|
||||
1. Import `getTemplateSrv` from the `runtime` package:
|
||||
|
||||
```ts
|
||||
import { getTemplateSrv } from '@grafana/runtime';
|
||||
```
|
||||
|
||||
1. In your `query` method, call the `replace` method with a user-defined template string:
|
||||
|
||||
```ts
|
||||
async query(options: DataQueryRequest<MyQuery>): Promise<DataQueryResponse> {
|
||||
const query = getTemplateSrv().replace('SELECT * FROM services WHERE id = "$service"', options.scopedVars);
|
||||
|
||||
const data = makeDbQuery(query);
|
||||
|
||||
return { data };
|
||||
}
|
||||
```
|
||||
|
||||
## Format multi-value variables
|
||||
|
||||
When a user selects multiple values for a variable, the value of the interpolated variable depends on the [variable format]({{< relref "../../../../dashboards/variables/variable-syntax#advanced-variable-format-options" >}}).
|
||||
|
||||
A data source plugin can define the default format option when no format is specified by adding a third argument to the interpolation function.
|
||||
|
||||
Let's change the SQL query to use CSV format by default:
|
||||
|
||||
```ts
|
||||
getTemplateSrv().replace('SELECT * FROM services WHERE id IN ($service)', options.scopedVars, 'csv');
|
||||
```
|
||||
|
||||
Now, when users write `$service`, the query looks like this:
|
||||
|
||||
```sql
|
||||
SELECT * FROM services WHERE id IN (admin,auth,billing)
|
||||
```
|
||||
|
||||
For more information on the available variable formats, refer to [Advanced variable format options]({{< relref "../../../../dashboards/variables/variable-syntax/index.md#advanced-variable-format-options" >}}).
|
||||
|
||||
## Set a variable from your plugin
|
||||
|
||||
Not only can you read the value of a variable, you can also update the variable from your plugin. Use `locationService.partial(query, replace)`.
|
||||
|
||||
The following example shows how to update a variable called `service`.
|
||||
|
||||
- `query` contains the query parameters you want to update. The query parameters that control variables are prefixed with `var-`.
|
||||
- `replace: true` tells Grafana to update the current URL state rather than creating a new history entry.
|
||||
|
||||
```ts
|
||||
import { locationService } from '@grafana/runtime';
|
||||
```
|
||||
|
||||
```ts
|
||||
locationService.partial({ 'var-service': 'billing' }, true);
|
||||
```
|
||||
|
||||
> **Note:** Grafana queries your data source whenever you update a variable. Excessive updates to variables can slow down Grafana and lead to a poor user experience.
|
||||
|
||||
## Add support for query variables to your data source
|
||||
|
||||
A [query variable]({{< relref "../../../../dashboards/variables/add-template-variables#add-a-query-variable" >}}) is a type of variable that allows you to query a data source for the values. By adding support for query variables to your data source plugin, users can create dynamic dashboards based on data from your data source.
|
||||
|
||||
Let's start by defining a query model for the variable query:
|
||||
|
||||
```ts
|
||||
export interface MyVariableQuery {
|
||||
namespace: string;
|
||||
rawQuery: string;
|
||||
}
|
||||
```
|
||||
|
||||
For a data source to support query variables, override the `metricFindQuery` in your `DataSourceApi` class. The `metricFindQuery` function returns an array of `MetricFindValue` which has a single property, `text`:
|
||||
|
||||
```ts
|
||||
async metricFindQuery(query: MyVariableQuery, options?: any) {
|
||||
// Retrieve DataQueryResponse based on query.
|
||||
const response = await this.fetchMetricNames(query.namespace, query.rawQuery);
|
||||
|
||||
// Convert query results to a MetricFindValue[]
|
||||
const values = response.data.map(frame => ({ text: frame.name }));
|
||||
|
||||
return values;
|
||||
}
|
||||
```
|
||||
|
||||
> **Note:** By default, Grafana provides a basic query model and editor for simple text queries. If that's all you need, then leave the query type as `string`:
|
||||
|
||||
```ts
|
||||
async metricFindQuery(query: string, options?: any)
|
||||
```
|
||||
|
||||
Let's create a custom query editor to allow the user to edit the query model.
|
||||
|
||||
1. Create a `VariableQueryEditor` component:
|
||||
|
||||
```ts
|
||||
import React, { useState } from 'react';
|
||||
import { MyVariableQuery } from './types';
|
||||
|
||||
interface VariableQueryProps {
|
||||
query: MyVariableQuery;
|
||||
onChange: (query: MyVariableQuery, definition: string) => void;
|
||||
}
|
||||
|
||||
export const VariableQueryEditor = ({ onChange, query }: VariableQueryProps) => {
|
||||
const [state, setState] = useState(query);
|
||||
|
||||
const saveQuery = () => {
|
||||
onChange(state, `${state.query} (${state.namespace})`);
|
||||
};
|
||||
|
||||
const handleChange = (event: React.FormEvent<HTMLInputElement>) =>
|
||||
setState({
|
||||
...state,
|
||||
[event.currentTarget.name]: event.currentTarget.value,
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="gf-form">
|
||||
<span className="gf-form-label width-10">Namespace</span>
|
||||
<input
|
||||
name="namespace"
|
||||
className="gf-form-input"
|
||||
onBlur={saveQuery}
|
||||
onChange={handleChange}
|
||||
value={state.namespace}
|
||||
/>
|
||||
</div>
|
||||
<div className="gf-form">
|
||||
<span className="gf-form-label width-10">Query</span>
|
||||
<input
|
||||
name="rawQuery"
|
||||
className="gf-form-input"
|
||||
onBlur={saveQuery}
|
||||
onChange={handleChange}
|
||||
value={state.rawQuery}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
Grafana saves the query model whenever one of the text fields loses focus (`onBlur`) and then previews the values returned by `metricFindQuery`.
|
||||
|
||||
The second argument to `onChange` allows you to set a text representation of the query that will appear next to the name of the variable in the variables list.
|
||||
|
||||
1. Configure your plugin to use the query editor:
|
||||
|
||||
```ts
|
||||
import { VariableQueryEditor } from './VariableQueryEditor';
|
||||
|
||||
export const plugin = new DataSourcePlugin<DataSource, MyQuery, MyDataSourceOptions>(DataSource)
|
||||
.setQueryEditor(QueryEditor)
|
||||
.setVariableQueryEditor(VariableQueryEditor);
|
||||
```
|
||||
|
||||
That's it! You can now try out the plugin by adding a [query variable]({{< relref "../../../../dashboards/variables/add-template-variables#add-a-query-variable" >}}) to your dashboard.
|
||||
@@ -1,89 +0,0 @@
|
||||
---
|
||||
aliases:
|
||||
- ../../../plugins/cross-plugin-linking/
|
||||
description: Learn how to add plugin links to a Grafana app plugin.
|
||||
keywords:
|
||||
- grafana
|
||||
- plugins
|
||||
- plugin
|
||||
- links
|
||||
- cross-plugin links
|
||||
- extensions
|
||||
- extensions api
|
||||
labels:
|
||||
products:
|
||||
- enterprise
|
||||
- oss
|
||||
title: Work with cross-plugin links
|
||||
weight: 800
|
||||
---
|
||||
|
||||
# Work with cross-plugin links
|
||||
|
||||
With the Plugins extension API, app plugins can register extension points of their own to display other plugins links. This is called _cross-plugin linking_, and you can use it to create more immersive user experiences with installed plugins.
|
||||
|
||||
## Available extension points within plugins
|
||||
|
||||
An extension point is a location in another plugin's UI where your plugin can insert links. All extension point IDs within plugins should follow the naming convention `plugins/<plugin-id>/<extension-point-id>`.
|
||||
|
||||
## How to create an extension point within a plugin
|
||||
|
||||
Use the `getPluginExtensions` method in `@grafana/runtime` to create an extension point within your plugin. An extension point is a way to specify where in the plugin UI other plugins links are rendered.
|
||||
|
||||
{{% admonition type="note" %}}
|
||||
Creating an extension point in a plugin creates a public interface for other plugins to interact with. Changes to the extension point ID or its context could break any plugin that attempts to register a link inside your plugin.
|
||||
{{% /admonition %}}
|
||||
|
||||
The `getPluginExtensions` method takes an object consisting of the `extensionPointId`, which must begin `plugin/<pluginId>`, and any contextual information that you want to provide. The `getPluginExtensions` method returns a list of `extensionLinks` that your program can loop over:
|
||||
|
||||
```typescript
|
||||
import { getPluginExtensions } from '@grafana/runtime';
|
||||
import { isPluginExtensionLink } from '@grafana/data';
|
||||
import { LinkButton } from '@grafana/ui';
|
||||
|
||||
function AppExtensionPointExample() {
|
||||
const { extensions } = getPluginExtensions({
|
||||
extensionPointId: 'plugin/another-app-plugin/menu',
|
||||
context: {
|
||||
pluginId: 'another-app-plugin',
|
||||
},
|
||||
});
|
||||
|
||||
if (extensions.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
{extensions.map((extension) => {
|
||||
if (isPluginExtensionLink(extension)) {
|
||||
return (
|
||||
<LinkButton href={extension.path} title={extension.description} key={extension.key}>
|
||||
{extension.title}
|
||||
</LinkButton>
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
The preceding example shows a component that renders `<LinkButton />` components for all link extensions that other plugins registered for the `plugin/another-app-plugin/menu` extension point ID. The context is passed as the second parameter to `getPluginExtensions`, which uses `Object.freeze` to make the context immutable before passing it to other plugins.
|
||||
|
||||
## Insert links into another plugin
|
||||
|
||||
Create links for other plugins in the same way you [extend the Grafana application UI]({{< relref "./extend-the-grafana-ui-with-links" >}}) with a link. Don't specify a `grafana/...` extension point. Instead, specify the plugin extension point `plugin/<pluginId>/<extensionPointId>`.
|
||||
|
||||
Given the preceding example, use a plugin link such as the following:
|
||||
|
||||
```typescript
|
||||
new AppPlugin().configureExtensionLink({
|
||||
title: 'Go to basic app',
|
||||
description: 'Will navigate the user to the basic app',
|
||||
extensionPointId: 'plugin/another-app-plugin/menu',
|
||||
path: '/a/myorg-basic-app/one',
|
||||
});
|
||||
```
|
||||
@@ -1,136 +0,0 @@
|
||||
---
|
||||
aliases:
|
||||
- ../../../plugins/custom-panel-option-editors/
|
||||
description: How to build a custom panel option editor.
|
||||
keywords:
|
||||
- grafana
|
||||
- plugins
|
||||
- plugin
|
||||
- custom panel option editor
|
||||
- customizing panel options
|
||||
- panel options
|
||||
labels:
|
||||
products:
|
||||
- enterprise
|
||||
- oss
|
||||
title: Build a custom panel option editor
|
||||
weight: 700
|
||||
---
|
||||
|
||||
# Build a custom panel option editor
|
||||
|
||||
The Grafana plugin platform comes with a range of editors that allow your users to customize a panel. The standard editors cover the most common types of options, such as text input and boolean switches. If you don't find the editor you're looking for, you can build your own.
|
||||
|
||||
## Panel option editor basics
|
||||
|
||||
The simplest editor is a React component that accepts two props:
|
||||
|
||||
- **`value`**: the current value of the option
|
||||
- **`onChange`**: updates the option's value
|
||||
|
||||
The editor in the example below lets the user toggle a boolean value by clicking a button:
|
||||
|
||||
**SimpleEditor.tsx**
|
||||
|
||||
```ts
|
||||
import React from 'react';
|
||||
import { Button } from '@grafana/ui';
|
||||
import { StandardEditorProps } from '@grafana/data';
|
||||
|
||||
export const SimpleEditor = ({ value, onChange }: StandardEditorProps<boolean>) => {
|
||||
return <Button onClick={() => onChange(!value)}>{value ? 'Disable' : 'Enable'}</Button>;
|
||||
};
|
||||
```
|
||||
|
||||
To use a custom panel option editor, use the `addCustomEditor` on the `OptionsUIBuilder` object in your `module.ts` file and set the `editor` property to the name of your custom editor component.
|
||||
|
||||
**module.ts**
|
||||
|
||||
```ts
|
||||
export const plugin = new PanelPlugin<SimpleOptions>(SimplePanel).setPanelOptions((builder) => {
|
||||
return builder.addCustomEditor({
|
||||
id: 'label',
|
||||
path: 'label',
|
||||
name: 'Label',
|
||||
editor: SimpleEditor,
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
## Add settings to your panel option editor
|
||||
|
||||
You can use your custom editor to customize multiple possible settings. To add settings to your editor, set the second template variable of `StandardEditorProps` to an interface that contains the settings you want to configure. Access the editor settings through the `item` prop.
|
||||
|
||||
Here's an example of an editor that populates a drop-down with a range of numbers. The `Settings` interface defines the range of the `from` and `to` properties.
|
||||
|
||||
**SimpleEditor.tsx**
|
||||
|
||||
```ts
|
||||
interface Settings {
|
||||
from: number;
|
||||
to: number;
|
||||
}
|
||||
|
||||
type Props = StandardEditorProps<number, Settings>;
|
||||
|
||||
export const SimpleEditor = ({ item, value, onChange }: Props) => {
|
||||
const options: Array<SelectableValue<number>> = [];
|
||||
|
||||
// Default values
|
||||
const from = item.settings?.from ?? 1;
|
||||
const to = item.settings?.to ?? 10;
|
||||
|
||||
for (let i = from; i <= to; i++) {
|
||||
options.push({
|
||||
label: i.toString(),
|
||||
value: i,
|
||||
});
|
||||
}
|
||||
|
||||
return <Select options={options} value={value} onChange={(selectableValue) => onChange(selectableValue.value)} />;
|
||||
};
|
||||
```
|
||||
|
||||
You can now configure the editor for each option by configuring the `settings` property to call `addCustomEditor`:
|
||||
|
||||
```ts
|
||||
export const plugin = new PanelPlugin<SimpleOptions>(SimplePanel).setPanelOptions((builder) => {
|
||||
return builder.addCustomEditor({
|
||||
id: 'index',
|
||||
path: 'index',
|
||||
name: 'Index',
|
||||
editor: SimpleEditor,
|
||||
settings: {
|
||||
from: 1,
|
||||
to: 10,
|
||||
},
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
## Use query results in your panel option editor
|
||||
|
||||
Option editors can access the results from the last query. This lets you update your editor dynamically based on the data returned by the data source.
|
||||
|
||||
The editor context is available through the `context` prop. The data frames returned by the data source are available under `context.data`.
|
||||
|
||||
**SimpleEditor.tsx**
|
||||
|
||||
```ts
|
||||
export const SimpleEditor = ({ item, value, onChange, context }: StandardEditorProps<string>) => {
|
||||
const options: SelectableValue<string>[] = [];
|
||||
|
||||
if (context.data) {
|
||||
const frames = context.data;
|
||||
|
||||
for (let i = 0; i < frames.length; i++) {
|
||||
options.push({
|
||||
label: frames[i].name,
|
||||
value: frames[i].name,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return <Select options={options} value={value} onChange={(selectableValue) => onChange(selectableValue.value)} />;
|
||||
};
|
||||
```
|
||||
@@ -1,146 +0,0 @@
|
||||
---
|
||||
aliases:
|
||||
- ../../../plugins/extend-the-grafana-ui-with-links/
|
||||
description: Learn how to add links to the Grafana user interface from an app plugin
|
||||
keywords:
|
||||
- grafana
|
||||
- plugins
|
||||
- plugin
|
||||
- links
|
||||
- extensions
|
||||
- app plugins
|
||||
labels:
|
||||
products:
|
||||
- enterprise
|
||||
- oss
|
||||
title: Use extensions to add links to app plugins
|
||||
weight: 760
|
||||
---
|
||||
|
||||
# Use extensions to add links to app plugins
|
||||
|
||||
You can use the Plugin extensions API with your Grafana app plugins to add links to the Grafana UI. This feature lets you send users to your plugin's pages from other spots in the Grafana application.
|
||||
|
||||
## Before you begin
|
||||
|
||||
Be sure your plugin meets the following requirements before proceeding:
|
||||
|
||||
- It must be an app plugin.
|
||||
- It must be preloaded (by setting the [preload property]({{< relref "../../metadata.md" >}}) to `true` in the `plugin.json`
|
||||
- It must be installed and enabled.
|
||||
|
||||
## Available extension points within Grafana
|
||||
|
||||
An _extension point_ is a location within the Grafana UI where a plugin can insert links. The IDs of all extension points within Grafana start with `grafana/`. For example, you can use the following extension point ID:
|
||||
|
||||
- `grafana/dashboard/panel/menu`: extension point for all panel dropdown menus in dashboards
|
||||
|
||||
## Add a link extension within a Grafana dashboard panel menu
|
||||
|
||||
To add a link extension within a Grafana dashboard panel menu, complete the following steps:
|
||||
|
||||
1. Define the link extension in your plugin's `module.ts` file.
|
||||
|
||||
1. Define a new instance of the `AppPlugin` class by using the `configureExtensionLink` method. This method requires:
|
||||
- an object that describes your link extension, including a `title` property for the link text
|
||||
- an `extensionPointId` method that tells Grafana where the link should appear
|
||||
- a `path` for the user to go to your plugin
|
||||
|
||||
```typescript
|
||||
new AppPlugin().configureExtensionLink({
|
||||
title: 'Go to basic app',
|
||||
description: 'Will send the user to the basic app',
|
||||
extensionPointId: 'grafana/dashboard/panel/menu',
|
||||
path: '/a/myorg-basic-app/one', // Must start with "/a/<PLUGIN_ID>/"
|
||||
});
|
||||
```
|
||||
|
||||
Your link will now appear in dashboard panel menus. When the user clicks the link, they will be sent to the path you defined earlier.
|
||||
|
||||
{{% admonition type="note" %}} Each plugin is limited to a maximum of two links per extension point.{{%
|
||||
/admonition %}}
|
||||
|
||||
## Add a link extension using context within Grafana
|
||||
|
||||
The above example works for simple cases. However, you may want to act on information from the app's panel from which the user is navigating.
|
||||
|
||||
To do this, use the `configure` property on the object that is passed to `configureExtensionLink()`. This property takes a function and returns an object that consists of a `title` property for the link text and a `path` to send the user to your plugin.
|
||||
|
||||
Alternatively, if you need to hide the link for certain scenarios, define the function to return _undefined_:
|
||||
|
||||
```typescript
|
||||
new AppPlugin().configureExtensionLink({
|
||||
title: 'Go to basic app',
|
||||
description: 'Will send the user to the basic app',
|
||||
extensionPointId: 'grafana/dashboard/panel/menu',
|
||||
path: '/a/myorg-basic-app/one',
|
||||
configure: (context: PanelContext) => {
|
||||
switch (context?.pluginId) {
|
||||
case 'timeseries':
|
||||
return {
|
||||
title: 'Go to page one',
|
||||
description: 'hello',
|
||||
path: '/a/myorg-basic-app/one',
|
||||
};
|
||||
|
||||
case 'piechart':
|
||||
return {
|
||||
title: 'Go to page two',
|
||||
path: '/a/myorg-basic-app/two',
|
||||
};
|
||||
|
||||
// Returning undefined tells Grafana to hide the link
|
||||
default:
|
||||
return undefined;
|
||||
}
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
The above example demonstrates how to return a different `path` based on which plugin the dashboard panel is using. If the clicked-upon panel is neither a time series nor a pie chart panel, then the `configure()` function returns _undefined_. When this happens, Grafana doesn't render the link.
|
||||
|
||||
{{% admonition type="note" %}} The context passed to the `configure()` function is bound by the `extensionPointId` into which you insert the link. Different extension points contain different contexts.{{%
|
||||
/admonition %}}
|
||||
|
||||
## Add an event handler to a link
|
||||
|
||||
Link extensions give you the means to direct users to a plugin page via href links within the Grafana UI. You can also use them to trigger `onClick` events to perform dynamic actions when clicked.
|
||||
|
||||
To add an event handler to a link in a panel menu, complete the following steps:
|
||||
|
||||
1. Define the link extension in the plugin's `module.ts` file.
|
||||
1. Create a new instance of the `AppPlugin` class, again using the `configureExtensionLink` method. This time, add an `onClick` property which takes a function. This function receives the click event and an object consisting of the `context` and an `openModal` function.
|
||||
|
||||
In the following example, we open a dialog.
|
||||
|
||||
```typescript
|
||||
new AppPlugin().configureExtensionLink({
|
||||
title: 'Go to basic app',
|
||||
description: 'Will send the user to the basic app',
|
||||
extensionPointId: 'grafana/dashboard/panel/menu',
|
||||
path: '/a/myorg-basic-app/one',
|
||||
onClick: (event, { context, openModal }) => {
|
||||
event.preventDefault();
|
||||
openModal({
|
||||
title: 'My plugin dialog',
|
||||
body: ({ onDismiss }) => <SampleModal onDismiss={onDismiss} pluginId={context?.pluginId} />,
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
type Props = {
|
||||
onDismiss: () => void;
|
||||
pluginId?: string;
|
||||
};
|
||||
|
||||
const SampleModal = ({ onDismiss, pluginId }: Props) => {
|
||||
return (
|
||||
<VerticalGroup spacing="sm">
|
||||
<p>This dialog was opened via the plugin extensions API.</p>
|
||||
<p>The panel is using a {pluginId} plugin to display data.</p>
|
||||
</VerticalGroup>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
As you can see, the plugin extensions API enables you to insert links into the UI of Grafana applications that send users to plugin features or trigger actions based on where the user clicked. The plugins extension API can also be used for [cross-plugin linking]({{< relref "./cross-plugin-linking" >}}).
|
||||
@@ -1,27 +0,0 @@
|
||||
---
|
||||
description: Get started with Grafana plugin development.
|
||||
keywords:
|
||||
- grafana
|
||||
- plugins
|
||||
- plugin
|
||||
- documentation
|
||||
labels:
|
||||
products:
|
||||
- enterprise
|
||||
- oss
|
||||
menuTitle: Get started with plugins
|
||||
title: Get started with Grafana plugin development
|
||||
weight: 200
|
||||
---
|
||||
|
||||
# Get started with Grafana plugin development
|
||||
|
||||
This section contains guidance for building plugins:
|
||||
|
||||
- [Develop with local Grafana]({{< relref "./development-with-local-grafana.md" >}})
|
||||
|
||||
Additional resources:
|
||||
|
||||
- [Get started with creating a plugin](https://grafana.github.io/plugin-tools/docs/get-started/)
|
||||
- [Types of Grafana plugins](/docs/grafana/latest/administration/plugin-management/)
|
||||
- [Set up your development environment](https://grafana.github.io/plugin-tools/docs/get-started/set-up-development-environment)
|
||||
@@ -1,192 +0,0 @@
|
||||
---
|
||||
aliases:
|
||||
- ../../plugins/development-with-local-grafana/
|
||||
description: How to develop with a local Grafana environment.
|
||||
keywords:
|
||||
- grafana
|
||||
- plugins
|
||||
- plugin
|
||||
- development environment
|
||||
- local environment
|
||||
labels:
|
||||
products:
|
||||
- enterprise
|
||||
- oss
|
||||
title: Develop with a local environment
|
||||
weight: 400
|
||||
---
|
||||
|
||||
# Develop with a local environment
|
||||
|
||||
Follow the steps in this guide to set up a development environment where you run Grafana and your plugin locally. With this setup, you can see your changes as you add them.
|
||||
|
||||
## Run Grafana in your host
|
||||
|
||||
To clone and run Grafana locally:
|
||||
|
||||
1. Download and set up Grafana. Refer to the [developer-guide](https://github.com/grafana/grafana/blob/HEAD/contribute/developer-guide.md).
|
||||
|
||||
2. Grafana looks for plugins, by default, in its `data/plugins` directory. You can create a symbolic link to your plugin repository to detect new changes:
|
||||
|
||||
```bash
|
||||
ln -s <plugin-path>/dist data/plugins/<plugin-name>
|
||||
```
|
||||
|
||||
3. Optional: If the preceding step doesn't work for you (for example, if you are running on Windows), then modify the default path in the Grafana configuration. Find the default path at `conf/custom.ini`) and point it to your plugin's directory:
|
||||
|
||||
```ini
|
||||
[paths]
|
||||
plugins = <path-to-your-plugin-parent-directory>
|
||||
```
|
||||
|
||||
## Run Grafana with docker-compose
|
||||
|
||||
Another option is to run Grafana with docker-compose so that it runs in a container. To do so, create the `docker-compose` file in your plugin directory.
|
||||
|
||||
{{% admonition type="note" %}}
|
||||
If your plugin already includes a docker-compose file, then skip this step.
|
||||
{{% /admonition %}}
|
||||
|
||||
```yaml
|
||||
version: '3.7'
|
||||
|
||||
services:
|
||||
grafana:
|
||||
# Change latest with your target version, if needed
|
||||
image: grafana/grafana:latest
|
||||
ports:
|
||||
- 3000:3000/tcp
|
||||
volumes:
|
||||
# Use your plugin folder (for example, redshift-datasource)
|
||||
- ./dist:/var/lib/grafana/plugins/<plugin-folder>
|
||||
- ./provisioning:/etc/grafana/provisioning
|
||||
environment:
|
||||
- TERM=linux
|
||||
- GF_LOG_LEVEL=debug
|
||||
- GF_DATAPROXY_LOGGING=true
|
||||
- GF_DEFAULT_APP_MODE=development
|
||||
```
|
||||
|
||||
## Run your plugin in development mode
|
||||
|
||||
Finally, start your plugin in development mode. Go to your plugin's root directory and follow these steps:
|
||||
|
||||
1. Build your plugin backend and start the frontend in watch mode:
|
||||
|
||||
```bash
|
||||
mage -v
|
||||
yarn watch
|
||||
```
|
||||
|
||||
2. Start the Grafana backend and frontend:
|
||||
|
||||
1. For a local copy of Grafana, go to the directory with Grafana source code and run:
|
||||
|
||||
```bash
|
||||
make run
|
||||
```
|
||||
|
||||
```bash
|
||||
yarn start
|
||||
```
|
||||
|
||||
2. Or, with docker-compose, in your plugin directory, run:
|
||||
|
||||
```bash
|
||||
docker-compose up
|
||||
```
|
||||
|
||||
After this, you should be able to see your plugin listed in Grafana, and then you can test your changes.
|
||||
|
||||
If you make a change in the frontend, you must refresh your browser. However, changes in the backend may require that you rebuild your plugin binaries and reload the plugin (`mage && mage reloadPlugin` for local development, or run `docker-compose up` again if you are using docker-compose).
|
||||
|
||||
## Run your backend plugin with a debugger
|
||||
|
||||
{{% admonition type="note" %}}
|
||||
The following method only works with a local Grafana instance and currently doesn't work with Docker.
|
||||
{{% /admonition %}}
|
||||
|
||||
Running a backend plugin with a debugger is supported in Visual Studio Code and GoLand out of the box, but it can also work with any other IDE or debugger.
|
||||
|
||||
You can run a backend plugin and attach a debugger to it, which allows you to set breakpoints and debug your backend plugin directly from your IDE of choice:
|
||||
|
||||
1. Go to your plugin's folder.
|
||||
|
||||
1. Check your `go.mod` to make sure `grafana-plugin-sdk-go` are at least on `v0.156.0`
|
||||
- If not, update it to the latest version:
|
||||
```
|
||||
go get -u github.com/grafana/grafana-plugin-sdk-go
|
||||
```
|
||||
1. Build your plugin at least once:
|
||||
```
|
||||
yarn build && mage
|
||||
```
|
||||
1. Install your plugin into your local Grafana instance.
|
||||
|
||||
Now that your plugin is ready to run, follow the instructions bellow for your IDE of choice.
|
||||
|
||||
### Visual Studio Code
|
||||
|
||||
1. If it's not already present, go to your plugin's folder and place the following file inside `.vscode/launch.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Standalone debug mode",
|
||||
"type": "go",
|
||||
"request": "launch",
|
||||
"mode": "debug",
|
||||
"program": "${workspaceFolder}/pkg",
|
||||
"env": {},
|
||||
"args": ["-standalone"]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
1. Press `F5` to run your plugin in debug mode.
|
||||
1. If Grafana isn't already running, run it.
|
||||
|
||||
> If you re-run the configuration, Grafana automatically reloads the plugin.
|
||||
|
||||
### GoLand
|
||||
|
||||
1. Create a new Run/Debug configuration:
|
||||
|
||||
- **Run kind**: Package
|
||||
- **Package path**: your `pkg` package
|
||||
- **Program arguments**: `-standalone`
|
||||
|
||||
1. Run the config (with or without the debugger).
|
||||
|
||||
1. If Grafana isn't already running, run it.
|
||||
|
||||
{{% admonition type="note" %}}
|
||||
If you re-run the configuration, Grafana automatically reloads the plugin.
|
||||
{{% /admonition %}}
|
||||
|
||||
### Other IDEs
|
||||
|
||||
Configure your code editor to run the following steps:
|
||||
|
||||
1. Build the executable file with debug flags.
|
||||
```
|
||||
mage build:debug
|
||||
```
|
||||
1. Run the plugin's executable file (inside `dist`) with `-standalone -debug` flags.
|
||||
```
|
||||
./gpx_xyz_linux_amd64 -standalone -debug
|
||||
```
|
||||
1. Attach a debugger to the process.
|
||||
|
||||
1. If Grafana isn't already running, run it.
|
||||
|
||||
> If you re-run the configuration, Grafana automatically reloads the plugin.
|
||||
|
||||
### Notes
|
||||
|
||||
- All logs are printed in the plugin's `stdout` rather than in Grafana logs.
|
||||
- If the backend plugin doesn't serve requests after you turn off debug mode, you can force a reset to the standalone mode. To do so, delete the files `dist/standalone.txt`, `dist/pid.txt`, and the executable file, and then restart Grafana.
|
||||
- Grafana doesn't support debugging backend plugins running inside Docker. But this is a [planned enhancement](https://github.com/grafana/grafana-plugin-sdk-go/issues/685).
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user