Kanject.Core.Logs.Abstractions
3.8.0
Prefix Reserved
dotnet add package Kanject.Core.Logs.Abstractions --version 3.8.0
NuGet\Install-Package Kanject.Core.Logs.Abstractions -Version 3.8.0
<PackageReference Include="Kanject.Core.Logs.Abstractions" Version="3.8.0" />
<PackageVersion Include="Kanject.Core.Logs.Abstractions" Version="3.8.0" />
<PackageReference Include="Kanject.Core.Logs.Abstractions" />
paket add Kanject.Core.Logs.Abstractions --version 3.8.0
#r "nuget: Kanject.Core.Logs.Abstractions, 3.8.0"
#:package Kanject.Core.Logs.Abstractions@3.8.0
#addin nuget:?package=Kanject.Core.Logs.Abstractions&version=3.8.0
#tool nuget:?package=Kanject.Core.Logs.Abstractions&version=3.8.0
Kanject.Core.Logs.Abstractions
Contracts for an application-level log store — write named log entries, page back through them by group and date range — plus a small distributed-lock contract. Application code depends on ILogManager and IDbLockRepository<TEntity>; a provider package supplies the storage (the Kanject provider targets Amazon DynamoDB).
This is for durable, queryable business or audit logs that your application reads back, not a replacement for Microsoft.Extensions.Logging.
Installation
dotnet add package Kanject.Core.Logs.Abstractions
Targets .NET 8, .NET 9 and .NET 10. Depends on Kanject.Core.Api.Abstractions, which provides the PaginatedQueryModel request base and the PayloadMetadata paging result.
Quick start
using Kanject.Core.Logs.Abstractions.Interfaces;
using Kanject.Core.Logs.Abstractions.Models;
public sealed class PaymentAudit(ILogManager logs)
{
private const string Group = "payments";
public Task<LogResponse> RecordAsync(string paymentId, string message) =>
logs.LogAsync(new LogRequest { LogGroup = Group, Log = $"{paymentId}: {message}" });
public async Task<(IReadOnlyList<GetLogsResponse> Entries, string? NextPageToken)> ReadAsync(
DateTime fromUtc, DateTime toUtc, string? pageToken)
{
var (payload, metadata) = await logs.GetLogsAsync(new GetSystemLogsRequest
{
LogGroup = Group,
StartDate = fromUtc,
EndDate = toUtc,
PageSize = 50,
PageToken = pageToken
});
return (payload.ToList(), metadata.HasNextPage ? metadata.PageToken : null);
}
}
With the DynamoDB provider, AddLogStore(...) from Kanject.Core.Logs.Provider.DynamoDb registers ILogManager (scoped), along with a scoped unit of work and repositories of its own; it leaves your application's shared NoSQL IUnitOfWork and IRepository<TEntity> registrations alone, whichever order you register the providers in. That provider indexes entries by log group and calendar month (UTC) and matches group names case-insensitively. It throws ApiValidationException when a GetLogsAsync request has no LogGroup, when EndDate is before StartDate, or when the two fall in different calendar months — query each month separately.
In that provider, Query keeps the entries whose log text contains it, case-sensitively, against the text exactly as it was logged: Query = "FAILED" matches "Payment FAILED" but not "payment failed". Search in the case your application writes its log text.
The log contract
| Member | Purpose |
|---|---|
ILogManager.LogAsync(LogRequest) |
Append an entry (LogGroup, Log); returns LogResponse with the assigned Id and CreatedOn |
ILogManager.GetLogsAsync(GetSystemLogsRequest) |
Page through entries; returns (IEnumerable<GetLogsResponse> payload, PayloadMetadata metadata) |
GetSystemLogsRequest extends PaginatedQueryModel (PageSize, PageToken) and adds LogGroup, Id, Query, StartDate and EndDate filters. Continue paging by passing metadata.PageToken back while metadata.HasNextPage is true.
ILogEntity (LogGroup, CreatedOn) is the minimal shape a provider's stored log entity implements.
A hand-written implementation for tests
ILogManager is small enough to fake without a mocking library:
using System.Collections.Concurrent;
using Kanject.Core.Api.Abstractions.Models;
using Kanject.Core.Logs.Abstractions.Interfaces;
using Kanject.Core.Logs.Abstractions.Models;
public sealed class InMemoryLogManager : ILogManager
{
private readonly ConcurrentQueue<LogResponse> _entries = new();
public Task<LogResponse> LogAsync(LogRequest model)
{
var entry = new LogResponse
{
Id = Guid.NewGuid(),
LogGroup = model.LogGroup,
Log = model.Log,
CreatedOn = DateTime.UtcNow
};
_entries.Enqueue(entry);
return Task.FromResult(entry);
}
public Task<(IEnumerable<GetLogsResponse> payload, PayloadMetadata metadata)> GetLogsAsync(
GetSystemLogsRequest model)
{
var page = _entries
.Where(e => e.LogGroup == model.LogGroup)
.Where(e => model.StartDate is null || e.CreatedOn >= model.StartDate)
.Where(e => model.EndDate is null || e.CreatedOn <= model.EndDate)
.OrderByDescending(e => e.CreatedOn)
.Take(model.PageSize > 0 ? model.PageSize : 50)
.Select(e => new GetLogsResponse
{
Id = e.Id, LogGroup = e.LogGroup, Log = e.Log, CreatedOn = e.CreatedOn
})
.ToList();
(IEnumerable<GetLogsResponse> payload, PayloadMetadata metadata) result =
(page, new PayloadMetadata(model.PageSize, pageItemCount: page.Count));
return Task.FromResult(result);
}
}
Register it in place of a provider: services.AddSingleton<ILogManager, InMemoryLogManager>();.
Distributed locks
IDbLockRepository<TEntity> guards work that must not run concurrently across instances — a scheduled job, a one-time migration:
using Kanject.Core.Logs.Abstractions.Repositories;
// JobLock is your lock entity; it implements IDbLockEntity (see below).
public sealed class NightlyReportJob(IDbLockRepository<JobLock> locks)
{
private const string LockId = "nightly-report";
public async Task RunAsync()
{
await locks.AcquireLockAsync(LockId, DateTime.UtcNow.AddMinutes(10), comment: "report run");
try
{
// ... do the work ...
}
finally
{
await locks.ReleaseLockAsync(LockId);
}
}
}
The lock entity implements IDbLockEntity (LockId, Log, InvokeCount, Ttl, plus ILogEntity's LogGroup and CreatedOn); a provider adds its own entity interfaces on top. In the DynamoDB provider, the DateTime passed to AcquireLockAsync is the lock's expiry time, not a length of time; acquiring a lock that is still held and unexpired throws ApiServiceException, and an expired lock is taken over.
Trimming and Native AOT
The package is built with the .NET trim and AOT analyzers enabled (IsAotCompatible). ILogManager carries no trimming annotations. The IDbLockRepository<TEntity> members are annotated [RequiresUnreferencedCode] and [RequiresDynamicCode], so calls to them report IL2026 / IL3050 in a trimmed or Native AOT publish.
Related packages
| Package | Role | Availability |
|---|---|---|
Kanject.Core.Api.Abstractions |
PaginatedQueryModel, PayloadMetadata and ApiServiceException |
nuget.org |
Kanject.Core.Logs.Provider.DynamoDb |
Amazon DynamoDB log store; AddLogStore(...) registers ILogManager and IDbLockRepository<TEntity> |
Commercial license (not on nuget.org) |
Kanject.Core.Logs.Provider.DynamoDb.Abstractions |
DynamoDB log and lock entities, the lock repository, and the log database context used by that provider | Commercial license (not on nuget.org) |
License
Licensed under the Kanject Code Libraries License Agreement (KCLLA); the full text ships in this package as LICENSE.md. Organizations whose trailing-twelve-month gross revenue and total funding raised are each below US$250,000 may use it at no cost under the Free Tier. At or above either threshold a commercial license is required — contact commercial@kanjectbusiness.com.
| Product | Versions 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 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 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. |
-
net10.0
- Kanject.Core.Api.Abstractions (>= 3.11.0)
-
net8.0
- Kanject.Core.Api.Abstractions (>= 3.11.0)
-
net9.0
- Kanject.Core.Api.Abstractions (>= 3.11.0)
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 |
|---|---|---|
| 3.8.0 | 0 | 10/5/2026 |
| 3.7.0 | 45 | 10/2/2026 |
| 3.6.1 | 109 | 9/27/2026 |
| 3.6.0 | 85 | 9/27/2026 |
| 3.5.7 | 91 | 9/26/2026 |
| 3.5.6 | 122 | 9/7/2026 |
| 3.5.5 | 105 | 8/27/2026 |
| 3.5.4 | 113 | 8/22/2026 |
| 3.5.3 | 125 | 8/10/2026 |
| 3.5.2 | 107 | 8/9/2026 |
| 3.5.1 | 119 | 8/5/2026 |
| 3.5.0 | 126 | 8/5/2026 |
| 3.4.0 | 118 | 8/3/2026 |
| 3.3.8 | 133 | 7/30/2026 |
| 3.3.7 | 130 | 7/18/2026 |
| 3.3.6 | 136 | 7/13/2026 |
| 3.3.5 | 125 | 7/11/2026 |
| 3.3.4 | 129 | 7/11/2026 |
| 3.3.3 | 132 | 7/9/2026 |
| 3.3.2 | 119 | 7/9/2026 |