Skip to content
This repository has been archived by the owner on Jan 24, 2025. It is now read-only.

Logger fixes #1

Merged
merged 11 commits into from
Jul 25, 2023
1 change: 0 additions & 1 deletion analysis_options.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ analyzer:

exclude:
- lib/src/generated/**.dart
- example/**.dart

linter:
rules:
Expand Down
19 changes: 19 additions & 0 deletions example/raw_data.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import "dart:io";
import "package:burt_network/burt_network.dart";

class TestSocket extends UdpSocket {
TestSocket({required super.port});

@override
void onData(Datagram packet) => logger.info("Received data: ${packet.data} from ${packet.port}");
}

void main() async {
final socket1 = TestSocket(port: 8001);
final socket2 = TestSocket(port: 8002);

await socket1.init();
await socket2.init();

socket1.sendData([1, 2, 3], SocketInfo(address: InternetAddress.loopbackIPv4, port: 8002));
}
15 changes: 15 additions & 0 deletions example/server.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import "package:burt_network/burt_network.dart";

class BasicServer extends ServerSocket {
BasicServer({required super.port, required super.device});

@override
void onMessage(WrappedMessage wrapper) => logger.info("Received ${wrapper.name} message: ${wrapper.data}");
}

void main() async {
final server = BasicServer(port: 8001, device: Device.SUBSYSTEMS); // Registers as the Subsystems Server on the Dashboard
final server2 = BasicServer(port: 8002, device: Device.VIDEO); // Registers as the Subsystems Server on the Dashboard
await server.init();
await server2.init();
}
10 changes: 5 additions & 5 deletions lib/burt_network.dart
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,14 @@
/// not have to use [UdpSocket] directly as it has no Protobuf support.
library;

export "src/server_socket.dart";
import "src/proto_socket.dart";
import "src/server_socket.dart";
import "src/udp_socket.dart";

export "src/log.dart";
export "src/proto_socket.dart";
export "src/server_socket.dart";
export "src/socket_info.dart";
export "src/udp_socket.dart";

export "generated.dart";

import "src/server_socket.dart";
import "src/proto_socket.dart";
import "src/udp_socket.dart";
25 changes: 21 additions & 4 deletions lib/src/log.dart
Original file line number Diff line number Diff line change
@@ -1,10 +1,27 @@
import "dart:io";
import "package:logger/logger.dart";

/// A filter to decide which messages get logged. Set [LogFilter.level] to change.
final logFilter = ProductionFilter();
/// The logger to use when running BURT programs. See [LoggerUtils] for usage.
Logger logger = Logger(printer: SimplePrinter(colors: stdout.supportsAnsiEscapes), filter: logFilter);
/// An alias for [Level].
typedef LogLevel = Level;

/// Holds the current [LogLevel] for [logger].
class BurtLogger {
/// The current [LogLevel] for [logger].
static LogLevel level = LogLevel.info;
}

/// A custom filter to work around a bug with `package:logger`.
///
/// See https://github.com/Bungeefan/logger/issues/38.
class BurtFilter extends LogFilter {
@override
bool shouldLog(LogEvent event) => event.level.index >= BurtLogger.level.index;
}

/// The logger to use when running BURT programs.
///
/// See [LoggerUtils] for usage. To change the minimum log level, use [BurtLogger.level].
final logger = Logger(printer: SimplePrinter(colors: stdout.supportsAnsiEscapes), filter: BurtFilter());

/// Helpful aliases for the [Logger] class.
extension LoggerUtils on Logger {
Expand Down
17 changes: 12 additions & 5 deletions lib/src/proto_socket.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import "package:burt_network/generated.dart";

import "udp_socket.dart";
import "socket_info.dart";
import "log.dart";

/// A [UdpSocket] to send and receive Protobuf messages.
///
Expand Down Expand Up @@ -87,13 +86,21 @@ abstract class ProtoSocket extends UdpSocket {
}

/// Wraps a [Message] and sends it to the [destination], or the given [socketOverride] if specified.
///
/// If you have already wrapped a message yourself, use [sendWrapper].
void sendMessage(Message message, {SocketInfo? socketOverride}) {
final wrapper = message.wrap();
final target = socketOverride ?? destination;
if (target == null) {
logger.critical("No destination or override was specificed");
throw ArgumentError.notNull("socketOverride");
}
if (target == null) return;
sendData(wrapper.writeToBuffer(), target);
}

/// Sends an already-wrapped [WrappedMessage] to the [destination], or the given [socketOverride].
///
/// Use this function instead of [sendMessage] if you need to manually wrap a message yourself.
void sendWrapper(WrappedMessage wrapper, {SocketInfo? socketOverride}) {
final target = socketOverride ?? destination;
if (target == null) return;
sendData(wrapper.writeToBuffer(), target);
}

Expand Down
1 change: 0 additions & 1 deletion lib/src/server_socket.dart
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,6 @@ abstract class ServerSocket extends ProtoSocket {
/// 4. If we are not connected to any dashboard, call [onConnect] and respond to it.
@override
void onHeartbeat(Connect heartbeat, SocketInfo source) {
logger.debug("Received heartbeat from $source");
if (heartbeat.receiver != device) { // (1)
logger.warning("Received a misaddressed heartbeat for ${heartbeat.receiver}");
} else if (isConnected) {
Expand Down
34 changes: 26 additions & 8 deletions lib/src/udp_socket.dart
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,11 @@ import "log.dart";
/// - Override [onData] to handle incoming data.
/// - Call [dispose] to close the socket. Messages can no longer be sent or received after this.
abstract class UdpSocket {
/// A collection of allowed [OSError] codes.
static const allowedErrors = {1234, 10054, 101, 10038, 9};

/// The port this socket is listening on. See [RawDatagramSocket.bind].
final int port;
int? port;

/// Opens a UDP socket on the given port that can send and receive data.
UdpSocket({required this.port});
Expand All @@ -33,20 +36,35 @@ abstract class UdpSocket {
/// This must be cancelled in [dispose].
late StreamSubscription<RawSocketEvent> _subscription;

/// Initializes the socket.
/// Initializes the socket, and restarts it if a known "safe" error occurs (see [allowedErrors]).
@mustCallSuper
Future<void> init() async {
logger.verbose("Listening on port $port");
_socket = await RawDatagramSocket.bind(InternetAddress.anyIPv4, port);
_subscription = _socket.listenForData(onData);
}
Future<void> init() async => runZonedGuarded<Future<void>>(
// This code cannot be a try/catch because the SocketException can be thrown at any time,
// even after this function has finished. It also cannot be caught by the caller of this function.
// Using [runZonedGuarded] ensures that the error is caught no matter when it is thrown.
() async { // Initialize the socket
_socket = await RawDatagramSocket.bind(InternetAddress.anyIPv4, port ?? 0);
_subscription = _socket.listenForData(onData);
if (port == null || port == 0) port = _socket.port;
logger.info("Listening on port $port");
},
(Object error, StackTrace stack) async { // Catch errors and restart the socket
if (error is SocketException && allowedErrors.contains(error.osError!.errorCode)) {
logger.warning("Socket error ${error.osError!.errorCode} on port $port. Restarting...");
await dispose();
await init();
} else {
Error.throwWithStackTrace(error, stack);
}
}
);

/// Closes the socket.
@mustCallSuper
Future<void> dispose() async {
logger.verbose("Closed the socket on port $port");
await _subscription.cancel();
_socket.close();
logger.info("Closed the socket on port $port");
}

/// Sends data to the given destination.
Expand Down
4 changes: 2 additions & 2 deletions test/proto_test.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import "dart:io";
import "package:burt_network/burt_network.dart";
import "package:burt_network/generated.dart";
import "package:test/test.dart";

final address = InternetAddress.loopbackIPv4;
Expand All @@ -9,6 +8,7 @@ final serverInfo = SocketInfo(address: address, port: 8000);
final clientInfo = SocketInfo(address: address, port: 8001);

void main() => group("ProtoSocket:", () {
BurtLogger.level = LogLevel.debug;
final server = TestServer(port: serverInfo.port, device: Device.SUBSYSTEMS);
final client = TestClient(
device: Device.DASHBOARD,
Expand Down Expand Up @@ -77,7 +77,7 @@ class TestServer extends ServerSocket {
}

class TestClient extends ProtoSocket {
TestClient({required super.port, required super.device, super.destination});
TestClient({required super.port, required super.device, super.destination}) : super(heartbeatInterval: const Duration(seconds: 1));

bool isConnected = false;
bool shouldSendHeartbeats = true;
Expand Down