RoushTech.Asio 0.1.0

dotnet add package RoushTech.Asio --version 0.1.0
                    
NuGet\Install-Package RoushTech.Asio -Version 0.1.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="RoushTech.Asio" Version="0.1.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="RoushTech.Asio" Version="0.1.0" />
                    
Directory.Packages.props
<PackageReference Include="RoushTech.Asio" />
                    
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 RoushTech.Asio --version 0.1.0
                    
#r "nuget: RoushTech.Asio, 0.1.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 RoushTech.Asio@0.1.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=RoushTech.Asio&version=0.1.0
                    
Install as a Cake Addin
#tool nuget:?package=RoushTech.Asio&version=0.1.0
                    
Install as a Cake Tool

Asio

CI NuGet (RoushTech.Asio) NuGet (RoushTech.Asio.Redis) NuGet (RoushTech.Asio.SignalR) License: MIT

Real-time background job log streaming for ASP.NET Core.

Asio lets you stream log output from background jobs (Hangfire, hosted services, or any async workload) to connected clients in real-time, with full session replay for clients that connect after a job has already started. It hooks into the standard ILogger infrastructure — any Logger.Log* call made within a job context is automatically captured and forwarded to the session, with no changes to existing logging code.

Named after Asio, the genus of eared owls — watching quietly in the background.


Packages

Package Description
RoushTech.Asio Core: session tracking, ILoggerProvider, Channel<T> drain pipeline
RoushTech.Asio.Redis Redis-backed session log storage via StackExchange.Redis
RoushTech.Asio.SignalR Real-time delivery via ASP.NET Core SignalR

How It Works

  1. When a job starts, it calls JobSessionService.ActivateSession(sessionId), which sets an AsyncLocal<Guid?> on the current async context.
  2. A custom ILoggerProvider (JobSessionLoggerProvider) checks this AsyncLocal on every log call. If a session is active, it writes the entry to an in-process Channel<T>.
  3. JobSessionDrainService (a BackgroundService) reads from the channel and calls JobSessionService.AppendLog, which persists the entry via IJobSessionStore and pushes it live via IJobSessionSink.
  4. Clients connect to JobSessionHub and call Watch(sessionId). The hub replays all persisted log entries from the store, then keeps the client subscribed to live updates for the remainder of the job.

Because sessions are stored in Redis with a 24-hour TTL, clients can disconnect and reconnect at any time and receive the full log history.


Installation

dotnet add package RoushTech.Asio
dotnet add package RoushTech.Asio.Redis
dotnet add package RoushTech.Asio.SignalR

Setup

1. Register services

In Program.cs:

using RoushTech.Asio;
using RoushTech.Asio.Redis;
using RoushTech.Asio.SignalR;

builder.Services
    .AddAsio()
    .AddAsioRedis(builder.Configuration)  // reads ConnectionStrings:Redis
    // ...
    .AddSignalR()
    .AddAsioSignalR();

2. Map the hub

app.UseEndpoints(endpoints =>
{
    endpoints.MapAsioHub(); // default: /hubs/job-session
    // or with a custom path:
    endpoints.MapAsioHub("/hubs/my-jobs");
});

3. Configure Redis connection string

{
  "ConnectionStrings": {
    "Redis": "localhost:6379,password=yourpassword"
  }
}

4. Configure log levels

Asio respects standard ILogger configuration. To control what level Asio captures independently of other providers, use the "Asio" section:

{
  "Logging": {
    "LogLevel": {
      "Default": "Warning"
    },
    "Asio": {
      "LogLevel": {
        "Default": "Warning",
        "YourApp.Namespace": "Debug"
      }
    }
  }
}

5. Add Redis health check

builder.Services
    .AddHealthChecks()
    .AddAsioRedis(tags: ["critical"]);

Usage in a Background Job

Generate a session ID in your controller or wherever you enqueue the job, return it to the caller, and pass it into the job:

[HttpPost("{id}/run")]
public IActionResult TriggerJob([FromRoute] Guid id)
{
    var sessionId = Guid.NewGuid();
    BackgroundJobClient.Enqueue<MyJobService>("queue", s => s.Run(id, sessionId));
    return Ok(new { sessionId });
}

In your job, activate the session at the start and complete it at the end:

public class MyJobService(
    ILogger<MyJobService> logger,
    JobSessionService jobSessionService)
{
    public async Task Run(Guid id, Guid sessionId)
    {
        JobSessionService.ActivateSession(sessionId);

        try
        {
            // All Logger calls below are automatically captured in the session.
            logger.LogInformation("Starting job for {Id}", id);

            await DoWork(id);

            logger.LogInformation("Job completed successfully.");
        }
        finally
        {
            await jobSessionService.CompleteSession(sessionId);
        }
    }
}

No other changes are needed — existing ILogger calls throughout the call tree are captured automatically for the duration of the activated session.


Frontend Integration

The hub exposes a small contract:

Direction Name Payload
Client → Server Watch(sessionId: string) Subscribes the caller. Replays all persisted log entries to the caller, then joins them to the live group for that session.
Server → Client LogMessage (message: string, level: number)level matches Microsoft.Extensions.Logging.LogLevel (Trace=0 … Critical=5).
Server → Client SessionComplete (hasError: boolean) — fired once when the job completes.

A minimal Vue 3 + @microsoft/signalr reference component lives at samples/vue/JobSessionLog.vue. It connects to the hub, replays history, renders live log lines with level-based coloring, and re-replays on reconnect. It's intentionally framework-light (no UI library dependency) — wrap it in your own dialog/modal as needed.

<JobSessionLog :session-id="sessionId" @complete="onJobComplete" />

Architecture Summary

Job (any async context)
  └─ Logger.LogInformation(...)
       └─ JobSessionLoggerProvider.IsEnabled() ── checks AsyncLocal session
            └─ ChannelWriter.TryWrite()          ── synchronous, non-blocking

JobSessionDrainService (BackgroundService, per host)
  └─ ChannelReader.ReadAllAsync()
       └─ JobSessionService.AppendLog()
            ├─ IJobSessionStore.AppendLog()      ── persists to Redis (24h TTL)
            └─ IJobSessionSink.PushLog()         ── pushes via SignalR

Client (browser)
  └─ HubConnection.invoke("Watch", sessionId)
       └─ JobSessionHub.Watch()
            ├─ replay all persisted logs to caller
            └─ subscribe to live updates via SignalR group

License

MIT — see LICENSE.

Product Compatible and additional computed target framework versions.
.NET net9.0 is compatible.  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 was computed.  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 (2)

Showing the top 2 NuGet packages that depend on RoushTech.Asio:

Package Downloads
RoushTech.Asio.SignalR

Real-time delivery of RoushTech.Asio job session logs to browser clients via ASP.NET Core SignalR.

RoushTech.Asio.Redis

Redis-backed session log storage for RoushTech.Asio via StackExchange.Redis. Multi-node safe with configurable TTL.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
0.1.0 202 5/27/2026