この投稿は「UiPath Advent Calendar 2024」の7日目のエントリです。
長時間実行中のジョブ
OCのジョブ管理で「ジョブが実行中のままで、手動で停止する」という時があります。
「気付くまでに時間がかかると、後続ジョブのリカバリ対応に追われてしまうため、なるべく早く気づきたい!」という悩みを「APIを使って定期監視することで解決しよう」という話です。
どういう時に、長時間実行になるのか?
そもそも「アクティビティに適切なタイムアウトが設定されていれば、タイムアウトエラーで終了する」はずですが、エラーにならずに実行中のままになることがあります。例えば、
- ミリ秒のはずが秒で指定していた(指定ミス。待機時間が1000倍に)
- アクティビティによってはレアなタイミングで、タイムアウト設定が無視されてずっと待機状態になる
いずれも、意図しないケースです。
プロセス実行には「ジョブ開始、トリガー開始、Appsからの実行」などがありますが、トリガーには 「ジョブの実行を終了するスケジュールを設定」 という「開始から一定時間経過で、自動停止させる機能」があります。
逆に言うと、トリガー以外では、この「長時間動いている場合は自動停止させる」という機能がありません。例えば、OCから手動で再実行した場合や、UiPathAppsの「プロセスを開始」で実行したジョブは、この時限制約が無く、無限に動き続けます。
ちなみに「Apps経由実行」はその性質上「業務ユーザーに開放している」ことが多く、管理者の監視下にないため、気付くのが遅れてしまいがち。
なので「Apps経由実行」を実現すると、この「長時間実行を検知したい」という課題にぶつかります。
APIで定期的に監視する
長時間実行を監視する方法として、例えば、30分おきにAPIで実行中のジョブを監視して、「2時間以上 実行中 のジョブがないか」をチェックする方法があります。
今回は、どこかのマシンからPowerShellでAPI呼び出しをする想定で実装します。タスクスケジューラーで一定時間おきに実行するように設定すれば、定期監視の出来上がりです。
どこかで勝手に実行して欲しい!という場合は、クラウド契約してサーバーレスで実行かと思いますが、個人的には、PowerAutomateの「スケジュール済みクラウド フロー」で実行するのがオススメです。(安い&作りやすい&他にも色々使い道がある)
コードサンプル
以下は「開始から120分以上経過したジョブがあれば、Slackに通知する」PowerShellのコードです。
# テナントURL
$baseUrl = 'https://cloud.uipath.com/{個別のID}/{テナント名}'
# プライベートアクセストークン
$token = 'rt_・・・・・・(AutomationCloudで取得できます)'
# フォルダー
$ary_folder = @()
$ary_folder += New-Object PSObject -Property @{Id = "6127328"; Name = "管理部"}
$ary_folder += New-Object PSObject -Property @{Id = "6127329"; Name = "マーケ部"}
# しきい値(開始からN分以上経過したジョブがあれば通知)
$th_Minutes = 120
# Slack投稿
$slack_channel = "#uipath"
$slack_token = "xoxb-・・・・・・"
# 実行中ジョブの取得
$ary_job_running = @()
$url = $baseUrl + '/orchestrator_/odata/Jobs?$filter=State%20eq%20''Running''' # 実行中のみ
foreach($folder in $ary_folder){
$headers = @{"Authorization" = "Bearer " + $token; "X-UIPATH-OrganizationUnitId" = $folder.id}
$response = Invoke-RestMethod -Uri $url -Method Get -Headers $headers -ContentType "application/json"
if ($response.StatusCode -lt 300){
foreach($dat in $response.value){
$item = New-Object PSObject -Property @{
StartTime = (Get-Date -Date $dat.StartTime).AddHours(0).ToString("yyyy/MM/dd HH:mm:ss")
LapseTime = (Get-Date -Date "2000/01/01 00:00:00").AddSeconds((New-TimeSpan -Start $dat.StartTime -End $now).TotalSeconds).ToString("HH:mm:ss")
LapseMinutes = [Math]::Floor((New-TimeSpan -Start $dat.StartTime -End (Get-Date)).TotalMinutes)
State = $dat.State
MachineName = $dat.HostMachineName
UnitName = $folder.Name
Name = if($dat.SourceType -eq "Schedule"){ $dat.Source }else{ $dat.ReleaseName } # 実行元が「Schedule(タイムトリガー)」なら実行名
SourceType = $dat.SourceType
}
$ary_job_running += $item
}
}else{
Write-Host $response.StatusCode
Write-Host $response.StatusDescription
Throw 'OC-API 呼び出しエラー'
}
Write-Host "[GetJob]Sleep - 5 Seconds...(Optional delay between requests)"
Sleep 5
}
Write-Host ("[Getjob]Result - data count: " + $ary_job_running.count)
$ary_job_running = $ary_job_running | Select-Object "StartTime","LapseTime","LapseMinutes","State","MachineName","UnitName","SourceType","Name"
$ary_job_running | Format-Table
# N分以上経過に絞り込み
$ary_alert = ""
foreach($job in $ary_job_running){
if($job.LapseMinutes -ge $th_Minute){
$ary_alert += $job.StartTime + " 開始 (" + $job.LapseMinutes + "分経過) " + $job.UnitName + "「" + $job.Name + "」@" + $job.MachineName
}
}
Write-Host ("[Getjob]Filter - data count: " + $ary_alert.count)
# Slack投稿
if($ary_alert.count -gt 0){
$message = @{channel = $slack_channel; text = ":alarm_clock: 長時間実行中ジョブ検出 ※ 閾値:" + $th_Minute + "分\n" + $ary_alert -join '\n'}
$response = Invoke-WebRequest -Uri "https://slack.com/api/chat.postMessage" `
-Method Post `
-ContentType "application/json" `
-Headers @{Authorization = "Bearer $slack_token"} `
-Body ($message | ConvertTo-Json)
$response = Invoke-RestMethod -Uri $url -Method Get -Headers $headers -ContentType "application/json"
Write-Host ("[SlackPost]Response - StatusCode: " + $response.StatusCode)
if ($response.StatusCode -lt 300){
Write-Host ("[SlackPost]Response - Content: " + $response.Content)
}else{
Write-Host $response.StatusCode
Write-Host $response.StatusDescription
Throw 'SlackAPI 呼び出しエラー'
}
}
OCのAPI「odata/Jobs」から実行中のジョブを取得し、実行開始から一定期間経過したジョブがあれば、Slackに通知しています。
(私はもう少し手を加えて、プロセス名で保守担当を割り出して、メンションをつけて通知する形で運用しています。通知が飛んでくれば 早く気付けるので)
実行結果・出力内容
上記コードを実行すると、長時間実行中のジョブがあれば、指定のSlackチャンネルに通知が投稿されます。
終わりに
以上、APIで長時間実行ジョブを検出する方法の紹介でした。
OCありの現場で「RPAの本数を増やしたい・規模を拡大したい」場合、無いと困る機能なので、なにかの参考になれば幸いです。(UiPathの標準機能であれば良いんですけどね。)