Dynatrace is great for creating simple and not so simple dashboards and notebooks. The use-case we describe can be solved by a standard dashboard but let’s use this use-case anyway to explore custom apps in Dynatrace.
Our Use-case
Our imaginary setup consists of two systems – a booking system and a processing layer. The booking system takes orders saves them to a DB (which we don’t have access to) and logs them into a log with a transaction ID. The processing layer picks up the order and processes it via various transformations. It also logs the intermediate states but only the first log entry has the transaction ID and session ID, the subsequent log entries only have a newly created session ID. So the first log looks like this:

From the above we want to filter on the highlighted trd_id and use to it to extract the session_id which we’ll use to extract the logs produced by the processing layer.
Next, we’ll use a DQL to match on the session_id…
fetch logs
| parse content, """DATA 'trx_id:'LD:trx_id', session_id:'LD:session_id"""
| parse content, """DATA 'session_id:'LD:session_idx','"""
| filter isNotNull(trx_id) or isNotNull(session_idx)
| filter matchesValue(session_id,"465654-65454-5458") or matchesValue(session_idx,"465654-65454-5458")
| sort timestamp
| fieldsAdd session_id = if(isNull(session_id),session_idx, else: session_id)
| fields trx_id, session_id, content
… to produce the final output:

As final result, we want something like the below – a form with the ability to enter a transaction ID and find all log entries associated with it from both the booking system and the processing layer:

I’ve had a lot of experience with Dynatrace over the last few years, creating dashboards, notebooks, custom extension (SQL, snmp and Python) so it wasn’t all that difficult to get started on the Dynatrace side of things with the DQLs etc. However, for custom apps you need a few molre resources:
to start with this guide - https://docs.dynatrace.com/docs/platform/appengine
and here: https://developer.dynatrace.com.
You also need to get some basic understanding of react - https://developer.dynatrace.com/develop/react/. There are also lots of thirds party guides you can have a look at.
The solution
Here is the code of the page with commentary:
import React from 'react';
import { useState } from 'react';
import { Flex } from '@dynatrace/strato-components/layouts';
import { Heading, Text } from '@dynatrace/strato-components/typography';
import { TitleBar } from '@dynatrace/strato-components-preview/layouts';
import { convertToColumns } from '@dynatrace/strato-components-preview/conversion-utilities';
import { DataTable, TableColumn, TableRow } from '@dynatrace/strato-components-preview/tables';
import { ProgressCircle } from '@dynatrace/strato-components/content';
import { useDqlQuery } from '@dynatrace-sdk/react-hooks';
import { Controller, useForm } from 'react-hook-form';
import { logTrxQuery, logSessionQuery } from '../queries';
// The queries mentioned above are captured in the queries.ts file and are parameterised to take trx_id and session_id as inputs
import { FormField, Label, TextInput } from '@dynatrace/strato-components-preview/forms';
import { Button } from '@dynatrace/strato-components/buttons';
function ResultsTable({ trxid }) {
// The below defines the output table and how it ties to the recordset
const columns: TableColumn[] = [
{
header: 'Transaction ID',
accessor: 'trx_id',
autoWidth: true,
},
{
header: 'Session ID',
accessor: 'session_id',
autoWidth: true,
},
{
header: 'Content',
accessor: 'content',
autoWidth: true,
},
];
// Below we define the first query to be executed
const result = useDqlQuery({
body: {
query: logTrxQuery(trxid),
},
},
);
// Extract session_id from the result
let session_id;
if (!result.isLoading) {
result?.data?.records.forEach((value, key) => {
session_id = value?.session_id?.toString() ?? ""
})
}
// Run the second query supplying the session ID
const resultSess = useDqlQuery({
body: {
query: logSessionQuery(session_id),
},
},
);
if (resultSess.isLoading) {return (<ProgressCircle />)}
else {
return (
<DataTable data={resultSess?.data?.records || []} columns={columns}>
<DataTable.Pagination />
</DataTable>
);
}
}
// This is where it all comes together
export const LogMesh = () => {
const [trxid, setTrxid] = useState("");
const [sent, setSent] = useState(false);
const handleChange = (event) => {
setTrxid(event)
setSent(false)
};
function handleClick(e) {
setSent(true);
}
return (
<Flex width="100%" flexDirection="column" justifyContent="center" gap={16}>
<TitleBar>
<TitleBar.Title>Log meshing</TitleBar.Title>
</TitleBar>
<FormField>
<Label>Enter Transaction ID</Label>
<TextInput type="text" id="trxid" name="trxid" value={trxid} onChange={handleChange}/>
<Button onClick={handleClick} variant="emphasized">Search</Button>
{sent && <ResultsTable trxid={trxid}/>}
</FormField>
</Flex>
);
};
If you'd like the full code or any help with your custom app, let us know here. Thanks for reading.
Comments