#grpc #tonic #tower #tracing

tonic-debug

A debugging and diagnostics middleware for tonic gRPC servers

1 unstable release

Uses new Rust 2024

new 0.1.0 Mar 11, 2026

#23 in #tonic

Apache-2.0

55KB
1K SLoC

tonic-debug

GitHub Workflow Status License A debugging and diagnostics middleware for tonic gRPC servers.

Problem

Debugging gRPC services built with tonic is hard:

  • Messages are binary-encoded protobufs — not human-readable
  • Connection issues between clients and servers are opaque
  • Standard logging lacks the detail needed to quickly diagnose problems

Solution

tonic-debug is a Tower middleware that slots into your tonic server and provides:

  • Human-readable protobuf inspection — decodes protobuf wire format without needing .proto schemas, showing field numbers, wire types, and values
  • Connection lifecycle observability — tracks connection events (connect, disconnect, errors) with active/total counters via tower/hyper integration
  • Structured logging via tracing — all output goes through the tracing crate with structured fields for easy filtering and aggregation
  • Optional OpenTelemetry export — enable the opentelemetry feature flag to forward spans and attributes to your OTel collector

Quick Start

Add tonic-debug to your Cargo.toml:

[dependencies]
tonic-debug = "0.1"

Then add the DebugLayer to your tonic server:

use tonic_debug::DebugLayer;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Initialize tracing (e.g. with tracing-subscriber)
    tracing_subscriber::fmt()
        .with_env_filter("tonic_debug=debug")
        .init();

    tonic::transport::Server::builder()
        .layer(DebugLayer::new())
        .add_service(/* your gRPC service */)
        .serve("0.0.0.0:50051".parse()?)
        .await?;

    Ok(())
}

Configuration

Fine-tune what gets logged using the builder API or DebugConfig:

use tonic_debug::{DebugLayer, DebugConfig};

// Builder API
let layer = DebugLayer::new()
    .log_headers(true)
    .log_bodies(true)
    .log_response_frames(true)
    .max_body_bytes(8192)
    .hex_dump(false);

// Or use DebugConfig directly
let layer = DebugLayer::with_config(DebugConfig {
    log_headers: true,
    log_bodies: true,
    log_response_frames: true,
    max_body_bytes: 8192,
    hex_dump: false,
});
Option Default Description
log_headers true Log request/response headers and custom metadata
log_bodies true Log protobuf message contents
log_response_frames true Log individual response body frames as they stream
max_body_bytes 4096 Maximum bytes to capture for body inspection
hex_dump false Include hex dump of raw bytes in output

Connection Tracking

Track client connection lifecycle with ConnectionTrackerLayer:

use tonic_debug::{ConnectionTrackerLayer, ConnectionMetrics};

let metrics = ConnectionMetrics::new();
let connection_layer = ConnectionTrackerLayer::with_metrics(metrics.clone());

// Query metrics at any time
println!("Active: {}", metrics.active_connections());
println!("Total: {}", metrics.total_connections());
println!("Errors: {}", metrics.connection_errors());

Protobuf Inspection

The crate decodes raw protobuf wire format without schemas, producing output like:

gRPC frame (compressed=false, 15 bytes)
  field 1 (varint): 42
  field 2 (length-delimited): "hello world"

You can also use the inspection utilities directly:

use tonic_debug::inspect;

let data = &[0x08, 0x96, 0x01]; // field 1, varint 150
if let Some(fields) = inspect::decode_protobuf(data) {
    for field in &fields {
        println!("{}", field);
    }
}

Feature Flags

Feature Description
opentelemetry Adds OpenTelemetry semantic convention attributes and
span enrichment via tracing-opentelemetry

Enable with:

[dependencies]
tonic-debug = { version = "0.1", features = ["opentelemetry"] }

Log Levels

tonic-debug uses the following tracing levels:

Level What gets logged
INFO Request/response summary, connection events
WARN Non-OK gRPC status codes
ERROR Connection errors, response body errors
DEBUG Headers, custom metadata, response frames, protobuf fields
TRACE Stream completion summaries

🏛️ License

Licensed under either of:

🧩 Contribution

This is a free and open project and lives from contributions of the community.

See our Contribution Guide

Dependencies

~9–13MB
~157K SLoC