toMiddleware method
Builds the shelf middleware this value configures.
Implementation
@override
Middleware toMiddleware() {
return (Handler inner) {
return (Request request) async {
final parts = RequestParts.of(request);
final parent = TraceContext.parse(parts.headers['traceparent']);
final span = Span(
name: '${parts.method} ${request.requestedUri.path}',
context: parent?.child() ?? TraceContext.start(),
parentSpanId: parent?.spanId,
attributes: {
'http.request.method': parts.method,
'url.path': request.requestedUri.path,
if (request.requestedUri.query.isNotEmpty)
'url.query': request.requestedUri.query,
if (serviceName != null) 'service.name': serviceName,
},
);
// The router fills this on the way through; a layer wraps the matcher,
// so it cannot read the route off its own request.
final matched = MatchedRouteSlot();
final traced = request.change(
context: {matchedRouteSlotKey: matched},
);
try {
final response = await CurrentSpan.runWith(
span,
() => Future.sync(() => inner(traced)),
);
// The route is only known once the matcher has run, so the span is
// renamed here rather than guessed up front. An explicit `nameSpan`
// wins: a handler that said what it is doing knows better than the
// route table, which is the case for a mounted service.
if (matched.route case final route?
when !span.attributes.containsKey('http.route')) {
span
..name = '${parts.method} $route'
..setAttribute('http.route', route);
}
span
..setAttribute('http.response.status_code', response.statusCode)
// A 4xx is the server working correctly, so only 5xx is an error;
// marking 404 as failure buries the failures that matter.
..end(
status: response.statusCode >= 500
? SpanStatus.error
: SpanStatus.ok);
_export(span);
return response.change(
headers: {'traceparent': span.context.traceparent},
);
} on HijackException {
// Taking over the socket is how a WebSocket upgrade *succeeds*, and
// `shelf` signals it by throwing. Treated as an error, every working
// WebSocket endpoint reads as a total failure in a trace backend —
// the connections that succeeded are exactly the ones marked broken.
span
..setAttribute('http.response.status_code', 101)
..end(status: SpanStatus.ok);
_export(span);
rethrow;
} on Object catch (error) {
span
..setAttribute('error.type', error.runtimeType.toString())
..end(status: SpanStatus.error);
_export(span);
rethrow;
}
};
};
}