Analytics — Dashboard & Notebook Query Extraction A pipeline of three platform JavaScript scripts under — two extractors feeding a shared analyzer runner: Each script is invoked via: wraps the function return value under . When you need to inspect the result, read it directly from the output — no required. Pass the full raw output as to 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 (before ) | in extract-timeseries-dashboard…

+ name.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\{head-tags}') + '(?=[^a-zA-Z0-9_]|$)',\n 'g',\n );\n result = result.replace(re, quoted);\n }\n // Drop filter clauses referencing still-unresolved $variables.\n const EXPR = '[^$]*?';\n const VARNAME = '\\\\$[a-zA-Z_][a-zA-Z0-9_]*';\n result = result.replace(new RegExp(`\\\\s*\\\\band\\\\b\\\\s+(?:not\\\\s+)?in\\\\s*\\\\(${EXPR},\\\\s*${VARNAME}\\\\s*\\\\)`, 'gi'), '');\n result = result.replace(new RegExp(`(?:not\\\\s+)?in\\\\s*\\\\(${EXPR},\\\\s*${VARNAME}\\\\s*\\\\)\\\\s*\\\\band\\\\b\\\\s*`, 'gi'), '');\n result = result.replace(new RegExp(`\\\\bnot\\\\s+in\\\\s*\\\\(${EXPR},\\\\s*${VARNAME}\\\\s*\\\\)`, 'gi'), 'false');\n result = result.replace(new RegExp(`\\\\bin\\\\s*\\\\(${EXPR},\\\\s*${VARNAME}\\\\s*\\\\)`, 'gi'), 'true');\n result = result.replace(/\\$[a-zA-Z_][a-zA-Z0-9_]*/g, 'null');\n return result;\n}\n\nconst SCORE_FIELDS = ['noveltyScore', 'anomalyScore', 'correlationCoefficient', 'correlation', 'coefficient'];\n\n// Analyzer outputs are nested (arrays of per-dimension objects, and scores can sit\n// one or two levels in — e.g. correlationCoefficient in each array element, or\n// novelties[].noveltyScore). Walk the structure looking for an explicit scoreField\n// or one of the known score fields, then fall back to any top-level \"*score*\" number.\nfunction findKnownScore(node, scoreField) {\n if (node == null || typeof node !== 'object') return null;\n if (Array.isArray(node)) {\n for (const el of node) {\n const s = findKnownScore(el, scoreField);\n if (s != null) return s;\n }\n return null;\n }\n if (scoreField && typeof node[scoreField] === 'number') return node[scoreField];\n for (const field of SCORE_FIELDS) {\n if (typeof node[field] === 'number') return node[field];\n }\n for (const v of Object.values(node)) {\n if (v && typeof v === 'object') {\n const s = findKnownScore(v, scoreField);\n if (s != null) return s;\n }\n }\n return null;\n}\n\nfunction extractScore(output, scoreField) {\n const known = findKnownScore(output, scoreField);\n if (known != null) return known;\n // Best-effort fallback for unknown analyzers: any top-level \"*score*\" number.\n if (output && typeof output === 'object' && !Array.isArray(output)) {\n for (const [k, v] of Object.entries(output)) {\n if (k.toLowerCase().includes('score') && typeof v === 'number') return v;\n }\n }\n return null;\n}\n\nasync function mapConcurrent(items, n, fn) {\n const out = new Array(items.length);\n let cursor = 0;\n const workers = Array.from({ length: Math.min(n, items.length) }, async () => {\n while (true) {\n const i = cursor++;\n if (i >= items.length) return;\n out[i] = await fn(items[i], i);\n }\n });\n await Promise.all(workers);\n return out;\n}\n\n// ─── Main export ──────────────────────────────────────────────────────────────\n\nexport default async function ({\n analyzerName,\n queries: queriesInput,\n timeframe,\n analyzerParams = {},\n metricQuery,\n metricQueryFrom,\n variables,\n maxConcurrent = 4,\n minScore,\n scoreField,\n} = {}) {\n try {\n if (typeof analyzerName !== 'string' || analyzerName.trim() === '') {\n return {\n ok: false,\n error: { code: 'invalid_input', message: 'payload.analyzerName must be a non-empty string' },\n };\n }\n\n // Resolve metricQuery from a previous run's output when not given directly.\n if (metricQuery == null && metricQueryFrom != null) {\n metricQuery = topDqlFromResults(metricQueryFrom, scoreField);\n if (metricQuery == null) {\n return {\n ok: false,\n error: { code: 'invalid_input', message: 'metricQueryFrom contained no results with a dqlQuery' },\n };\n }\n }\n\n const queries = normalizeQueries(queriesInput);\n if (!Array.isArray(queries) || queries.length === 0) {\n return {\n ok: false,\n error: {\n code: 'invalid_input',\n message: 'payload.queries must be a non-empty TimeseriesDQLQuerySet (array or extractor envelope)',\n },\n };\n }\n\n const tf = normalizeTimeframe(timeframe) ?? DEFAULT_TIMEFRAME;\n const work = queries.filter((q) => q && typeof q.dqlQuery === 'string' && q.isTimeseries !== false);\n\n // The primary/correlation query gets the same treatment as the per-tile queries:\n // strip embedded `| timeframe` for absolute windows and substitute $variables.\n // Otherwise a primary sourced from an extracted tile (or metricQueryFrom) could\n // still carry $var tokens or a different embedded window than the correlated set.\n const resolvedMetricQuery = metricQuery != null\n ? applyVariables(stripEmbeddedTimeframe(metricQuery, tf.startTime), variables)\n : null;\n\n // Sanitize fan-out: coerce to a positive integer so a bad value (0, negative,\n // non-numeric) can't silently produce zero workers and an empty result set.\n const concurrency = Math.max(1, Math.floor(Number(maxConcurrent)) || 4);\n\n const results = await mapConcurrent(work, concurrency, async (q) => {\n try {\n const resolvedDQL = applyVariables(\n stripEmbeddedTimeframe(q.dqlQuery, tf.startTime),\n variables,\n );\n\n const pearsonMode = /pearson/i.test(analyzerName);\n const body = resolvedMetricQuery\n ? pearsonMode\n ? {\n referenceTimeseries: resolvedMetricQuery,\n toCorrelateTimeseries: resolvedDQL,\n generalParameters: { timeframe: tf, resolveDimensionalQueryData: true },\n ...analyzerParams,\n }\n : {\n timeSeriesData: resolvedMetricQuery,\n correlatedTimeSeriesData: resolvedDQL,\n generalParameters: { timeframe: tf, resolveDimensionalQueryData: true },\n ...analyzerParams,\n }\n : {\n timeSeriesData: resolvedDQL,\n generalParameters: { timeframe: tf, resolveDimensionalQueryData: true },\n ...analyzerParams,\n };\n\n const raw = await executeAnalyzer(analyzerName, body);\n return {\n ok: true,\n item: {\n id: q.id,\n title: q.title ?? '',\n dqlQuery: q.dqlQuery,\n output: raw?.result?.output ?? null,\n executionStatus: raw?.result?.executionStatus ?? 'COMPLETED',\n },\n };\n } catch (e) {\n return { ok: false, id: q.id, error: e.message ?? String(e) };\n }\n });\n\n const completed = [];\n const errors = [];\n for (const r of results) {\n if (r.ok) completed.push(r.item);\n else errors.push({ id: r.id, error: r.error });\n }\n\n const filtered = minScore != null\n ? completed.filter((item) => {\n const score = extractScore(item.output, scoreField);\n return score != null && score >= minScore;\n })\n : completed;\n\n return {\n ok: true,\n checkedAt: new Date().toISOString(),\n analyzerName,\n summary: { checked: work.length, completed: completed.length, errors: errors.length },\n results: filtered,\n errors,\n };\n } catch (e) {\n return {\n ok: false,\n error: { code: e.code ?? 'internal_error', message: e.message ?? String(e) },\n };\n }\n}\n","content_type":"text/javascript; charset=utf-8","language":"javascript","size":16262,"content_sha256":"437f640af14273a2c6b6d3a19c71944c1b3f65ed39cd7253a00f6bf01b463be2"}],"content_json":{"type":"doc","content":[{"type":"heading","attrs":{"level":1},"content":[{"text":"Analytics — Dashboard & Notebook Query Extraction","type":"text"}]},{"type":"paragraph","content":[{"text":"A pipeline of three platform JavaScript scripts under ","type":"text"},{"text":"scripts/","type":"text","marks":[{"type":"code_inline"}]},{"text":" — two extractors feeding a shared analyzer runner:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":""},"content":[{"text":"scripts/extract-timeseries-dashboard.js ──┐\nscripts/extract-timeseries-notebook.js ──┴──► queryset.json ──► scripts/run-analyzer.js (any Davis analyzer)","type":"text"}]},{"type":"paragraph","content":[{"text":"Each script is invoked via:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"dtctl exec function -f scripts/\u003cscript>.js --payload '\u003cjson>' -o json","type":"text"}]},{"type":"paragraph","content":[{"text":"-o json","type":"text","marks":[{"type":"code_inline"}]},{"text":" wraps the function return value under ","type":"text"},{"text":".result","type":"text","marks":[{"type":"code_inline"}]},{"text":". When you need to inspect the result, read it directly from the output — no ","type":"text"},{"text":"jq","type":"text","marks":[{"type":"code_inline"}]},{"text":" required. Pass the full raw output as ","type":"text"},{"text":"queries","type":"text","marks":[{"type":"code_inline"}]},{"text":" to ","type":"text"},{"text":"run-analyzer.js","type":"text","marks":[{"type":"code_inline"}]},{"text":" and it unwraps automatically.","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Parsing a dashboard URL","type":"text"}]},{"type":"paragraph","content":[{"text":"When the entry point is a Dynatrace dashboard URL, extract the three components the scripts need:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":""},"content":[{"text":"https://\u003ctenant>/ui/apps/dynatrace.dashboards/dashboard/\u003cID>#from=\u003cfrom>&to=\u003cto>&vfilter_\u003cname>=\u003cval>...","type":"text"}]},{"type":"table","attrs":{"layout":null},"content":[{"type":"tr","content":[{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"URL part","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Script destination","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Path segment after ","type":"text"},{"text":"/dashboard/","type":"text","marks":[{"type":"code_inline"}]},{"text":" (before ","type":"text"},{"text":"#","type":"text","marks":[{"type":"code_inline"}]},{"text":")","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"id","type":"text","marks":[{"type":"code_inline"}]},{"text":" in extract-timeseries-dashboard.js payload","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"#from=","type":"text","marks":[{"type":"code_inline"}]},{"text":" value (URL-decode ","type":"text"},{"text":"%3A","type":"text","marks":[{"type":"code_inline"}]},{"text":" → ","type":"text"},{"text":":","type":"text","marks":[{"type":"code_inline"}]},{"text":")","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"timeframe.startTime","type":"text","marks":[{"type":"code_inline"}]},{"text":" in run-analyzer.js (only needed if running analysis)","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"#to=","type":"text","marks":[{"type":"code_inline"}]},{"text":" value (URL-decode)","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"timeframe.endTime","type":"text","marks":[{"type":"code_inline"}]},{"text":" in run-analyzer.js (only needed if running analysis)","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"vfilter_\u003cname>=\u003cvalue>","type":"text","marks":[{"type":"code_inline"}]},{"text":" params","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"variables","type":"text","marks":[{"type":"code_inline"}]},{"text":" map in run-analyzer.js (strip ","type":"text"},{"text":"vfilter_","type":"text","marks":[{"type":"code_inline"}]},{"text":" prefix)","type":"text"}]}]}]}]},{"type":"paragraph","content":[{"text":"The timeframe in the URL fragment is the dashboard's ","type":"text"},{"text":"display","type":"text","marks":[{"type":"em"}]},{"text":" window. It is ","type":"text"},{"text":"not","type":"text","marks":[{"type":"strong"}]},{"text":" injected into the extracted DQL — the ","type":"text"},{"text":"extractor","type":"text","marks":[{"type":"strong"}]},{"text":" returns the DQL verbatim with its original ","type":"text"},{"text":"$variable","type":"text","marks":[{"type":"code_inline"}]},{"text":" tokens and any embedded ","type":"text"},{"text":"| timeframe","type":"text","marks":[{"type":"code_inline"}]},{"text":" clauses intact. Use the parsed ","type":"text"},{"text":"from","type":"text","marks":[{"type":"code_inline"}]},{"text":"/","type":"text"},{"text":"to","type":"text","marks":[{"type":"code_inline"}]},{"text":" values only when calling ","type":"text"},{"text":"run-analyzer.js","type":"text","marks":[{"type":"code_inline"}]},{"text":" to set the analysis window. If the user just wants to list the queries, the timeframe is informational only.","type":"text"}]},{"type":"paragraph","content":[{"text":"Note: when you pass ","type":"text"},{"text":"run-analyzer.js","type":"text","marks":[{"type":"code_inline"}]},{"text":" an ","type":"text"},{"text":"absolute","type":"text","marks":[{"type":"strong"}]},{"text":" ","type":"text"},{"text":"timeframe.startTime","type":"text","marks":[{"type":"code_inline"}]},{"text":" (not a ","type":"text"},{"text":"now...","type":"text","marks":[{"type":"code_inline"}]},{"text":" expression), it strips any embedded ","type":"text"},{"text":"| timeframe ...","type":"text","marks":[{"type":"code_inline"}]},{"text":" stage from the DQL before analysis, so the analyzer honors your requested window rather than the query's baked-in one. For relative (","type":"text"},{"text":"now...","type":"text","marks":[{"type":"code_inline"}]},{"text":") windows the embedded ","type":"text"},{"text":"| timeframe","type":"text","marks":[{"type":"code_inline"}]},{"text":" 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.","type":"text"}]},{"type":"paragraph","content":[{"text":"Quick bash parse (pure bash + sed/awk — no python needed):","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"DASHBOARD_URL=\"https://abc123.apps.dynatrace.com/ui/apps/dynatrace.dashboards/dashboard/5bea16c7-029b-43b6-9735-459db2d25bbf#from=2026-05-28T04%3A00Z&to=2026-05-28T05%3A00Z&vfilter_host_group=prod&vfilter_workload=my-svc\"\n\n# Minimal URL-decoder: turn %XX into \\xXX and let printf interpret it.\nurldecode() { local s=\"${1//+/ }\"; printf '%b' \"${s//%/\\\\x}\"; }\n\nDOC_ID=$(echo \"$DASHBOARD_URL\" | sed 's/#.*//' | awk -F/ '{print $NF}')\nFROM=$(urldecode \"$(echo \"$DASHBOARD_URL\" | sed -n 's/.*[#&]from=\\([^&]*\\).*/\\1/p')\")\nTO=$(urldecode \"$(echo \"$DASHBOARD_URL\" | sed -n 's/.*[#&]to=\\([^&]*\\).*/\\1/p')\")\nHOST_GROUP=$(echo \"$DASHBOARD_URL\" | sed -n 's/.*[#&]vfilter_host_group=\\([^&]*\\).*/\\1/p')\nWORKLOAD=$(echo \"$DASHBOARD_URL\" | sed -n 's/.*[#&]vfilter_workload=\\([^&]*\\).*/\\1/p')","type":"text"}]},{"type":"paragraph","content":[{"text":"For notebooks: path segment after ","type":"text"},{"text":"/notebook/","type":"text","marks":[{"type":"code_inline"}]},{"text":", or ","type":"text"},{"text":"#share=","type":"text","marks":[{"type":"code_inline"}]},{"text":" value for ","type":"text"},{"text":"/document/v0/#share=\u003cID>","type":"text","marks":[{"type":"code_inline"}]},{"text":" links.","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Step 1 — Extract queries","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"From a dashboard","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"# All tiles\ndtctl exec function -f scripts/extract-timeseries-dashboard.js \\\n --payload '{\"id\":\"\u003cdashboard-id-or-name>\"}' -o json\n\n# Only tiles whose title matches a name the user mentioned (e.g. \"CPU usage\", \"Kafka lag\")\ndtctl exec function -f scripts/extract-timeseries-dashboard.js \\\n --payload '{\"id\":\"\u003cdashboard-id>\",\"titleFilter\":\"CPU usage\"}' -o json","type":"text"}]},{"type":"paragraph","content":[{"text":"When the user names a specific tile, chart, or section","type":"text","marks":[{"type":"strong"}]},{"text":", pass its name as ","type":"text"},{"text":"titleFilter","type":"text","marks":[{"type":"code_inline"}]},{"text":" rather than extracting the full dashboard. ","type":"text"},{"text":"titleFilter","type":"text","marks":[{"type":"code_inline"}]},{"text":" is a case-insensitive substring or ","type":"text"},{"text":"/regex/flags","type":"text","marks":[{"type":"code_inline"}]},{"text":" pattern. This keeps the queryset small and focused.","type":"text"}]},{"type":"paragraph","content":[{"text":"When it is not clear which tile(s) the user wants","type":"text","marks":[{"type":"strong"}]},{"text":", 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:","type":"text"}]},{"type":"ordered_list","attrs":{"order":1,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"List tile names with ","type":"text"},{"text":"listOnly: true","type":"text","marks":[{"type":"code_inline"}]},{"text":" (no DQL, just titles):","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"dtctl exec function -f scripts/extract-timeseries-dashboard.js \\\n --payload '{\"id\":\"\u003cdashboard-id>\",\"listOnly\":true}' -o json\n# Returns: {\"result\":{\"ok\":true,\"tiles\":[{\"id\":\"...\",\"title\":\"CPU Usage\",\"visualization\":\"lineChart\"},...]}}","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Show the tile names to the user and ask which tile(s) they mean.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Re-run with ","type":"text"},{"text":"titleFilter","type":"text","marks":[{"type":"code_inline"}]},{"text":" for only the tile(s) of interest.","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"This avoids pulling 20–50 DQL queries into context when only 1–2 are relevant.","type":"text"}]},{"type":"paragraph","content":[{"text":"Payload knobs:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"id","type":"text","marks":[{"type":"code_inline"}]},{"text":" (required) — dashboard ID (UUID) or exact name. Preset IDs like ","type":"text"},{"text":"my.dynatrace.infraops.preview.*","type":"text","marks":[{"type":"code_inline"}]},{"text":" work.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"titleFilter","type":"text","marks":[{"type":"code_inline"}]},{"text":" — case-insensitive substring (","type":"text"},{"text":"\"CPU usage\"","type":"text","marks":[{"type":"code_inline"}]},{"text":") or ","type":"text"},{"text":"/regex/flags","type":"text","marks":[{"type":"code_inline"}]},{"text":" (","type":"text"},{"text":"\"/^kafka/i\"","type":"text","marks":[{"type":"code_inline"}]},{"text":").","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"listOnly","type":"text","marks":[{"type":"code_inline"}]},{"text":" — when ","type":"text"},{"text":"true","type":"text","marks":[{"type":"code_inline"}]},{"text":", returns ","type":"text"},{"text":"tiles: [{id, title, visualization}]","type":"text","marks":[{"type":"code_inline"}]},{"text":" without DQL. Use for disambiguation.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"compact","type":"text","marks":[{"type":"code_inline"}]},{"text":" — when ","type":"text"},{"text":"true","type":"text","marks":[{"type":"code_inline"}]},{"text":", returns only ","type":"text"},{"text":"{id, title, dqlQuery}","type":"text","marks":[{"type":"code_inline"}]},{"text":" per tile (drops description, visualization, isTimeseries). Saves ~40% per-tile tokens. In ","type":"text"},{"text":"listOnly","type":"text","marks":[{"type":"code_inline"}]},{"text":" mode, drops visualization too.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"includeSkipped","type":"text","marks":[{"type":"code_inline"}]},{"text":" — when ","type":"text"},{"text":"true","type":"text","marks":[{"type":"code_inline"}]},{"text":", returns the full ","type":"text"},{"text":"skipped[]","type":"text","marks":[{"type":"code_inline"}]},{"text":" array. Default: only ","type":"text"},{"text":"skippedCount","type":"text","marks":[{"type":"code_inline"}]},{"text":" is returned.","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"Response envelope:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"json"},"content":[{"text":"{\n \"ok\": true,\n \"documentId\": \"...\", \"documentName\": \"...\", \"documentVersion\": 7,\n \"queries\": [\n { \"id\": \"\u003ctile-key>\", \"title\": \"...\", \"description\": \"...\",\n \"dqlQuery\": \"timeseries avg(dt.host.cpu.usage)\",\n \"visualization\": \"lineChart\", \"isTimeseries\": true }\n ],\n \"skipped\": [{ \"id\": \"...\", \"reason\": \"non-data tile (markdown)\" }]\n}","type":"text"}]},{"type":"paragraph","content":[{"text":"On failure: ","type":"text"},{"text":"{ \"ok\": false, \"error\": { \"code\": \"...\", \"message\": \"...\" } }","type":"text","marks":[{"type":"code_inline"}]},{"text":".","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"From a notebook","type":"text"}]},{"type":"paragraph","content":[{"text":"Same envelope, different schema walk:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"# All cells\ndtctl exec function -f scripts/extract-timeseries-notebook.js \\\n --payload '{\"id\":\"\u003cnotebook-id-or-name>\"}' -o json\n\n# A specific section (if cell titles are set)\ndtctl exec function -f scripts/extract-timeseries-notebook.js \\\n --payload '{\"id\":\"\u003cnotebook-id>\",\"titleFilter\":\"JVM memory\"}' -o json","type":"text"}]},{"type":"paragraph","content":[{"text":"Notebook cells often have empty titles — prefer addressing cells by ","type":"text"},{"text":"id","type":"text","marks":[{"type":"code_inline"}]},{"text":" from the envelope if targeting a specific one.","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Step 2 — Run an analyzer","type":"text"}]},{"type":"paragraph","content":[{"text":"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:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"# Run extractor, save output\ndtctl exec function -f scripts/extract-timeseries-dashboard.js \\\n --payload '{\"id\":\"\u003cid>\",\"titleFilter\":\"CPU usage\",\"compact\":true}' -o json > queryset.json\n\n# Shell substitution: $(cat queryset.json) is expanded by the shell, not the model\ndtctl exec function -f scripts/run-analyzer.js \\\n --payload '{\n \"analyzerName\": \"dt.statistics.NoveltyScoreAnalyzer\",\n \"queries\": '\"$(cat queryset.json)\"',\n \"timeframe\": { \"startTime\": \"...\", \"endTime\": \"...\" },\n \"analyzerParams\": { ... }\n }' -o json","type":"text"}]},{"type":"paragraph","content":[{"text":"run-analyzer.js","type":"text","marks":[{"type":"code_inline"}]},{"text":" unwraps the ","type":"text"},{"text":"{\"result\":{...}}","type":"text","marks":[{"type":"code_inline"}]},{"text":" dtctl envelope automatically — pass the raw saved output as-is. No ","type":"text"},{"text":"jq","type":"text","marks":[{"type":"code_inline"}]},{"text":" or parsing step is needed: normalization of the array / envelope / dtctl-output shapes happens inside the script.","type":"text"}]},{"type":"paragraph","content":[{"text":"For large querysets (many tiles), the inline ","type":"text"},{"text":"$(cat ...)","type":"text","marks":[{"type":"code_inline"}]},{"text":" form can hit shell argument-length limits. Build the payload file and use dtctl's ","type":"text"},{"text":"--data","type":"text","marks":[{"type":"code_inline"}]},{"text":" flag instead — still no ","type":"text"},{"text":"jq","type":"text","marks":[{"type":"code_inline"}]},{"text":" and still out of model context:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"{ printf '{\"analyzerName\":\"dt.statistics.NoveltyScoreAnalyzer\",\"timeframe\":{\"startTime\":\"now-1h\",\"endTime\":\"now\"},\"queries\":'\n cat queryset.json\n printf '}'; } > payload.json\n\ndtctl exec function -f scripts/run-analyzer.js --data payload.json -o json","type":"text"}]},{"type":"paragraph","content":[{"text":"Key payload knobs for ","type":"text"},{"text":"run-analyzer.js","type":"text","marks":[{"type":"code_inline"}]},{"text":":","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"minScore","type":"text","marks":[{"type":"code_inline"}]},{"text":" — drop results below this threshold (e.g. ","type":"text"},{"text":"0.5","type":"text","marks":[{"type":"code_inline"}]},{"text":"). Auto-detects score field from ","type":"text"},{"text":"noveltyScore","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"anomalyScore","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"correlationCoefficient","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"correlation","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"coefficient","type":"text","marks":[{"type":"code_inline"}]},{"text":". Pass ","type":"text"},{"text":"scoreField","type":"text","marks":[{"type":"code_inline"}]},{"text":" to override.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"scoreField","type":"text","marks":[{"type":"code_inline"}]},{"text":" — explicit field name to read score from (e.g. ","type":"text"},{"text":"\"noveltyScore\"","type":"text","marks":[{"type":"code_inline"}]},{"text":").","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"The ","type":"text"},{"text":"queries","type":"text","marks":[{"type":"code_inline"}]},{"text":" field accepts any of: a raw array, the extractor envelope (","type":"text"},{"text":"{queries:[...]}","type":"text","marks":[{"type":"code_inline"}]},{"text":"), or the full dtctl output (","type":"text"},{"text":"{\"result\":{\"queries\":[...]}}","type":"text","marks":[{"type":"code_inline"}]},{"text":"). All three are normalized automatically.","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Common analyzers","type":"text"}]},{"type":"table","attrs":{"layout":null},"content":[{"type":"tr","content":[{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Goal","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"analyzerName","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"analyzerParams","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Find anomalous metrics","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"dt.statistics.anomaly_detection.SeasonalBaselineAnomalyDetectionAnalyzer","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"{ \"trainingTimeframe\": { \"startTime\": \"now-8d\", \"endTime\": \"now-1d\" } }","type":"text","marks":[{"type":"code_inline"}]},{"text":" (optional)","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Score how novel each metric is","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"dt.statistics.NoveltyScoreAnalyzer","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"{ \"detectionMode\": \"ALL\", \"minNoveltyScore\": 0 }","type":"text","marks":[{"type":"code_inline"}]},{"text":" (optional)","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Correlate against a primary metric","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"dt.statistics.SimplePearsonCorrelationAnalyzer","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"set ","type":"text"},{"text":"metricQuery","type":"text","marks":[{"type":"code_inline"}]},{"text":" instead (changes call shape)","type":"text"}]}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Correlation mode","type":"text"}]},{"type":"paragraph","content":[{"text":"Pass ","type":"text"},{"text":"metricQuery","type":"text","marks":[{"type":"code_inline"}]},{"text":" to correlate every query in the set against a single primary DQL string:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"dtctl exec function -f scripts/run-analyzer.js \\\n --payload '{\n \"analyzerName\": \"dt.statistics.SimplePearsonCorrelationAnalyzer\",\n \"queries\": '\"$(cat queryset.json)\"',\n \"metricQuery\": \"\u003cdqlQuery of the primary tile, copied from extractor output>\",\n \"timeframe\": { \"startTime\": \"...\", \"endTime\": \"...\" }\n }' -o json","type":"text"}]},{"type":"paragraph","content":[{"text":"When chaining from a previous analyzer run (e.g. anomaly detection → correlation), use ","type":"text"},{"text":"metricQueryFrom","type":"text","marks":[{"type":"code_inline"}]},{"text":" instead. The script picks the highest-scored result's ","type":"text"},{"text":"dqlQuery","type":"text","marks":[{"type":"code_inline"}]},{"text":" automatically:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"dtctl exec function -f scripts/run-analyzer.js \\\n --payload '{\n \"analyzerName\": \"dt.statistics.SimplePearsonCorrelationAnalyzer\",\n \"queries\": '\"$(cat queryset.json)\"',\n \"metricQueryFrom\": '\"$(cat findings.json)\"',\n \"timeframe\": { \"startTime\": \"...\", \"endTime\": \"...\" }\n }' -o json","type":"text"}]},{"type":"paragraph","content":[{"text":"metricQuery","type":"text","marks":[{"type":"code_inline"}]},{"text":" takes precedence if both are set.","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Variable substitution","type":"text"}]},{"type":"paragraph","content":[{"text":"Dashboard queries often contain ","type":"text"},{"text":"$variable","type":"text","marks":[{"type":"code_inline"}]},{"text":" tokens (from URL ","type":"text"},{"text":"vfilter_*","type":"text","marks":[{"type":"code_inline"}]},{"text":" params). Pass them via ","type":"text"},{"text":"variables","type":"text","marks":[{"type":"code_inline"}]},{"text":" to substitute before execution:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"dtctl exec function -f scripts/run-analyzer.js \\\n --payload '{\n \"analyzerName\": \"dt.statistics.anomaly_detection.SeasonalBaselineAnomalyDetectionAnalyzer\",\n \"queries\": [...],\n \"timeframe\": { \"startTime\": \"2026-05-28T04:00Z\", \"endTime\": \"2026-05-28T05:00Z\" },\n \"variables\": { \"host_group\": \"prod\", \"workload\": \"my-svc\" }\n }' -o json","type":"text"}]},{"type":"paragraph","content":[{"text":"Build ","type":"text"},{"text":"variables","type":"text","marks":[{"type":"code_inline"}]},{"text":" from ","type":"text"},{"text":"vfilter_*","type":"text","marks":[{"type":"code_inline"}]},{"text":" URL params by stripping the ","type":"text"},{"text":"vfilter_","type":"text","marks":[{"type":"code_inline"}]},{"text":" prefix. Trailing ","type":"text"},{"text":"*","type":"text","marks":[{"type":"code_inline"}]},{"text":" wildcards are stripped automatically. Unresolved tokens are cleaned up from DQL filter clauses rather than left to error.","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Response shape","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"json"},"content":[{"text":"{\n \"ok\": true,\n \"checkedAt\": \"...\",\n \"analyzerName\": \"...\",\n \"summary\": { \"checked\": 12, \"completed\": 11, \"errors\": 1 },\n \"results\": [\n {\n \"id\": \"tile-key\", \"title\": \"CPU usage\", \"dqlQuery\": \"...\",\n \"output\": \u003craw analyzer output>,\n \"executionStatus\": \"COMPLETED\"\n }\n ],\n \"errors\": [ { \"id\": \"...\", \"error\": \"...\" } ]\n}","type":"text"}]},{"type":"paragraph","content":[{"text":"The ","type":"text"},{"text":"output","type":"text","marks":[{"type":"code_inline"}]},{"text":" field is the raw analyzer result. Interpret it based on the analyzer:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Anomaly detection","type":"text","marks":[{"type":"strong"}]},{"text":": look for ","type":"text"},{"text":"anomalyScore","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"anomalies[]","type":"text","marks":[{"type":"code_inline"}]},{"text":", or ","type":"text"},{"text":"raisedAlerts[]","type":"text","marks":[{"type":"code_inline"}]},{"text":" in each output entry. Score ≥ 0.7 → abnormal, ≥ 0.4 → borderline.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Novelty","type":"text","marks":[{"type":"strong"}]},{"text":": look for ","type":"text"},{"text":"noveltyScore","type":"text","marks":[{"type":"code_inline"}]},{"text":" (or the closest score-like numeric field). Score ≥ 0.7 → novel.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Correlation","type":"text","marks":[{"type":"strong"}]},{"text":": look for ","type":"text"},{"text":"correlationCoefficient","type":"text","marks":[{"type":"code_inline"}]},{"text":". Sort by ","type":"text"},{"text":"|correlationCoefficient|","type":"text","marks":[{"type":"code_inline"}]},{"text":" descending; drop entries where ","type":"text"},{"text":"|correlationCoefficient| \u003c 0.5","type":"text","marks":[{"type":"code_inline"}]},{"text":".","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"End-to-end: \"what's abnormal on this dashboard?\"","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"# 1. Extract queries — shell reads file, JSON stays out of model context\ndtctl exec function -f scripts/extract-timeseries-dashboard.js \\\n --payload '{\"id\":\"5bea16c7-029b-43b6-9735-459db2d25bbf\",\"compact\":true}' \\\n -o json > queryset.json\n\n# 2. Run anomaly detection — $(cat queryset.json) expanded by shell, not model\ndtctl exec function -f scripts/run-analyzer.js \\\n --payload '{\n \"analyzerName\": \"dt.statistics.anomaly_detection.SeasonalBaselineAnomalyDetectionAnalyzer\",\n \"queries\": '\"$(cat queryset.json)\"',\n \"timeframe\": { \"startTime\": \"2026-05-28T04:00Z\", \"endTime\": \"2026-05-28T05:00Z\" },\n \"variables\": { \"host_group\": \"prod\", \"workload\": \"my-svc\" }\n }' -o json > findings.json\n\n# 3. Correlate — metricQueryFrom picks the top finding automatically\ndtctl exec function -f scripts/run-analyzer.js \\\n --payload '{\n \"analyzerName\": \"dt.statistics.SimplePearsonCorrelationAnalyzer\",\n \"queries\": '\"$(cat queryset.json)\"',\n \"metricQueryFrom\": '\"$(cat findings.json)\"',\n \"timeframe\": { \"startTime\": \"2026-05-28T04:00Z\", \"endTime\": \"2026-05-28T05:00Z\" }\n }' -o json","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Verifying a single extracted query","type":"text"}]},{"type":"paragraph","content":[{"text":"Read the ","type":"text"},{"text":"dqlQuery","type":"text","marks":[{"type":"code_inline"}]},{"text":" field from the extractor output and pass it directly:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"dtctl query --query \"\u003cdqlQuery copied from extractor output>\" -o json | head -40","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Gotchas","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Never read raw dashboard JSON yourself.","type":"text","marks":[{"type":"strong"}]},{"text":" ","type":"text"},{"text":"dtctl get dashboard \u003cid> -o json","type":"text","marks":[{"type":"code_inline"}]},{"text":" is typically 50–200 KB. The extractor reads it on the platform and returns a compact envelope (~5–15 KB).","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Never extract all tiles when only one is needed.","type":"text","marks":[{"type":"strong"}]},{"text":" A 50-tile dashboard returns 50 DQL queries into context. If the user names a tile, use ","type":"text"},{"text":"titleFilter","type":"text","marks":[{"type":"code_inline"}]},{"text":". If it's ambiguous, use ","type":"text"},{"text":"listOnly: true","type":"text","marks":[{"type":"code_inline"}]},{"text":" first to ask which tile — then extract only that one.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Variables are required for filtered dashboards.","type":"text","marks":[{"type":"strong"}]},{"text":" Queries with unsubstituted ","type":"text"},{"text":"$variable","type":"text","marks":[{"type":"code_inline"}]},{"text":" tokens silently drop entity filters (e.g. ","type":"text"},{"text":"in(field, $undefined)","type":"text","marks":[{"type":"code_inline"}]},{"text":" evaluates to ","type":"text"},{"text":"true","type":"text","marks":[{"type":"code_inline"}]},{"text":"). Always pass ","type":"text"},{"text":"variables","type":"text","marks":[{"type":"code_inline"}]},{"text":" when the URL has ","type":"text"},{"text":"vfilter_*","type":"text","marks":[{"type":"code_inline"}]},{"text":" params.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Schema drift.","type":"text","marks":[{"type":"strong"}]},{"text":" If a tile lands in ","type":"text"},{"text":"skipped","type":"text","marks":[{"type":"code_inline"}]},{"text":" with reason ","type":"text"},{"text":"no DQL query found","type":"text","marks":[{"type":"code_inline"}]},{"text":", the dashboard schema has a query location the extractor doesn't know about — add it to the ","type":"text"},{"text":"pickQuery","type":"text","marks":[{"type":"code_inline"}]},{"text":" candidate list in the script.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Analyzer availability.","type":"text","marks":[{"type":"strong"}]},{"text":" Not all Davis analyzers are available on every tenant. If a call comes back with ","type":"text"},{"text":"Could not find an analyzer with name '...'","type":"text","marks":[{"type":"code_inline"}]},{"text":" or ","type":"text"},{"text":"is not a function","type":"text","marks":[{"type":"code_inline"}]},{"text":", list what's actually registered: ","type":"text"},{"text":"dtctl get analyzers -o json","type":"text","marks":[{"type":"code_inline"}]},{"text":".","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Statistical fallback removed.","type":"text","marks":[{"type":"strong"}]},{"text":" ","type":"text"},{"text":"run-analyzer.js","type":"text","marks":[{"type":"code_inline"}]},{"text":" only calls Davis analyzers. For historical anomaly detection, pass a long ","type":"text"},{"text":"trainingTimeframe","type":"text","marks":[{"type":"code_inline"}]},{"text":" via ","type":"text"},{"text":"analyzerParams","type":"text","marks":[{"type":"code_inline"}]},{"text":" (e.g. ","type":"text"},{"text":"{ \"trainingTimeframe\": { \"startTime\": \"now-30d\", \"endTime\": \"now-1d\" } }","type":"text","marks":[{"type":"code_inline"}]},{"text":"), or query the DQL directly.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Comments in queries.","type":"text","marks":[{"type":"strong"}]},{"text":" Queries starting with ","type":"text"},{"text":"// comment","type":"text","marks":[{"type":"code_inline"}]},{"text":" lines are classified correctly by the extractor (leading line/block comments are stripped before the ","type":"text"},{"text":"timeseries","type":"text","marks":[{"type":"code_inline"}]},{"text":" check).","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Scripts reference","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"scripts/extract-timeseries-dashboard.js","type":"text","marks":[{"type":"link","attrs":{"href":"scripts/extract-timeseries-dashboard.js","title":null}}]},{"text":" — extracts timeseries DQL from a dashboard","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"scripts/extract-timeseries-notebook.js","type":"text","marks":[{"type":"link","attrs":{"href":"scripts/extract-timeseries-notebook.js","title":null}}]},{"text":" — same for notebooks","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"scripts/run-analyzer.js","type":"text","marks":[{"type":"link","attrs":{"href":"scripts/run-analyzer.js","title":null}}]},{"text":" — generic Davis analyzer runner","type":"text"}]}]}]}]},"metadata":{"date":"2026-11-02","name":"dt-obs-analytics","author":"@skillopedia","source":{"stars":172,"repo_name":"dynatrace-for-ai","origin_url":"https://github.com/Dynatrace/dynatrace-for-ai/blob/HEAD/skills/dt-obs-analytics/SKILL.md","repo_owner":"Dynatrace","body_sha256":"c7552cc90197bd64db0c87f4c28cf94626ffb16bc32db6be9b77664d0974bd6d","cluster_key":"92e1a2ac07feb03c4d88381efa4e32f204eb79181aacd8f8d6049d43fb602d90","clean_bundle":{"format":"clean-skill-bundle-v1","source":"Dynatrace/dynatrace-for-ai/skills/dt-obs-analytics/SKILL.md","text_bytes":36160,"source_commit":"306fb94e36a53de026cb05a58b5b440fe6d7f000","skills_sh_rank":11398,"attachment_count":3,"text_attachments":3,"attachment_storage":"skill_attachments","binary_attachments":0},"cluster_size":1,"skill_md_path":"skills/dt-obs-analytics/SKILL.md","import_metadata":{"date":"2026-11-02","author":"@skillopedia","version":"v1","category":"data-analytics","category_label":"Data"},"exact_dupes_collapsed_into_this":0},"license":"Apache-2.0","version":"v1","category":"data-analytics","import_tag":"skills-sh-top20k-2026-11","description":"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 anomalies, score novelty, or correlate its metrics. The trigger is a dashboard or notebook as the data source, not a general DQL question. This skill extracts timeseries queries efficiently without reading the full raw document JSON, then optionally runs Davis analyzers on the extracted metrics. Trigger phrases: \"what's wrong on this dashboard\", \"analyze this notebook\", \"find anomalies\", \"novelty score\", \"correlate metrics\", \"extract DQL from dashboard\", \"dashboard URL\", \"tile\", \"run-analyzer\", \"timeseries extraction\", \"Davis analyzer\"."}},"renderedAt":1793666229773}

Analytics — Dashboard & Notebook Query Extraction A pipeline of three platform JavaScript scripts under — two extractors feeding a shared analyzer runner: Each script is invoked via: wraps the function return value under . When you need to inspect the result, read it directly from the output — no required. Pass the full raw output as to 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 (before ) | in extract-timeseries-dashboard…