diff --git a/lib/src/client.dart b/lib/src/client.dart index 346c59e..ed332f7 100644 --- a/lib/src/client.dart +++ b/lib/src/client.dart @@ -15,8 +15,10 @@ import 'dart:async'; import 'dart:js'; +import 'dart:js_util' as js_util; import 'package:js/js.dart'; +import 'package:opentelemetry/api.dart'; import 'package:w_common/disposable_browser.dart'; import 'package:sockjs_client_wrapper/src/events.dart'; @@ -53,6 +55,16 @@ class SockJSClient extends Disposable { final StreamController _onOpenController = StreamController.broadcast(); + /// Context used to capture open and close events while establishing the + /// initial connection. + /// + /// This value is set to the current context during construction, then set to + /// root after the first open or close event. This is to correlate events + /// related to initial connection with the construction of this object without + /// creating long running traces due to events from much further along in the + /// sessions lifetime. + Context _context; + /// Constructs a new [SockJSClient] that will attempt to connect to a SockJS /// server at the given [uri]. /// @@ -70,7 +82,7 @@ class SockJSClient extends Disposable { /// final options = new SockJSOptions( /// transports: ['websocket', 'xhr-streaming', 'xhr-polling']); /// final client = new SockJSClient(uri, options: options); - SockJSClient(Uri uri, {SockJSOptions? options}) { + SockJSClient(Uri uri, {SockJSOptions? options}) : _context = Context.current { try { _jsClient = js_interop.SockJS(uri.toString(), null, options?._toJs()); // ignore: avoid_catches_without_on_clauses @@ -117,6 +129,8 @@ class SockJSClient extends Disposable { /// The event will include the selected transport as well as the server URL. Stream get onOpen => _onOpenController.stream; + num get timeout => js_util.getProperty(_jsClient, '_timeout'); + /// Close this client. /// /// Optionally, a [closeCode] and [reason] can be provided. @@ -153,6 +167,13 @@ class SockJSClient extends Disposable { } void _onClose(js_interop.SockJSCloseEvent event) { + spanFromContext(_context).addEvent('sockjs.close', attributes: [ + Attribute.fromString('sockjs.transport', _jsClient.transport), + Attribute.fromDouble('sockjs.timeout', timeout.toDouble()), + Attribute.fromInt('sockjs.close.code', event.code), + Attribute.fromString('sockjs.close.reason', event.reason), + ]); + _context = Context.root; // Reset context to root for future events. _onCloseController.add(SockJSCloseEvent( event.code, event.reason, @@ -165,6 +186,11 @@ class SockJSClient extends Disposable { } void _onOpen(_) { + spanFromContext(_context).addEvent('sockjs.open', attributes: [ + Attribute.fromString('sockjs.transport', _jsClient.transport), + Attribute.fromDouble('sockjs.timeout', timeout.toDouble()), + ]); + _context = Context.root; // Reset context to root for future events. _onOpenController.add(SockJSOpenEvent( _jsClient.transport, Uri.parse(_jsClient.url), diff --git a/pubspec.yaml b/pubspec.yaml index 9e775cd..277aac0 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -9,6 +9,7 @@ environment: dependencies: js: ^0.6.1 w_common: '>=2.0.0 <4.0.0' + opentelemetry: ^0.18.10 dev_dependencies: build_runner: ^2.1.2 diff --git a/test/sockjs_client_wrapper_test.dart b/test/sockjs_client_wrapper_test.dart index 69914a3..60f905c 100644 --- a/test/sockjs_client_wrapper_test.dart +++ b/test/sockjs_client_wrapper_test.dart @@ -96,6 +96,12 @@ void main() { client.close(); await Future.wait([client.onClose.first, client.didDispose]); }); + + test('timeout option should set a timeout', () async { + final options = SockJSOptions(timeout: 1000); + final client = SockJSClient(_echoUri, options: options)..close(); + expect(client.timeout, equals(1000)); + }); }); }