Querying the Grail
Hands-onQuerying the Grail
Grail is Dynatrace's unified data lake — metrics, logs, traces, entities, events, all in one place. DQL (Dynatrace Query Language) is how you talk to it. In this module, you'll query real data and display it in your app.
The useDql Hook
One hook, one query, live data:
import { useDql } from "@dynatrace-sdk/react-hooks";
export const HostList = () => {
const { data, error, isLoading } = useDql({
query: `fetch dt.entity.host
| fields entity.name, cpuCores, osType
| sort entity.name asc
| limit 20`
});
if (isLoading) return <p>Loading...</p>;
if (error) return <p>Error: {error.message}</p>;
return <pre>{JSON.stringify(data, null, 2)}</pre>;
};
The hook handles authentication, polling, and caching automatically. Your app.config.json must include the right scopes — storage:entities:read for entity queries, storage:metrics:read for timeseries.
DQL Essentials
Entity Queries (fetch)
// All hosts
fetch dt.entity.host
| fields entity.name, cpuCores, osType, state
// Filter by name
fetch dt.entity.host
| filter contains(entity.name, "prod")
| fields entity.name, cpuCores
// With relationships
fetch dt.entity.host
| expand runs_on = runs[dt.entity.process_group]
| fields entity.name, runs_on
Metric Queries (timeseries)
// CPU usage per host, last 2 hours
timeseries avg(dt.host.cpu.usage), by:{dt.entity.host}, from:-2h
// Memory usage with 5-minute buckets
timeseries avg(dt.host.memory.usage), by:{dt.entity.host}, from:-1h, interval:5m
// Multiple metrics
timeseries cpu=avg(dt.host.cpu.usage), mem=avg(dt.host.memory.usage),
by:{dt.entity.host}, from:-2h
Key rules:
fetchis for entities and logs — returns rows of datatimeseriesis for metrics — returns time-bucketed aggregationstimeseriesrequires an aggregation:avg(),sum(),min(),max(),count()by:{}groups results by dimensions (replaces classic:splitBy())
Displaying Data in a Table
Strato's DataTable is the standard way to show tabular data:
import { useDql } from "@dynatrace-sdk/react-hooks";
import { DataTable, convertToColumns } from "@dynatrace/strato-components/tables";
import { Flex, TitleBar } from "@dynatrace/strato-components/layouts";
export const HostList = () => {
const { data, isLoading } = useDql({
query: `fetch dt.entity.host
| fields entity.name, cpuCores, osType, state
| sort entity.name asc
| limit 50`
});
const columns = data ? convertToColumns(data) : [];
return (
<Flex flexDirection="column" gap={16} padding={32}>
<TitleBar>
<TitleBar.Title>Hosts</TitleBar.Title>
</TitleBar>
<DataTable
data={data?.records ?? []}
columns={columns}
loading={isLoading}
/>
</Flex>
);
};
convertToColumns auto-generates column definitions from the DQL result schema. For custom columns, define them manually:
const columns = [
{ accessor: "entity.name", header: "Host Name" },
{ accessor: "cpuCores", header: "CPU Cores" },
{ accessor: "osType", header: "OS" },
];
Required Scopes
Add these to your app.config.json scopes array:
[
{ "name": "storage:entities:read", "comment": "Fetch entities via DQL" },
{ "name": "storage:metrics:read", "comment": "Query metrics via DQL timeseries" },
{ "name": "storage:buckets:read", "comment": "Access Grail data buckets" }
]
Missing scopes = empty results or permission errors. Always check scopes first when debugging.
What's Next
In Module 5, we explore the Strato component arsenal — buttons, chips, progress circles, and more. Your data deserves better than a raw table.