Skip to content

Instantly share code, notes, and snippets.

@lukepighetti
Last active September 7, 2021 20:54
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save lukepighetti/5283229a351ab394376e84cff8277bdb to your computer and use it in GitHub Desktop.
Save lukepighetti/5283229a351ab394376e84cff8277bdb to your computer and use it in GitHub Desktop.
Logging service using extensions for channels with multiple outputs
import 'package:logger/logger.dart';
void main() async {
/// Setup logging service
final remoteUrl = Uri.parse('http://localhost:8080');
final logger = LoggingService(
loggers: {
ConsoleLogger(),
RemoteLogger(baseUrl: remoteUrl),
},
);
/// Do a normal log event
logger.log('main', 'log normally');
/// Time a future and log it
await logger.logFuture(
'main',
'log future',
Future.delayed(Duration(seconds: 1)),
);
}
import 'dart:convert';
import 'package:http/http.dart' as http;
/// The base class for a logging channel or service
abstract class Logger {
/// Log a message
void log(String channel, String message);
}
/// The logging service used by the app
class LoggingService implements Logger {
LoggingService({required this.loggers});
/// All the loggers to dispatch log events to
final Set<Logger> loggers;
@override
void log(String channel, String message) {
/// Dispatch this log event to all [loggers]
for (var logger in loggers) {
logger.log(channel, message);
}
}
/// Log a future and return the result
Future<T> logFuture<T>(
String channel, String message, Future<T> future) async {
final s = Stopwatch();
s.start();
final result = await future;
s.stop();
log(channel, '${s.elapsed}: $message');
return result;
}
}
/// A logging channel that outputs to the local development console
class ConsoleLogger implements Logger {
@override
void log(String channel, String message) {
print('[$channel] (${DateTime.now()}) $message');
}
}
/// A logging channel that sends messages to a remote logging endpoint
class RemoteLogger implements Logger {
RemoteLogger({required this.baseUrl});
final Uri baseUrl;
@override
void log(String channel, String message) async {
final timestamp = DateTime.now();
try {
await http.post(
baseUrl.resolve('/log'),
headers: {
'Content-Type': 'application/json',
},
body: json.encode({
'channel': '$channel',
'timestamp': '$timestamp',
'message': '$message',
}),
);
} catch (e) {
print('[RemoteLogger] error: $e');
}
}
}
name: logger
description: A scalable logging system.
# version: 1.0.0
# homepage: https://www.example.com
environment:
sdk: ">=2.12.0 <3.0.0"
dependencies:
http: ^0.13.1
dev_dependencies:
pedantic: ^1.9.0
import 'dart:convert';
import 'dart:io';
import 'package:logger/logger.dart';
void main(List<String> arguments) async {
final host = InternetAddress.loopbackIPv4;
final port = 8080;
final logger = LoggingService(
loggers: {
ConsoleLogger(),
},
);
final server = await HttpServer.bind(host, port);
logger.log('setup', 'bound to $host:$port');
server.listen((req) async {
/// `/log`
if (req.uri.path == '/log') {
try {
/// Parse the body
final body = await utf8.decoder.bind(req).join();
var data = jsonDecode(body) as Map;
/// Extract the data
final channel = data['channel'];
final message = data['message'];
/// Log the event
logger.log(channel, '$message');
req.response.statusCode = HttpStatus.ok;
} catch (e) {
logger.log('error', '$e');
req.response.statusCode = HttpStatus.badRequest;
} finally {
await req.response.close();
}
}
/// Unknown path
else {
logger.log('error', 'invalid path ${req.uri.path}');
}
});
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment