sp_SQLFlightRecorder is a lightweight, open-source, pure T-SQL stored procedure that captures safe SQL Server diagnostic snapshots and turns them into concise findings and recommendations, alongside a timeline of the discrete events recorded in the same window.
It is built for the question every DBA gets at 2 AM:
“SQL Server was slow earlier — what happened?”
sp_SQLFlightRecorder gives you a local, low-friction flight recorder for SQL Server: collect small snapshots over time, then report on blocking, waits, I/O, memory pressure, restarts, configuration signals, and collection coverage.
Developed by Ysaias Portes — Forward Thinkers Consulting, LLC.
Not related to Microsoft's flight recorders. SQL Server Analysis Services has a built-in feature called Flight Recorder (
FlightRecorderCurrent.trc), and SQL Server has an internal "black-box" trace sometimes described the same way. This project is unrelated to both. It is a stored procedure you install yourself, and it stores its data in tables you can query.
Current release: v1.1.3 (2026-08-27) — see the CHANGELOG and releases. v1.1.3 fixes a defect in which @OutputFormat = N'Markdown' rendered only one finding of however many the report produced; if you acted on a Markdown report from v1.0.0–v1.1.2, re-run it. Default, FindingsOnly, and TimelineOnly were never affected. v1.1.0 made retention operationally safe by default: Agent-capable installs create both a collector job (Collect, then Purge) and a daily purge backstop job, retention values are validated, and the repository is indexed for purge/report at scale. From v1.0.0 the output contract is frozen for v1.x: rule IDs, output columns, and the forward-only schema are "1.0 is forever" promises.
- What it does
- What the report returns
- Sample output
- Why this tool exists
- What it is not
- How it fits with other SQL Server tools
- When it is a big help
- Quick start
- Parameters
- Examples
- Scheduling collection
- Configuration and retention
- Uninstall
- Requirements
- Safety notes
- Documentation
- License
sp_SQLFlightRecorder installs one stored procedure:
dbo.sp_SQLFlightRecorderWhen you run Install, it creates local FR_* repository tables in your database. When you run Collect, it captures bounded diagnostic data. When you run Report, it reads the repository and produces findings and timeline output.
Typical workflow:
Install once.
Collect every minute.
Report when something goes wrong.
Purge old data.
Uninstall cleanly when done.
The goal is not to replace your monitoring stack. The goal is to give DBAs a simple, portable, evidence-based incident recorder that is already inside SQL Server.
Report returns two things, and they answer different questions.
Findings are the analytical output. Rules evaluate the collected snapshots and emit ranked, severity-graded conclusions — blocking chains, wait spikes, I/O latency, memory grants, plan regressions. If you read one result set, read this one.
The timeline is a chronological record of discrete, timestamped occurrences in the same window. It is a correlation aid you read alongside the findings — "the latency spike began two minutes after that configuration change" — not a narration of the findings themselves. Findings do not appear in the timeline, and they are not meant to.
The timeline records nine event types:
EventType |
Recorded from |
|---|---|
SnapshotCaptured |
Each successful Collect |
ErrorLogEvent |
Error-log entries (restart, I/O, corruption, failover, memory, high-severity) |
SchemaChange |
DDL activity |
StatsUpdate |
Statistics updates |
DeadlockObserved |
Captured deadlock graphs |
AgentJobFailed |
SQL Agent job runs that failed |
BackupStarted |
Full and differential backups (log backups excluded) |
ConfigurationChange |
Server or database setting changes |
LogReuseWaitChanged |
Transaction-log reuse-wait transitions |
AvailabilityStateChanged |
Availability-group synchronization health changes |
On a healthy server with nothing discrete happening, the timeline will contain nothing but SnapshotCaptured rows — one per collection. This is expected, not a failure, and it can happen even when the same report returns many findings.
The reason is that the two outputs draw on different evidence. The timeline's sources are discrete occurrences with a moment attached. Most rules, by contrast, evaluate sampled state and deltas — open transactions, wait-type spikes, file I/O latency, top CPU consumers — drawn from FR_Request, FR_Wait, and FR_FileStat, none of which produce timeline events. There is no single point in time to place on a timeline for "the top wait type rose over the window"; that conclusion belongs to the window, which is what a finding describes.
So a report showing thirty findings and thirty-seven SnapshotCaptured events is consistent output. An empty timeline is permitted for the same reason.
Timeline rows carry a RuleId so an event can point at a related rule — a ConfigurationChange event references FR_R0021, for instance, so you can connect the event to the finding that interprets it. The reference runs in that direction only: a finding never creates a timeline event.
A seven-hour window on a busy production instance, reported after the fact. Database names have been changed; every value, threshold, and timing below is unmodified.
EXEC dbo.sp_SQLFlightRecorder
@Mode = N'Report',
@OutputFormat = N'FindingsOnly',
@StartTime = '2026-08-24 02:00',
@EndTime = '2026-08-24 09:00';| # | Severity | Conf. | Type | Category | Rule | Database | Evidence |
|---|---|---|---|---|---|---|---|
| 1 | Critical | High | Observed | Blocking | FR_R0007_BlockingStorm |
— | Peak blocked sessions in one snapshot: 6; threshold: 5 |
| 2 | High | High | Observed | Tempdb | FR_R0008_TempdbVersionStoreGrowth |
— | Min version store: 128 KB; max: 6669440 KB; escalation threshold: 5242880 KB |
| 3 | High | Medium | Inferred | IO | FR_R0004_FileIoLatencySpike |
master |
windowLatencyMs=97.96; baselineAvgMs=23.104; floorMs=20; baselineSamples=502 |
| 4 | High | Medium | Inferred | QueryStore | FR_R0015_QueryPlanRegression |
UserScripts |
QsQueryId=124810; latestAvgUs=92465981; bestPriorAvgUs=3218773; factor>=2.00 |
| 6 | High | Medium | Inferred | QueryStore | FR_R0015_QueryPlanRegression |
ETLStaging |
QsQueryId=743889; latestAvgUs=512947; bestPriorAvgUs=39; factor>=2.00 |
| 11 | Medium | High | Observed | QueryStore | FR_R0016_TopCpuConsumerInWindow |
Operations |
QsQueryId=31281; totalCpuUs=607890292740; execCount=60; avgLogicalReads=1226132077 |
| 15 | Medium | Medium | Inferred | Waits | FR_R0003_TopWaitTypeSpike |
— | WaitType=SOS_WORK_DISPATCHER; DeltaWaitMs=3778160175; Class=Non-critical (Medium) |
| 16 | Medium | Medium | Inferred | IO | FR_R0004_FileIoLatencySpike |
msdb |
windowLatencyMs=520.55; baselineAvgMs=187.766; floorMs=20 |
| 17 | Medium | Medium | Inferred | IO | FR_R0004_FileIoLatencySpike |
SSISDB |
windowLatencyMs=198.14; baselineAvgMs=65.894; floorMs=20 |
| 26 | Informational | High | Observed | Coverage | FR_R0026_CoverageAndCapabilitySummary |
— | Snapshots=420; SkippedOrPartial=AlwaysOnState (Always On not enabled, capability-gated) |
Read in order: long-running open transactions held row versions, the tempdb version store grew from 128 KB to roughly 6.5 GB, six sessions were blocked within a single snapshot, and file I/O latency ran four times its own baseline across nine databases. None of it was still visible by the time anyone asked what had happened.
The coverage row is part of the output, not an afterthought. It reports how many snapshots the window contained and which collectors were skipped, so the absence of a finding can be told apart from the absence of evidence.
FindingsOnly returns 16 columns per finding. The first row from the run above,
shown vertically:
FindingOrdinal 1
Severity Critical
Confidence High
EvidenceType Observed
Category Blocking
RuleId FR_R0007_BlockingStorm
Title Blocking storm observed
Summary Multiple sessions were blocked within a single snapshot in the window.
Evidence Peak blocked sessions in one snapshot: 6; threshold: 5
Recommendation Consider reviewing the lead blocker and the involved transactions
only after validating which session is at the head of the chain.
DatabaseName NULL
ObjectName NULL
SessionId NULL
StartTimeUtc 2026-08-24 06:30:00.055
EndTimeUtc 2026-08-24 12:52:00.072
MoreInfo Computed from FR_Request.BlockingSessionId per snapshot. Folds the
same anchor as FR_R0001/FR_R0002. Folded 96 blocking contributor(s):
FR_R0001_ActiveBlockingChain(SessionId=204);
FR_R0002_LongRunningOpenTransaction(SessionId=52); ...
Four columns are worth calling out.
Recommendation states what to look at and what to confirm first. It does
not tell you to kill a session. The tool does not diagnose on your behalf.
Confidence and EvidenceType separate what was directly observed from what
was inferred from deltas. A blocking storm counted in a snapshot is Observed /
High; an I/O latency elevation measured against a rolling baseline is
Inferred / Medium. You can weigh them differently, because they are not the
same kind of claim.
DatabaseName, ObjectName, and SessionId are populated where the finding
is scoped to one — the I/O and Query Store rows above name their database. They
are NULL on instance-wide findings rather than being filled with a guess.
MoreInfo names the source and the method: which repository table the value
came from, which decision record governs the calculation, and — when several
related findings were folded into one — how many contributors were folded and
which sessions they came from. The blocking storm above folded 96 contributing
findings into a single row.
Many SQL Server troubleshooting tools are excellent at one thing:
- current requests
- waits
- blocking
- file latency
- configuration
- SQL Agent history
- Query Store
- performance counters
- server metadata
But during an incident, DBAs often have to jump between multiple scripts, tools, tabs, dashboards, and historical sources.
sp_SQLFlightRecorder helps consolidate the basics into one repeatable flow:
Collect useful diagnostic snapshots.
Store them locally.
Report on what changed.
Show findings with evidence.
Keep everything DBA-readable.
It is designed to help answer:
- Was there blocking?
- Were waits abnormal?
- Did SQL Server restart?
- Were memory grants pending?
- Was I/O latency high?
- Were collectors skipped?
- Do we have enough data to trust the report?
sp_SQLFlightRecorder is not:
- a monitoring platform
- an alerting system
- a dashboard
- an AI/ML anomaly detector
- a remediation tool
- a replacement for Query Store
- a replacement for Extended Events
- a replacement for a senior DBA
- a tool that automatically fixes anything
It diagnoses. It does not take corrective action.
It will not kill sessions, force plans, shrink files, clear cache, change indexes, or “fix” your server for you.
I use and recommend the tools below. sp_SQLFlightRecorder is not a replacement for any of them and does not try to be.
- sp_WhoIsActive — what is running right now
- First Responder Kit — health checks, plan cache analysis, index analysis, first-response triage
- Ola Hallengren's Maintenance Solution — backups, integrity checks, index and statistics maintenance
- dbatools — estate-wide automation and administration
- Glenn Berry's Diagnostic Queries — manual DMV inspection and configuration review
- Query Store — per-database query runtime and plan history
- Extended Events — precise, customizable event capture
sp_SQLFlightRecorder fills a narrower gap: a small amount of retained, server-wide evidence, already inside SQL Server, for the case where the incident is over and nobody was watching. It collects bounded snapshots on a schedule, keeps them for a short configurable window, and reports on what changed.
Several of these tools can be scheduled to persist their output, and if you already do that, you may not need this one. What sp_SQLFlightRecorder offers is a single procedure with no dependencies, a curated evidence set spanning waits, blocking, I/O, memory, restarts, configuration, and Agent history in one repository, and a report that pairs ranked findings with the discrete events recorded over the same window.
sp_SQLFlightRecorder is especially useful when:
- users report slowness after the fact
- you need recent diagnostic history but do not have a full monitoring platform
- you want a lightweight collector in a client environment
- you need repeatable DBA triage output
- you want to compare snapshots before, during, and after an incident
- you want to document what happened with evidence
- you need something easier to deploy than a full monitoring stack
- you are helping a team that has “some SQL problem” but no historical data
It is also useful for consultants because it is self-contained:
One SQL file.
One stored procedure.
Local repository tables.
No service.
No installer.
No external runtime.
Test first in a non-production database.
Open and execute sp_SQLFlightRecorder.sql.
This creates or updates:
dbo.sp_SQLFlightRecorderEXEC dbo.sp_SQLFlightRecorder @Mode = N'About';
EXEC dbo.sp_SQLFlightRecorder @Mode = N'Help';EXEC dbo.sp_SQLFlightRecorder @Mode = N'Install';EXEC dbo.sp_SQLFlightRecorder @Mode = N'Status';EXEC dbo.sp_SQLFlightRecorder @Mode = N'Collect';
WAITFOR DELAY '00:00:05';
EXEC dbo.sp_SQLFlightRecorder @Mode = N'Collect';EXEC dbo.sp_SQLFlightRecorder
@Mode = N'Report',
@MinSeverity = N'Informational',
@OutputFormat = N'Default';sp_SQLFlightRecorder is controlled by parameters. The most important one is @Mode.
EXEC dbo.sp_SQLFlightRecorder
@Mode = N'Help';| Parameter | Default | Used by | Meaning |
|---|---|---|---|
@Mode |
Help |
All | Chooses what the procedure does. Examples: Install, Collect, Report, Configure, Purge, Uninstall. |
@DatabaseName |
NULL |
Report |
Optional database filter for report output. |
@StartTime |
NULL |
Report |
Optional report start time. If omitted, the procedure chooses a default window. Default 1 hour back. |
@EndTime |
NULL |
Report |
Optional report end time. If omitted, the procedure chooses a default window. Current datetime. |
@MinSeverity |
Low |
Report |
Minimum severity to show: Informational, Low, Medium, High, or Critical. |
@MaxFindings |
200 |
Report |
Maximum number of findings to return. Valid range: 10 to 2000. |
@TopN |
50 |
Collect |
Per-collector row cap for top-N style collection. Valid range: 1 to 1000. |
@OutputFormat |
Default |
Report |
Report format: Default, FindingsOnly, TimelineOnly, or Markdown. |
@IncludeQueryPlans |
0 |
Report |
Reserved / no-op in this build. Plan capture and plan-XML analysis are disabled by design; no plan output is returned. 1 records a Skipped step at Collect and one Informational coverage finding at Report. |
@WhatIf |
0 |
Purge, Uninstall |
Preview what would happen without making changes. |
@PreserveRunLog |
0 |
Uninstall |
When 1, Uninstall renames FR_RunLog/FR_RunLogStep to timestamped FR_RunLog_Archive_<yyyymmdd_hhmmss> tables instead of dropping them. |
@Debug |
0 |
Collect |
1 routes Collect to CollectDebug: validates collector readiness and writes no collector rows. |
@ConfigKey |
NULL |
Configure |
Configuration key to update. If omitted, returns current configuration. |
@ConfigValue |
NULL |
Configure |
New value for @ConfigKey. |
@CreateAgentJob |
0 |
Install |
Explicit opt-in to create/update the SQL Agent jobs: the per-minute collector job (Collect step, then a Purge cleanup step) and a daily purge backstop job. |
@TimeZone |
NULL |
Report |
Display-only IANA/Windows time zone for Markdown output (SQL 2016+/Azure; falls back to UTC elsewhere). Storage and sorting stay UTC. |
| Mode | What it does |
|---|---|
Help |
Shows usage information. This is the default mode. |
About |
Shows version and build information. |
Install |
Creates the local FR_* repository tables and seed data. |
Status |
Shows current configuration, rules, recent runs, and repository footprint. |
Collect |
Captures one diagnostic snapshot. |
CollectDebug |
Runs diagnostic/debug collection behavior. |
Report |
Reads collected snapshots and returns findings/timeline output. |
Configure |
Shows or updates configuration values. |
Purge |
Deletes old repository data based on retention settings. |
Uninstall |
Removes SQLFlightRecorder repository objects. |
These examples are copy/paste friendly.
EXEC dbo.sp_SQLFlightRecorder
@Mode = N'Install';Only use this if SQL Agent is available and you want scheduled collection.
This creates or updates two jobs, idempotently: SQLFlightRecorder Collect
(every minute: a Collect step, then a Purge cleanup step) and
SQLFlightRecorder Purge (a daily retention backstop at 02:30 server time).
EXEC dbo.sp_SQLFlightRecorder
@Mode = N'Install',
@CreateAgentJob = 1;EXEC dbo.sp_SQLFlightRecorder
@Mode = N'Status';EXEC dbo.sp_SQLFlightRecorder
@Mode = N'Collect';Use this if you want collection to be more conservative.
EXEC dbo.sp_SQLFlightRecorder
@Mode = N'Collect',
@TopN = 25;EXEC dbo.sp_SQLFlightRecorder
@Mode = N'Report';DECLARE @StartTime datetime2(3) = DATEADD(hour, -1, SYSUTCDATETIME());
DECLARE @EndTime datetime2(3) = SYSUTCDATETIME();
EXEC dbo.sp_SQLFlightRecorder
@Mode = N'Report',
@StartTime = @StartTime,
@EndTime = @EndTime;EXEC dbo.sp_SQLFlightRecorder
@Mode = N'Report',
@DatabaseName = N'YourDatabaseName';EXEC dbo.sp_SQLFlightRecorder
@Mode = N'Report',
@MinSeverity = N'High';EXEC dbo.sp_SQLFlightRecorder
@Mode = N'Report',
@MaxFindings = 500;EXEC dbo.sp_SQLFlightRecorder
@Mode = N'Report',
@OutputFormat = N'FindingsOnly';EXEC dbo.sp_SQLFlightRecorder
@Mode = N'Report',
@OutputFormat = N'TimelineOnly';Returns discrete events only — see What the report returns. A window with nothing discrete in it yields only SnapshotCaptured rows; the findings are in the other result set.
EXEC dbo.sp_SQLFlightRecorder
@Mode = N'Report',
@OutputFormat = N'Markdown';@IncludeQueryPlans is reserved and is a no-op in this build: plan capture
and plan-XML analysis are disabled by design, so no plan output is returned.
Setting it to 1 records a Skipped QueryPlans step at Collect and one
Informational coverage finding at Report explaining this. Use Query Store for
plan-level evidence.
EXEC dbo.sp_SQLFlightRecorder
@Mode = N'Report',
@IncludeQueryPlans = 1;EXEC dbo.sp_SQLFlightRecorder
@Mode = N'Configure';EXEC dbo.sp_SQLFlightRecorder
@Mode = N'Configure',
@ConfigKey = N'SnapshotRetentionDays',
@ConfigValue = N'7';EXEC dbo.sp_SQLFlightRecorder
@Mode = N'Configure',
@ConfigKey = N'RunLogRetentionDays',
@ConfigValue = N'28';EXEC dbo.sp_SQLFlightRecorder
@Mode = N'Configure',
@ConfigKey = N'MaxRowsPerCollector',
@ConfigValue = N'50';Use a semicolon-delimited list.
EXEC dbo.sp_SQLFlightRecorder
@Mode = N'Configure',
@ConfigKey = N'DisabledRules',
@ConfigValue = N'FR_R0003_TopWaitTypeSpike;FR_R0004_FileIoLatencySpike';Always preview purge first.
EXEC dbo.sp_SQLFlightRecorder
@Mode = N'Purge',
@WhatIf = 1;EXEC dbo.sp_SQLFlightRecorder
@Mode = N'Purge',
@WhatIf = 0;EXEC dbo.sp_SQLFlightRecorder
@Mode = N'Uninstall',
@WhatIf = 1;EXEC dbo.sp_SQLFlightRecorder
@Mode = N'Uninstall',
@PreserveRunLog = 1;EXEC dbo.sp_SQLFlightRecorder
@Mode = N'Uninstall',
@PreserveRunLog = 0;Uninstall removes the repository objects. If you also want to remove the stored procedure:
DROP PROCEDURE dbo.sp_SQLFlightRecorder;For useful history, run Collect on a schedule — and always schedule Purge
too. Purge is mandatory operational maintenance: without it the FR_*
repository grows without bound and Report slows down or stops returning
quickly.
Typical cadence:
Collect: every 1 minute
Purge: after each collect (cleanup step) plus a daily backstop
If your build supports SQL Agent job creation, create both jobs explicitly:
EXEC dbo.sp_SQLFlightRecorder
@Mode = N'Install',
@CreateAgentJob = 1;This ensures (idempotently — re-running never duplicates jobs, steps, or schedules):
SQLFlightRecorder Collect— every minute; step 1 runsCollect, step 2 runsPurge(@WhatIf = 0) as normal cleanup.SQLFlightRecorder Purge— daily at 02:30 server time; a retention backstop in case the collector job is disabled, changed, or failing.
Verify the jobs:
SELECT
name,
enabled,
date_created,
date_modified
FROM msdb.dbo.sysjobs
WHERE name LIKE N'%SQLFlightRecorder%';If SQL Agent is unavailable, schedule externally with sqlcmd, a DBA
automation tool, Windows Task Scheduler, cron, or your preferred job runner —
and schedule both statements:
sqlcmd -S MyServer -d MyDatabase -E -Q "EXEC dbo.sp_SQLFlightRecorder @Mode = N'Collect';"
sqlcmd -S MyServer -d MyDatabase -E -Q "EXEC dbo.sp_SQLFlightRecorder @Mode = N'Purge', @WhatIf = 0;"Platform-specific recipes — cron +
sqlcmdon Linux, SQL Agent jobs on Azure SQL Managed Instance, and Elastic Jobs or an external scheduler on Azure SQL Database (which has no SQL Agent and no msdb — it must schedule bothCollectandPurgeexternally): see docs/operations/scheduling.md.
Configuration is stored in:
dbo.FR_ConfigThe most commonly tuned keys (full reference: docs/configuration.md):
| Key | Purpose |
|---|---|
SchemaVersion |
Installed repository schema version |
SnapshotIntervalSeconds |
Intended collection interval |
SnapshotRetentionDays |
Snapshot data retention (allowed 1–31) |
RunLogRetentionDays |
Run-log retention (allowed 1–124) |
MaxRowsPerCollector |
Per-collector row cap |
RepositoryTableWarnRows |
Row threshold for the Status size warning |
DisabledRules |
Semicolon-delimited disabled rule list |
View configuration:
EXEC dbo.sp_SQLFlightRecorder @Mode = N'Configure';or:
SELECT *
FROM dbo.FR_Config
ORDER BY ConfigKey;Recommended starting retention:
SnapshotRetentionDays = 7
RunLogRetentionDays = 28
Retention is validated: SnapshotRetentionDays accepts 1–31 and
RunLogRetentionDays accepts 1–124; out-of-range values are refused.
SQLFlightRecorder is an operational diagnostic recorder, not a long-term
warehouse — longer retention grows the repository and raises report cost.
Export data you need to keep longer.
Always preview purge before deleting:
EXEC dbo.sp_SQLFlightRecorder
@Mode = N'Purge',
@WhatIf = 1;Then run the real purge (this is what the Agent jobs run for you):
EXEC dbo.sp_SQLFlightRecorder
@Mode = N'Purge',
@WhatIf = 0;Status includes a retention-health result set that warns when the oldest
snapshot exceeds retention, purge is not keeping up, a purge job/step is
missing on Agent-capable platforms, or an FR_* table crosses
RepositoryTableWarnRows.
Uninstall removes the FR_* repository objects and both SQL Agent jobs
(SQLFlightRecorder Collect and SQLFlightRecorder Purge) if this tool
created them. It is idempotent and does not fail if a job is already gone.
Preview uninstall first:
EXEC dbo.sp_SQLFlightRecorder
@Mode = N'Uninstall',
@WhatIf = 1;Remove repository objects:
EXEC dbo.sp_SQLFlightRecorder
@Mode = N'Uninstall';Preserve the run log (renames FR_RunLog/FR_RunLogStep to timestamped FR_*_Archive_* tables):
EXEC dbo.sp_SQLFlightRecorder
@Mode = N'Uninstall',
@PreserveRunLog = 1;Remove the procedure itself:
DROP PROCEDURE dbo.sp_SQLFlightRecorder;Verify remaining objects:
SELECT
s.name AS SchemaName,
o.name AS ObjectName,
o.type_desc AS ObjectType
FROM sys.objects AS o
JOIN sys.schemas AS s
ON s.schema_id = o.schema_id
WHERE o.name LIKE N'FR[_]%'
OR o.name = N'sp_SQLFlightRecorder'
ORDER BY o.name;Primary supported range: SQL Server 2014 through 2025. SQL Server 2012 is legacy best-effort with a known SchemaActivity collector degradation, listed in the table for completeness.
| Version | Platform | Support status |
|---|---|---|
| 2017, 2019, 2022, 2025 | Linux | Supported core — verified per-push in automated Tier-1 CI. |
| 2017, 2019, 2022, 2025 | Windows | Supported. Same capability-based code path as Linux (the tool branches on capability flags and EngineEdition, never on OS); no manual attestation on file yet. |
| 2017, 2019, 2022, 2025 | SQL Server on Azure VM (IaaS) | Supported as the matching Linux or Windows row — it is the ordinary engine on a VM you administer, not a separate product. |
| 2016 | Windows only | Manual Tier-2 target — verified against v1.0.0. |
| 2014 | Windows only | Manual Tier-2 target — verified against v1.0.0. |
| 2012 | Windows only | Legacy best-effort. The v1.0.0 lifecycle was manually tested and completed, but with a known SchemaActivity collector degradation — collect runs return PartialSuccess and schema-activity data is incomplete. |
| Azure SQL Managed Instance | Azure PaaS | Verified by manual Tier-2 attestation against v1.0.0. Full collector set; only AlwaysOnState skipped, capability-gated. SQL Agent available. |
| Azure SQL Database | Azure PaaS | Verified by manual Tier-2 attestation against v1.0.0, with four expected capability-gated skips (AgentJobs, BackupHistory, Deadlocks, AlwaysOnState). No SQL Agent, no msdb. |
Tier-2 "verified" means one manual lifecycle run, not the per-push automated gate that covers the Tier-1 Linux rows. No equivalence is claimed between Azure SQL Database and Managed Instance, or between either and on-prem.
Full detail, including per-target evidence and caveats: docs/compatibility/matrix.md and docs/compatibility/support-policy.md.
Also required:
- A user database for the repository
- Permission to create/alter the stored procedure
- Permission to create repository tables
VIEW SERVER STATEfor collection- SQL Agent permissions only if using optional job creation
Notes:
- SQL Agent is not available in SQL Server Express.
- Azure SQL Database does not support SQL Agent.
- Capability-gated collectors skip cleanly where a source is absent:
AgentJobsandBackupHistorywithout msdb (Azure SQL Database),Deadlockson Azure SQL Database,AlwaysOnStateand the advanced HA collector without Always On, Query Store before SQL 2016 or with no Query Store-enabled database, the opt-in error log on Azure SQL Database, and the opt-in buffer pool above 256 GB target memory.SchemaActivitydegrades on SQL Server 2012 (collect runs returnPartialSuccess). Per-platform detail: docs/compatibility/matrix.md.
sp_SQLFlightRecorder is DBA-safe by construction:
- The default mode is
Help. Running the procedure with no parameters prints usage and does nothing else. - Collection is bounded. Most collectors cap at
MaxRowsPerCollector(default 50); file stats cap atTOP (5000)rows, perf counters atTOP (100)over an 8-counter allow-list,FR_Configurationstores all ofsys.configurations(roughly 80–110 rows), and several collectors write a single row per snapshot. Query Store and schema activity apply the cap per database across up to 50 databases. The@TopNparameter is validated 1–1000; theMaxRowsPerCollectorconfig key is not range-checked. Reportevaluates findings and the timeline only from theFR_*repository tables. The only live reads on any invocation are the fixed two-value capability probe (target server memory and host platform).- Destructive modes are explicit, and
PurgeandUninstallboth accept@WhatIf = 1to preview without changing anything. - SQL Agent job creation requires an explicit
@CreateAgentJob = 1. It never happens by default. - The procedure never issues
KILL, never changes server or database configuration, and never modifies user objects — a CI linter enforces the forbidden list. It creates and drops only its ownFR_*objects and, opt-in only, the two named Agent jobs. - No automatic remediation.
Still, treat it like any DBA tool:
Read the help.
Test in non-production.
Preview destructive actions.
Review permissions.
Schedule conservatively.
Watch repository growth.
Full documentation: docs/user-guide.md
Configuration reference: docs/configuration.md
Main implementation file: sp_SQLFlightRecorder.sql
Useful commands:
EXEC dbo.sp_SQLFlightRecorder @Mode = N'Help';
EXEC dbo.sp_SQLFlightRecorder @Mode = N'About';
EXEC dbo.sp_SQLFlightRecorder @Mode = N'Status';MIT License.
Copyright © Forward Thinkers Consulting, LLC.
sp_SQLFlightRecorder is for DBAs who want evidence, not guesses.
It does not try to be magic.
It tries to be useful when the phone rings.
