ErrTapDocs

.NET SDK

Install the ErrTap NuGet package for ASP.NET Core — middleware for unhandled exceptions plus manual capture and structured logs. Includes a snippet for classic ASP.NET Framework.

Install

dotnet add package ErrTap

Targets .NET 8 (net8.0) and uses the ASP.NET Core shared framework, with no other dependencies.

ASP.NET Core setup

// Program.cs
using ErrTap;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddErrTap(o =>
{
    o.Dsn = builder.Configuration["ErrTap:Dsn"] ?? ""; // https://et_<key>@<host>/<project>
    o.Environment = builder.Environment.EnvironmentName;
    o.Release = builder.Configuration["ErrTap:Release"];
});

var app = builder.Build();
app.UseErrTap(); // report unhandled exceptions, then rethrow
app.Run();
// appsettings.json
{
  "ErrTap": {
    "Dsn": "https://et_<key>@<host>/<project>"
  }
}

UseErrTap() adds middleware that reports any unhandled exception and rethrows it, so your existing error pages and logging keep working. Reports include the request path and query as url, and the method and status: 500 as tags. If you use UseExceptionHandler or the developer exception page, add UseErrTap() after them so exceptions reach ErrTap first.

Manual capture & logs

Inject ErrTapClient, or use the static ErrTap.ErrTap facade anywhere after setup. The class and its namespace share a name, so qualify it (a bare ErrTap.CaptureException resolves to the namespace and doesn't compile):

try { /* … */ }
catch (Exception ex)
{
    ErrTap.ErrTap.CaptureException(ex, new Dictionary<string, object?>
    {
        ["tags"] = new Dictionary<string, object?> { ["flow"] = "checkout" },
        ["user"] = new Dictionary<string, object?> { ["id"] = userId },
    });
    throw;
}

ErrTap.ErrTap.Info("checkout started", new Dictionary<string, object?> { ["orderId"] = id });
// also: Debug / Warning / Error / CaptureMessage

// Without DI (console apps, workers):
ErrTap.ErrTap.Init(new ErrTapOptions { Dsn = Environment.GetEnvironmentVariable("ERRTAP_DSN") ?? "" });

// Before a short-lived worker exits:
await ErrTap.ErrTap.FlushAsync();

The extra dictionary accepts tags, context, user, level, fingerprint, url, release and environment. A tags entry replaces the configured Tags for that event.

Options

FieldTypeDescription
DsnstringURL DSN (https://et_<key>@<host>/<project>) or bare key (requires Endpoint)
Endpointstring?Override for /ingest/error; required when Dsn is a bare key
EnvironmentstringDefaults to production
Releasestring?Ties errors to a deploy
TagsIDictionary<string, object?>?Attached to events and logs that don't set their own tags

Classic ASP.NET (Framework)

There's no NuGet package for System.Web — post directly to the ingest API from Application_Error:

// Global.asax.cs — Application_Error (.NET Framework / System.Web)
using System;
using System.Net.Http;
using System.Text;
using System.Web;
using System.Web.Script.Serialization;

protected void Application_Error(object sender, EventArgs e)
{
    var ex = Server.GetLastError()?.GetBaseException();
    if (ex == null) return;

    var json = new JavaScriptSerializer().Serialize(new
    {
        message = ex.Message,
        type = ex.GetType().FullName,
        stacktrace = ex.ToString(),
        environment = "production",
    });

    try
    {
        using (var client = new HttpClient())
        using (var req = new HttpRequestMessage(HttpMethod.Post, "https://your-host/ingest/error"))
        {
            client.Timeout = TimeSpan.FromSeconds(5);
            req.Headers.TryAddWithoutValidation("Authorization", "DSN et_<your-key>");
            req.Content = new StringContent(json, Encoding.UTF8, "application/json");
            using (var response = client.SendAsync(req).GetAwaiter().GetResult()) { }
        }
    }
    catch { /* telemetry must not replace the application error */ }
}

See the Ingest API reference for the full payload shape.