Skip to content

Commit 5d8b7d2

Browse files
authored
Use panel setting to determine API mode (#128)
* Add buildModeInfo to plugin description * Use a panel setting to determine childCount or not * Revert supportsChildCount from pluginJson * Try and restore e2e behaviour * Correct defaults * Correct env behaviour
1 parent 2a8b105 commit 5d8b7d2

12 files changed

Lines changed: 94 additions & 51 deletions

File tree

.config/webpack/webpack.config.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -193,9 +193,10 @@ const config = async (env: Env): Promise<Configuration> => {
193193
plugins: [
194194
new BuildModeWebpackPlugin(),
195195
virtualPublicPath,
196-
// Define environment variables for build-time configuration
196+
// Define environment variable for setting default panel option value
197+
// Default is true (child count enabled). Set SUPPORTS_CHILD_COUNT=0 to disable.
197198
new webpack.DefinePlugin({
198-
'process.env.SUPPORTS_CHILD_COUNT': JSON.stringify(process.env.SUPPORTS_CHILD_COUNT === '1'),
199+
'process.env.SUPPORTS_CHILD_COUNT': process.env.SUPPORTS_CHILD_COUNT !== '0',
199200
}),
200201
// Insert create plugin version information into the bundle
201202
new webpack.BannerPlugin({

.github/workflows/ci.yml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -134,8 +134,8 @@ jobs:
134134
- name: Install dependencies
135135
run: bun install --frozen-lockfile
136136

137-
- name: Build plugin
138-
run: bun run build
137+
- name: Build plugin (without child count for standard Tempo tests)
138+
run: bun run build:without-child-count
139139

140140
- name: Start Grafana
141141
run: |
@@ -171,8 +171,8 @@ jobs:
171171
- name: Stop grafana docker (after regular tests)
172172
run: docker compose down
173173

174-
- name: Build with child count support
175-
run: bun run build:with-child-count
174+
- name: Build plugin (with child count, default)
175+
run: bun run build
176176

177177
- name: Start Grafana (for child count tests)
178178
run: |

.github/workflows/release.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ jobs:
3333
run: bun install --frozen-lockfile
3434

3535
- name: Build plugin
36-
run: bun run build:with-child-count
36+
run: bun run build
3737

3838
- name: Package unsigned plugin
3939
run: bun ./scripts/package-plugin.js

CHANGELOG.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,18 @@
11
# Changelog
22

3+
## [0.2.0] - 2025-11-04
4+
5+
### Changed
6+
7+
- Converted `SUPPORTS_CHILD_COUNT` from build-time environment variable to runtime panel option. The plugin now uses a panel setting "Enable G-Research Tempo API support" that can be configured per panel when editing a dashboard. This allows a single build to work with both standard Grafana Tempo API and G-Research custom Tempo API.
8+
- Removed `build:with-child-count` script. Use `bun run build` for all builds.
9+
- Updated release workflow to use standard build process.
10+
11+
### Added
12+
13+
- Panel option "Enable G-Research Tempo API support" for runtime configuration of child count support.
14+
- Support for setting default panel option value via `SUPPORTS_CHILD_COUNT` environment variable for local development (e.g., `SUPPORTS_CHILD_COUNT=1 bun run dev`).
15+
316
## [0.1.5] - 2025-10-31
417

518
### Added

README.md

Lines changed: 32 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -19,18 +19,34 @@ For detailed usage instructions and troubleshooting, see [HELP.md](HELP.md).
1919

2020
App plugins can let you create a custom out-of-the-box monitoring experience by custom pages, nested data sources and panel plugins.
2121

22-
## Build-time Configuration
22+
## Panel Configuration
2323

24-
The plugin supports build-time configuration through environment variables. Create a `.env` file in the root directory and set the following variables:
24+
The plugin supports runtime configuration through panel options. To configure the setting:
2525

26-
- `SUPPORTS_CHILD_COUNT`: Set to `'1'` to enable child count support, `'0'` to disable it. This affects whether the plugin will use `childCount` attributes from the backend. Default is `'false'`.
26+
1. Create or edit a panel using this plugin on a dashboard
27+
2. In the panel edit mode, look for the "Enable G-Research Tempo API support" option in the panel options panel (right sidebar)
28+
3. Toggle the setting as needed
2729

28-
Example `.env` file:
30+
**Setting**: Enable G-Research Tempo API support
31+
32+
- A boolean setting that controls whether the plugin uses child count support for G-Research custom Tempo API. When enabled, the plugin will use `childCount` attributes from the backend. When disabled, it works with the standard Grafana Tempo API.
33+
34+
### Development Default
35+
36+
By default, child count support is **enabled** (for G-Research custom Tempo API). For local development with standard Grafana Tempo API, you can disable it:
2937

3038
```bash
31-
SUPPORTS_CHILD_COUNT=0
39+
# Default: child count enabled (for G-Research custom Tempo API)
40+
bun run dev
41+
42+
# Disable for standard Grafana Tempo API
43+
SUPPORTS_CHILD_COUNT=0 bun run dev
44+
# Or use the convenience script:
45+
bun run dev:without-child-count
3246
```
3347

48+
This sets the default value for new panels, but you can still override it per panel in the UI.
49+
3450
## Get started
3551

3652
### Frontend
@@ -53,12 +69,6 @@ SUPPORTS_CHILD_COUNT=0
5369
bun run build
5470
```
5571

56-
To build with child count support enabled:
57-
58-
```bash
59-
bun run build:with-child-count
60-
```
61-
6272
4. Run the tests (using Jest)
6373

6474
```bash
@@ -256,13 +266,12 @@ Minor differences:
256266

257267
## API discrepancies
258268

259-
To differentiate between the Grafana Tempo API and the G-Research–flavoured Tempo API, the plugin checks the `SUPPORTS_CHILD_COUNT` environment variable.
260-
Building with `SUPPORTS_CHILD_COUNT=1` results in the runtime behavior described above.
269+
To differentiate between the Grafana Tempo API and the G-Research–flavoured Tempo API, the plugin uses a panel setting called "Enable G-Research Tempo API support". When enabled, the plugin will use the G-Research custom API features like child count support. When disabled, it works with the standard Grafana Tempo API.
261270

262271
## Testing
263272

264273
We run end-to-end using Playwright and `@grafana/plugin-e2e`.
265-
There are two ways to run the tests, there is setup for when `SUPPORTS_CHILD_COUNT=0` or `SUPPORTS_CHILD_COUNT=1`.
274+
There are two ways to run the tests, depending on which API you want to test against (standard Grafana Tempo API or G-Research custom API).
266275
In both cases, we rely on a provisioned Docker compose setup.
267276

268277
**Playwright requires Chromium as a dependency**
@@ -275,18 +284,18 @@ _Why is this not part of our package.json?_
275284

276285
Chromium cannot be installed in our production environment, so we do not include it as a required dependency in package.json.
277286

278-
### SUPPORTS_CHILD_COUNT = 0
287+
### Standard Grafana Tempo API
279288

280289
Run `bun run server` to start the regular developer setup.
281290
Here we shall target the Grafana Tempo API as mentioned in [./docker-compose.yaml].
282291

283292
Run
284293

285294
```shell
286-
bun run build
295+
bun run build:without-child-count
287296
```
288297

289-
to build a bundle without `SUPPORTS_CHILD_COUNT`.
298+
to build the plugin without child count support (for standard Grafana Tempo API). When creating or editing a panel on a dashboard, make sure "Enable G-Research Tempo API support" is disabled in the panel options.
290299

291300
Next, we need to provision sample data to our Tempo store.
292301
Run
@@ -303,7 +312,7 @@ Afterwards all pieces are in place to run the e2e tests:
303312
bun run e2e
304313
```
305314

306-
### SUPPORTS_CHILD_COUNT = 1
315+
### G-Research Custom Tempo API
307316

308317
To simulate the production API, we have constructed a different Docker setup in [local-tempo-docker-compose.yml](./local-tempo-docker-compose.yml).
309318
There we also have a `Tempo` service, so from Grafana's point of view nothing will have changed.
@@ -323,12 +332,14 @@ In production, this would be the .NET side of things, for our local setup, we ca
323332
bun run tests/test-api.ts
324333
```
325334

326-
Next, build our plugin using `SUPPORTS_CHILD_COUNT=1` via
335+
Next, build our plugin (default has child count enabled):
327336

328337
```shell
329-
bun run build:with-child-count
338+
bun run build
330339
```
331340

341+
The default panel option will have "Enable G-Research Tempo API support" enabled, which is correct for this setup.
342+
332343
Afterwards, you should be able to run the tests using:
333344

334345
```shell
@@ -338,7 +349,7 @@ bun run e2e
338349
### Updating the test Trace
339350

340351
In `scripts/e2e-tempo-trace.js`, we have a test scenario.
341-
To ensure we use the same data during `SUPPORTS_CHILD_COUNT=1`.
352+
To ensure we use the same data when testing with G-Research custom Tempo API.
342353
You can extract the last trace to [tests/test-trace.json](./tests/test-trace.json) via
343354

344355
```shell

package.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
{
22
"name": "grafana-incremental-trace-viewer",
3-
"version": "0.1.5",
3+
"version": "0.2.0",
44
"scripts": {
55
"build": "webpack -c ./.config/webpack/webpack.config.ts --env production",
6-
"build:with-child-count": "SUPPORTS_CHILD_COUNT=1 webpack -c ./.config/webpack/webpack.config.ts --env production",
6+
"build:without-child-count": "SUPPORTS_CHILD_COUNT=0 webpack -c ./.config/webpack/webpack.config.ts --env production",
77
"dev": "webpack -w -c ./.config/webpack/webpack.config.ts --env development",
8+
"dev:without-child-count": "SUPPORTS_CHILD_COUNT=0 webpack -w -c ./.config/webpack/webpack.config.ts --env development",
89
"test": "jest --watch --onlyChanged",
910
"test:ci": "jest --passWithNoTests --maxWorkers 4",
1011
"typecheck": "tsc --noEmit",

src/components/Span/SpanDetailsPanel.tsx

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,7 @@ import { IconButton, Input } from '@grafana/ui';
55
import type { SpanInfo } from '../../types';
66
import { formatUnixNanoToDateTime, formatDuration } from 'utils/utils.timeline';
77
import { useQuery } from '@tanstack/react-query';
8-
import {
9-
KeyValue,
10-
AnyValue,
11-
FetchFunction,
12-
TagAttributes,
13-
getTagAttributesForSpan,
14-
supportsChildCount,
15-
} from 'utils/utils.api';
8+
import { KeyValue, AnyValue, FetchFunction, TagAttributes, getTagAttributesForSpan } from 'utils/utils.api';
169
import { Accordion } from './Accordion';
1710

1811
function collectTagAttributes(result: KeyValue[]): TagAttributes {
@@ -143,10 +136,12 @@ export function SpanDetailPanel({
143136
span,
144137
onClose,
145138
fetchFn,
139+
supportsChildCount,
146140
}: {
147141
span: SpanInfo;
148142
onClose: () => void;
149143
fetchFn: FetchFunction<any>;
144+
supportsChildCount: boolean;
150145
}) {
151146
const [expandedSections, setExpandedSections] = useState({
152147
additionalData: false,

src/components/TraceDetail.tsx

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { useVirtualizer } from '@tanstack/react-virtual';
44
import { testIds } from './testIds';
55
import { Span as SpanComponent, SpanDetailPanel } from './Span';
66
import { mkUnixEpochFromNanoSeconds, mkUnixEpochFromMiliseconds, formatDuration } from '../utils/utils.timeline';
7-
import { FetchFunction, search, SearchResponse, Span, supportsChildCount } from '../utils/utils.api';
7+
import { FetchFunction, search, SearchResponse, Span } from '../utils/utils.api';
88
import type { QueryInfo as TraceDetailProps } from './TraceViewerPanel';
99
import { SpanOverlayDrawer } from './Span/SpanOverlayDrawer';
1010
import { ChildStatus, SpanInfo } from 'types';
@@ -140,14 +140,18 @@ async function extractSpans(
140140
return spans;
141141
}
142142

143-
const pipeSelect = `| select (span:name, resource.service.name${supportsChildCount ? ', childCount' : ''})`;
143+
function getPipeSelect(supportsChildCount: boolean): string {
144+
return `| select (span:name, resource.service.name${supportsChildCount ? ', childCount' : ''})`;
145+
}
144146

145147
async function loadMoreSpans(
146148
fetchFn: FetchFunction<SearchResponse>,
147149
traceId: string,
148150
idToLevelMap: Map<string, number>,
149-
span: SpanInfo
151+
span: SpanInfo,
152+
supportsChildCount: boolean
150153
): Promise<SpanInfo[]> {
154+
const pipeSelect = getPipeSelect(supportsChildCount);
151155
const q = `{ trace:id = "${traceId}" && span:parentID = "${span.spanId}" } ${pipeSelect}`;
152156
const start = mkUnixEpochFromNanoSeconds(span.startTimeUnixNano);
153157
// As a precaution, we add 1 second to the end time.
@@ -166,7 +170,8 @@ function TraceDetail({
166170
panelWidth,
167171
panelHeight,
168172
timeRange,
169-
}: TraceDetailProps & { timeRange: TimeRange }): React.JSX.Element {
173+
supportsChildCount,
174+
}: TraceDetailProps & { timeRange: TimeRange; supportsChildCount: boolean }): React.JSX.Element {
170175
// Should we assert for traceId and datasourceId?
171176
if (!traceId || !datasourceUid) {
172177
throw new Error('traceId and datasourceId are required');
@@ -212,6 +217,7 @@ function TraceDetail({
212217
setLoadingMessage('Loading root nodes of trace');
213218
const start = mkUnixEpochFromMiliseconds(startTimeInMs);
214219
const end = mkUnixEpochFromMiliseconds(startTimeInMs + durationInMs);
220+
const pipeSelect = getPipeSelect(supportsChildCount);
215221
const q = `{ trace:id = "${traceId}" && nestedSetParent = -1 } ${pipeSelect}`;
216222
const data = await search(fetchFn, q, start, end);
217223
// We pass in hasMore: false because we are fetching the first round of children later.
@@ -233,7 +239,7 @@ function TraceDetail({
233239
}
234240
allSpans.push(span);
235241
if (!hasNoChildren) {
236-
const moreSpans = await loadMoreSpans(fetchFn, traceId, idToLevelMap.current, span);
242+
const moreSpans = await loadMoreSpans(fetchFn, traceId, idToLevelMap.current, span, supportsChildCount);
237243
allSpans.push(...moreSpans);
238244
}
239245
}
@@ -328,7 +334,7 @@ function TraceDetail({
328334
});
329335

330336
// Load the children
331-
const spans = await loadMoreSpans(fetchFn, traceId, idToLevelMap.current, span);
337+
const spans = await loadMoreSpans(fetchFn, traceId, idToLevelMap.current, span, supportsChildCount);
332338

333339
// Update the parent span to show children
334340
queryClient.setQueryData<SpanInfo[]>(queryKey, (oldData) => {
@@ -524,6 +530,7 @@ function TraceDetail({
524530
setSelectedSpanElementYOffset(null);
525531
}}
526532
fetchFn={fetchFn}
533+
supportsChildCount={supportsChildCount}
527534
/>
528535
)}
529536
</SpanOverlayDrawer>

src/components/TraceViewerPanel.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,11 @@ import { Icon, TextLink } from '@grafana/ui';
44
import TraceDetail from './TraceDetail';
55
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
66
import { HelpModal } from './HelpModal';
7+
import { PanelOptions } from '../types';
78

89
const queryClient = new QueryClient();
910

10-
interface Props extends PanelProps<{}> {}
11+
interface Props extends PanelProps<PanelOptions> {}
1112

1213
export type QueryInfo = {
1314
datasourceUid: string;
@@ -152,6 +153,7 @@ export const TraceViewerPanel: React.FC<Props> = ({ options, data, width, height
152153
// Grafana adds padding-block of 8px
153154
panelHeight={height + 16}
154155
timeRange={timeRange}
156+
supportsChildCount={options.supportsChildCount ?? false}
155157
/>
156158
</QueryClientProvider>
157159
);

src/module.ts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,19 @@
11
import { PanelPlugin } from '@grafana/data';
22
import { TraceViewerPanel } from './components/TraceViewerPanel';
33
import './styles/tailwind.css';
4+
import { PanelOptions } from './types';
45

5-
export const plugin = new PanelPlugin<{}>(TraceViewerPanel);
6+
// Allow setting default value via environment variable for local development
7+
// Default is true (child count enabled). Set SUPPORTS_CHILD_COUNT=0 to disable.
8+
// Usage: SUPPORTS_CHILD_COUNT=0 bun run dev
9+
// Note: webpack DefinePlugin replaces process.env.SUPPORTS_CHILD_COUNT with a boolean literal (true or false)
10+
const defaultSupportsChildCount = process.env.SUPPORTS_CHILD_COUNT as unknown as boolean;
11+
12+
export const plugin = new PanelPlugin<PanelOptions>(TraceViewerPanel).setPanelOptions((builder) => {
13+
return builder.addBooleanSwitch({
14+
path: 'supportsChildCount',
15+
name: 'Enable G-Research Tempo API support',
16+
description: 'Enable child count support for G-Research custom Tempo API. Disable for standard Grafana Tempo API.',
17+
defaultValue: defaultSupportsChildCount, // Default is true (child count enabled)
18+
});
19+
});

0 commit comments

Comments
 (0)