dt-obs-analytics

Analyze dashboards and notebooks using Davis analyzers — anomaly detection, novelty scoring, and correlation. Use when the user references a specific Dynatrace dashboard or notebook (by URL, UUID, or name) and asks what it shows, which DQL queries it runs, whether a tile looks off, or wants to find

By dynatrace · 541 installs

npx skills add dynatrace/dynatrace-for-ai --skill dt-obs-analytics

Source repository · Upstream listing

Analytics — Dashboard & Notebook Query Extraction A pipeline of three platform JavaScript scripts under scripts/ — two extractors feeding a shared analyzer runner: Each script is invoked via: o json wraps the function return value under .result . When you need to inspect the result, read it directly from the output — no jq required. Pass the full raw output as queries to run analyzer.js and it unwraps automatically. Parsing a dashboard URL When the entry point is a Dynatrace dashboard URL, extract the three components the scripts need: URL part Script destination Path segment after /dashboard/ (before ) id in extract timeseries dashboard.js payload from= value (URL decode %3A → : ) timeframe.startTime in run analyzer.js (only needed if running analysis) to= value (URL decode) timeframe.endTime in run analyzer.js (only needed if running analysis) vfilter <name =<value params variables map in run analyzer.js (strip vfilter prefix) The timeframe in the URL fragment is the dashboard's display window. It is not injected into the extracted DQL — the extractor returns the DQL verbatim with its original $variable tokens and any embedded timeframe clauses intact. Use the parsed from / to values only when calling run analyzer.js to set the analysis window. If the user just wants to list the queries, the timeframe is informational only. Note: when you pass run analyzer.js an absolute timeframe.startTime (not a now... expression), it strips any embedded timeframe ... stage from the DQL before analysis, so the analyzer honors your requested window rather than the query's baked in one. For relative ( now... ) windows the embedded timeframe is left intact. This means the query actually analyzed can differ from the extracted text — expected behavior, noted here so results line up with the window you asked for. Quick bash parse (pure bash + sed/awk — no python needed): For notebooks: path segment after /notebook/ , or share= value for /document/v0/ share=<ID links. Step 1 — Extract queries From a dashboard When the user names a specific tile, chart, or section , pass its name as titleFilter rather than extracting the full dashboard. titleFilter is a case insensitive substring or /regex/flags pattern. This keeps the queryset small and focused. When it is not clear which tile(s) the user wants , do NOT extract all DQL — dashboards can have 20–50 tiles and returning all queries causes significant context bloat. Instead use a two step flow: 1. List tile names with listOnly: true (no DQL, just titles): 2. Show the tile names to the user and ask which tile(s) they mean. 3. Re run with titleFilter for only the tile(s) of interest. This avoids pulling 20–50 DQL queries into context when only 1–2 are relevant. Payload knobs: id (required) — dashboard ID (UUID) or exact name. Preset IDs like my.dynatrace.infraops.preview. work. titleFilter — case insensitive substring ( "CPU usage" ) or /regex/flags ( "/^kafka/i" ). listOnly — when true , returns tiles: [{id, title, visualization}] without DQL. Use for disambiguation. compact — when true , returns only {id, title, dqlQuery} per tile (drops description, visualization, isTimeseries). Saves ~40% per tile tokens. In listOnly mode, drops visualization too. includeSkipped — when true , returns the full skipped[] array. Default: only skippedCount is returned. Response envelope: On failure: { "ok": false, "error": { "code": "...", "message": "..." } } . From a notebook Same envelope, different schema walk: Notebook cells often have empty titles — prefer addressing cells by id from the envelope if targeting a specific one. Step 2 — Run an analyzer Save the extractor output to a file, then pass it via shell substitution — the shell reads the file, so the JSON never enters the model's context: run analyzer.js unwraps the {"result":{...}} dtctl envelope automatically — pass the raw saved output as is. No jq or parsing step is needed: normalization of the array / envelope / dtctl output shapes happens inside the script. For large querysets (many tiles), the inline $(cat ...) form can hit shell argument length limits. Build the payload file and use dtctl's data flag instead — still no jq and still out of model context: Key payload knobs for run analyzer.js : minScore — drop results below this threshold (e.g. 0.5 ). Auto detects score field from noveltyScore , anomalyScore , correlationCoefficient , correlation , coefficient . Pass scoreField to override. scoreField — explicit field name to read score from (e.g. "noveltyScore" ). The queries field accepts any of: a raw array, the extractor envelope ( {queries:[...]} ), or the full dtctl output ( {"result":{"queries":[...]}} ). All three are normalized automatically. Common analyzers Goal analyzerName analyzerParams Find anomalous metrics dt.statistics.anomaly detection.SeasonalBaselineAnomalyDetectionAnalyzer { "trainingTimeframe": { "startTime": "now 8d", "endTime": "now 1d" } } (optional) Score how novel each metric is dt.statistics.NoveltyScoreAnalyzer { "detectionMode": "ALL", "minNoveltyScore": 0 } (optional) Correlate against a primary metric dt.statistics.SimplePearsonCorrelationAnalyzer set metricQuery instead (changes call shape) Correlation mode Pass metricQuery to correlate every query in the set against a single primary DQL string: When chaining from a previous analyzer run (e.g. anomaly detection → correlation), use metricQueryFrom instead. The script picks the highest scored result's dqlQuery automatically: metricQuery takes precedence if both are set. Variable substitution Dashboard queries often contain $variable tokens (from URL vfilter params). Pass them via variables to substitute before execution: Build variables from vfilter URL params by stripping the vfilter prefix. Trailing wildcards are stripped automatically. Unresolved tokens are cleaned up from DQL filter clauses rather than left to error. Response shape The output field is the raw analyzer result. Interpret it based on the analyzer: Anomaly detection : look for anomalyScore , anomalies[] , or raisedAlerts[] in each output entry. Score ≥ 0.7 → abnormal, ≥ 0.4 → borderline. Novelty : look for noveltyScore (or the closest score like numeric field). Score ≥ 0.7 → novel. Correlation : look for correlationCoefficient . Sort by correlationCoefficient descending; drop entries where correlationCoefficient < 0.5 . End to end: "what's abnormal on this dashboard?" Verifying a single extracted query Read the dqlQuery field from the extractor output and pass it directly: Gotchas Never read raw dashboard JSON yourself. dtctl get dashboard <id o json is typically 50–200 KB. The extractor reads it on the platform and returns a compact envelope (~5–15 KB). Never extract all tiles when only one is needed. A 50 tile dashboard returns 50 DQL queries into context. If the user names a tile, use titleFilter . If it's ambiguous, use listOnly: true first to ask which tile — then extract only that one. Variables are required for filtered dashboards. Queries with unsubstituted $variable tokens silently drop entity filters (e.g. in(field, $undefined) evaluates to true ). Always pass variables when the URL has vfilter params. Schema drift. If a tile lands in skipped with reason no DQL query found , the dashboard schema has a query location the extractor doesn't know about — add it to the pickQuery candidate list in the script. Analyzer availability. Not all Davis analyzers are available on every tenant. If a call comes back with Could not find an analyzer with name '...' or is not a function , list what's actually registered: dtctl get analyzers o json . Statistical fallback removed. run analyzer.js only calls Davis analyzers. For historical anomaly detection, pass a long trainingTimeframe via analyzerParams (e.g. { "trainingTimeframe": { "startTime": "now 30d", "endTime": "now 1d" } } ), or query the DQL directly. Comments in queries. Queries starting with // comment lines are classified correctly by the extractor (leading line/block comments are stripped before the timeseries check). Scripts reference [scripts/extract timeseries dashboard.js](scripts/extract timeseries dashboard.js) — extracts timeseries DQL from a dashboard [scripts/extract timeseries notebook.js](scripts/extract timeseries notebook.js) — same for notebooks [scripts/run analyzer.js](scripts/run analyzer.js) — generic Davis analyzer runner