Logging Integration
The Hiero SDK provides hooks for inspecting every protobuf message sent to and received from the Hedera network. This gives you full visibility into HAPI request/response traffic without needing a network sniffer.
HAPI request/response hooks
IConsensusContext exposes two callbacks:
| Property | Signature | When called |
|---|---|---|
OnSendingRequest |
Action<IMessage>? |
Just before the protobuf is sent to the network |
OnResponseReceived |
Action<int, IMessage>? |
After receiving a response; the int is the retry attempt (0 = first try) |
Both callbacks receive Google.Protobuf.IMessage — the raw protobuf object. Use JsonFormatter to render it as readable JSON.
Basic example: log all HAPI traffic
using Google.Protobuf;
var formatter = new JsonFormatter(JsonFormatter.Settings.Default);
await using var client = new ConsensusClient(ctx =>
{
ctx.Endpoint = new ConsensusNodeEndpoint(nodeAccount, new Uri(endpointUrl));
ctx.Payer = payerAccount;
ctx.Signatory = new Signatory(payerKey);
ctx.OnSendingRequest = message =>
{
Console.WriteLine($"[TX] {message.Descriptor.Name}");
Console.WriteLine(formatter.Format(message));
};
ctx.OnResponseReceived = (retry, message) =>
{
Console.WriteLine($"[RX attempt {retry}] {message.Descriptor.Name}");
Console.WriteLine(formatter.Format(message));
};
});
// Every call now logs the raw protobuf traffic
var balance = await client.GetAccountBalanceAsync(account);
Integrate with ILogger
Wire the hooks into your application's logging pipeline:
builder.Services.AddSingleton<ConsensusClient>(sp =>
{
var config = sp.GetRequiredService<IConfiguration>();
var logger = sp.GetRequiredService<ILogger<ConsensusClient>>();
var formatter = new JsonFormatter(JsonFormatter.Settings.Default);
return new ConsensusClient(ctx =>
{
ctx.Endpoint = new ConsensusNodeEndpoint(/* ... */);
ctx.Payer = /* ... */;
ctx.Signatory = /* ... */;
ctx.OnSendingRequest = message =>
logger.LogDebug("HAPI TX: {Type} {Body}",
message.Descriptor.Name,
formatter.Format(message));
ctx.OnResponseReceived = (retry, message) =>
logger.LogDebug("HAPI RX (attempt {Retry}): {Type} {Body}",
retry,
message.Descriptor.Name,
formatter.Format(message));
});
});
Per-call hooks via configure callback
Attach hooks to a single call without affecting the client-wide configuration:
var receipt = await client.TransferAsync(from, to, amount, ctx =>
{
ctx.OnSendingRequest = msg => Console.WriteLine($"Sending: {msg.Descriptor.Name}");
ctx.OnResponseReceived = (r, msg) => Console.WriteLine($"Received ({r}): {msg.Descriptor.Name}");
});
What the hooks reveal
The OnSendingRequest callback receives the full Transaction or Query protobuf, which contains:
- Transaction body (operation type, parameters, fee limit, memo)
- Signature map (which keys signed)
- Transaction ID and validity window
The OnResponseReceived callback receives the network's response, including:
- Precheck status code
- For queries: the full result payload
- The retry attempt number (useful for diagnosing
BUSYretries)
gRPC channel-level logging
For lower-level HTTP/2 and connection diagnostics, enable .NET's built-in gRPC logging:
{
"Logging": {
"LogLevel": {
"Grpc": "Debug",
"System.Net.Http": "Debug"
}
}
}
Logging transaction IDs
Every SDK call returns a receipt with a TransactionId. Log it to correlate application operations with Hedera network state:
var receipt = await client.TransferAsync(from, to, amount);
logger.LogInformation("Transfer {Status} -- TxId: {TxId}",
receipt.Status, receipt.TransactionId);
Logging mirror node requests
MirrorRestClient wraps a standard HttpClient. Enable HTTP-level logging through IHttpClientFactory:
builder.Services.AddHttpClient<MirrorRestClient>((sp, http) =>
{
http.BaseAddress = new Uri(config["Hedera:MirrorRestUrl"]!);
})
.AddStandardResilienceHandler(); // optional: adds retry + logging
builder.Logging.AddFilter("System.Net.Http.HttpClient.MirrorRestClient", LogLevel.Debug);
See also
IConsensusContext.OnSendingRequest— API reference- Dependency injection — registering clients with
IHttpClientFactory - Error handling — catching and logging exceptions