本文重点讲解如何将OpenMetadata的异常提醒通过Webhook发送到钉钉,设置数据质量规则较简单,看下图即可。有一些规则,也可以自定义SQL。

设置好规则后,添加提醒。

目的地选择 webhook,注意url不能是内网

OpenMetadata 不支持直接发钉钉消息 ,它只能发通用的 HTTP Webhook(JSON 格式)。而钉钉机器人有自己的消息格式要求(markdown 结构、签名验证等)。
需要写个脚本充当中间层
OpenMetadata → 钉钉的 Webhook 转发服务
OpenMetadata 告警触发
↓
POST http://xxx:8787/dingtalk
↓
解析 OpenMetadata 的 JSON 数据
提取:表名、规则名、状态、结果、时间
↓
组装成钉钉 markdown 消息格式
计算 HMAC 签名(防伪造)
↓
POST https://oapi.dingtalk.com/robot/send(钉钉官方接口)
↓
钉钉群收到告警消息
核心功能总结:
- 格式转换:把 OpenMetadata 的通用 JSON 转成钉钉能识别的 markdown 消息
- 签名计算:钉钉加签安全模式需要 HMAC-SHA256 签名,脚本自动计算
- 字段提取:从复杂的 OpenMetadata 事件数据里提取关键信息(表名、状态、检测结果等)展示给人看
下面是python代码:
python
from flask import Flask, request
import requests, json, time, hmac, hashlib, base64, urllib.parse
app = Flask(__name__)
DINGTALK_WEBHOOK = "https://oapi.dingtalk.com/robot/send?access_token=xxx" # 通过钉钉群创建机器人获得,群之间不能共用
DINGTALK_SECRET = "SECxxx" # 没开加签则留空字符串
def get_sign():
timestamp = str(round(time.time() * 1000))
secret_enc = DINGTALK_SECRET.encode('utf-8')
string_to_sign = f'{timestamp}\n{DINGTALK_SECRET}'
hmac_code = hmac.new(secret_enc, string_to_sign.encode('utf-8'), digestmod=hashlib.sha256).digest()
sign = urllib.parse.quote_plus(base64.b64encode(hmac_code))
return timestamp, sign
@app.route("/dingtalk", methods=["POST"])
def forward():
data = request.json or {}
print("收到数据keys:", list(data.keys()))
# 兼容两种结构:fieldsUpdated 在顶层 或 在 changeDescription 里
change_desc = data.get("changeDescription") or data
new_value = {}
for field in change_desc.get("fieldsUpdated", []):
if field.get("name") == "testCaseResult":
new_value = field.get("newValue", {})
break
# entity 可能是 JSON 字符串、字典、或不存在
entity_raw = data.get("entity", "{}")
try:
entity_obj = json.loads(entity_raw) if isinstance(entity_raw, str) else entity_raw
except:
entity_obj = {}
case_fqn = new_value.get("testCaseFQN", entity_obj.get("fullyQualifiedName", "未知"))
# entityFQN 可能不存在,安全取第二段
entity_fqn = entity_obj.get("entityFQN", "")
status = new_value.get("testCaseStatus", "未知")
result = new_value.get("result", "")
ev_ts = data.get("timestamp", 0)
ev_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(ev_ts / 1000)) if ev_ts else "未知"
color = "⚠️" if status in ("Failed", "FAILED") else "✅"
msg = {
"msgtype": "markdown",
"markdown": {
"title": "数据质量告警",
"text": (
f"## {color} 数据质量告警\n\n"
f"**Case FQN**:`{case_fqn}`\n\n"
f"**表**:`{entity_fqn}`\n\n"
f"**结果**:{result}\n\n"
f"**状态**:{status}\n\n"
f"**时间**:{ev_time}\n\n"
)
}
}
print("发送msg:", json.dumps(msg, ensure_ascii=False))
url = DINGTALK_WEBHOOK
if DINGTALK_SECRET:
ts, sign = get_sign()
url += f"×tamp={ts}&sign={sign}"
resp = requests.post(url, json=msg)
result_json = resp.json()
print("钉钉返回:", result_json)
if result_json.get("errcode", 0) != 0:
return {"code": 500, "body": result_json}, 500
return {"code": resp.status_code, "body": result_json}
if __name__ == "__main__":
app.run(host="0.0.0.0", port=8787)
如果不通,下图标红pending会有提醒积压,如果报错说明是通的,如500,需看日志。消息格式需符合钉钉要求!

查看相关日志
powershell
docker logs openmetadata_server --tail=200 2>&1 | grep -iE "event|alert|webhook|pending|notification|error|exception"