Context-rich observability and security insights at hand - The Dynatrace Backstage plugins enable you to fetch observability and security data from Dynatrace to be displayed at software components managed in your Backstage Software Catalog. The data is in tabular format with smart links to Dynatrace Apps for deeper analysis and root cause investigation in case of a related problem or security vulnerability.
The plugins support Kubernetes entities by default. This means that it comes with a pre-configured query for Kubernetes deployments and a dedicated component for data representation.
This repository contains a complete Backstage installation at its root, with the
individual plugins in the plugins directory. Backstage is configured to
include the plugins so you can start the app and see them in action.
Table of contents:
Three plugins are required to fetch and visualize the data from Dynatrace:
- DQL - A plugin showing DQL query results from Dynatrace in Backstage.
- DQL Backend - The backend for the DQL plugin.
- DQL Common - Common functionality shared between the DQL frontend and backend plugin.
With the Backstage plugins, the Backstage Software Catalog component can be
associated with real-time monitoring data. The screenshot shows three Kubernetes
deployments of the easytrade-frontend component running in different
namespaces, i.e., development, hardening, and production. The table
provides smart links for more details about the workload and logs in Dynatrace.
While Kubernetes deployments are supported by default, any data can be fetched from Dynatrace and displayed in Backstage. See below how to configure custom queries.
Next are the instructions to install, integrate, configure, and run the Dyantrace Backstage plugins.
We are publishing our packages to NPM and removed the release candidate (v1.0.0-rc1) packages from the GitHub registry. If you were using the v1.0.0-rc1 packages, please update your
.yarnrc.ymlcorrespondingly.
Install the DQL plugins into Backstage:
yarn --cwd packages/app add @dynatrace/backstage-plugin-dql
yarn --cwd packages/backend add @dynatrace/backstage-plugin-dql-backend
yarn --cwd packages/backend add @dynatrace/backstage-plugin-dql-commonAdd the DQL plugin to the respective component type pages in your
packages/app/src/components/catalog/EntityPage.tsx:
<EntityDqlQueryCard
title="Kubernetes Deployments"
queryId="dynatrace.kubernetes-deployments"
/>See the EntityPage.tsx file in this repository
(packages/app/src/components/catalog/EntityPage.tsx) for a full example.
Add the DQL backend plugin to in packages/backend/src/index.ts:
const backend = createBackend();
...
backend.add(import('@dynatrace/backstage-plugin-dql-backend'));
...See the index.ts file in this repository (packages/backend/src/index.ts) for
a full example.
Before configuring a Dynatrace connection, an OAuth 2.0 client is required.
How to create an OAuth 2.0 client
- In Dynatrace, go to Account Management.
- Select Identity & access management > OAuth clients.
- Select Create client.
- Enter a client description and the user email.
- Select at least the following scopes.
storage:buckets:readstorage:entities:readstorage:events:readstorage:metrics:readstorage:security.events:read
- Scroll down and select Create client.
- Copy your client ID, client secret, and Dynatrace account URN. These settings are required for the Backstage plugin configuration.
Create or update the app-config.local.yaml file (excluded by .gitignore)
configuring the connection to the Dynatrace environment:
dynatrace:
environments:
- name: xxxxxxxx
url: https://xxxxxxxx.apps.dynatrace.com
tokenUrl: https://sso.dynatrace.com/sso/oauth2/token
accountUrn: <accountUrn>
clientId: <clientId>
clientSecret: <clientSecret>Read Platform Token documentation for more details.
dynatrace:
environments:
- name: xxxxxxxx
url: https://xxxxxxxx.apps.dynatrace.com
platformToken: <platformToken>See below how to configure multiple Dynatrace environments.
To start the app, run:
yarn install
yarn devBackstage is pre-configured but relies on appropriate data to be available in the demo Dynatrace environment. See catalog-info.yaml for details.
Find here additional features to customize the plugin for different requirements.
Setup LOG_QUERY=true node env variable to be able to log DQL queries during
execution
If the component in Backstage should display data from multiple Dynatrace
environments, add each Dynatrace environment to the dynatrace.environments
list in the app-config.local.yaml file.
dynatrace:
environments:
- name: xxxxxxxx
url: https://xxxxxxxx.apps.dynatrace.com
tokenUrl: https://sso.dynatrace.com/sso/oauth2/token
accountUrn: <accountUrn>
clientId: <clientId>
clientSecret: <clientSecret>
- name: yyyyyyyy
url: https://yyyyyyyy.apps.dynatrace.com
tokenUrl: https://sso.dynatrace.com/sso/oauth2/token
accountUrn: <accountUrn>
clientId: <clientId>
clientSecret: <clientSecret>Using the EntityKubernetesDeploymentsCard, you can see the Kubernetes
deployments in your Dynatrace environment.
<EntityKubernetesDeploymentsCard
title="Show me my Kubernetes deployments"
queryId="dynatrace.kubernetes-deployments"
/>Convention: Kubernetes pods will be listed for the corresponding Backstage component if they are properly annotated in the deployment descriptor. See annotations.
[!NOTE] Please ensure you have the
storage:entities:readpermission to read Kubernetes deployments.
Example:
backstage.io/kubernetes-id: <backstage-namespace>.<backstage-component-name> *)
backstage.io/kubernetes-namespace: namespace
backstage.io/kubernetes-label-selector: stage=hardening,name=frontend*) While any value can be defined, it is recommended to set the backstage namespace followed by the component name.
- The annotation
backstage.io/kubernetes-idwill look for the Kubernetes labelbackstage.io/kubernetes-id. - The annotation
backstage.io/kubernetes-namespacewill look for the Kubernetes namespace. - The annotation
backstage.io/kubernetes-label-selectorwill look for the labels defined in it. Sobackstage.io/kubernetes-label-selector: stage=hardening,name=frontendwill look for a Kubernetes labelstagewith the valuehardeningand a labelnamewith the valuefrontend.
If a backstage.io/kubernetes-label-selector is given,
backstage.io/kubernetes-id is ignored.
If no namespace is given, it looks for all namespaces. There is no fallback to
default.
The query for fetching the monitoring data for Kubernetes deployments is defined
here:
dynatrace.kubernetes-deployments.
You can change this query for all cards or override it using a custom query.
Convention: To display the pod's version in Backstage, annotate it using the following key-value pair:
app.kubernetes.io/version: <version>Using the EntityDqlQueryCard with the queryId dynatrace.srg-validations, you
can see the validations of the site reliability guardians in your Dynatrace
environment.
<EntityDqlQueryCard
title="Site Reliability Guardian Validations"
queryId="dynatrace.srg-validations"
/>[!NOTE] Please ensure you have the
storage:events:readandstorage:bizevents:readpermissions to read guardian validations.
To filter for specific guardians, you can filter tags defined in the
metadata.annotations property of the catalog-info.yaml file under the key
dynatrace.com/guardian-tags.
apiVersion: backstage.io/v1alpha1
kind: Component
metadata:
name: demo-backstage
description: Backstage Demo instance.
annotations:
backstage.io/kubernetes-id: kubernetescustom
dynatrace.com/guardian-tags: 'service=my-service,stage=development,novalue'There are two ways to filter tags:
- Key-Value Match: The tag must match both the key and the value. For
example, the key
servicemust have the valuemy-service. - Key Exists: The tag key must exist with any value. For example, the key
novalue.
The difference between these filtering methods is indicated by the presence of
the = symbol.
For the example provided above, the filtered guardian should have at least these tags:
service: my-service
stage: development
novalue:Set dynatrace.com/guardian-tags to empty, if do not want to filter for
specific tags.
dynatrace.com/guardian-tags: ''See How to create a Site Reliability Guardian and Guardian tags.
The query for fetching the monitoring data for Site Reliability Guardian
validations is defined here:
dynatrace.srg-validations. You
can change this query for all cards or override it using a custom query.
You can also register your custom queries and use them with
EntityDqlQueryCard:
dynatrace:
queries:
- id: my-custom-query
query: >
fetch events | filter event.kind == "DAVIS_EVENT" | fields event.kind,
timestamp[!NOTE] Depending on your custom query ensure you have the relevant permissions.
Queries can contain placeholders, which will be replaced with the values from
the context in which it is executed. These placeholders are prefixed with $$
in order to escape the
environment variable substitution
of Backstage. The following placeholders are available:
$${componentNamespace}the namespace of the Backstage component, as defined in the Backstage catalog$${componentName}the name of the Backstage component, as defined in the Backstage catalog$${environmentName}the name of the environment (e.g.production), as defined in the Dynatrace environment configuration$${environmentUrl}the URL of the environment (e.g.https://my-environment.dynatrace.com), as defined in the Dynatrace environment configuration
For example, to filter for the events of the component, you could use the following in your query:
filter backstageComponent == "$${componentNamespace}.$${componentName}"
To be able to render correctly, the DQL must return an array. Links are also supported and can be added to the table if transformed like this:
Logs: { type: 'link', title: 'Link to Logs', url: 'https...' }
An example of a valid DQL result would be:
[
{
"Name": "backstage",
"Namespace": "hardening",
"LogCount": 0,
"latestLog": null,
"hasLogs": false,
"Link": {
"type": "link",
"text": "Click me",
"url": "https://backstage.io"
},
"metadata": {
"time": {
"timestamp": "xyz"
}
}
}
]To include the result table for your custom query, you would reference the query
by its ID with the custom. prefix:
<EntityDqlQueryCard
title="My Custom Query Results"
queryId="custom.my-custom-query"
/>You can register custom queries in the metadata.dynatrace.queries property of
a component within the catalog-info.yaml file
apiVersion: backstage.io/v1alpha1
kind: Component
metadata:
name: demo-backstage
description: Backstage Demo instance.
annotations:
backstage.io/kubernetes-id: kubernetescustom
dynatrace.com/guardian-tags: 'service=my-service,stage=development,novalue'
dynatrace:
queries:
- id: Error Logs
description: Fetch Error Logs
query: >
fetch logs, from: -2d
| filter status == "ERROR"
| sort timestamp desc
| fieldsAdd content=if(isNull(content), "N/A", else: content)
| fieldsAdd source=if(isNull(log.source), "N/A", else:
log.source)
| fieldsAdd host=if(isNull(host.name), "N/A", else: host.name)
| fieldsAdd environment = "${environmentName}"
| fieldsKeep timestamp, source, content, host, environment
spec:
type: website
owner: user:default/mjakl
lifecycle: experimental
system: integrationsAs mentioned before, queries can contain placeholders. In the catalog-info.yaml
file, the placeholders are prefixed with a single $. Please find the supported
placeholders listed
here.
To include the result tables for the custom queries of the entity, you would use:
<EntityCatalogInfoQueryCard />This component displays a result table for each query. The order in which the tables are displayed depends on the order of the entity's queries defined in the catalog-info.yaml file.
It is possible to specify the environments in which each query defined in the
app-config.yaml and catalog-info.yaml is executed.
By default, queries are executed against all defined environments.
Example query defined in catalog-info.yaml with environment limitation:
apiVersion: backstage.io/v1alpha1
kind: Component
metadata:
name: demo-backstage
description: Backstage Demo instance.
annotations:
backstage.io/kubernetes-id: kubernetescustom
dynatrace.com/guardian-tags: 'service=my-service,stage=development,novalue'
dynatrace:
queries:
- id: Error Logs
description: Fetch Error Logs
environments:
- tenant1
- tenant2
query: >
fetch logs, from: -2d
| filter status == "ERROR"
| sort timestamp desc
| fieldsAdd content=if(isNull(content), "N/A", else: content)
| fieldsAdd source=if(isNull(log.source), "N/A", else: log.source)
| fieldsAdd host=if(isNull(host.name), "N/A", else: host.name)
| fieldsAdd environment = "${environmentName}"
| fieldsKeep timestamp, source, content, host, environmentThe environments defined for a query must align with the environments names
configured in the app-config.local.yaml file.
To use comments within your custom DQL queries, use literal block style YAML
style, indicated by a pipe (|). See
YAML Spec | Literal Style.
query: |
fetch logs
// Filter for errors
| filter loglevel == "ERROR"
/*
* Count them
*/
| summarize sum=count() // Assign column labelThese are sample custom queries for the app-config.yaml. To use these queries
in the catalog-info.yaml, you need to remove one $ from the placeholders.
Query Error Logs:
fetch logs, from: -2d
| filter status == "ERROR"
| sort timestamp desc
| fieldsAdd content=if(isNull(content), "N/A", else: content)
| fieldsAdd source=if(isNull(log.source), "N/A", else: log.source)
| fieldsAdd host=if(isNull(host.name), "N/A", else: host.name)
| fieldsAdd environment = "$${environmentName}"
| fieldsKeep timestamp, source, content, host, environment
Query Problem Events:
fetch events, from: -2d
| filter event.kind=="DAVIS_PROBLEM"
| fieldsAdd category=if(isNull(event.category), "N/A", else: event.category)
| fieldsAdd id=if(isNull(event.id), "N/A", else: event.id)
| fieldsAdd status=if(isNull(event.status), "N/A", else: event.status)
| fieldsAdd name=if(isNull(event.name), "N/A", else: event.name)
| fieldsAdd environment = "$${environmentName}"
| fieldsKeep timestamp, category, id, name, status, environment
Query Security Vulnerabilities:
fetch security.events, from: -2d
| filter event.provider=="Dynatrace"
| filter event.kind=="SECURITY_EVENT"
| filter event.type=="VULNERABILITY_STATUS_CHANGE_EVENT"
| filter event.level=="VULNERABILITY"
| fieldsAdd environment = "$${environmentName}"
| fieldsKeep timestamp, event.status, vulnerability.display_id, event.id, vulnerability.title, vulnerability.risk.level, vulnerability.display_id, environment
To link from a table cell to a Dynatrace app, the DQL query must contain a
record with the proper type, text, and url. This is an example to link to
the Dynatrace Kubernetes app filtered by Kubernetes pods:
fetch dt.entity.cloud_application, from: -10m
| fieldsAdd Workload = record({type="link", text=entity.name, url=concat("\${environmentUrl}/ui/apps/dynatrace.kubernetes/resources/pod?entityId=", id)})
The query returns a result of:
[
{
"Workload": {
"type": "link",
"text": "<ENTITY_NAME>",
"url": "https://<ENVIRONMENT_URL>/ui/apps/dynatrace.kubernetes/resources/pod?entityId=<ENTITY_ID>"
}
}
]This repository includes end-to-end tests using Playwright to verify the plugin functionality.
To run e2e tests locally, you need:
-
Dynatrace Environment with the following:
- A Dynatrace tenant with API and App URLs
- One authentication method for query execution:
- A
Platform Token
with scopes:
storage:buckets:readstorage:events:read
- Or an OAuth client with equivalent read scopes for DQL and storage access
- A
Platform Token
with scopes:
-
Environment Variables set in your terminal (only the variables for your chosen auth method are required):
Platform Token auth:
export TENANT_APP_URL="https://{your-environment-id}.apps.dynatrace.com" export PLATFORM_TOKEN="<your-platform-token>"
OAuth auth:
export TENANT_APP_URL="https://{your-environment-id}.apps.dynatrace.com" export OAUTH_CLIENT_ID="<your-oauth-client-id>" export OAUTH_CLIENT_SECRET="<your-oauth-client-secret>" export OAUTH_ACCOUNT_URN="<your-oauth-account-urn>" export OAUTH_TOKEN_URL="https://sso.dynatrace.com/sso/oauth2/token"
- Install Playwright browsers (first time only):
yarn e2e:install-browsers- Run with Platform Token auth:
yarn e2e:platform-token- Run with OAuth auth:
yarn e2e:oauth- Optional manual breakdown (both auth modes use the same flow):
# Generate config
yarn e2e:config:platform-token
# or
yarn e2e:config:oauth
# Type-check and execute tests
yarn tsc:e2e
yarn e2eThe templates use a deterministic data record(...) query with a fixed testId
so no extra data ingestion is required.
Or run in UI mode for debugging:
yarn e2e:uiThe e2e tests validate:
- ✅ Configuration is read correctly from
app-config.local.yaml - ✅ Backstage development server starts successfully
- ✅ Authentication via Dynatrace Platform Token works
- ✅ Authentication via Dynatrace OAuth client credentials works
- ✅ Custom DQL queries execute and populate data tables with expected results
- ✅ Fixed test data from the query template is rendered in the UI
E2E tests run automatically in GitHub Actions on every push and pull request to
the main branch. The CI workflow:
- Runs e2e as a dedicated reusable workflow that can also be started directly via workflow dispatch
- Executes two sequential passes:
platform-tokenfirst, thenoauth - Generates temporary
app-config.local.yamlfrom auth-mode-specific templates for each pass - Runs Playwright tests (Chromium)
- Uploads auth-mode-specific Playwright reports as artifacts
Note: E2E tests are skipped on forked pull requests to protect repository secrets.
This Backstage integration is an open source project. The features are fully supported by Dynatrace.
Get help:
- Ask a question in the product forums
Open a GitHub issue to:
- Report minor defects, minor items, or typos
- Ask for improvements or changes
- Ask any questions related to the community effort
- Contribute as per our Contribution guidelines
SLAs don't apply for GitHub tickets.
Customers can open a ticket on the Dynatrace support portal to:
- Get support from the Dynatrace technical support engineering team
- Manage and resolve product related technical issues
SLAs apply according to the customer's support level.
Everyone is welcome to contribute to this repository. See our contributing guidelines, raise issues or submit pull requests.
