Skip to main content

Using ExecutionLog views in SQL Server Reporting Services to monitor performance

After upgrading our SQL Server Reporting Services (SSRS) environment, we noticed some isuses: reports were slow, users were complaining, and we had no idea where to start. The good news is that SSRS has been quietly collecting detailed execution data the whole time — and a set of built-in views makes it surprisingly easy to query.

This post walks through the ExecutionLog views, what they contain, and how to turn that data into actionable performance insights.

What are the ExecutionLog views?

SSRS logs every report execution to the ReportServer database. Three views expose this data at different levels of detail:

  • ExecutionLog — A simple view covering the basics: report name, user, start time, and duration. Good for quick lookups.
  • ExecutionLog2 — Adds AdditionalInfo, an XML column with richer metadata such as estimated row counts and data source connection details.
  • ExecutionLog3 — The most complete view. Breaks execution time into three distinct phases — TimeDataRetrieval, TimeProcessing, and TimeRendering — which is where the real diagnostic power lies.

For most performance work, ExecutionLog3 is the one you want.

Understanding the key columns

Before diving into queries, it helps to know what you're working with in ExecutionLog3:

Column Description
ReportPath The full path to the report on the server
UserName The account that ran the report
RequestType Interactive (user-initiated) or Subscription
Format Output format: HTML4.0, PDF, EXCEL, etc.
TimeStart / TimeEnd Wall-clock start and end of execution
TimeDataRetrieval Milliseconds spent fetching data from sources
TimeProcessing MMilliseconds spent processing/aggregating data
TimeRendering Milliseconds spent rendering the output format
Status rsSuccess, rsProcessingAborted, or an error code
RowCount Number of rows returned by the primary dataset
ByteCount Size of the rendered output in bytes
Source Whether the result came from Live, Cache, or Snapshot

The time breakdown is the most valuable part. A report with high TimeDataRetrieval probably needs query tuning or indexing. High TimeProcessing suggests complex expressions or large datasets being crunched in SSRS itself. High TimeRendering often points to reports with too many elements, sub-reports, or large images.

Useful queries to get started

1. Find your slowest reports

SELECT
    ReportPath,
    COUNT(*)                            AS ExecutionCount,
    AVG(TimeDataRetrieval)              AS AvgDataRetrieval_ms,
    AVG(TimeProcessing)                 AS AvgProcessing_ms,
    AVG(TimeRendering)                  AS AvgRendering_ms,
    AVG(TimeDataRetrieval
        + TimeProcessing
        + TimeRendering)                AS AvgTotal_ms,
    MAX(TimeDataRetrieval
        + TimeProcessing
        + TimeRendering)                AS MaxTotal_ms
FROM ExecutionLog3
WHERE TimeStart >= DATEADD(day, -30, GETDATE())
  AND Status = 'rsSuccess'
GROUP BY ReportPath
ORDER BY AvgTotal_ms DESC;

This gives you a ranked list of your worst performers over the past 30 days. Focus on reports that combine high average time and high execution count — those have the most impact on your environment.

2. Diagnose where time is being spent

SELECT
    ReportPath,
    AVG(TimeDataRetrieval)  AS AvgData_ms,
    AVG(TimeProcessing)     AS AvgProcessing_ms,
    AVG(TimeRendering)      AS AvgRendering_ms,
    AVG(TimeDataRetrieval) * 100.0
        / NULLIF(AVG(TimeDataRetrieval + TimeProcessing + TimeRendering), 0)
                            AS PctData,
    AVG(TimeProcessing) * 100.0
        / NULLIF(AVG(TimeDataRetrieval + TimeProcessing + TimeRendering), 0)
                            AS PctProcessing,
    AVG(TimeRendering) * 100.0
        / NULLIF(AVG(TimeDataRetrieval + TimeProcessing + TimeRendering), 0)
                            AS PctRendering
FROM ExecutionLog3
WHERE TimeStart >= DATEADD(day, -7, GETDATE())
  AND Status = 'rsSuccess'
GROUP BY ReportPath
HAVING AVG(TimeDataRetrieval + TimeProcessing + TimeRendering) > 5000
ORDER BY AVG(TimeDataRetrieval + TimeProcessing + TimeRendering) DESC;

The percentage columns immediately show you where to focus. If 85% of execution time is data retrieval, start with the underlying queries — not the report layout.

3. Spot failed executions

SELECT
    ReportPath,
    Status,
    COUNT(*) AS FailureCount,
    MAX(TimeStart) AS MostRecent
FROM ExecutionLog3
WHERE TimeStart >= DATEADD(day, -7, GETDATE())
  AND Status <> 'rsSuccess'
GROUP BY ReportPath, Status
ORDER BY FailureCount DESC;

Failures that users never report are surprisingly common. This query surfaces them.

4. Identify peak usage times

SELECT
    DATEPART(hour, TimeStart)   AS HourOfDay,
    DATEPART(weekday, TimeStart) AS DayOfWeek,
    COUNT(*)                    AS ExecutionCount,
    AVG(TimeDataRetrieval
        + TimeProcessing
        + TimeRendering)        AS AvgTotal_ms
FROM ExecutionLog3
WHERE TimeStart >= DATEADD(day, -30, GETDATE())
GROUP BY DATEPART(hour, TimeStart), DATEPART(weekday, TimeStart)
ORDER BY DayOfWeek, HourOfDay;

Knowing when your environment is under the most load helps you schedule subscriptions more intelligently and plan maintenance windows.

5. Check cache effectiveness

SELECT
    ReportPath,
    Source,
    COUNT(*) AS ExecutionCount
FROM ExecutionLog3
WHERE TimeStart >= DATEADD(day, -7, GETDATE())
GROUP BY ReportPath, Source
ORDER BY ReportPath, Source;

If a frequently-run report shows Live for nearly every execution, it's a strong candidate for caching or snapshots. Reports served from Cache are dramatically faster for end users and lighter on your data sources.

Practical tips

Mind the log retention. By default, SSRS keeps 60 days of execution history. You can change this in Report Server Configuration Manager, or schedule a job to archive data to a separate table before it rolls off.

Watch for parameter-driven outliers. A report might average 3 seconds but occasionally spike to 45 seconds because someone runs it with a 5-year date range. Look at your MAX values alongside averages, and consider adding parameter validation to the report itself.

Combine with subscription data. Subscriptions that run at 6am and take 20 minutes may be blocking interactive users who arrive at 6:30. Knowing both patterns lets you schedule more thoughtfully.

Look at RowCount alongside time. A report returning 500,000 rows into an Excel export isn't necessarily broken — but it might be a sign a user is working around a missing feature elsewhere in your system.

Don't ignore ByteCount. Very large rendered outputs (say, multi-MB PDFs generated repeatedly) can quietly stress your network and storage. Flag these for review with the report owners.

Wrapping up

I'm happy to see that Microsoft integrated telemetry out-of-the-box. The ExecutionLog views require no configuration, no agents, and no third-party tools — the data is already there, waiting to be queried. Starting with ExecutionLog3 and a handful of the queries above, you can move from reactive firefighting to proactive performance management in an afternoon.

Popular posts from this blog

Podman– Command execution failed with exit code 125

After updating WSL on one of the developer machines, Podman failed to work. When we took a look through Podman Desktop, we noticed that Podman had stopped running and returned the following error message: Error: Command execution failed with exit code 125 Here are the steps we tried to fix the issue: We started by running podman info to get some extra details on what could be wrong: >podman info OS: windows/amd64 provider: wsl version: 5.3.1 Cannot connect to Podman. Please verify your connection to the Linux system using `podman system connection list`, or try `podman machine init` and `podman machine start` to manage a new Linux VM Error: unable to connect to Podman socket: failed to connect: dial tcp 127.0.0.1:2655: connectex: No connection could be made because the target machine actively refused it. That makes sense as the podman VM was not running. Let’s check the VM: >podman machine list NAME         ...

Azure DevOps/ GitHub emoji

I’m really bad at remembering emoji’s. So here is cheat sheet with all emoji’s that can be used in tools that support the github emoji markdown markup: All credits go to rcaviers who created this list.

VS Code Planning mode

After the introduction of Plan mode in Visual Studio , it now also found its way into VS Code. Planning mode, or as I like to call it 'Hannibal mode', extends GitHub Copilot's Agent Mode capabilities to handle larger, multi-step coding tasks with a structured approach. Instead of jumping straight into code generation, Planning mode creates a detailed execution plan. If you want more details, have a look at my previous post . Putting plan mode into action VS Code takes a different approach compared to Visual Studio when using plan mode. Instead of a configuration setting that you can activate but have limited control over, planning is available as a separate chat mode/agent: I like this approach better than how Visual Studio does it as you have explicit control when plan mode is activated. Instead of immediately diving into execution, the plan agent creates a plan and asks some follow up questions: You can further edit the plan by clicking on ‘Open in Editor’: ...