CherryPeak.CherryBoard.Client 1.5.0

dotnet add package CherryPeak.CherryBoard.Client --version 1.5.0
                    
NuGet\Install-Package CherryPeak.CherryBoard.Client -Version 1.5.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="CherryPeak.CherryBoard.Client" Version="1.5.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="CherryPeak.CherryBoard.Client" Version="1.5.0" />
                    
Directory.Packages.props
<PackageReference Include="CherryPeak.CherryBoard.Client" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add CherryPeak.CherryBoard.Client --version 1.5.0
                    
#r "nuget: CherryPeak.CherryBoard.Client, 1.5.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package CherryPeak.CherryBoard.Client@1.5.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=CherryPeak.CherryBoard.Client&version=1.5.0
                    
Install as a Cake Addin
#tool nuget:?package=CherryPeak.CherryBoard.Client&version=1.5.0
                    
Install as a Cake Tool

CherryBoard Client

A lightweight, high-performance .NET client library for capturing and sending errors to CherryBoard Dashboard. Similar to Sentry, but tailored for your infrastructure.

Features

  • 🚀 Automatic Exception Capture - Middleware automatically captures unhandled exceptions
  • 📡 Reliable Transport (v1.3.0+) - A background worker delivers errors continuously (near real-time), batched; buffered errors are flushed on shutdown and global unhandled/unobserved exceptions are captured
  • 🔄 Retry Logic - Exponential backoff retry mechanism for failed requests
  • 💾 Offline Queue - Local queuing support for offline scenarios
  • 🎯 Sampling - Configure sample rates to control error volume
  • 🔍 Rich Context - Automatically captures HTTP request details, user info, exception type/inner exception, and metadata
  • ⚡ Non-Blocking - Capturing enqueues and returns immediately; never blocks your app (drops, rather than blocks, when the buffer is full)
  • 📝 Log Integration (v1.1.0+) - Forward Warning/Error/Critical logs to CherryBoard, including background jobs and hosted services the middleware can't see
  • ⏱️ Request Timing (v1.4.0+) - Optional per-route latency tracking, aggregated in-process and shown next to the browser SDK's numbers for the same route

Installation

dotnet add package CherryPeak.CherryBoard.Client

Quick Start

1. Configure in Program.cs

using CherryPeak.CherryBoard.Client.Extensions;

var builder = WebApplication.CreateBuilder(args);

// Add CherryBoard
builder.Services.AddCherryBoard(options =>
{
    options.ApiKey = "your-api-key-here";
    options.ApiUrl = "https://your-cherryboard-host"; // your CherryBoard instance URL
    options.Environment = builder.Environment.EnvironmentName;
    options.EnableAutomaticCapture = true;
    options.MaxBatchSize = 50;       // max errors per delivery request
    options.MaxQueueItems = 1000;    // (v1.3.0+) in-memory buffer cap; drops when full
    options.SampleRate = 1.0;        // Capture 100% of errors
    
    // For development with self-signed certificates (localhost)
    if (builder.Environment.IsDevelopment())
    {
        options.DisableSslValidation = true;
    }
});

// (v1.1.0+) Forward Warning/Error/Critical logs to CherryBoard — including background
// jobs, hosted services, and caught-and-logged errors the middleware never sees.
builder.Logging.AddCherryBoardLogging();

var app = builder.Build();

// Register middleware (if EnableAutomaticCapture is true, it will capture unhandled exceptions)
app.UseCherryBoard();

app.Run();

2. Configuration Options

Option Description Valid Range Default
ApiKey Your CherryPeak API key Required, non-empty N/A
ApiUrl CherryBoard Dashboard API URL Required, non-empty N/A
Environment Environment name (Production, Staging, etc.) Any string "Production"
EnableAutomaticCapture Enable automatic capture middleware for unhandled exceptions true/false true
MaxBatchSize Max errors sent in a single delivery request 1-1000 50
MaxQueueItems (v1.3.0+) In-memory buffer cap; new errors are dropped (never block) when full 1-1,000,000 1000
ShutdownTimeoutSeconds (v1.3.0+) Max seconds to flush buffered errors on shutdown 0-60 2
MaxRetries Number of retry attempts for failed requests 0-10 3
EnableOfflineQueue Enable local queuing for offline support true/false true
SampleRate Percentage of errors to capture (0.0-1.0) 0.0-1.0 1.0
DisableSslValidation Disable SSL validation for self-signed certs (dev only) true/false false
MinimumLogLevel (v1.1.0+) Lowest log level forwarded by AddCherryBoardLogging() Trace-None Warning
EnablePerformanceTracking (v1.4.0+) Time requests and report per-route rollups. Also needs UseCherryBoardPerformance() true/false false
MetricsFlushIntervalSeconds (v1.4.0+) How often rollups are posted 10-3600 60
MetricsMaxRoutes (v1.4.0+) Distinct routes tracked per process 1-2000 200
BatchIntervalSeconds Deprecated (v1.3.0) — delivery is now continuous, so this is no longer used 1-3600 30

Note: Configuration is validated during AddCherryBoard() registration. Invalid values will throw ArgumentException with clear error messages.

3. Manual Error Capture

You can also manually capture exceptions:

public class MyService
{
    private readonly ICherryBoardClient _client;

    public MyService(ICherryBoardClient client)
    {
        _client = client;
    }

    public async Task DoSomethingAsync()
    {
        try
        {
            // Your code
        }
        catch (Exception ex)
        {
            // Manually capture exception with custom metadata
            await _client.CaptureExceptionAsync(ex, errorData =>
            {
                errorData.UserId = "user-123";
                errorData.Metadata = System.Text.Json.JsonSerializer.Serialize(new
                {
                    CustomField = "CustomValue",
                    Operation = "DoSomething"
                });
            });

            throw; // Re-throw if needed
        }
    }
}

4. Manual Error Data

Send custom error data without an exception. CaptureExceptionAsync fills ExceptionType, StackTrace, and InnerException automatically; with CaptureErrorAsync you can set them yourself — the dashboard surfaces the exception type and inner-exception chain on the issue page.

await _client.CaptureErrorAsync(new ErrorData
{
    Message = "Custom error message",
    Severity = 3, // 0=Debug, 1=Info, 2=Warning, 3=Error, 4=Critical
    ExceptionType = "MyApp.Domain.OrderValidationException",
    InnerException = "Npgsql.NpgsqlException: Failed to connect to <db-host>:5432",
    Metadata = System.Text.Json.JsonSerializer.Serialize(new
    {
        CustomField = "Value",
        UserAction = "Upload"
    })
});

5. Flush on Application Shutdown

Automatic since v1.3.0 — the client registers a hosted service that flushes buffered errors on shutdown (and hooks AppDomain.UnhandledException / TaskScheduler.UnobservedTaskException), so you don't need to wire anything up. You can still flush manually if you have a special case:

var client = app.Services.GetRequiredService<ICherryBoardClient>();
await client.FlushAsync();

Configuration Validation

All configuration options are validated during AddCherryBoard() registration. If any values are outside their valid ranges, an ArgumentException will be thrown immediately with a clear error message.

Example validation errors:

// ❌ Error: MaxBatchSize must be between 1 and 1000
builder.Services.AddCherryBoard(options =>
{
    options.MaxBatchSize = 5000; // Too large!
});

// ❌ Error: SampleRate must be between 0.0 and 1.0
builder.Services.AddCherryBoard(options =>
{
    options.SampleRate = 1.5; // Invalid!
});

// ✅ Valid configuration
builder.Services.AddCherryBoard(options =>
{
    options.MaxBatchSize = 100;     // Valid: 1-1000
    options.SampleRate = 0.5;       // Valid: 0.0-1.0
    options.MaxQueueItems = 5000;   // Valid: 1-1,000,000
});

Capturing Background Jobs & Logged Errors (v1.1.0+)

The automatic middleware only sees unhandled exceptions on the HTTP request pipeline. Errors in background jobs (e.g. Hangfire), hosted services, or anything that's caught and logged never reach it. AddCherryBoardLogging() closes that gap by forwarding log entries to CherryBoard app-wide.

builder.Services.AddCherryBoard(options => { /* ... */ });

// Forward logs at or above MinimumLogLevel (default: Warning) to CherryBoard.
builder.Logging.AddCherryBoardLogging();

Once registered you need no per-call-site code — just log normally:

// In a Hangfire job, IHostedService, or anywhere the HTTP middleware can't reach:
_logger.LogError(ex, "Failed to upload file {File}", fileName); // → captured automatically

What each layer captures

Layer Captures Use for
app.UseCherryBoard() (middleware) Unhandled exceptions on HTTP requests, + request path/method/user/IP Web request errors
builder.Logging.AddCherryBoardLogging() Any LogWarning/LogError/LogCritical app-wide — jobs, hosted services, caught-and-logged Non-HTTP code paths
ICherryBoardClient.CaptureExceptionAsync / CaptureErrorAsync Explicit reports with custom metadata Custom context

Log levels map to severity: Critical → Critical, Error → Error, Warning → Warning.

Built-in safeguards:

  • No recursion — the integration ignores its own log output, so a failed send can't loop.
  • No duplicates — an unhandled HTTP exception captured by the middleware is not re-captured via its log entry.
  • Non-blocking — entries are queued (respecting SampleRate); logging never waits on the network.

Request Timing (v1.4.0+)

Measures how long each request takes to process and reports per-route rollups to the dashboard, where they appear next to the browser SDK's numbers for the same route.

Off by default. Two things to enable it:

builder.Services.AddCherryBoard(options =>
{
    options.ApiKey = "your-api-key-here";
    options.ApiUrl = "https://your-cherryboard-host";
    options.EnablePerformanceTracking = true;
});

var app = builder.Build();

// Register early. Whatever runs before this is not timed, so putting it near the
// top of the pipeline measures the request as the caller experienced it rather
// than just your handler.
app.UseCherryBoardPerformance();

Timings appear on the environment page in the dashboard within a few minutes.

Why measure both sides

The browser SDK already reports how long API calls take. That number includes DNS, TLS, the network and transferring the response; this one is server processing alone.

Neither answers the question by itself, but the gap between them does:

What you see What it points at
Both numbers similar Your code — the time is being spent in the handler
Frontend much higher Payload size or the network, not the handler
Backend high, few calls A slow dependency on an endpoint nobody hits often

What it costs

A Stopwatch per request and an in-memory counter update. Rollups are posted on a timer, so the payload is bounded by how many endpoints the app has rather than how much traffic it serves — a service handling a million requests sends the same small summary as one handling a hundred.

Delivery failures are swallowed by design. A dropped window is a gap in a chart; a thrown exception would be an outage.

What is not recorded

Responses the server deliberately holds open are skipped: server-sent events (text/event-stream) and protocol upgrades (HTTP 101, e.g. WebSockets).

For these, elapsed time is how long the client stayed subscribed, not how long anything took to compute. Recorded as latency it is not merely noisy but wrong — and being by far the largest number, a perfectly healthy stream would sit permanently at the top of the slowest-routes table and drag the environment's overall percentile with it.

Detection is by media type and status code, so this applies to any streaming endpoint without needing to be configured.

For anything else you want left out — long-polling, large downloads, an endpoint that is slow by design — mark it:

[HttpGet("download/{id}")]
[CherryBoardIgnorePerformance]
public async Task<IActionResult> Download(Guid id) { ... }

It works on a whole controller too, and is inherited. Use it sparingly: a route that is genuinely slow is the thing this feature exists to show you. Exclude the ones where elapsed time is not latency, not the ones you would rather not see.

Note the browser SDK measures streams differently: it reads only fetch and XHR resource timings, so EventSource never appears. A fetch() that streams its body would be measured until the stream closes, and would be over-counted the same way.

What gets recorded

Paths are normalized before anything is stored, so /api/orders/12345 becomes /api/orders/:id. This deliberately uses the same rules as the browser SDK rather than ASP.NET's matched route template: the two sides have to produce identical strings for the comparison above to work at all.

  • Query strings are never recorded — they carry tokens and email addresses, and say nothing about how long a request took.
  • 404s group under a single label, since the set of paths nobody serves is unbounded.
  • 4xx is not counted as an error. A caller's bad request is not your endpoint failing, and counting it would make validation look like breakage.

How It Works

  1. Automatic Capture: The middleware intercepts unhandled exceptions, enriches them with HTTP context (request path, method, IP, user agent, etc.), and sends them asynchronously to CherryBoard Dashboard.

  2. Continuous delivery (v1.3.0+): Captured errors are written to a bounded in-memory queue and a single background worker sends them continuously (near real-time), batching only what has accumulated. The queue is flushed on application shutdown, so buffered errors aren't lost on deploy/restart.

  3. Retry Logic: Failed requests are automatically retried with exponential backoff (2^attempt seconds).

  4. Sampling: Control error volume by setting a sample rate (e.g., 0.5 = 50% of errors).

  5. Non-blocking: Capturing never blocks your application — it enqueues and returns immediately (and drops, rather than blocks, if the queue is full).

Architecture

Application Exception / LogError / unhandled exception
        ↓
Capture → bounded in-memory queue (drop-when-full)
        ↓
Background worker (continuous, batched)
        ↓
Send to API with Retry        ← flushed on shutdown
        ↓
CherryBoard Dashboard

Best Practices

  1. Set Appropriate Sample Rate: For high-traffic applications, consider setting SampleRate to 0.1 (10%) to reduce volume.

  2. Configure Environment: Always set the Environment option to distinguish between Production, Staging, and Development errors.

  3. Flush on Shutdown: Handled automatically since v1.3.0 — no manual registration needed.

  4. Don't Catch and Ignore: Let exceptions bubble up so the middleware can capture them, or manually capture before handling.

License

MIT

Support

For issues or questions, visit GitHub Issues

Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.5.0 677 8/31/2026
1.4.2 100 8/31/2026
1.4.1 107 8/31/2026
1.4.0 112 8/31/2026
1.3.1 525 6/24/2026
1.3.0 134 6/24/2026
1.2.0 133 6/24/2026
1.1.0 132 6/24/2026
1.0.1 902 1/24/2026
1.0.0 592 1/19/2026