SDKs / C# / .NET

C# / .NET

HttpClient and System.Text.Json ship with the framework, so nothing collides with what your application pins.

Requires .NET 8+Package InsightRecorder.InsightSource on GitHub →

Before you start

Where your InsightRecorder runs
The base URL every call goes to — http://localhost:8080 when you are running it locally, your own hostname otherwise.
An API key
Settings → API keys, scoped to logs:write and bug:write. The token (crk_…) is shown exactly once — it is hashed at rest — and is revocable. Do not use a login JWT: it expires within the hour and is not meant for machines.
  1. 01

    Install

    No package references of its own.

    dotnet add package InsightRecorder.Insight
  2. 02

    Register the client

    As a singleton. An injected HttpClient is neither disposed nor mutated — with IHttpClientFactory both lifetime and configuration belong to the container.

    var client = new InsightClient(new InsightOptions
    {
        BaseUrl = builder.Configuration["Insight:BaseUrl"],
        ApiKey  = builder.Configuration["Insight:ApiKey"],
    });
    
    await using var shipper = client.CreateShipper();
  3. 03

    Ship a log line

    Bridge your ILogger provider to this call.

    shipper.Send(new LogEntry("checkout failed")
    {
        Level = "error",
        TraceId = Activity.Current?.TraceId.ToString(),
    }.With("order_id", "1234"));
  4. 04

    Verify it is working

    Ship one line and dispose the shipper. Disposal is what flushes, which is why a hosted service's StopAsync is the right place for it.

    shipper.Send(new LogEntry("insightrecorder smoke test")
    {
        Level = "error",
        TraceId = "smoke-1",
    });
    
    await shipper.DisposeAsync();

    Then look for the line at /app/logs and the report at /app/bugs.

    If nothing shows up

    Nothing arrived? Check shipper.Dropped — a rising count means the queue is undersized. Delivery errors are silent unless you set OnError. Never set Timeout on an injected HttpClient: it throws once that client has made a request, which is why this SDK builds a per-request linked token instead.

Before you ship

  • A body-less POST needs :httpc's 4-tuple form; the SDK handles that so summarize/analyze calls work.
PROMPT Install the C# / .NET SDK with an AI agent
Raw .md

Paste this into a coding agent running inside your project, or point the agent at /docs/prompts/csharp directly. It is self-contained: the agent needs no prior knowledge of InsightRecorder.

# Install and wire the InsightRecorder .NET SDK

Paste this into a coding agent (Claude Code, Cursor, …) running inside your
.NET service's repository. It is self-contained — the agent needs no prior
knowledge of InsightRecorder.

---

You are working in a .NET service. Install and wire the InsightRecorder SDK
(`InsightRecorder.Insight`) so it ships its logs to InsightRecorder and reports unhandled
exceptions.

**Context you need:**

- InsightRecorder collects application logs and bug reports. This SDK is the supported
  way for a .NET service to talk to it.
- Requires **.NET 8+**. It has **zero third-party dependencies** — `HttpClient`
  and `System.Text.Json` ship with the framework — so it cannot conflict with
  versions this project already pins.
- Authentication uses a long-lived **API key** shaped like `crk_…`, created in
  the InsightRecorder UI under *Settings → API keys* with the `logs:write` scope (add
  `bug:write` to report errors). **Do not** use a login JWT: those expire after
  an hour and are not meant for machines.

  Add `bug:read` for anything the SDK reads back (`ListBugs/GetBug`), and `logs:read` if you query logs. A write-only key is the common mistake: shipping works, the first read returns 403.

- **Wire trace correlation if the application uses OpenTelemetry.** A log line
  and the request that wrote it are one investigation, and that join is the
  reason to ship logs here rather than anywhere else — see the "Trace
  correlation" section of the README for what this language needs (several do it
  automatically; the rest take one hook). Without it, every log call has to carry
  `trace_id` by hand, which nobody sustains past the first week.

**Steps:**

1. `dotnet add package InsightRecorder.Insight` in the service project (not the test
   project).

2. Read configuration through the project's existing `IConfiguration` — bind
   `Insight:BaseUrl` and `Insight:ApiKey`, sourced from environment variables
   (`Insight__BaseUrl`, `Insight__ApiKey`) or the configured secret store. Never
   hard-code them, and never put the key in `appsettings.json`. If either is
   empty, the service must start normally with shipping disabled — telemetry is
   never a startup dependency.

3. Register the client as a **singleton** in `Program.cs` / `Startup.cs`,
   following the project's DI style:

   ```csharp
   builder.Services.AddSingleton(_ => new InsightClient(new InsightOptions
   {
       BaseUrl = builder.Configuration["Insight:BaseUrl"]!,
       ApiKey  = builder.Configuration["Insight:ApiKey"]!,
   }));
   builder.Services.AddSingleton(sp => sp.GetRequiredService<InsightClient>().CreateShipper());
   ```

4. Bridge logging. If the project uses `Microsoft.Extensions.Logging` (it almost
   certainly does), add a small `ILoggerProvider`/`ILogger` implementation whose
   `Log` method builds a `LogEntry` and calls `shipper.Send(...)`, and register
   it **alongside** the existing providers, never replacing them. Map: log level
   → `Level`, the formatted message → the entry message,
   `Activity.Current?.TraceId` → `TraceId`, and scope/state values → `.With(key, value)`.

   If the project uses Serilog, write the equivalent `ILogEventSink`.

5. Report unhandled exceptions where this project already handles them — an
   exception-handling middleware, an `IExceptionHandler` (ASP.NET Core 8), or an
   MVC filter:

   ```csharp
   await client.ReportBugAsync(new Dictionary<string, object?>
   {
       ["title"] = $"{e.GetType().Name}: {e.Message}",
       ["severity"] = "P0",
   });
   ```

   Report and then let the exception continue, so existing handling still runs.

6. Flush on shutdown. Register an `IHostedService` whose `StopAsync` calls
   `await shipper.DisposeAsync()`, or resolve and dispose it from
   `IHostApplicationLifetime.ApplicationStopping`. Without it the final batch is
   lost.

**Rules — do not violate these:**

- Do **not** write a logger provider that awaits an HTTP call per log event. The
  SDK batches in the background, caps each request at `BatchSize`, retries with
  backoff, and respects the server's quota.
- Do **not** `await` or block on `shipper.Send(...)` — it is synchronous and
  non-blocking on purpose, and drops when its queue is full.
- Do **not** create a new `HttpClient` per request; if the project uses
  `IHttpClientFactory`, pass the client through `InsightOptions.HttpClient` (the
  SDK will not dispose one it did not create).
- Do **not** log the API key, and do not commit it.
- Do **not** capture request bodies, headers, cookies, or query strings in
  reports: they routinely carry credentials and personal data.

**Verify before you finish:**

1. `dotnet build` and `dotnet test` pass.
2. Run the service with the configuration set, exercise an endpoint, then
   confirm the log lines appear in InsightRecorder at `/app/logs`.
3. Trigger a deliberate exception and confirm a bug appears at `/app/bugs`.
4. Confirm the service still starts with the configuration **absent**.
5. Report what you changed, and state explicitly where the shipper is disposed.

If anything is ambiguous — which logging stack, how DI is wired, how shutdown is
handled — inspect the repository and follow what is already there. Do not
restructure the service to fit the SDK.