DragoAnt.System.Text.Json.Observer.Http
2.0.0-beta.1
dotnet add package DragoAnt.System.Text.Json.Observer.Http --version 2.0.0-beta.1
NuGet\Install-Package DragoAnt.System.Text.Json.Observer.Http -Version 2.0.0-beta.1
<PackageReference Include="DragoAnt.System.Text.Json.Observer.Http" Version="2.0.0-beta.1" />
<PackageVersion Include="DragoAnt.System.Text.Json.Observer.Http" Version="2.0.0-beta.1" />
<PackageReference Include="DragoAnt.System.Text.Json.Observer.Http" />
paket add DragoAnt.System.Text.Json.Observer.Http --version 2.0.0-beta.1
#r "nuget: DragoAnt.System.Text.Json.Observer.Http, 2.0.0-beta.1"
#:package DragoAnt.System.Text.Json.Observer.Http@2.0.0-beta.1
#addin nuget:?package=DragoAnt.System.Text.Json.Observer.Http&version=2.0.0-beta.1&prerelease
#tool nuget:?package=DragoAnt.System.Text.Json.Observer.Http&version=2.0.0-beta.1&prerelease
DragoAnt.System.Text.Json.Observer.Http
Logs masked JSON request and response bodies of an HttpClient, using DragoAnt.System.Text.Json.Observer to mask them.
Getting started
dotnet add package DragoAnt.System.Text.Json.Observer.Http
Targets net8.0, net9.0 and net10.0.
Usage
using System.Net.Http.Json;
using DragoAnt.System.Text.Json.Observer;
using DragoAnt.System.Text.Json.Observer.Http;
services.AddSingleton<IJsonBodyMaskerProvider, PaymentMaskers>();
services.AddHttpClient("PaymentApi", client => client.BaseAddress = new Uri("https://api.example.com/"))
.AddJsonBodyLogging(options =>
{
options.When = JsonBodyLogWhen.OnFailure;
options.MaxBodyBytes = 8 * 1024;
});
// At the call site: attach the body model types and an operation name.
using var request = new HttpRequestMessage(HttpMethod.Post, "charges")
{
Content = JsonContent.Create(charge),
};
using var response = await httpClient.SendWithBodyLoggingAsync<ChargeRequest, ChargeResponse>(
request, "Charge", cancellationToken);
public sealed class PaymentMaskers : IJsonBodyMaskerProvider
{
private static readonly JsonObserver Charge = JsonObserver.Obj(rules => rules.Match("cardNumber").Mask("****", MaskNulls.Mask));
// null logs the body as "[body withheld]".
public JsonObserver? GetMasker(Type? modelType, string clientName) =>
modelType == typeof(ChargeRequest) ? Charge : null;
}
Without an IJsonBodyMaskerProvider every value of an object or array body is masked. Each logged call becomes one JsonBodyLogEntry, written by an IJsonBodyLogSink; the default sink writes one structured ILogger message, at Information for a success and Warning otherwise. Register your own IJsonBodyLogSink to send entries elsewhere.
Behaviour
- Which calls —
WhenisNever,OnFailure(the default: a non-success status code, an exception, a cancellation or a timeout) orAlways.TimeoutmeansHttpClient.Timeoutelapsed for a call throughSendWithBodyLoggingAsyncwhile the caller's token was not canceled. Every other cancellation, and any call whenHttpClient.Timeoutis infinite, isCanceled. - Bodies — at most
MaxBodyBytesbytes of each body are read and masked (default 4096; 0 turns body logging off).RequestBodyStatusandResponseBodyStatussay what was logged:Masked,Truncated,Invalid,Unrecognized,Incomplete,Withheld,Skipped,NotBuffered,RaworFailed. When no JSON could be written, the body is a marker such as[body not JSON]or[body not logged: text/plain]. - Masking options —
MaskOptionspassesHashKey,HashKeyId,MaxValueBytes,MaxDepth,Strategy,IgnoreNulls,Indented,RelaxedEscapingandNameCaseInsensitiveto the masker.MaxOutputBytesis capped atMaxBodyBytes, andCommentsis alwaysDropAll. - Skipped bodies — a non-JSON media type, a charset other than UTF-8, or a
Content-Encodingsuch as gzip. A requestStreamContentwhose stream cannot be read twice is logged as[body not buffered]. - The caller is unaffected — the response is returned as soon as its headers arrive, so
HttpCompletionOption.ResponseHeadersReadkeeps streaming; the body stays readable in full; the original exception of a failed call is rethrown with its stack trace; and a failure inside logging (a throwing sink, masker provider or logger) is reported through the handler's logger and never reaches the caller. - When the entry is written — the response body is captured in the background, so the entry is written once
MaxBodyBytesbytes were captured, the body ended, the read failed or the caller disposed the response. - Never logged — the query string:
Pathholds the path only. IncludeSensitive— logs bodies unmasked, marks the entryBodyUnmaskedand logs a warning. Use it for local debugging only.- Options are named after the client and read on every call, so a reload applies to the next call.
services.ConfigureAll<JsonBodyLoggingOptions>(...)changes every client. CallingAddJsonBodyLoggingtwice for one client adds the handler once.
Links
- Source and full documentation: https://github.com/DragoAnt/Extensions.System.Text.Json
- Release notes: https://github.com/DragoAnt/Extensions.System.Text.Json/releases
| 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 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. |
-
net10.0
- DragoAnt.System.Text.Json.Observer (>= 2.0.0-beta.1)
- Microsoft.Extensions.Http (>= 10.0.12)
- Microsoft.Extensions.Logging.Abstractions (>= 10.0.12)
- Microsoft.Extensions.Options (>= 10.0.12)
-
net8.0
- DragoAnt.System.Text.Json.Observer (>= 2.0.0-beta.1)
- Microsoft.Extensions.Http (>= 8.0.1)
- Microsoft.Extensions.Logging.Abstractions (>= 8.0.2)
- Microsoft.Extensions.Options (>= 8.0.2)
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 |
|---|---|---|
| 2.0.0-beta.1 | 69 | 10/22/2026 |