feat(tvix/tracing): http propagation for axum
It introduces a new accept_trace function for axum0.7 which can be used to accept a header trace from a received request. This function can be used for tonic 0.12 once that version is released, and the specific `accept_trace` function within `tvix_tracing::propagate::tonic` can then be removed. This also integrates http propagation into the nar_bridge crate. Change-Id: I46dcc797d494bb3977c2633753e7060d88d29129 Reviewed-on: https://cl.tvl.fyi/c/depot/+/11925 Reviewed-by: Brian Olsen <me@griff.name> Tested-by: BuildkiteCI Reviewed-by: Simon Hauser <simon.hauser@helsinki-systems.de> Reviewed-by: flokli <flokli@flokli.de>
This commit is contained in:
		
							parent
							
								
									fdc0cf0c94
								
							
						
					
					
						commit
						1515a970be
					
				
					 8 changed files with 207 additions and 11 deletions
				
			
		
							
								
								
									
										22
									
								
								tvix/Cargo.lock
									
										
									
										generated
									
									
									
								
							
							
						
						
									
										22
									
								
								tvix/Cargo.lock
									
										
									
										generated
									
									
									
								
							|  | @ -2262,6 +2262,8 @@ dependencies = [ | ||||||
|  "tokio-util", |  "tokio-util", | ||||||
|  "tonic", |  "tonic", | ||||||
|  "tonic-build", |  "tonic-build", | ||||||
|  |  "tower", | ||||||
|  |  "tower-http 0.5.2", | ||||||
|  "tracing", |  "tracing", | ||||||
|  "tracing-subscriber", |  "tracing-subscriber", | ||||||
|  "tvix-castore", |  "tvix-castore", | ||||||
|  | @ -4350,6 +4352,23 @@ dependencies = [ | ||||||
|  "tracing", |  "tracing", | ||||||
| ] | ] | ||||||
| 
 | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "tower-http" | ||||||
|  | version = "0.5.2" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "1e9cd434a998747dd2c4276bc96ee2e0c7a2eadf3cae88e52be55a05fa9053f5" | ||||||
|  | dependencies = [ | ||||||
|  |  "bitflags 2.4.2", | ||||||
|  |  "bytes", | ||||||
|  |  "http 1.1.0", | ||||||
|  |  "http-body 1.0.0", | ||||||
|  |  "http-body-util", | ||||||
|  |  "pin-project-lite", | ||||||
|  |  "tower-layer", | ||||||
|  |  "tower-service", | ||||||
|  |  "tracing", | ||||||
|  | ] | ||||||
|  | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "tower-layer" | name = "tower-layer" | ||||||
| version = "0.3.2" | version = "0.3.2" | ||||||
|  | @ -4755,7 +4774,7 @@ dependencies = [ | ||||||
|  "tonic-build", |  "tonic-build", | ||||||
|  "tonic-reflection", |  "tonic-reflection", | ||||||
|  "tower", |  "tower", | ||||||
|  "tower-http", |  "tower-http 0.4.4", | ||||||
|  "tracing", |  "tracing", | ||||||
|  "tracing-indicatif", |  "tracing-indicatif", | ||||||
|  "tvix-castore", |  "tvix-castore", | ||||||
|  | @ -4768,6 +4787,7 @@ dependencies = [ | ||||||
| name = "tvix-tracing" | name = "tvix-tracing" | ||||||
| version = "0.1.0" | version = "0.1.0" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  |  "axum 0.7.5", | ||||||
|  "http 0.2.11", |  "http 0.2.11", | ||||||
|  "indicatif", |  "indicatif", | ||||||
|  "lazy_static", |  "lazy_static", | ||||||
|  |  | ||||||
							
								
								
									
										116
									
								
								tvix/Cargo.nix
									
										
									
									
									
								
							
							
						
						
									
										116
									
								
								tvix/Cargo.nix
									
										
									
									
									
								
							|  | @ -6941,6 +6941,15 @@ rec { | ||||||
|             packageId = "tonic"; |             packageId = "tonic"; | ||||||
|             features = [ "tls" "tls-roots" ]; |             features = [ "tls" "tls-roots" ]; | ||||||
|           } |           } | ||||||
|  |           { | ||||||
|  |             name = "tower"; | ||||||
|  |             packageId = "tower"; | ||||||
|  |           } | ||||||
|  |           { | ||||||
|  |             name = "tower-http"; | ||||||
|  |             packageId = "tower-http 0.5.2"; | ||||||
|  |             features = [ "trace" ]; | ||||||
|  |           } | ||||||
|           { |           { | ||||||
|             name = "tracing"; |             name = "tracing"; | ||||||
|             packageId = "tracing"; |             packageId = "tracing"; | ||||||
|  | @ -6960,7 +6969,7 @@ rec { | ||||||
|           { |           { | ||||||
|             name = "tvix-tracing"; |             name = "tvix-tracing"; | ||||||
|             packageId = "tvix-tracing"; |             packageId = "tvix-tracing"; | ||||||
|             features = [ "tonic" ]; |             features = [ "tonic" "axum" ]; | ||||||
|           } |           } | ||||||
|           { |           { | ||||||
|             name = "url"; |             name = "url"; | ||||||
|  | @ -13669,7 +13678,7 @@ rec { | ||||||
|         }; |         }; | ||||||
|         resolvedDefaultFeatures = [ "__common" "balance" "buffer" "default" "discover" "futures-core" "futures-util" "indexmap" "limit" "load" "log" "make" "pin-project" "pin-project-lite" "rand" "ready-cache" "slab" "timeout" "tokio" "tokio-util" "tracing" "util" ]; |         resolvedDefaultFeatures = [ "__common" "balance" "buffer" "default" "discover" "futures-core" "futures-util" "indexmap" "limit" "load" "log" "make" "pin-project" "pin-project-lite" "rand" "ready-cache" "slab" "timeout" "tokio" "tokio-util" "tracing" "util" ]; | ||||||
|       }; |       }; | ||||||
|       "tower-http" = rec { |       "tower-http 0.4.4" = rec { | ||||||
|         crateName = "tower-http"; |         crateName = "tower-http"; | ||||||
|         version = "0.4.4"; |         version = "0.4.4"; | ||||||
|         edition = "2018"; |         edition = "2018"; | ||||||
|  | @ -13769,6 +13778,99 @@ rec { | ||||||
|         }; |         }; | ||||||
|         resolvedDefaultFeatures = [ "default" "trace" "tracing" ]; |         resolvedDefaultFeatures = [ "default" "trace" "tracing" ]; | ||||||
|       }; |       }; | ||||||
|  |       "tower-http 0.5.2" = rec { | ||||||
|  |         crateName = "tower-http"; | ||||||
|  |         version = "0.5.2"; | ||||||
|  |         edition = "2018"; | ||||||
|  |         sha256 = "1xakj3x0anp55gjqibiwvzma5iz0w9pcjsr7qk97sx4qm4sd970y"; | ||||||
|  |         authors = [ | ||||||
|  |           "Tower Maintainers <team@tower-rs.com>" | ||||||
|  |         ]; | ||||||
|  |         dependencies = [ | ||||||
|  |           { | ||||||
|  |             name = "bitflags"; | ||||||
|  |             packageId = "bitflags 2.4.2"; | ||||||
|  |           } | ||||||
|  |           { | ||||||
|  |             name = "bytes"; | ||||||
|  |             packageId = "bytes"; | ||||||
|  |           } | ||||||
|  |           { | ||||||
|  |             name = "http"; | ||||||
|  |             packageId = "http 1.1.0"; | ||||||
|  |           } | ||||||
|  |           { | ||||||
|  |             name = "http-body"; | ||||||
|  |             packageId = "http-body 1.0.0"; | ||||||
|  |           } | ||||||
|  |           { | ||||||
|  |             name = "http-body-util"; | ||||||
|  |             packageId = "http-body-util"; | ||||||
|  |           } | ||||||
|  |           { | ||||||
|  |             name = "pin-project-lite"; | ||||||
|  |             packageId = "pin-project-lite"; | ||||||
|  |           } | ||||||
|  |           { | ||||||
|  |             name = "tower-layer"; | ||||||
|  |             packageId = "tower-layer"; | ||||||
|  |           } | ||||||
|  |           { | ||||||
|  |             name = "tower-service"; | ||||||
|  |             packageId = "tower-service"; | ||||||
|  |           } | ||||||
|  |           { | ||||||
|  |             name = "tracing"; | ||||||
|  |             packageId = "tracing"; | ||||||
|  |             optional = true; | ||||||
|  |             usesDefaultFeatures = false; | ||||||
|  |           } | ||||||
|  |         ]; | ||||||
|  |         devDependencies = [ | ||||||
|  |           { | ||||||
|  |             name = "bytes"; | ||||||
|  |             packageId = "bytes"; | ||||||
|  |           } | ||||||
|  |         ]; | ||||||
|  |         features = { | ||||||
|  |           "async-compression" = [ "dep:async-compression" ]; | ||||||
|  |           "auth" = [ "base64" "validate-request" ]; | ||||||
|  |           "base64" = [ "dep:base64" ]; | ||||||
|  |           "catch-panic" = [ "tracing" "futures-util/std" ]; | ||||||
|  |           "compression-br" = [ "async-compression/brotli" "futures-core" "tokio-util" "tokio" ]; | ||||||
|  |           "compression-deflate" = [ "async-compression/zlib" "futures-core" "tokio-util" "tokio" ]; | ||||||
|  |           "compression-full" = [ "compression-br" "compression-deflate" "compression-gzip" "compression-zstd" ]; | ||||||
|  |           "compression-gzip" = [ "async-compression/gzip" "futures-core" "tokio-util" "tokio" ]; | ||||||
|  |           "compression-zstd" = [ "async-compression/zstd" "futures-core" "tokio-util" "tokio" ]; | ||||||
|  |           "decompression-br" = [ "async-compression/brotli" "futures-core" "tokio-util" "tokio" ]; | ||||||
|  |           "decompression-deflate" = [ "async-compression/zlib" "futures-core" "tokio-util" "tokio" ]; | ||||||
|  |           "decompression-full" = [ "decompression-br" "decompression-deflate" "decompression-gzip" "decompression-zstd" ]; | ||||||
|  |           "decompression-gzip" = [ "async-compression/gzip" "futures-core" "tokio-util" "tokio" ]; | ||||||
|  |           "decompression-zstd" = [ "async-compression/zstd" "futures-core" "tokio-util" "tokio" ]; | ||||||
|  |           "follow-redirect" = [ "futures-util" "iri-string" "tower/util" ]; | ||||||
|  |           "fs" = [ "futures-util" "tokio/fs" "tokio-util/io" "tokio/io-util" "dep:http-range-header" "mime_guess" "mime" "percent-encoding" "httpdate" "set-status" "futures-util/alloc" "tracing" ]; | ||||||
|  |           "full" = [ "add-extension" "auth" "catch-panic" "compression-full" "cors" "decompression-full" "follow-redirect" "fs" "limit" "map-request-body" "map-response-body" "metrics" "normalize-path" "propagate-header" "redirect" "request-id" "sensitive-headers" "set-header" "set-status" "timeout" "trace" "util" "validate-request" ]; | ||||||
|  |           "futures-core" = [ "dep:futures-core" ]; | ||||||
|  |           "futures-util" = [ "dep:futures-util" ]; | ||||||
|  |           "httpdate" = [ "dep:httpdate" ]; | ||||||
|  |           "iri-string" = [ "dep:iri-string" ]; | ||||||
|  |           "metrics" = [ "tokio/time" ]; | ||||||
|  |           "mime" = [ "dep:mime" ]; | ||||||
|  |           "mime_guess" = [ "dep:mime_guess" ]; | ||||||
|  |           "percent-encoding" = [ "dep:percent-encoding" ]; | ||||||
|  |           "request-id" = [ "uuid" ]; | ||||||
|  |           "timeout" = [ "tokio/time" ]; | ||||||
|  |           "tokio" = [ "dep:tokio" ]; | ||||||
|  |           "tokio-util" = [ "dep:tokio-util" ]; | ||||||
|  |           "tower" = [ "dep:tower" ]; | ||||||
|  |           "trace" = [ "tracing" ]; | ||||||
|  |           "tracing" = [ "dep:tracing" ]; | ||||||
|  |           "util" = [ "tower" ]; | ||||||
|  |           "uuid" = [ "dep:uuid" ]; | ||||||
|  |           "validate-request" = [ "mime" ]; | ||||||
|  |         }; | ||||||
|  |         resolvedDefaultFeatures = [ "default" "trace" "tracing" ]; | ||||||
|  |       }; | ||||||
|       "tower-layer" = rec { |       "tower-layer" = rec { | ||||||
|         crateName = "tower-layer"; |         crateName = "tower-layer"; | ||||||
|         version = "0.3.2"; |         version = "0.3.2"; | ||||||
|  | @ -15335,7 +15437,7 @@ rec { | ||||||
|           } |           } | ||||||
|           { |           { | ||||||
|             name = "tower-http"; |             name = "tower-http"; | ||||||
|             packageId = "tower-http"; |             packageId = "tower-http 0.4.4"; | ||||||
|             features = [ "trace" ]; |             features = [ "trace" ]; | ||||||
|           } |           } | ||||||
|           { |           { | ||||||
|  | @ -15413,6 +15515,11 @@ rec { | ||||||
|         edition = "2021"; |         edition = "2021"; | ||||||
|         src = lib.cleanSourceWith { filter = sourceFilter; src = ./tracing; }; |         src = lib.cleanSourceWith { filter = sourceFilter; src = ./tracing; }; | ||||||
|         dependencies = [ |         dependencies = [ | ||||||
|  |           { | ||||||
|  |             name = "axum"; | ||||||
|  |             packageId = "axum 0.7.5"; | ||||||
|  |             optional = true; | ||||||
|  |           } | ||||||
|           { |           { | ||||||
|             name = "http"; |             name = "http"; | ||||||
|             packageId = "http 0.2.11"; |             packageId = "http 0.2.11"; | ||||||
|  | @ -15494,12 +15601,13 @@ rec { | ||||||
|           } |           } | ||||||
|         ]; |         ]; | ||||||
|         features = { |         features = { | ||||||
|  |           "axum" = [ "dep:axum" ]; | ||||||
|           "otlp" = [ "dep:tracing-opentelemetry" "dep:opentelemetry" "dep:opentelemetry-otlp" "dep:opentelemetry_sdk" "dep:opentelemetry-http" "reqwest-tracing?/opentelemetry_0_22" ]; |           "otlp" = [ "dep:tracing-opentelemetry" "dep:opentelemetry" "dep:opentelemetry-otlp" "dep:opentelemetry_sdk" "dep:opentelemetry-http" "reqwest-tracing?/opentelemetry_0_22" ]; | ||||||
|           "reqwest" = [ "dep:reqwest-tracing" ]; |           "reqwest" = [ "dep:reqwest-tracing" ]; | ||||||
|           "tonic" = [ "dep:tonic" "dep:http" ]; |           "tonic" = [ "dep:tonic" "dep:http" ]; | ||||||
|           "tracy" = [ "dep:tracing-tracy" ]; |           "tracy" = [ "dep:tracing-tracy" ]; | ||||||
|         }; |         }; | ||||||
|         resolvedDefaultFeatures = [ "default" "otlp" "reqwest" "tonic" "tracy" ]; |         resolvedDefaultFeatures = [ "axum" "default" "otlp" "reqwest" "tonic" "tracy" ]; | ||||||
|       }; |       }; | ||||||
|       "typeid" = rec { |       "typeid" = rec { | ||||||
|         crateName = "typeid"; |         crateName = "typeid"; | ||||||
|  |  | ||||||
|  | @ -5,6 +5,8 @@ edition = "2021" | ||||||
| 
 | 
 | ||||||
| [dependencies] | [dependencies] | ||||||
| axum = { version = "0.7.5", features = ["http2"] } | axum = { version = "0.7.5", features = ["http2"] } | ||||||
|  | tower = "0.4.13" | ||||||
|  | tower-http = { version = "0.5", features = ["trace"] } | ||||||
| bytes = "1.4.0" | bytes = "1.4.0" | ||||||
| clap = { version = "4.0", features = ["derive", "env"] } | clap = { version = "4.0", features = ["derive", "env"] } | ||||||
| data-encoding = "2.3.3" | data-encoding = "2.3.3" | ||||||
|  | @ -19,7 +21,7 @@ tokio-util = { version = "0.7.9", features = ["io", "io-util", "compat"] } | ||||||
| tonic = { version = "0.11.0", features = ["tls", "tls-roots"] } | tonic = { version = "0.11.0", features = ["tls", "tls-roots"] } | ||||||
| tvix-castore = { path = "../castore" } | tvix-castore = { path = "../castore" } | ||||||
| tvix-store = { path = "../store" } | tvix-store = { path = "../store" } | ||||||
| tvix-tracing = { path = "../tracing", features = ["tonic"] } | tvix-tracing = { path = "../tracing", features = ["tonic", "axum"] } | ||||||
| tracing = "0.1.37" | tracing = "0.1.37" | ||||||
| tracing-subscriber = "0.3.16" | tracing-subscriber = "0.3.16" | ||||||
| url = "2.4.0" | url = "2.4.0" | ||||||
|  |  | ||||||
|  | @ -1,5 +1,7 @@ | ||||||
| use clap::Parser; | use clap::Parser; | ||||||
| use nar_bridge::AppState; | use nar_bridge::AppState; | ||||||
|  | use tower::ServiceBuilder; | ||||||
|  | use tower_http::trace::{DefaultMakeSpan, TraceLayer}; | ||||||
| use tracing::info; | use tracing::info; | ||||||
| 
 | 
 | ||||||
| /// Expose the Nix HTTP Binary Cache protocol for a tvix-store.
 | /// Expose the Nix HTTP Binary Cache protocol for a tvix-store.
 | ||||||
|  | @ -57,7 +59,19 @@ async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> { | ||||||
| 
 | 
 | ||||||
|     let state = AppState::new(blob_service, directory_service, path_info_service); |     let state = AppState::new(blob_service, directory_service, path_info_service); | ||||||
| 
 | 
 | ||||||
|     let app = nar_bridge::gen_router(cli.priority).with_state(state); |     let app = nar_bridge::gen_router(cli.priority) | ||||||
|  |         .layer( | ||||||
|  |             ServiceBuilder::new() | ||||||
|  |                 .layer( | ||||||
|  |                     TraceLayer::new_for_http().make_span_with( | ||||||
|  |                         DefaultMakeSpan::new() | ||||||
|  |                             .level(tracing::Level::INFO) | ||||||
|  |                             .include_headers(true), | ||||||
|  |                     ), | ||||||
|  |                 ) | ||||||
|  |                 .map_request(tvix_tracing::propagate::axum::accept_trace), | ||||||
|  |         ) | ||||||
|  |         .with_state(state); | ||||||
| 
 | 
 | ||||||
|     let listen_address = &cli.listen_args.listen_address.unwrap_or_else(|| { |     let listen_address = &cli.listen_args.listen_address.unwrap_or_else(|| { | ||||||
|         "[::]:8000" |         "[::]:8000" | ||||||
|  |  | ||||||
|  | @ -24,6 +24,8 @@ http  = { version = "0.2.11", optional = true } | ||||||
| 
 | 
 | ||||||
| reqwest-tracing = { version = "0.4.8", default-features = false, optional = true } | reqwest-tracing = { version = "0.4.8", default-features = false, optional = true } | ||||||
| 
 | 
 | ||||||
|  | axum = { version = "0.7.5", optional = true } | ||||||
|  | 
 | ||||||
| [features] | [features] | ||||||
| default = [] | default = [] | ||||||
| otlp = [ | otlp = [ | ||||||
|  | @ -44,6 +46,9 @@ tonic = [ | ||||||
| reqwest = [ | reqwest = [ | ||||||
|   "dep:reqwest-tracing", |   "dep:reqwest-tracing", | ||||||
| ] | ] | ||||||
|  | axum = [ | ||||||
|  |   "dep:axum", | ||||||
|  | ] | ||||||
| 
 | 
 | ||||||
| [lints] | [lints] | ||||||
| workspace = true | workspace = true | ||||||
|  |  | ||||||
|  | @ -6,6 +6,6 @@ | ||||||
|   meta.ci.targets = lib.filter (x: lib.hasPrefix "with-features" x || x == "no-features") (lib.attrNames passthru); |   meta.ci.targets = lib.filter (x: lib.hasPrefix "with-features" x || x == "no-features") (lib.attrNames passthru); | ||||||
|   passthru = depot.tvix.utils.mkFeaturePowerset { |   passthru = depot.tvix.utils.mkFeaturePowerset { | ||||||
|     inherit (old) crateName; |     inherit (old) crateName; | ||||||
|     features = [ "otlp" "tracy" "tonic" "reqwest" ]; |     features = [ "otlp" "tracy" "tonic" "reqwest" "axum" ]; | ||||||
|   }; |   }; | ||||||
| }) | }) | ||||||
|  |  | ||||||
							
								
								
									
										48
									
								
								tvix/tracing/src/propagate/axum.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								tvix/tracing/src/propagate/axum.rs
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,48 @@ | ||||||
|  | #[cfg(feature = "otlp")] | ||||||
|  | use opentelemetry::{global, propagation::Extractor}; | ||||||
|  | #[cfg(feature = "otlp")] | ||||||
|  | use tracing_opentelemetry::OpenTelemetrySpanExt; | ||||||
|  | 
 | ||||||
|  | // TODO: accept_trace can be shared with tonic, as soon as tonic upstream has a release with
 | ||||||
|  | // support for axum07. Latest master already has support for axum07 but there is not release yet:
 | ||||||
|  | // https://github.com/hyperium/tonic/pull/1740
 | ||||||
|  | 
 | ||||||
|  | /// Trace context propagation: associate the current span with the otlp trace of the given request,
 | ||||||
|  | /// if any and valid. This only sets the parent trace if the otlp feature is also enabled.
 | ||||||
|  | pub fn accept_trace<B>(request: axum::http::Request<B>) -> axum::http::Request<B> { | ||||||
|  |     // we only extract and set a parent trace if otlp feature is enabled, otherwise this feature is
 | ||||||
|  |     // an noop and we return the request as is
 | ||||||
|  |     #[cfg(feature = "otlp")] | ||||||
|  |     { | ||||||
|  |         // Current context, if no or invalid data is received.
 | ||||||
|  |         let parent_context = global::get_text_map_propagator(|propagator| { | ||||||
|  |             propagator.extract(&HeaderExtractor(request.headers())) | ||||||
|  |         }); | ||||||
|  |         tracing::Span::current().set_parent(parent_context); | ||||||
|  |     } | ||||||
|  |     request | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /// Helper for extracting headers from HTTP Requests. This is used for OpenTelemetry context
 | ||||||
|  | /// propagation over HTTP.
 | ||||||
|  | #[cfg(feature = "otlp")] | ||||||
|  | struct HeaderExtractor<'a>(&'a axum::http::HeaderMap); | ||||||
|  | 
 | ||||||
|  | #[cfg(feature = "otlp")] | ||||||
|  | impl<'a> Extractor for HeaderExtractor<'a> { | ||||||
|  |     /// Get a value for a key from the HeaderMap.  If the value is not valid ASCII, returns None.
 | ||||||
|  |     fn get(&self, key: &str) -> Option<&str> { | ||||||
|  |         self.0.get(key).and_then(|v| { | ||||||
|  |             let s = v.to_str(); | ||||||
|  |             if let Err(ref error) = s { | ||||||
|  |                 tracing::warn!(%error, ?v, "cannot convert header value to ASCII") | ||||||
|  |             }; | ||||||
|  |             s.ok() | ||||||
|  |         }) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /// Collect all the keys from the HeaderMap.
 | ||||||
|  |     fn keys(&self) -> Vec<&str> { | ||||||
|  |         self.0.keys().map(|k| k.as_str()).collect() | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | @ -4,6 +4,5 @@ pub mod tonic; | ||||||
| #[cfg(feature = "reqwest")] | #[cfg(feature = "reqwest")] | ||||||
| pub mod reqwest; | pub mod reqwest; | ||||||
| 
 | 
 | ||||||
| // TODO: Helper library for axum or another http server, see
 | #[cfg(feature = "axum")] | ||||||
| // https://github.com/hseeberger/hello-tracing-rs/blob/main/hello-tracing-common/src/otel/http.rs
 | pub mod axum; | ||||||
| // as an example and we can reuse tonic::accept_trace fun, at least for a tower::ServiceBuilder
 |  | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue