关于 sla-management
面向 ServiceNow 管理员和开发者的 SLA 管理技能,涵盖规则配置、任务计时、违约处理与合规报告,并提供 ES5 示例和 MCP 工具流程。
主要功能
- 01创建和配置 SLA 定义
- 02查询任务 SLA 记录与状态
- 03暂停和恢复 SLA 计时
- 04处理违约通知与升级
- 05计算 SLA 合规率
适用场景
- 01设定响应与解决时限
- 02在外部等待期间暂停 SLA
- 03在违约前后通知团队
- 04按日期范围检查合规率
MCP 相关
介绍如何使用 ServiceNow MCP 工具查找 SLA 定义、查询任务 SLA 记录、测试脚本和创建业务规则。
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ serac-labs/ serac --skill sla-management安装后在 Claude Code 中输入/sla-management确认已加载。
详细介绍
ServiceNow 的 SLA 管理
SLA(服务级别协议)管理用于跟踪并确保履行服务承诺。
SLA 组件
| 组件 | 表 | 用途 |
|---|---|---|
| SLA 定义 | contract_sla | SLA 规则和条件 |
| 任务 SLA | task_sla | 任务上的 SLA 实例 |
| SLA 工作流 | wf_workflow | SLA 违约通知 |
| SLA 计划 | cmn_schedule | 工作时间定义 |
SLA 流程
Task Created
↓
SLA Definition Conditions Match
↓
Task SLA Record Created
↓
Timer Starts (based on schedule)
↓
SLA Stages: In Progress → Breached (if not met)
↓
Task Resolved/Closed
↓
SLA Achieved or Breached
SLA 定义(ES5)
创建 SLA 定义
// Create SLA Definition (ES5 ONLY!)
var sla = new GlideRecord("contract_sla")
sla.initialize()
// Basic info
sla.setValue("name", "P1 Incident Response Time")
sla.setValue("type", "SLA") // SLA, OLA, UC
sla.setValue("table", "incident")
// Target duration
sla.setValue("duration_type", "response") // response or resolution
sla.setValue("duration", "PT15M") // 15 minutes (ISO 8601)
// Conditions - when SLA attaches
sla.setValue("start_condition", "priority=1^active=true")
sla.setValue("stop_condition", "work_notes.changesTo()")
sla.setValue("pause_condition", "state=3") // Pause when On Hold
sla.setValue("cancel_condition", "state=8") // Cancel when Cancelled
// Schedule (business hours)
sla.setValue("schedule", getScheduleSysId("8-5 M-F"))
// Enable
sla.setValue("active", true)
sla.insert()
SLA 条件说明
// Start Condition: When SLA timer begins
// Example: P1 incidents when created
var startCondition = "priority=1^active=true^sys_created_onRELATIVEGT@minute@ago@0"
// Stop Condition: When SLA is achieved
// Example: When work notes are added (response) or resolved (resolution)
var responseStop = "work_notes.changes()"
var resolutionStop = "state=6^ORstate=7" // Resolved or Closed
// Pause Condition: Timer pauses
// Example: On Hold or Awaiting User Info
var pauseCondition = "state=3^ORstate=-5"
// Cancel Condition: SLA cancelled without breach
// Example: Incident cancelled or duplicate
var cancelCondition = "state=8^ORclose_code=Duplicate"
任务 SLA 操作(ES5)
查询任务 SLA
// Find SLAs for an incident (ES5 ONLY!)
var incidentSysId = "incident_sys_id"
var taskSla = new GlideRecord("task_sla")
taskSla.addQuery("task", incidentSysId)
taskSla.query()
while (taskSla.next()) {
gs.info(
"SLA: " +
taskSla.sla.getDisplayValue() +
" | Stage: " +
taskSla.stage.getDisplayValue() +
" | Breached: " +
taskSla.getValue("has_breached") +
" | Planned End: " +
taskSla.getValue("planned_end_time"),
)
}
检查 SLA 状态
// SLA Status Helper (ES5 ONLY!)
var SLAHelper = Class.create()
SLAHelper.prototype = {
initialize: function () {},
/**
* Get SLA status for a task
* @param {string} taskSysId - Task sys_id
* @returns {Array} - Array of SLA status objects
*/
getSLAStatus: function (taskSysId) {
var slaStatuses = []
var taskSla = new GlideRecord("task_sla")
taskSla.addQuery("task", taskSysId)
taskSla.addQuery("active", true)
taskSla.query()
while (taskSla.next()) {
var now = new GlideDateTime()
var plannedEnd = new GlideDateTime(taskSla.getValue("planned_end_time"))
var timeLeft = GlideDateTime.subtract(now, plannedEnd)
slaStatuses.push({
name: taskSla.sla.getDisplayValue(),
stage: taskSla.stage.getDisplayValue(),
hasBreached: taskSla.getValue("has_breached") === "true",
percentageComplete: taskSla.getValue("percentage"),
plannedEnd: taskSla.getValue("planned_end_time"),
timeLeft: this._formatDuration(timeLeft),
isAtRisk: this._isAtRisk(taskSla),
})
}
return slaStatuses
},
/**
* Check if any SLA is at risk (>75% elapsed)
*/
_isAtRisk: function (taskSla) {
var percentage = parseFloat(taskSla.getValue("percentage"))
return percentage >= 75 && taskSla.getValue("has_breached") !== "true"
},
_formatDuration: function (duration) {
var totalSeconds = duration.getNumericValue() / 1000
var hours = Math.floor(totalSeconds / 3600)
var minutes = Math.floor((totalSeconds % 3600) / 60)
return hours + "h " + minutes + "m"
},
type: "SLAHelper",
}
暂停/恢复 SLA
// Pause SLAs when incident goes On Hold (ES5 ONLY!)
// Business Rule: after, update, incident
;(function executeRule(current, previous) {
// Check if state changed to On Hold
if (current.state.changesTo("3")) {
pauseIncidentSLAs(current.getUniqueValue())
}
// Check if state changed from On Hold
if (previous.state == "3" && current.state != "3") {
resumeIncidentSLAs(current.getUniqueValue())
}
})(current, previous)
function pauseIncidentSLAs(incidentId) {
var taskSla = new GlideRecord("task_sla")
taskSla.addQuery("task", incidentId)
taskSla.addQuery("active", true)
taskSla.addQuery("stage", "!=", "breached")
taskSla.query()
while (taskSla.next()) {
var slaDef = new GlideRecord("contract_sla")
if (slaDef.get(taskSla.getValue("sla"))) {
// Only pause if SLA has pause condition
if (slaDef.getValue("pause_condition")) {
taskSla.pause = true
taskSla.pause_time = new GlideDateTime()
taskSla.update()
}
}
}
}
SLA 工作流
违约通知脚本(ES5)
// SLA Workflow Activity: Send breach notification (ES5 ONLY!)
;(function executeActivity() {
var taskSla = current
var task = taskSla.task.getRefRecord()
// Get escalation recipients
var recipients = []
// Add assigned user
if (task.assigned_to) {
recipients.push(task.assigned_to.getValue("email"))
}
// Add assignment group manager
if (task.assignment_group) {
var group = task.assignment_group.getRefRecord()
if (group.manager) {
recipients.push(group.manager.email)
}
}
// Send notification
if (recipients.length > 0) {
gs.eventQueue("sla.breach.notification", task, recipients.join(","), taskSla.sla.getDisplayValue())
}
})()
SLA 升级规则
// SLA Escalation Script Include (ES5 ONLY!)
var SLAEscalation = Class.create()
SLAEscalation.prototype = {
initialize: function () {},
/**
* Escalate breached SLA
*/
escalateBreached: function (taskSlaSysId) {
var taskSla = new GlideRecord("task_sla")
if (!taskSla.get(taskSlaSysId)) {
return false
}
var task = taskSla.task.getRefRecord()
// Increase priority
var currentPriority = parseInt(task.getValue("priority"), 10)
if (currentPriority > 1) {
task.setValue("priority", currentPriority - 1)
}
// Set escalation flag
task.setValue("escalation", 1)
// Add work note
task.work_notes = "SLA Breached: " + taskSla.sla.getDisplayValue() + "\nAutomatic escalation applied."
task.update()
// Notify on-call
this._notifyOnCall(task)
return true
},
_notifyOnCall: function (task) {
// Get on-call schedule
var oncall = new OnCallRotation()
var onCallUser = oncall.getOnCallUser(task.assignment_group)
if (onCallUser) {
gs.eventQueue("sla.oncall.notification", task, onCallUser.sys_id, "")
}
},
type: "SLAEscalation",
}
SLA 报告
SLA 合规性查询(ES5)
// Calculate SLA compliance rate (ES5 ONLY!)
function getSLAComplianceRate(slaName, startDate, endDate) {
var ga = new GlideAggregate("task_sla")
ga.addQuery("sla.name", slaName)
ga.addQuery("end_time", ">=", startDate)
ga.addQuery("end_time", "<=", endDate)
ga.addQuery("active", false) // Completed SLAs only
ga.addAggregate("COUNT")
ga.addAggregate("COUNT", "has_breached")
ga.groupBy("has_breached")
ga.query()
var total = 0
var breached = 0
while (ga.next()) {
var count = parseInt(ga.getAggregate("COUNT"), 10)
total += count
if (ga.getValue("has_breached") === "true") {
breached = count
}
}
if (total === 0) {
return { compliance: 100, total: 0, breached: 0 }
}
var achieved = total - breached
var compliance = Math.round((achieved / total) * 100 * 10) / 10
return {
compliance: compliance,
total: total,
achieved: achieved,
breached: breached,
}
}
// Usage
var stats = getSLAComplianceRate("P1 Incident Response", gs.beginningOfThisMonth(), gs.endOfThisMonth())
gs.info("P1 Response SLA Compliance: " + stats.compliance + "%")
MCP 工具集成
可用工具
| 工具 | 用途 |
|---|---|
snow_artifact_manage (action='find') | 查找 SLA 定义 |
snow_query_table | 查询 task_sla 记录 |
snow_execute_script | 测试 SLA 脚本 |
snow_create_business_rule | 创建 SLA 触发器 |
示例工作流
// 1. Find existing SLAs
await snow_artifact_manage({
action: "find",
type: "contract_sla",
query: "P1",
})
// 2. Query SLA breaches
await snow_query_table({
table: "task_sla",
query: "has_breached=true^end_time>=javascript:gs.beginningOfThisMonth()",
fields: "sla,task,end_time,business_duration",
})
// 3. Check SLA compliance
await snow_execute_script({
script:
'var stats = getSLAComplianceRate("P1 Response", gs.beginningOfThisMonth(), gs.endOfThisMonth()); gs.info(JSON.stringify(stats));',
})
最佳实践
- 名称清晰 - “P1 Incident Response 15min”
- 工作时间 - 使用适当的计划
- 暂停条件 - 等待外部处理时暂停
- 升级 - 违约前发出通知
- 指标 - 跟踪合规率
- 测试 - 使用各种场景进行测试
- 文档 - 记录 SLA 条款
- 仅使用 ES5 - 不使用现代 JavaScript 语法
评论
支持 Markdown