Skip to content

Commit

Permalink
chore(api): Improve WS logging (#3401)
Browse files Browse the repository at this point in the history
Improves logging in the WebSocketBloc so that events have metadata logged as well. Since this is currently done with `debug` log level, there should be no new information exposed in production logs.
  • Loading branch information
dnys1 committed Jul 28, 2023
1 parent e623b02 commit cd16932
Show file tree
Hide file tree
Showing 4 changed files with 141 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import 'package:amplify_core/amplify_core.dart';

/// A GraphQL request with a few extra properties used to decode the response or use the correct API if the backend has multiple.
class GraphQLRequest<T> {
class GraphQLRequest<T> with AWSSerializable<Map<String, Object?>> {
GraphQLRequest({
required this.document,
this.apiName,
Expand Down Expand Up @@ -59,11 +59,19 @@ class GraphQLRequest<T> {
/// See https://docs.amplify.aws/lib/graphqlapi/advanced-workflows/q/platform/flutter/.
final ModelType? modelType;

Map<String, dynamic> serializeAsMap() => <String, dynamic>{
@Deprecated('Use toJson instead')
Map<String, dynamic> serializeAsMap() => toJson();

@override
Map<String, Object?> toJson() => {
'id': id,
'document': document,
'variables': variables,
'headers': headers,
'cancelToken': id,
if (apiName != null) 'apiName': apiName,
if (authorizationMode != null)
'authorizationMode': authorizationMode!.name,
if (decodePath != null) 'decodePath': decodePath,
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -191,13 +191,15 @@ class WebSocketBloc with AWSDebuggable, AmplifyLoggerMixin {
// Send to sub bloc but also clean up in this bloc
Stream<WebSocketState> _complete(SubscriptionComplete event) async* {
logger.verbose('Closing subscription ${event.subscriptionId}...');
yield* _sendEventToSubBloc(event);

_currentState.subscriptionBlocs.remove(event.subscriptionId);

if (_currentState.subscriptionBlocs.isEmpty) {
add(const ShutdownEvent());
}
logger.verbose('Closed subscription ${event.subscriptionId}...');
}

/// Receives connection ack message from ws channel.
Expand Down Expand Up @@ -412,6 +414,7 @@ class WebSocketBloc with AWSDebuggable, AmplifyLoggerMixin {

/// Shut down the bloc & clean up
Stream<WebSocketState> _shutdown() async* {
logger.verbose('Shutting down bloc');
yield _currentState.shutdown();
// TODO(dnys1): Yield broken on web debug build.
yield* const Stream.empty();
Expand All @@ -424,7 +427,7 @@ class WebSocketBloc with AWSDebuggable, AmplifyLoggerMixin {
if (_currentState is! ConnectedState) {
return;
}
await _currentState.service.unsubscribe(event.req.id);
await _currentState.service.unsubscribe(event.request.id);
// TODO(dnys1): Yield broken on web debug build.
yield* const Stream.empty();
}
Expand Down Expand Up @@ -464,6 +467,7 @@ class WebSocketBloc with AWSDebuggable, AmplifyLoggerMixin {
]);

done.complete();
logger.verbose('Successfully shut down bloc');
}

/// Connectivity stream monitors network availability on a hardware level.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,18 +11,29 @@ abstract class SubscriptionEvent extends WebSocketEvent {

/// The associated subscription id
final String subscriptionId;

@override
Map<String, Object?> toJson() => {
'subscriptionId': subscriptionId,
};
}

/// Web Socket Subscription start ack message event
class SubscriptionStartAckEvent extends SubscriptionEvent {
/// Creates a start ack event
const SubscriptionStartAckEvent(super.subscriptionId);

@override
String get runtimeTypeName => 'SubscriptionStartAckEvent';
}

/// Web Socket Subscription pending event
class SubscriptionPendingEvent extends SubscriptionEvent {
/// Creates a pending event
const SubscriptionPendingEvent(super.subscriptionId);

@override
String get runtimeTypeName => 'SubscriptionPendingEvent';
}

/// Web Socket Subscription data message event
Expand All @@ -36,6 +47,15 @@ class SubscriptionDataEvent extends SubscriptionEvent {

/// The data [payload]
final SubscriptionDataPayload payload;

@override
String get runtimeTypeName => 'SubscriptionDataEvent';

@override
Map<String, Object?> toJson() => {
'subscriptionId': subscriptionId,
'payload': payload.toJson(),
};
}

/// Web Socket Subscription complete message event
Expand All @@ -45,6 +65,9 @@ class SubscriptionComplete extends SubscriptionEvent {
const SubscriptionComplete(
super.subscriptionId,
);

@override
String get runtimeTypeName => 'SubscriptionComplete';
}

/// Web Socket Subscription event message event
Expand All @@ -58,4 +81,13 @@ class SubscriptionErrorEvent extends SubscriptionEvent {

/// The web socket error
final WebSocketError wsError;

@override
String get runtimeTypeName => 'SubscriptionErrorEvent';

@override
Map<String, Object?> toJson() => {
'subscriptionId': subscriptionId,
'wsError': wsError.toJson(),
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
part of '../blocs/web_socket_bloc.dart';

/// Base WebSocketEvent
abstract class WebSocketEvent {
abstract class WebSocketEvent
with AWSSerializable<Map<String, Object?>>, AWSDebuggable {
/// Base WebSocketEvent
const WebSocketEvent();
}
Expand All @@ -13,6 +14,12 @@ abstract class WebSocketEvent {
class InitEvent extends WebSocketEvent {
/// Trigger a new connection
const InitEvent();

@override
String get runtimeTypeName => 'InitEvent';

@override
Map<String, Object?> toJson() => const {};
}

/// Event for new web socket connect ack message
Expand All @@ -22,12 +29,26 @@ class ConnectionAckMessageEvent extends WebSocketEvent {

/// Message payload containing timeout in ms.
final ConnectionAckMessagePayload payload;

@override
String get runtimeTypeName => 'ConnectionAckMessageEvent';

@override
Map<String, Object?> toJson() => {
'payload': payload.toJson(),
};
}

/// Shut down web socket channel
class ShutdownEvent extends WebSocketEvent {
/// Create a ShutdownEvent
const ShutdownEvent();

@override
String get runtimeTypeName => 'ShutdownEvent';

@override
Map<String, Object?> toJson() => const {};
}

/// Base class for network related events
Expand All @@ -37,26 +58,46 @@ class NetworkEvent extends WebSocketEvent {

/// The underlying network state of the connection, ie Disconnected or Connected.
final NetworkState networkState;

@override
String get runtimeTypeName => 'NetworkEvent';

@override
Map<String, Object?> toJson() => {
'networkState': networkState.name,
};
}

/// Discrete class for when the network is connected.
/// Triggers when connectivity plugin detects network connection at the hardware level.
class NetworkFoundEvent extends NetworkEvent {
/// Create a network found event with status 'Connected'
const NetworkFoundEvent() : super(NetworkState.connected);

@override
String get runtimeTypeName => 'NetworkFoundEvent';
}

/// Discrete class for when the network is disconnected
/// Triggers when connectivity plugin detects no network connection at the hardware level.
class NetworkLossEvent extends NetworkEvent {
/// Create a network found event with status 'Disconnected'
const NetworkLossEvent() : super(NetworkState.disconnected);

@override
String get runtimeTypeName => 'NetworkLossEvent';
}

/// Triggers when a successful ping to AppSync is made
class PollSuccessEvent extends WebSocketEvent {
/// Create a successful Poll event
const PollSuccessEvent();

@override
String get runtimeTypeName => 'PollSuccessEvent';

@override
Map<String, Object?> toJson() => const {};
}

/// Triggers when a ping to AppSync failed
Expand All @@ -66,18 +107,38 @@ class PollFailedEvent extends WebSocketEvent {

/// Underlying exception
final Exception exception;

@override
String get runtimeTypeName => 'PollFailedEvent';

@override
Map<String, Object?> toJson() => {
'exception': exception.toString(),
};
}

/// Trigger reconnection of web socket channel and underlying subscriptions
class ReconnectEvent extends WebSocketEvent {
/// Create a reconnection event
const ReconnectEvent();

@override
String get runtimeTypeName => 'ReconnectEvent';

@override
Map<String, Object?> toJson() => const {};
}

/// Keep alive (ka) event sent from AppSync
class KeepAliveEvent extends WebSocketEvent {
/// ka event
const KeepAliveEvent();

@override
String get runtimeTypeName => 'KeepAliveEvent';

@override
Map<String, Object?> toJson() => const {};
}

/// Web Socket Event containing an exception
Expand All @@ -87,12 +148,26 @@ class WsErrorEvent extends WebSocketEvent {

/// Underlying exception
final Exception exception;

@override
String get runtimeTypeName => 'WsErrorEvent';

@override
Map<String, Object?> toJson() => {
'exception': exception.toString(),
};
}

/// Connection Error while setting up web socket connection
class ConnectionErrorEvent extends WebSocketEvent {
/// Create an Connection error event
const ConnectionErrorEvent();

@override
String get runtimeTypeName => 'ConnectionErrorEvent';

@override
Map<String, Object?> toJson() => const {};
}

/// Subscribe Event to establish a new subscription on the bloc
Expand All @@ -106,13 +181,29 @@ class SubscribeEvent<T> extends WebSocketEvent {

/// Callback to use once start ack event is created
final void Function()? onEstablished;

@override
String get runtimeTypeName => 'SubscribeEvent<$T>';

@override
Map<String, Object?> toJson() => {
'request': request.toJson(),
};
}

/// Unsubscribe event to remove registration from AppSync and remove from bloc
class UnsubscribeEvent extends WebSocketEvent {
/// Provide the GraphQLRequest to unsubscribe
const UnsubscribeEvent(this.req);
const UnsubscribeEvent(this.request);

/// Underlying request to unsubscribe
final GraphQLRequest<Object?> req;
final GraphQLRequest<Object?> request;

@override
String get runtimeTypeName => 'UnsubscribeEvent';

@override
Map<String, Object?> toJson() => {
'request': request.toJson(),
};
}

0 comments on commit cd16932

Please sign in to comment.