Created
February 26, 2026 14:20
-
-
Save donpaul120/e708590843cc89d33e175cb7304d2a48 to your computer and use it in GitHub Desktop.
EmbraceDioInterceptor that also respects sending the http status code for onError
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| class CustomEmbraceDioInterceptor extends Interceptor { | |
| CustomEmbraceDioInterceptor({String Function(RequestOptions)? spanNameBuilder}) | |
| : _spanNameBuilder = spanNameBuilder ?? _defaultName; | |
| static const String _contentLengthHeader = 'Content-Length'; | |
| final String Function(RequestOptions) _spanNameBuilder; | |
| final _startTimes = HashMap<RequestOptions, int>(); | |
| static String _defaultName(RequestOptions o) => '${o.method} ${o.uri.path}'; | |
| @override | |
| void onRequest( | |
| RequestOptions options, | |
| RequestInterceptorHandler handler, | |
| ) async { | |
| _startTimes[options] = DateTime.now().millisecondsSinceEpoch; | |
| await _injectTraceparent(options); | |
| handler.next(options); | |
| } | |
| @override | |
| void onResponse( | |
| Response<dynamic> response, | |
| ResponseInterceptorHandler handler, | |
| ) { | |
| try { | |
| final request = response.requestOptions; | |
| final startTimeMs = _startTimes.remove(request) ?? 0; | |
| final endTimeMs = DateTime.now().millisecondsSinceEpoch; | |
| Embrace.instance.recordCompletedSpan( | |
| _spanNameBuilder(request), | |
| startTimeMs, | |
| endTimeMs, | |
| attributes: { | |
| 'http.request.method': request.method, | |
| 'url.full': request.uri.toString(), | |
| 'http.response.status_code': '${response.statusCode ?? 0}', | |
| 'http.request.body.size': '${_bytesSent(request)}', | |
| 'http.response.body.size': '${_bytesReceived(response)}', | |
| }, | |
| ); | |
| } catch (e) { | |
| EmbracePlatform.instance | |
| .logInternalError('Could not capture network request', e.toString()); | |
| } finally { | |
| handler.next(response); | |
| } | |
| } | |
| @override | |
| void onError(DioException err, ErrorInterceptorHandler handler) { | |
| try { | |
| final request = err.requestOptions; | |
| final startTimeMs = _startTimes.remove(request) ?? 0; | |
| final endTimeMs = DateTime.now().millisecondsSinceEpoch; | |
| final statusCode = err.response?.statusCode; | |
| Logger.getLogger().info("onError - Recording Span"); | |
| Embrace.instance.recordCompletedSpan( | |
| _spanNameBuilder(request), | |
| startTimeMs, | |
| endTimeMs, | |
| errorCode: statusCode != null ? ErrorCode.failure : ErrorCode.unknown, | |
| attributes: { | |
| 'http.request.method': request.method, | |
| 'url.full': request.uri.toString(), | |
| if (statusCode != null) ...{ | |
| 'http.response.status_code': '$statusCode', | |
| 'http.request.body.size': '${_bytesSent(request)}', | |
| 'http.response.body.size': '${_bytesReceived(err.response)}', | |
| } else | |
| 'error.message': err.message ?? '', | |
| }, | |
| ); | |
| } catch (e) { | |
| EmbracePlatform.instance | |
| .logInternalError('Could not capture network error', e.toString()); | |
| } finally { | |
| handler.next(err); | |
| } | |
| } | |
| Future<void> _injectTraceparent(RequestOptions options) async { | |
| if (options.headers.containsKey('traceparent')) return; | |
| final traceparent = | |
| await Embrace.instance.generateW3cTraceparent(null, null); | |
| if (traceparent != null) { | |
| options.headers['traceparent'] = traceparent; | |
| } | |
| } | |
| int _bytesSent(RequestOptions request) { | |
| final data = request.data; | |
| // TODO: do better calculation | |
| return data is String ? data.length : 0; | |
| } | |
| int _bytesReceived(Response<dynamic>? response) { | |
| if (response == null) return 0; | |
| final header = response.headers.value(_contentLengthHeader); | |
| if (header != null) return int.tryParse(header) ?? 0; | |
| final type = response.requestOptions.responseType; | |
| if (response.data != null && | |
| (type == ResponseType.plain || type == ResponseType.json)) { | |
| return response.data.toString().length; | |
| } | |
| return 0; | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment