> For the complete documentation index, see [llms.txt](https://docs.umbraco.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.umbraco.com/umbraco-cms/13.latest/fundamentals/backoffice/logviewer.md).

# Log Viewer

Umbraco ships with a built-in Log Viewer feature. This allows you to filter, view log entries, perform complex search queries, and analyze logs for debugging. You can find the Log viewer in the **Settings** section of the Umbraco backoffice.

{% embed url="<https://youtu.be/PDqIRVygAQ4?t=102>" %}
Learn how to use the Log Viewer to read and understand logs for your Umbraco CMS website.
{% endembed %}

## Benefits

Umbraco's Log Viewer uses structured logging and a query language, so you can search for specific scenarios instead of scanning raw text. For example, finding every log entry tied to one request ID, or every entry where `Duration` exceeds `1000ms`. This makes debugging and pattern-spotting easier.

## Example Queries

Here are some example queries to help you get started. For more details on the syntax, see the [serilog-filters-expressions](https://github.com/serilog/serilog-filters-expressions) project.

* **Find all logs that are from the namespace 'Umbraco.Core'** `StartsWith(SourceContext, 'Umbraco.Core')`
* **Find all logs that have the property 'Duration' and the duration is greater than 1000ms** `Has(Duration) and Duration > 1000`
* **Find all logs where the message has localhost in it with SQL like** `@Message like '%localhost%'`

## Saved Searches

If you frequently use a custom query, you can save it for quick access. Type your query in the search box and click the heart icon to save it with a friendly name. Saved queries are stored in the `umbracoLogViewerQuery` table in the database.

## Implementing Your Own Log Viewer

Umbraco allows you to implement a custom `ILogViewer` to fetch logs from alternative sources, such as **Azure Table Storage**.

### Creating a Custom Log Viewer

To fetch logs from Azure Table Storage, implement the `SerilogLogViewerSourceBase` class from `Umbraco.Cms.Core.Logging.Viewer`.

{% hint style="info" %}
This implementation requires the `Azure.Data.Tables` NuGet package.
{% endhint %}

{% code title="AzureTableLogViewer.cs" %}

```csharp
using Azure;
using Azure.Data.Tables;
using Serilog.Events;
using Serilog.Formatting.Compact.Reader;
using Umbraco.Cms.Core.Logging.Viewer;
using Umbraco.Cms.Core.Models;

namespace My.Website;

public class AzureTableLogViewer : SerilogLogViewerSourceBase
{
    public AzureTableLogViewer(ILogViewerConfig logViewerConfig, Serilog.ILogger serilogLog, ILogLevelLoader logLevelLoader)
        : base(logViewerConfig, logLevelLoader, serilogLog)
    {
    }

    public override bool CanHandleLargeLogs => false;

    public override bool CheckCanOpenLogs(LogTimePeriod logTimePeriod)
        => logTimePeriod.EndTime - logTimePeriod.StartTime < TimeSpan.FromDays(5);

    protected override IReadOnlyList<LogEvent> GetLogs(LogTimePeriod logTimePeriod, ILogFilter filter, int skip, int take)
    {
        // This example uses a connection string compatible with the Azurite emulator
        // https://learn.microsoft.com/en-us/azure/storage/common/storage-use-azurite
        var client =
            new TableClient(
                "UseDevelopmentStorage=true",
                "LogEventEntity");

        // Filter by timestamp to avoid retrieving all logs from the table, preventing memory and performance issues
        IEnumerable<AzureTableLogEntity> results = client.Query<AzureTableLogEntity>(
            entity => entity.Timestamp >= logTimePeriod.StartTime.Date &&
                      entity.Timestamp <= logTimePeriod.EndTime.Date.AddDays(1).AddSeconds(-1));

        return results
            .Select(x => LogEventReader.ReadFromString(x.Data))
            .Where(filter.TakeLogEvent)
            .Skip(skip)
            .Take(take)
            .ToList();
    }

    public override IReadOnlyList<SavedLogSearch> GetSavedSearches()
    {
        // This method is optional. If you store saved searches in Azure Table Storage, implement fetching logic here.
        return base.GetSavedSearches();
    }

    public override IReadOnlyList<SavedLogSearch> AddSavedSearch(string name, string query)
    {
        // This method is optional. If you store saved searches in Azure Table Storage, implement adding logic here.
        return base.AddSavedSearch(name, query);
    }

    public override IReadOnlyList<SavedLogSearch> DeleteSavedSearch(string name, string query)
    {
        // This method is optional. If you store saved searches in Azure Table Storage, implement deleting logic here.
        return base.DeleteSavedSearch(name, query);
    }

    public class AzureTableLogEntity : ITableEntity
    {
        public required string Data { get; set; }

        public required string PartitionKey { get; set; }

        public required string RowKey { get; set; }

        public DateTimeOffset? Timestamp { get; set; }

        public ETag ETag { get; set; }
    }
}
```

{% endcode %}

Azure Table Storage requires entities to implement the `ITableEntity` interface. Since Umbraco’s default log entity does not implement this, a custom entity (`AzureTableLogEntity`) must be created to ensure logs are correctly fetched and stored.

{% hint style="warning" %}
The connection string above must match the one used by the Serilog sink configured in [Configuring Logging to Azure Table Storage](#configuring-logging-to-azure-table-storage). If the two point at different storage accounts, this repository queries a table that was never written to. The Log Viewer then fails with an error stating the table does not exist. Read the connection string from configuration rather than hardcoding it, so both sides always agree.
{% endhint %}

### Register implementation

Umbraco needs to be made aware that there is a new implementation of an `ILogViewer` to register. We also need to replace the default JSON LogViewer that we ship in the core of Umbraco.

{% code title="AzureTableLogsComposer.cs" %}

```csharp
using Umbraco.Cms.Core.Composing;
using Umbraco.Cms.Infrastructure.DependencyInjection;

namespace My.Website;

public class AzureTableLogsComposer : IComposer
{
    public void Compose(IUmbracoBuilder builder) => builder.SetLogViewer<AzureTableLogViewer>();
}
```

{% endcode %}

### Configuring Logging to Azure Table Storage

With the above two classes, the setup is in place to view logs from an Azure Table. However, logs are not yet persisted into the Azure Table Storage account. To enable persistence, configure the Serilog logging pipeline to store logs in Azure Table Storage.

1. Install `Serilog.Sinks.AzureTableStorage` from NuGet.
2. Add a new sink to `appsettings.json` with credentials to persist logs to Azure.

The following sink needs to be added to the [`Serilog:WriteTo`](https://github.com/serilog/serilog-sinks-azuretablestorage#json-configuration) array.

{% code title="appsettings.json" %}

```json
{
"Name": "AzureTableStorage",
"Args": {
    "storageTableName": "LogEventEntity",
    "formatter": "Serilog.Formatting.Compact.CompactJsonFormatter, Serilog.Formatting.Compact",
    "connectionString": "UseDevelopmentStorage=true"
    }
}
```

{% endcode %}

This example uses the same Azurite-compatible connection string as the repository above, so the two stay in sync for local testing.

Replace it with your real Azure Storage connection string when deploying, for example: `DefaultEndpointsProtocol=https;AccountName=ACCOUNT_NAME;AccountKey=KEY;EndpointSuffix=core.windows.net`. Update the repository's connection string to match.

For more in-depth information about logging and how to configure it, see the [Logging](/umbraco-cms/13.latest/fundamentals/code/debugging/logging.md) article.

### Compact Log Viewer - Desktop App

[Compact Log Viewer](https://www.microsoft.com/store/apps/9N8RV8LKTXRJ?cid=storebadge\&ocid=badge). A desktop tool is available for viewing and querying JSON log files in the same way as the built-in Log Viewer in Umbraco.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.umbraco.com/umbraco-cms/13.latest/fundamentals/backoffice/logviewer.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
