关于 scheduled-jobs
面向 ServiceNow 管理者和开发者的定时任务技能,涵盖周期性脚本、批处理、维护及任务监控,并提供 ES5 示例与 MCP 工具工作流。
主要功能
- 01创建周期性脚本任务
- 02配置常见运行频率
- 03处理批量任务与条件执行
- 04查看任务历史和指标
- 05遵循 ES5 语法编写脚本
适用场景
- 01定期关闭过期记录
- 02按计划生成并发送报表
- 03批量处理大型数据集
- 04排查任务运行状态和错误
MCP 相关
文档介绍了用于创建任务、查找已有任务、测试脚本及检查执行日志的 MCP 工具,并展示创建任务的工作流。
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ serac-labs/ serac --skill scheduled-jobs安装后在 Claude Code 中输入/scheduled-jobs确认已加载。
详细介绍
ServiceNow 定时作业
定时作业可自动执行周期性任务、批处理和维护操作。
作业类型
| 类型 | 表 | 用途 |
|---|---|---|
| Scheduled Script Execution | sysauto_script | 运行自定义脚本 |
| Report Scheduler | sysauto_report | 生成并通过电子邮件发送报告 |
| Table Cleaner | sys_auto_flush | 删除旧记录 |
| LDAP Refresh | ldap_server_config | 同步 LDAP 数据 |
| Discovery | discovery_schedule | 网络发现 |
Scheduled Script Execution(ES5)
基本定时作业
// Table: sysauto_script
// Name: Close Stale Incidents
// Run: Daily at 2:00 AM
// Script (ES5 ONLY!):
;(function executeScheduledJob() {
var LOG_PREFIX = "[CloseStaleIncidents] "
var closedCount = 0
// Find incidents inactive for 30 days
var staleDate = new GlideDateTime()
staleDate.addDaysLocalTime(-30)
var gr = new GlideRecord("incident")
gr.addQuery("state", "IN", "1,2,3") // New, In Progress, On Hold
gr.addQuery("sys_updated_on", "<", staleDate)
gr.addQuery("active", true)
gr.query()
gs.info(LOG_PREFIX + "Found " + gr.getRowCount() + " stale incidents")
while (gr.next()) {
gr.state = 7 // Closed
gr.close_code = "Closed/Resolved by Caller"
gr.close_notes = "Auto-closed due to 30 days of inactivity"
gr.update()
closedCount++
}
gs.info(LOG_PREFIX + "Closed " + closedCount + " stale incidents")
})()
带错误处理的定时作业(ES5)
// Name: Sync User Data
// Run: Every 6 hours
;(function executeScheduledJob() {
var LOG_PREFIX = "[SyncUserData] "
var stats = {
processed: 0,
updated: 0,
errors: 0,
}
try {
// Get users needing sync
var gr = new GlideRecord("sys_user")
gr.addQuery("u_needs_sync", true)
gr.addQuery("active", true)
gr.setLimit(1000) // Process in batches
gr.query()
while (gr.next()) {
stats.processed++
try {
var updated = syncUserFromSource(gr)
if (updated) {
stats.updated++
}
} catch (e) {
stats.errors++
gs.error(LOG_PREFIX + "Error syncing user " + gr.user_name + ": " + e.message)
}
}
gs.info(LOG_PREFIX + "Sync complete: " + JSON.stringify(stats))
// Send summary email if errors
if (stats.errors > 0) {
sendErrorSummary(stats)
}
} catch (e) {
gs.error(LOG_PREFIX + "Job failed: " + e.message)
notifyAdmins("User sync job failed: " + e.message)
}
function syncUserFromSource(userGr) {
// Sync logic here
userGr.u_needs_sync = false
userGr.u_last_sync = new GlideDateTime()
return userGr.update()
}
function sendErrorSummary(stats) {
gs.eventQueue("user.sync.errors", null, JSON.stringify(stats), "")
}
function notifyAdmins(message) {
gs.eventQueue("system.job.failure", null, message, "")
}
})()
批处理作业(ES5)
// Name: Process Large Dataset
// Run: Weekly on Sunday at 1:00 AM
;(function executeScheduledJob() {
var LOG_PREFIX = "[BatchProcessor] "
var BATCH_SIZE = 500
var MAX_RUNTIME = 3600000 // 1 hour in ms
var startTime = new Date().getTime()
var processed = 0
var hasMore = true
while (hasMore && !isTimeExceeded()) {
hasMore = processBatch()
}
if (hasMore) {
gs.warn(LOG_PREFIX + "Job stopped due to time limit. Processed: " + processed)
// Re-queue for next run
queueContinuation()
} else {
gs.info(LOG_PREFIX + "Job complete. Total processed: " + processed)
}
function processBatch() {
var gr = new GlideRecord("u_large_table")
gr.addQuery("u_processed", false)
gr.setLimit(BATCH_SIZE)
gr.query()
if (!gr.hasNext()) {
return false
}
while (gr.next()) {
processRecord(gr)
processed++
}
return true
}
function processRecord(gr) {
// Processing logic
gr.u_processed = true
gr.u_processed_date = new GlideDateTime()
gr.update()
}
function isTimeExceeded() {
var elapsed = new Date().getTime() - startTime
return elapsed > MAX_RUNTIME
}
function queueContinuation() {
// Queue another run
var job = new GlideRecord("sysauto_script")
if (job.get("name", "Process Large Dataset - Continuation")) {
job.next_action = new GlideDateTime()
job.update()
}
}
})()
定时配置
运行频率
| 频率 | Cron | 示例 |
|---|---|---|
| 每 5 分钟 | 0 */5 * * * ? | 健康检查 |
| 每小时 | 0 0 * * * ? | 数据同步 |
| 每天午夜 | 0 0 0 * * ? | 清理 |
| 每周日 | 0 0 0 ? * SUN | 报告 |
| 每月 1 日 | 0 0 0 1 * ? | 账单处理 |
| 自定义 | Various | 特定需求 |
创建定时作业(ES5)
// Create scheduled job programmatically (ES5 ONLY!)
var job = new GlideRecord("sysauto_script")
job.initialize()
job.setValue("name", "Nightly Cleanup")
job.setValue("active", true)
// Schedule: Daily at 2:00 AM
job.setValue("run_type", "daily")
job.setValue("run_time", "02:00:00")
// Or use explicit schedule
job.setValue("run_dayofweek", "daily")
// Script
job.setValue(
"script",
"(function executeScheduledJob() {\n" +
' var gr = new GlideRecord("sys_audit_delete");\n' +
' gr.addQuery("sys_created_on", "<", gs.daysAgo(90));\n' +
" gr.deleteMultiple();\n" +
' gs.info("Cleanup complete");\n' +
"})();",
)
// Run as system
job.setValue("run_as", "") // Empty = System
job.insert()
条件执行
// Job that checks conditions before running (ES5 ONLY!)
;(function executeScheduledJob() {
var LOG_PREFIX = "[ConditionalJob] "
// Check if job should run
if (!shouldRun()) {
gs.info(LOG_PREFIX + "Skipping execution - conditions not met")
return
}
// Execute main logic
executeMainTask()
function shouldRun() {
// Check business hours
var now = new GlideDateTime()
var hour = parseInt(now.getLocalTime().getByFormat("HH"), 10)
// Only run outside business hours (before 6am or after 8pm)
if (hour >= 6 && hour < 20) {
return false
}
// Check for active change freeze
var freeze = new GlideRecord("change_request")
freeze.addQuery("type", "freeze")
freeze.addQuery("state", "implement")
freeze.query()
if (freeze.hasNext()) {
gs.info(LOG_PREFIX + "Change freeze active")
return false
}
return true
}
function executeMainTask() {
// Main job logic here
gs.info(LOG_PREFIX + "Executing main task")
}
})()
作业监控
检查作业状态(ES5)
// Query scheduled job history (ES5 ONLY!)
var history = new GlideRecord("sys_trigger")
history.addQuery("name", "CONTAINS", "Nightly Cleanup")
history.orderByDesc("sys_created_on")
history.setLimit(10)
history.query()
while (history.next()) {
gs.info(
"Job: " +
history.getValue("name") +
" | State: " +
history.getValue("state") +
" | Next: " +
history.getValue("next_action"),
)
}
带指标的作业(ES5)
// Job that records performance metrics (ES5 ONLY!)
;(function executeScheduledJob() {
var LOG_PREFIX = "[MetricsJob] "
var startTime = new Date().getTime()
var metrics = {
startTime: new GlideDateTime().getDisplayValue(),
recordsProcessed: 0,
errors: 0,
}
try {
// Main processing
var gr = new GlideRecord("incident")
gr.addQuery("active", true)
gr.query()
while (gr.next()) {
processRecord(gr)
metrics.recordsProcessed++
}
} catch (e) {
metrics.errors++
gs.error(LOG_PREFIX + "Error: " + e.message)
} finally {
// Record metrics
metrics.endTime = new GlideDateTime().getDisplayValue()
metrics.duration = (new Date().getTime() - startTime) / 1000
recordMetrics(metrics)
}
function processRecord(gr) {
// Processing logic
}
function recordMetrics(metrics) {
var metricsRecord = new GlideRecord("u_job_metrics")
metricsRecord.initialize()
metricsRecord.setValue("u_job_name", "MetricsJob")
metricsRecord.setValue("u_start_time", metrics.startTime)
metricsRecord.setValue("u_end_time", metrics.endTime)
metricsRecord.setValue("u_duration", metrics.duration)
metricsRecord.setValue("u_records_processed", metrics.recordsProcessed)
metricsRecord.setValue("u_errors", metrics.errors)
metricsRecord.insert()
gs.info(LOG_PREFIX + "Metrics: " + JSON.stringify(metrics))
}
})()
MCP 工具集成
可用工具
| 工具 | 用途 |
|---|---|
snow_schedule_job | 创建定时作业 |
snow_artifact_manage (action=find) | 查找现有作业 |
snow_execute_script | 测试作业脚本 |
snow_get_logs | 查看作业执行日志 |
工作流示例
// 1. Create scheduled job
await snow_schedule_job({
name: "Daily Report Generator",
run_type: "daily",
run_time: "06:00:00",
script: "/* report generation script */",
active: true,
})
// 2. Test the script
await snow_execute_script({
script: "/* test job script */",
})
// 3. Check logs
await snow_get_logs({
filter: 'message CONTAINS "Daily Report"',
limit: 50,
})
最佳实践
- 日志记录 - 记录详尽日志,便于调试
- 错误处理 - 使用 try-catch 并发送通知
- 批处理 - 分批处理大型数据集
- 时间限制 - 检查运行时间,防止超时
- 非高峰时段 - 安排在使用率较低的时段运行
- 幂等性 - 多次运行也安全
- 监控 - 记录指标和状态
- 仅限 ES5 - 不使用现代 JavaScript 语法
评论
支持 Markdown