关于 problem-management
面向 ServiceNow 开发与运维人员的 Problem Management 技能,涵盖问题生命周期、根因分析、已知错误和解决流程,并提供 ES5 脚本示例。
主要功能
- 01管理问题生命周期
- 02从事件创建问题
- 03记录根因分析结果
- 04维护已知错误和变通方案
- 05查询问题并测试脚本
适用场景
- 01汇总关联事件以创建问题
- 02分析重复事件的根本原因
- 03记录已知错误供后续检索
- 04测试问题管理自动化脚本
MCP 相关
文档列出用于查询问题与已知错误、查找开发工件、测试脚本和创建业务规则的 MCP 工具。
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ serac-labs/ serac --skill problem-management安装后在 Claude Code 中输入/problem-management确认已加载。
详细介绍
ServiceNow 问题管理
问题管理旨在识别事件的根本原因并实施永久解决方案。
问题生命周期
New (1)
↓
Assess (2)
↓
Root Cause Analysis (3)
↓
Fix in Progress (4) ← Known Error Database
↓
Resolved (5)
↓
Closed (6)
Cancelled (7) ← Can occur from any state
关键表
| 表 | 用途 |
|---|---|
problem | 问题记录 |
problem_task | 问题调查任务 |
known_error | 已知错误数据库(KEDB) |
incident | 相关事件 |
创建问题(ES5)
根据事件创建问题
// Create problem from multiple incidents (ES5 ONLY!)
function createProblemFromIncidents(incidentSysIds, problemData) {
// Create problem
var problem = new GlideRecord("problem")
problem.initialize()
problem.setValue("short_description", problemData.title)
problem.setValue("description", problemData.description)
problem.setValue("category", problemData.category)
problem.setValue("subcategory", problemData.subcategory)
// Set impact based on related incidents
var highestImpact = 3
for (var i = 0; i < incidentSysIds.length; i++) {
var incident = new GlideRecord("incident")
if (incident.get(incidentSysIds[i])) {
var incImpact = parseInt(incident.getValue("impact"), 10)
if (incImpact < highestImpact) {
highestImpact = incImpact
}
}
}
problem.setValue("impact", highestImpact)
problem.setValue("urgency", highestImpact)
// Assignment
if (problemData.assignmentGroup) {
problem.setValue("assignment_group", problemData.assignmentGroup)
}
// Affected CI
if (problemData.cmdb_ci) {
problem.setValue("cmdb_ci", problemData.cmdb_ci)
}
var problemSysId = problem.insert()
// Link incidents to problem
for (var j = 0; j < incidentSysIds.length; j++) {
var incidentToLink = new GlideRecord("incident")
if (incidentToLink.get(incidentSysIds[j])) {
incidentToLink.setValue("problem_id", problemSysId)
incidentToLink.work_notes = "Linked to Problem: " + problem.getValue("number")
incidentToLink.update()
}
}
return {
sys_id: problemSysId,
number: problem.getValue("number"),
}
}
主动创建问题
// Identify patterns for proactive problems (ES5 ONLY!)
function identifyProactiveProblems() {
var LOG_PREFIX = "[ProactiveProblem] "
var threshold = 5 // Minimum incidents to trigger
// Find recurring incident patterns
var ga = new GlideAggregate("incident")
ga.addQuery("opened_at", ">=", gs.daysAgo(30))
ga.addQuery("active", false) // Only closed incidents
ga.addNotNullQuery("cmdb_ci")
ga.addAggregate("COUNT")
ga.groupBy("cmdb_ci")
ga.groupBy("category")
ga.groupBy("subcategory")
ga.addHaving("COUNT", ">", threshold)
ga.orderByAggregate("COUNT", "DESC")
ga.query()
var patterns = []
while (ga.next()) {
var count = parseInt(ga.getAggregate("COUNT"), 10)
var ci = ga.getValue("cmdb_ci")
var category = ga.getValue("category")
var subcategory = ga.getValue("subcategory")
// Check if problem already exists
if (!problemExistsForPattern(ci, category, subcategory)) {
patterns.push({
cmdb_ci: ci,
ci_name: ga.cmdb_ci.getDisplayValue(),
category: category,
subcategory: subcategory,
incident_count: count,
})
gs.info(
LOG_PREFIX +
"Pattern found: " +
count +
" incidents for CI " +
ga.cmdb_ci.getDisplayValue() +
" (" +
category +
"/" +
subcategory +
")",
)
}
}
return patterns
}
function problemExistsForPattern(ci, category, subcategory) {
var problem = new GlideRecord("problem")
problem.addQuery("cmdb_ci", ci)
problem.addQuery("category", category)
problem.addQuery("subcategory", subcategory)
problem.addQuery("state", "NOT IN", "5,6,7") // Not resolved/closed/cancelled
problem.query()
return problem.hasNext()
}
根本原因分析(ES5)
RCA 工作流
// Start RCA process (ES5 ONLY!)
function startRootCauseAnalysis(problemSysId) {
var problem = new GlideRecord("problem")
if (!problem.get(problemSysId)) {
return false
}
// Move to RCA state
problem.setValue("state", 3) // Root Cause Analysis
problem.update()
// Create RCA tasks
var tasks = [
{ title: "Gather incident data", order: 100 },
{ title: "Interview stakeholders", order: 200 },
{ title: "Review system logs", order: 300 },
{ title: "Identify contributing factors", order: 400 },
{ title: "Document root cause", order: 500 },
]
for (var i = 0; i < tasks.length; i++) {
createProblemTask(problemSysId, tasks[i])
}
return true
}
function createProblemTask(problemSysId, taskData) {
var task = new GlideRecord("problem_task")
task.initialize()
task.setValue("problem", problemSysId)
task.setValue("short_description", taskData.title)
task.setValue("order", taskData.order)
task.setValue("state", 1) // New
return task.insert()
}
记录根本原因
// Document RCA findings (ES5 ONLY!)
function documentRootCause(problemSysId, rcaData) {
var problem = new GlideRecord("problem")
if (!problem.get(problemSysId)) {
return false
}
// Set root cause fields
problem.setValue("cause_notes", rcaData.rootCause)
problem.setValue("u_contributing_factors", rcaData.contributingFactors)
// 5 Whys analysis
if (rcaData.fiveWhys) {
problem.setValue("u_five_whys", JSON.stringify(rcaData.fiveWhys))
}
// Set root cause category
problem.setValue("u_root_cause_category", rcaData.category)
// Document fix
problem.setValue("fix_notes", rcaData.proposedFix)
// Move to Fix in Progress if ready
if (rcaData.readyToFix) {
problem.setValue("state", 4) // Fix in Progress
}
problem.update()
// Add to work notes
problem.work_notes =
"Root Cause Documented:\n" +
"-------------------\n" +
rcaData.rootCause +
"\n\n" +
"Proposed Fix:\n" +
"-------------\n" +
rcaData.proposedFix
problem.update()
return true
}
已知错误数据库(ES5)
创建已知错误
// Create Known Error from Problem (ES5 ONLY!)
function createKnownError(problemSysId, workaroundInfo) {
var problem = new GlideRecord("problem")
if (!problem.get(problemSysId)) {
return null
}
// Create Known Error
var ke = new GlideRecord("known_error")
ke.initialize()
// Copy from problem
ke.setValue("short_description", problem.getValue("short_description"))
ke.setValue("description", problem.getValue("description"))
ke.setValue("cause_notes", problem.getValue("cause_notes"))
ke.setValue("cmdb_ci", problem.getValue("cmdb_ci"))
ke.setValue("category", problem.getValue("category"))
ke.setValue("subcategory", problem.getValue("subcategory"))
// Workaround
ke.setValue("workaround", workaroundInfo.description)
ke.setValue("u_workaround_effectiveness", workaroundInfo.effectiveness)
// Link to problem
ke.setValue("problem", problemSysId)
// Set state
ke.setValue("state", "published")
var keSysId = ke.insert()
// Update problem with known error link
problem.setValue("known_error", keSysId)
problem.setValue("known_error_state", "accepted")
problem.update()
return {
sys_id: keSysId,
number: ke.getValue("number"),
}
}
搜索已知错误
// Search KEDB for matching workarounds (ES5 ONLY!)
var KEDBSearch = Class.create()
KEDBSearch.prototype = {
initialize: function () {},
/**
* Find matching known errors for an incident
*/
findForIncident: function (incidentSysId) {
var incident = new GlideRecord("incident")
if (!incident.get(incidentSysId)) {
return []
}
var matches = []
// Search by CI
if (incident.cmdb_ci) {
var ciMatches = this._searchByCI(incident.getValue("cmdb_ci"))
matches = matches.concat(ciMatches)
}
// Search by category
var catMatches = this._searchByCategory(incident.getValue("category"), incident.getValue("subcategory"))
matches = matches.concat(catMatches)
// Search by keywords
var keywordMatches = this._searchByKeywords(incident.getValue("short_description"))
matches = matches.concat(keywordMatches)
// Deduplicate
return this._deduplicate(matches)
},
_searchByCI: function (ciSysId) {
var results = []
var ke = new GlideRecord("known_error")
ke.addQuery("cmdb_ci", ciSysId)
ke.addQuery("state", "published")
ke.query()
while (ke.next()) {
results.push(this._toObject(ke, "CI Match"))
}
return results
},
_searchByCategory: function (category, subcategory) {
var results = []
var ke = new GlideRecord("known_error")
ke.addQuery("category", category)
if (subcategory) {
ke.addQuery("subcategory", subcategory)
}
ke.addQuery("state", "published")
ke.query()
while (ke.next()) {
results.push(this._toObject(ke, "Category Match"))
}
return results
},
_searchByKeywords: function (description) {
var results = []
var keywords = description.split(" ").filter(function (w) {
return w.length > 3
})
var ke = new GlideRecord("known_error")
ke.addQuery("state", "published")
var qc = null
for (var i = 0; i < keywords.length && i < 5; i++) {
if (qc === null) {
qc = ke.addQuery("short_description", "CONTAINS", keywords[i])
} else {
qc.addOrCondition("short_description", "CONTAINS", keywords[i])
}
}
ke.query()
while (ke.next()) {
results.push(this._toObject(ke, "Keyword Match"))
}
return results
},
_toObject: function (gr, matchType) {
return {
sys_id: gr.getUniqueValue(),
number: gr.getValue("number"),
title: gr.getValue("short_description"),
workaround: gr.getValue("workaround"),
matchType: matchType,
}
},
_deduplicate: function (matches) {
var seen = {}
var unique = []
for (var i = 0; i < matches.length; i++) {
if (!seen[matches[i].sys_id]) {
seen[matches[i].sys_id] = true
unique.push(matches[i])
}
}
return unique
},
type: "KEDBSearch",
}
问题解决(ES5)
解决问题
// Resolve problem with fix (ES5 ONLY!)
function resolveProblem(problemSysId, resolutionData) {
var problem = new GlideRecord("problem")
if (!problem.get(problemSysId)) {
return { success: false, message: "Problem not found" }
}
// Validate
if (!resolutionData.fixNotes) {
return { success: false, message: "Fix notes required" }
}
// Update problem
problem.setValue("state", 5) // Resolved
problem.setValue("fix_notes", resolutionData.fixNotes)
problem.setValue("resolution_code", resolutionData.resolutionCode)
problem.setValue("resolved_at", new GlideDateTime())
problem.setValue("resolved_by", gs.getUserID())
// Link to change if permanent fix deployed
if (resolutionData.changeRequest) {
problem.setValue("rfc", resolutionData.changeRequest)
}
problem.update()
// Update related Known Error if exists
if (problem.known_error) {
var ke = new GlideRecord("known_error")
if (ke.get(problem.getValue("known_error"))) {
ke.setValue("state", "closed")
ke.setValue("u_permanent_fix", resolutionData.fixNotes)
ke.update()
}
}
// Notify incident owners
notifyLinkedIncidents(problemSysId, "Problem resolved: " + problem.getValue("number"))
return {
success: true,
number: problem.getValue("number"),
}
}
function notifyLinkedIncidents(problemSysId, message) {
var incident = new GlideRecord("incident")
incident.addQuery("problem_id", problemSysId)
incident.addQuery("active", true)
incident.query()
while (incident.next()) {
incident.work_notes = message
incident.update()
}
}
MCP 工具集成
可用工具
| 工具 | 用途 |
|---|---|
snow_query_table | 查询问题和已知错误 |
snow_artifact_manage (action=find) | 查找相关开发工件 |
snow_execute_script | 测试问题脚本 |
snow_create_business_rule | 创建问题自动化 |
示例工作流
// 1. Find open problems
await snow_query_table({
table: "problem",
query: "active=true",
fields: "number,short_description,state,assignment_group",
})
// 2. Search KEDB
await snow_query_table({
table: "known_error",
query: "state=published^short_descriptionLIKEemail",
fields: "number,short_description,workaround",
})
// 3. Find recurring incidents for proactive problems
await snow_execute_script({
script: `
var patterns = identifyProactiveProblems();
gs.info('Patterns found: ' + patterns.length);
`,
})
最佳实践
- 事件关联 - 关联所有相关事件
- 根本原因优先 - 查找实际原因,而非症状
- 5 个为什么技巧 - 深入追查根本原因
- 已知错误 - 记录临时解决方法
- 永久修复 - 关联变更请求
- 指标 - 跟踪问题解决时间
- 主动识别 - 在升级前发现规律
- 仅限 ES5 - 不使用现代 JavaScript 语法
评论
支持 Markdown