Files
inquiry_robot/deploy/Watch-Agent8910.ps1
T

213 lines
7.8 KiB
PowerShell
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 只读:拉服务器新系统 8910 的「业务/错误」日志,并同时落到本机 logs/。
# 用法:
# powershell -NoProfile -ExecutionPolicy Bypass -File .\deploy\Watch-Agent8910.ps1
# powershell -NoProfile -ExecutionPolicy Bypass -File .\deploy\Watch-Agent8910.ps1 -Once
# 双击 Watch-Agent8910.bat / Watch-Agent8910-Once.bat
#
# 本机落盘(仓库根 logs/,按当天日期):
# logs/20260913_info.log — 业务信息(入队、reply=、发送成功等)
# logs/20260913_error.log — 告警/异常(WARNING、ERROR、Traceback、errcode 等)
#
# 乱码:默认 UTF-8;仍乱码可加 -Gbk。
# SSH:默认密钥 ~/.ssh/id_ed25519_ytd;可用 YTD_SSH_KEY / YTD_SSH_HOST 覆盖。
[CmdletBinding()]
param(
[switch]$Once,
[switch]$All,
[switch]$Access,
[switch]$Gbk,
[switch]$NoLocalLog,
[int]$Tail = 120
)
$ErrorActionPreference = 'Stop'
$SshKey = if ($env:YTD_SSH_KEY) { $env:YTD_SSH_KEY } else { Join-Path $env:USERPROFILE '.ssh\id_ed25519_ytd' }
$SshHost = if ($env:YTD_SSH_HOST) { $env:YTD_SSH_HOST } else { 'ytd_extdev_0903@129.211.181.80' }
$LogOut = 'E:\wwwroot\logs\agent-http.out.log'
$LogErr = 'E:\wwwroot\logs\agent-http.err.log'
$IncomingRemotePath = 'C:/Users/ytd_extdev_0903/incoming/_watch_agent8910.ps1'
$IncomingLocalRun = 'C:\Users\ytd_extdev_0903\incoming\_watch_agent8910.ps1'
$Pattern = '回调|inject-text|inject:|outbox|inbox|errcode|errmsg|60020|ERROR|WARNING|Traceback|Exception|ModuleNotFoundError|sender=|touser=|crypto|ChannelRuntime|dispatch intent|企微发送|验签|解密|reply='
if (-not (Test-Path $SshKey)) {
throw "缺少 SSH 密钥: $SshKey"
}
try {
chcp 65001 | Out-Null
[Console]::InputEncoding = [System.Text.UTF8Encoding]::new($false)
[Console]::OutputEncoding = [System.Text.UTF8Encoding]::new($false)
$OutputEncoding = [System.Text.UTF8Encoding]::new($false)
} catch {
Write-Host 'warn: 无法设置本机控制台 UTF-8'
}
# 仓库根/logs(本脚本在 deploy/ 下)
$RepoRoot = Split-Path -Parent $PSScriptRoot
$LocalLogDir = Join-Path $RepoRoot 'logs'
$Day = Get-Date -Format 'yyyyMMdd'
$LocalInfoLog = Join-Path $LocalLogDir ($Day + '_info.log')
$LocalErrorLog = Join-Path $LocalLogDir ($Day + '_error.log')
$Utf8NoBom = New-Object System.Text.UTF8Encoding $false
if (-not $NoLocalLog) {
New-Item -ItemType Directory -Force -Path $LocalLogDir | Out-Null
$sessionHdr = '===== watch start ' + (Get-Date).ToString('yyyy-MM-dd HH:mm:ss') + ' host=' + $SshHost + ' once=' + $Once.IsPresent + ' ====='
[System.IO.File]::AppendAllText($LocalInfoLog, $sessionHdr + [Environment]::NewLine, $Utf8NoBom)
[System.IO.File]::AppendAllText($LocalErrorLog, $sessionHdr + [Environment]::NewLine, $Utf8NoBom)
}
# 写入本地 *_error.log 的判定(其余进 *_info.log)
# 注意:不能用裸 ERROR(-match 不区分大小写,会误伤 “error.log” 说明行)
function Write-LocalLine([string]$line) {
Write-Host $line
if ($NoLocalLog) { return }
# 脚本自身说明行一律进 info,避免关键字误伤
if ($line -match '^(目标:|主日志|访问日志|日志编码|过滤:|本机落盘:|说明:|---+|=====)') {
$isErr = $false
} else {
$isErr = ($line -cmatch '\] WARNING |\] ERROR |Traceback|Exception|ModuleNotFoundError') -or
($line -match 'errcode=\d+|errmsg=.+|health_fail|MISSING |解密失败|验签/解密失败|relation "agent_inbox"|not allow to access from your ip')
}
$target = if ($isErr) { $LocalErrorLog } else { $LocalInfoLog }
try {
[System.IO.File]::AppendAllText($target, $line + [Environment]::NewLine, $Utf8NoBom)
} catch {
Write-Host ('warn: 写本地日志失败: ' + $_.Exception.Message)
}
}
Write-LocalLine ("目标: $SshHost")
Write-LocalLine ("主日志(业务/错误): $LogErr")
if ($Access -or $All) { Write-LocalLine ("访问日志: $LogOut") }
Write-LocalLine ("日志编码: " + $(if ($Gbk) { 'Default/GBK' } else { 'UTF-8' }))
if ($All) {
Write-LocalLine '过滤: 关闭'
} else {
Write-LocalLine ("过滤: $Pattern")
}
if (-not $NoLocalLog) {
Write-LocalLine ("本机落盘: $LocalInfoLog")
Write-LocalLine ("本机落盘: $LocalErrorLog")
}
Write-LocalLine '说明: 企微气泡里的 [test] 回复若已出现=发送成功;日志里看 reply= 字段'
Write-LocalLine '---'
$waitLiteral = if ($Once) { '$false' } else { '$true' }
$allLiteral = if ($All) { '$true' } else { '$false' }
$accessLiteral = if ($Access) { '$true' } else { '$false' }
$encName = if ($Gbk) { 'Default' } else { 'UTF8' }
$remoteScript = @"
`$ErrorActionPreference = 'Continue'
try {
chcp 65001 | Out-Null
[Console]::OutputEncoding = [System.Text.UTF8Encoding]::new(`$false)
`$OutputEncoding = [System.Text.UTF8Encoding]::new(`$false)
} catch {}
`$out = '$LogOut'
`$err = '$LogErr'
`$tail = $Tail
`$wait = $waitLiteral
`$all = $allLiteral
`$wantAccess = $accessLiteral
`$pat = '$Pattern'
`$enc = '$encName'
Write-Output '=== remote health 8910 ==='
try {
`$h = Invoke-WebRequest -Uri 'http://127.0.0.1:8910/health' -UseBasicParsing -TimeoutSec 5
Write-Output `$h.Content
} catch {
Write-Output ('health_fail: ' + `$_.Exception.Message)
}
Write-Output '=== business/error log (err) ==='
function Show-Err([string]`$line) {
if (`$all) { Write-Output ('[err] ' + `$line); return }
if (`$line -match `$pat) { Write-Output ('[err] ' + `$line) }
}
function Show-Out([string]`$line) {
if (-not `$wantAccess -and -not `$all) { return }
if (`$all) { Write-Output ('[out] ' + `$line); return }
if (`$line -match '/wecom/callback|inject-text') {
Write-Output ('[out] ' + `$line)
}
}
if (-not (Test-Path `$err)) { Write-Output ('MISSING ' + `$err) }
if (-not `$wait) {
if (Test-Path `$err) {
Get-Content -Path `$err -Tail `$tail -Encoding `$enc -ErrorAction SilentlyContinue | ForEach-Object { Show-Err `$_ }
}
if (`$wantAccess -or `$all) {
Write-Output '=== access log (out) ==='
if (Test-Path `$out) {
Get-Content -Path `$out -Tail `$tail -Encoding `$enc -ErrorAction SilentlyContinue | ForEach-Object { Show-Out `$_ }
}
}
exit 0
}
Write-Output 'following err.log ... (Ctrl+C to stop)'
`$watch = @()
if (Test-Path `$err) { `$watch += `$err }
if ((`$wantAccess -or `$all) -and (Test-Path `$out)) { `$watch += `$out }
if (`$watch.Count -eq 0) { throw 'no log files' }
Get-Content -Path `$watch -Tail `$tail -Wait -Encoding `$enc -ErrorAction SilentlyContinue | ForEach-Object {
`$line = `$_
if (`$line -match '^\s*INFO:\s+\S+\s+-\s+"(GET|POST|PUT|DELETE)') {
Show-Out `$line
} else {
Show-Err `$line
}
}
"@
$localTemp = Join-Path $env:TEMP 'ytd-watch-agent8910-remote.ps1'
[System.IO.File]::WriteAllText($localTemp, $remoteScript, $Utf8NoBom)
& scp.exe -o BatchMode=yes -o ConnectTimeout=12 -i $SshKey $localTemp "${SshHost}:${IncomingRemotePath}"
if ($LASTEXITCODE -ne 0) { throw "scp failed: $LASTEXITCODE" }
# 用 Process + UTF-8 读 SSH 输出,避免管道把中文解错再写入本地日志
$sshArgs = @(
'-o', 'BatchMode=yes',
'-o', 'ConnectTimeout=12',
'-i', $SshKey,
$SshHost,
"powershell -NoProfile -ExecutionPolicy Bypass -File `"$IncomingLocalRun`""
)
$psi = New-Object System.Diagnostics.ProcessStartInfo
$psi.FileName = 'ssh.exe'
$psi.Arguments = ($sshArgs | ForEach-Object {
if ($_ -match '\s') { '"' + $_ + '"' } else { $_ }
}) -join ' '
$psi.UseShellExecute = $false
$psi.RedirectStandardOutput = $true
$psi.RedirectStandardError = $true
$psi.CreateNoWindow = $true
$psi.StandardOutputEncoding = [System.Text.Encoding]::UTF8
$psi.StandardErrorEncoding = [System.Text.Encoding]::UTF8
$proc = New-Object System.Diagnostics.Process
$proc.StartInfo = $psi
[void]$proc.Start()
while (-not $proc.StandardOutput.EndOfStream) {
$line = $proc.StandardOutput.ReadLine()
if ($null -ne $line) { Write-LocalLine $line }
}
$errTail = $proc.StandardError.ReadToEnd()
if ($errTail) {
foreach ($eline in ($errTail -split "`r?`n")) {
if ($eline) { Write-LocalLine $eline }
}
}
$proc.WaitForExit()
exit $proc.ExitCode