Files
upstream-tldraw/internal/scripts/fetch-fly-logs.ts
upstream-mirror a774ac673a
Some checks failed
Deploy analytics worker / Deploy analytics worker to ${{ (github.ref == 'refs/heads/production' && 'production') || (github.ref == 'refs/heads/main' && 'staging') || 'preview' }} (push) Failing after 1s
i18n upload strings / Upload latest strings (push) Failing after 1s
Deploy bemo / Deploy bemo to ${{ (github.ref == 'refs/heads/bemo-production' && 'production') || (github.ref == 'refs/heads/main' && 'staging') || 'preview' }} (push) Failing after 1s
Deploy grafana resources / ${{ github.event_name == 'push' && 'Deploy grafana resources' || 'Validate grafana resources (dry run)' }} (push) Failing after 0s
End to end tests (dotcom) / Check for relevant changes (push) Failing after 0s
End to end tests (examples) / Check for relevant changes (push) Failing after 0s
End to end tests (mermaid readability) / Check for relevant changes (push) Failing after 1s
End to end tests (perf) / Check for relevant changes (push) Failing after 0s
Publish editor extensions / Publish editor extensions (push) Failing after 1s
Publish SDK packages / Remove the publish-packages label (push) Has been skipped
Checks / Tests & checks (push) Has been cancelled
Checks / Build all projects (push) Has been cancelled
Deploy templates / Deploy templates (push) Has been cancelled
End to end tests (dotcom) / End to end tests (dotcom) (push) Has been cancelled
End to end tests (mermaid readability) / End to end tests (mermaid readability) (push) Has been cancelled
End to end tests (perf) / End to end tests (perf) (push) Has been cancelled
Deploy .com / Deploy dotcom to ${{ (github.ref == 'refs/heads/production' && 'production') || (github.ref == 'refs/heads/main' && 'staging') || 'preview' }} (push) Has been cancelled
End to end tests (examples) / End to end tests (examples) (push) Has been cancelled
Publish SDK packages / Publish SDK packages (push) Has been cancelled
Publish SDK packages / Tell user released version (push) Has been cancelled
Publish SDK packages / Publishes code templates to separate repositories (push) Has been cancelled
Publish SDK packages / Deploys the template demos with the new SDK version (push) Has been cancelled
snapshot of https://github.com/tldraw/tldraw.git (main, 2026-09-23; blobs > 131072B omitted)
2026-09-23 16:12:52 +05:00

127 lines
4.1 KiB
TypeScript

/**
* Fetch logs from Fly.io's VictoriaLogs API.
*
* Usage:
* npx tsx internal/scripts/fetch-fly-logs.ts [options]
*
* Examples:
* npx tsx internal/scripts/fetch-fly-logs.ts --app production-zero-rm --last 2h
* npx tsx internal/scripts/fetch-fly-logs.ts --app production-zero-rm --last 30m --filter "Purged"
* npx tsx internal/scripts/fetch-fly-logs.ts --app production-zero-rm --start 2026-04-15T15:00:00Z --end 2026-04-15T18:00:00Z
* npx tsx internal/scripts/fetch-fly-logs.ts --app production-zero-rm --last 1h --filter "NOT DEBUG" -o logs.jsonl
* npx tsx internal/scripts/fetch-fly-logs.ts --app production-zero-rm --last 6h --filter "Purged" --pretty
* FLY_TOKEN=fo1_xxx npx tsx internal/scripts/fetch-fly-logs.ts --app production-zero-rm --last 1h
* npx tsx internal/scripts/fetch-fly-logs.ts --token fo1_xxx --app production-zero-rm --last 1h
*
* See fly-common.ts for app names and token configuration.
*/
import { FLY_ORG_SLUG, getFlyToken, parseDuration } from './fly-common'
const BASE_URL = `https://api.fly.io/victorialogs/${FLY_ORG_SLUG}/select/logsql/query`
function parseArgs() {
const args = process.argv.slice(2)
const opts: Record<string, string> = {}
for (let i = 0; i < args.length; i++) {
if (args[i] === '--token' || args[i] === '-t') opts.token = args[++i]
else if (args[i] === '--app' || args[i] === '-a') opts.app = args[++i]
else if (args[i] === '--start') opts.start = args[++i]
else if (args[i] === '--end') opts.end = args[++i]
else if (args[i] === '--last' || args[i] === '-l') opts.last = args[++i]
else if (args[i] === '--filter' || args[i] === '-f') opts.filter = args[++i]
else if (args[i] === '--limit') opts.limit = args[++i]
else if (args[i] === '--output' || args[i] === '-o') opts.output = args[++i]
else if (args[i] === '--pretty' || args[i] === '-p') opts.pretty = 'true'
else if (args[i] === '--help' || args[i] === '-h') {
console.log(
[
'Usage: npx tsx internal/scripts/fetch-fly-logs.ts [options]',
'',
'Options:',
' --token, -t Fly API token (falls back to FLY_TOKEN env var)',
' --app, -a Fly app name (required)',
' --last, -l Duration to look back, e.g. 30m, 2h, 1d (default: 1h)',
' --start Start time (ISO 8601), overrides --last',
' --end End time (ISO 8601), defaults to now',
' --filter, -f Additional LogsQL filter, e.g. "Purged", "NOT DEBUG"',
' --limit Max lines (default: unlimited)',
' --output, -o Write to file instead of stdout',
' --pretty, -p Pretty-print: timestamp + message only',
' --help, -h Show this help',
].join('\n')
)
process.exit(0)
} else {
console.error(`Unknown option: ${args[i]}`)
process.exit(1)
}
}
if (!opts.app) {
console.error('--app is required')
process.exit(1)
}
return opts
}
async function main() {
const opts = parseArgs()
const now = new Date()
const end = opts.end ?? now.toISOString()
let start: string
if (opts.start) {
start = opts.start
} else {
const ms = parseDuration(opts.last ?? '1h', 'ms')
start = new Date(now.getTime() - ms).toISOString()
}
let query = `fly.app.name: ${opts.app}`
if (opts.filter) query += ` AND ${opts.filter}`
const params = new URLSearchParams({ query, start, end })
if (opts.limit) params.set('limit', opts.limit)
const token = getFlyToken(opts.token)
const url = `${BASE_URL}?${params}`
const res = await fetch(url, { headers: { Authorization: `FlyV1 ${token}` } })
if (!res.ok) {
console.error(`HTTP ${res.status}: ${await res.text()}`)
process.exit(1)
}
const body = await res.text()
if (!body.trim()) {
console.error('No logs found.')
process.exit(0)
}
let output: string
if (opts.pretty) {
output = body
.trim()
.split('\n')
.map((line) => {
const entry = JSON.parse(line)
return `${entry._time} ${entry._msg}`
})
.sort()
.join('\n')
} else {
output = body
}
if (opts.output) {
const fs = await import('fs')
fs.writeFileSync(opts.output, output + '\n')
const lines = output.trim().split('\n').length
console.error(`Wrote ${lines} lines to ${opts.output}`)
} else {
process.stdout.write(output + '\n')
}
}
main()