Grafana Polystat如何实现悬停元素调用POST API展示自定义详情
该需求完全可以通过原生HTML+JavaScript实现,不需要深入掌握前端开发技能,也不需要修改Polystat插件源码、从零开发自定义可视化面板,整体开发量很小。
第一步:给Python Lambda接口配置跨域规则
因为Grafana前端页面对Lambda发起的是跨域POST请求,必须在Lambda返回头中加入CORS配置,参考代码如下:import json def lambda_handler(event, context): # 这里写你原有的查询RDS详情的逻辑 rds_detail = { "instance_id": "db-xxx", "instance_type": "db.r6g.large", "engine": "PostgreSQL", "engine_version": "14.7", "endpoint": "db-xxx.xxx.us-east-1.rds.amazonaws.com:5432", "status": "available" } return { "statusCode": 200, "headers": { "Access-Control-Allow-Origin": "你的Grafana服务访问域名", "Access-Control-Allow-Methods": "POST, OPTIONS", "Access-Control-Allow-Headers": "Content-Type" }, "body": json.dumps(rds_detail) }本地调试阶段可以临时把
Access-Control-Allow-Origin设为*,正式上线必须替换为实际Grafana域名,避免跨域安全风险。第二步:确保Polystat面板可提取实例唯一标识
确认你当前Polystat绑定的查询语句返回结果中,带有db_instance_identifier(RDS实例ID)维度,且该值会渲染在每个Polystat块的可见文本/标签属性中,后续脚本将通过这个值识别当前悬停的实例。如果当前Polystat块只展示资源消耗数值,把实例ID设为块的副标题即可,不需要额外改查询逻辑。第三步:注入悬停监听脚本
不需要修改Polystat插件配置,在当前仪表盘新增一个文本面板,把面板高度调整为1、透明度设为100%做成隐藏面板,切换到面板的HTML/JavaScript编辑模式,粘贴以下基础代码:// 防抖控制+本地缓存,避免频繁触发请求 let hoverTimer = null; const detailCache = new Map(); const HOVER_DELAY = 300; // 悬停300ms才触发请求,避免误触 // 监听全局鼠标悬停事件 document.addEventListener('mouseover', async (e) => { // 匹配Polystat的实例块元素,类名和你实际渲染的不一致可以通过F12开发者工具调整 const targetBlock = e.target.closest('.polystat-panel .polystat-item'); if (!targetBlock) return; // 提取当前块对应的实例ID,选择器根据实际渲染结构调整 const instanceId = targetBlock.querySelector('.item-label')?.textContent?.trim(); if (!instanceId) return; clearTimeout(hoverTimer); hoverTimer = setTimeout(async () => { let instanceDetail; // 命中缓存直接用,不重复发请求 if (detailCache.has(instanceId)) { instanceDetail = detailCache.get(instanceId); } else { // 发起POST请求拉取详情 const res = await fetch('替换为你的Lambda API访问地址', { method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify({instance_id: instanceId}) }); instanceDetail = await res.json(); detailCache.set(instanceId, instanceDetail); } // 渲染自定义详情弹窗 renderTooltip(targetBlock, instanceDetail); }, HOVER_DELAY); }); // 鼠标移出实例块时销毁弹窗 document.addEventListener('mouseout', (e) => { if (e.target.closest('.polystat-panel .polystat-item')) { clearTimeout(hoverTimer); document.getElementById('custom-rds-tooltip')?.remove(); } }); // 弹窗渲染逻辑 function renderTooltip(anchorEl, detail) { // 先移除已存在的旧弹窗 document.getElementById('custom-rds-tooltip')?.remove(); const tooltip = document.createElement('div'); tooltip.id = 'custom-rds-tooltip'; // 弹窗样式可自行调整,匹配Grafana原生风格 tooltip.style.cssText = ` position: absolute; background: #161719; border: 1px solid #3d3d3d; border-radius: 4px; padding: 12px; z-index: 9999; min-width: 280px; color: #ffffff; font-size: 13px; line-height: 1.8; box-shadow: 0 4px 12px rgba(0,0,0,0.4); `; // 填充详情内容,字段和你Lambda返回的结构对齐即可 tooltip.innerHTML = ` <div style="font-weight: 600; margin-bottom: 8px; font-size: 14px">${detail.instance_id}</div> <div>实例规格:${detail.instance_type}</div> <div>数据库引擎:${detail.engine} ${detail.engine_version}</div> <div>访问端点:<code style="background:#262628; padding:2px 6px; border-radius:3px">${detail.endpoint}</code></div> <div>运行状态:<span style="color: #73bf69">${detail.status}</span></div> `; // 把弹窗定位到实例块下方 const rect = anchorEl.getBoundingClientRect(); tooltip.style.top = `${rect.bottom + window.scrollY + 8}px`; tooltip.style.left = `${rect.left + window.scrollX}px`; document.body.appendChild(tooltip); }如果你的Grafana版本开启了严格CSP策略,不允许在原生文本面板执行脚本,可以替换使用社区的HTML面板插件,逻辑完全一致。
第四步:调试适配
打开浏览器F12开发者工具,悬停Polystat实例块时,先检查是否能正确提取到实例ID,如果提取失败就调整代码中的元素选择器(直接用开发者工具选中对应元素复制类名即可);如果请求报错,优先检查Lambda的CORS配置、请求入参格式是否和Lambda预期一致。
- 给请求过程加加载态:弹窗初始显示「实例详情加载中...」,请求返回后再替换为实际内容,体验更好
- 给本地缓存加过期时间:比如设置5分钟自动失效,避免实例配置变更后前端长期展示旧数据
- 可以把Polystat默认的原生Tooltip通过CSS隐藏,避免和自定义弹窗同时出现
- 弹窗样式可以完全对齐Grafana原生Tooltip的设计,视觉上更统一
内容的提问来源于stack exchange,提问作者dbNovice

