Visualize events
In this guide, you'll learn how to visualize multiple metrics and add annotations to the chart based on host events.
The Annotations component
In the Annotations
component, we'll visualize the CPU and memory used by a specific host. On top of that, we'll add chart annotations with Davis events from the same host. We'll use DQL to fetch the CPU, memory, and Davis events data via the useDqlQuery
hook. Finally, we'll use the TimeseriesChart
component for the visualization.
The DQL queries for CPU and memory usage use the DQL timeseries
command. After we have the results, we need to convert them to the appropriate type for the TimeseriesChart
using the convertQueryResultToTimeseries
function. Additionally, we'll merge both converted outputs to have one time series for each metric.
For the Davis events, the DQL query uses the record type events
and filters by event.kind
, so we only get what interests us. Similarly to the earlier queries, we need to convert the results to the appropriate type for the TimeseriesChart.Annotations
component.
import React from 'react';
import { Flex } from '@dynatrace/strato-components/layouts';
import { ProgressCircle } from '@dynatrace/strato-components/content';
import { convertQueryResultToTimeseries } from '@dynatrace/strato-components-preview/conversion-utilities';
import { Annotation, TimeseriesAnnotations, TimeseriesChart } from '@dynatrace/strato-components-preview/charts';
import { Surface } from '@dynatrace/strato-components-preview/layouts-core';
import { Colors } from '@dynatrace/strato-design-tokens';
import { QueryResult, ResultRecord } from '@dynatrace-sdk/client-query';
import { useDqlQuery } from '@dynatrace-sdk/react-hooks';
type EventAnnotation = Annotation & {
eventId: string;
color: string;
};
const hostId = 'HOST-3873B1D6B9280D7C';
const from = 'now() - 3d';
const EVENTS_QUERY = `fetch events, from: ${from}
| filter event.kind == "DAVIS_EVENT"
| filter dt.entity.host == "${hostId}"
| sort timestamp desc
| summarize {
event.name = takeFirst(event.name),
event.description = takeFirst(event.description),
event.start = takeFirst(event.start),
event.end = takeFirst(event.end),
event.status = takeFirst(event.status)
}, by:{event.id}`;
const HOST_CPU_QUERY = `timeseries from: ${from}, \`CPU Usage\` = avg(dt.host.cpu.usage),
filter:dt.entity.host == "${hostId}"`;
const HOST_RAM_QUERY = `timeseries from: ${from}, \`RAM Usage \` = avg(dt.host.memory.usage),
filter:dt.entity.host == "${hostId}"`;
const buildAnnotations = (data: QueryResult): EventAnnotation[] =>
data.records
.filter((record) => record !== null)
.map((event: ResultRecord) => {
const start = new Date(Number(event['event.start'] as string) / 1000 / 1000);
const end = event['event.end'] ? new Date(Number(event['event.end'] as string) / 1000 / 1000) : new Date();
const color =
event['event.status'] === 'ACTIVE'
? Colors.Charts.Loglevel.Warning.Default
: Colors.Charts.Loglevel.Debug.Default;
return {
start,
end,
color,
title: event['event.name'] as string,
description: event['event.description'] as string,
eventId: event['event.id'] as string,
};
});
const buildTimeseries = (...queryResults: (QueryResult | undefined)[]) =>
queryResults.flatMap((res) => (res ? convertQueryResultToTimeseries(res) : []));
export const Annotations = () => {
const hostCpuSeries = useDqlQuery({
body: { query: HOST_CPU_QUERY },
enrich: 'metric-metadata',
});
const hostRamSeries = useDqlQuery({
body: { query: HOST_RAM_QUERY },
enrich: 'metric-metadata',
});
const events = useDqlQuery({ body: { query: EVENTS_QUERY } });
const isLoading = hostCpuSeries.isLoading || hostRamSeries.isLoading || events.isLoading;
return (
<Flex flexDirection="column" padding={32}>
<Surface>
{isLoading && <ProgressCircle />}
{hostCpuSeries.data && hostRamSeries.data && (
<TimeseriesChart data={buildTimeseries(hostCpuSeries.data, hostRamSeries.data)}>
{events.data && events.data.records.length > 0 && (
<TimeseriesChart.Annotations>
<TimeseriesAnnotations.Track>
{buildAnnotations(events.data).map((event) => (
<TimeseriesAnnotations.Marker color={event.color} key={event.eventId} data={event} />
))}
</TimeseriesAnnotations.Track>
</TimeseriesChart.Annotations>
)}
</TimeseriesChart>
)}
</Surface>
</Flex>
);
};
This operation requires the following scopes:
storage:buckets:read
storage:events:read
storage:metrics:read
Summary
With these few lines of code, we've created a useful visualization in very little time. We hope you found this guide helpful and got the inspiration to build more Dynatrace Apps.