Added default logging

This commit is contained in:
thomashii 2022-02-20 09:48:12 +08:00
parent 6d2be10536
commit ff65e13c75
12 changed files with 85 additions and 144 deletions

View file

@ -1,5 +1,17 @@
# Change Log # Change Log
## 6.0.0
* Updated to min SDK 2.15.x
## 5.0.0
* Skipped release
## 4.0.0
* Skipped release
## 3.0.1 ## 3.0.1
* Updated `package:angel3_container` * Updated `package:angel3_container`

View file

@ -1,6 +1,6 @@
import 'dart:async'; import 'dart:async';
import 'dart:convert'; import 'dart:convert';
import 'dart:io' show stderr, Cookie; import 'dart:io' show Cookie;
import 'package:angel3_http_exception/angel3_http_exception.dart'; import 'package:angel3_http_exception/angel3_http_exception.dart';
import 'package:angel3_route/angel3_route.dart'; import 'package:angel3_route/angel3_route.dart';
import 'package:belatuk_combinator/belatuk_combinator.dart'; import 'package:belatuk_combinator/belatuk_combinator.dart';
@ -20,10 +20,6 @@ abstract class Driver<
final Angel app; final Angel app;
final bool useZone; final bool useZone;
bool _closed = false; bool _closed = false;
late Server _server;
// TODO: Ugly fix
bool isServerInitialised = false;
StreamSubscription<Request>? _sub; StreamSubscription<Request>? _sub;
//final log = Logger('Driver'); //final log = Logger('Driver');
@ -37,14 +33,7 @@ abstract class Driver<
Uri get uri; Uri get uri;
/// The native server running this instance. /// The native server running this instance.
Server? get server { Server? server;
// TODO: Ugly fix
if (isServerInitialised) {
return _server;
} else {
return null;
}
}
Future<Server> generateServer(address, int port) => Future<Server> generateServer(address, int port) =>
serverGenerator(address, port); serverGenerator(address, port);
@ -53,24 +42,21 @@ abstract class Driver<
Future<Server> startServer([address, int port = 0]) { Future<Server> startServer([address, int port = 0]) {
var host = address ?? '127.0.0.1'; var host = address ?? '127.0.0.1';
return generateServer(host, port).then((server) { return generateServer(host, port).then((server) {
_server = server; this.server = server;
// TODO: Ugly fix
isServerInitialised = true;
return Future.wait(app.startupHooks.map(app.configure)).then((_) { return Future.wait(app.startupHooks.map(app.configure)).then((_) {
app.optimizeForProduction(); app.optimizeForProduction();
_sub = server.listen((request) { _sub = this.server?.listen((request) {
var stream = createResponseStreamFromRawRequest(request); var stream = createResponseStreamFromRawRequest(request);
stream.listen((response) { stream.listen((response) {
// TODO: To be revisited // TODO: To be revisited
handleRawRequest(request, response); handleRawRequest(request, response);
}); });
}); });
return Future.value(_server); return Future.value(this.server!);
}); });
}).catchError((error) { }).catchError((error) {
app.logger?.severe('Failed to create server', error); app.logger.severe('Failed to create server', error);
throw ArgumentError('[Driver]Failed to create server'); throw ArgumentError('[Driver]Failed to create server');
}); });
} }
@ -163,7 +149,7 @@ abstract class Driver<
..registerSingleton<ParseResult<RouteResult>?>(tuple.item3) ..registerSingleton<ParseResult<RouteResult>?>(tuple.item3)
..registerSingleton<ParseResult?>(tuple.item3); ..registerSingleton<ParseResult?>(tuple.item3);
if (!app.environment.isProduction && app.logger != null) { if (!app.environment.isProduction) {
req.container?.registerSingleton<Stopwatch>(Stopwatch()..start()); req.container?.registerSingleton<Stopwatch>(Stopwatch()..start());
} }
@ -204,22 +190,16 @@ abstract class Driver<
message: ee?.toString() ?? '500 Internal Server Error'); message: ee?.toString() ?? '500 Internal Server Error');
} }
if (app.logger != null) { var error = e.error ?? e;
var error = e.error ?? e; var trace = Trace.from(StackTrace.current).terse;
var trace = Trace.from(StackTrace.current).terse; app.logger.severe(e.message, error, trace);
app.logger?.severe(e.message, error, trace);
}
return handleAngelHttpException(e, st, req, res, request, response); return handleAngelHttpException(e, st, req, res, request, response);
}); });
} else { } else {
var zoneSpec = ZoneSpecification( var zoneSpec = ZoneSpecification(
print: (self, parent, zone, line) { print: (self, parent, zone, line) {
if (app.logger != null) { app.logger.info(line);
app.logger?.info(line);
} else {
parent.print(zone, line);
}
}, },
handleUncaughtError: (self, parent, zone, error, stackTrace) { handleUncaughtError: (self, parent, zone, error, stackTrace) {
var trace = Trace.from(stackTrace).terse; var trace = Trace.from(stackTrace).terse;
@ -237,9 +217,7 @@ abstract class Driver<
stackTrace: stackTrace, message: error.toString()); stackTrace: stackTrace, message: error.toString());
} }
if (app.logger != null) { app.logger.severe(e.message, error, trace);
app.logger?.severe(e.message, error, trace);
}
return handleAngelHttpException( return handleAngelHttpException(
e, trace, req, res, request, response); e, trace, req, res, request, response);
@ -248,23 +226,15 @@ abstract class Driver<
closeResponse(response); closeResponse(response);
// Ideally, we won't be in a position where an absolutely fatal error occurs, // Ideally, we won't be in a position where an absolutely fatal error occurs,
// but if so, we'll need to log it. // but if so, we'll need to log it.
if (app.logger != null) { app.logger.severe(
app.logger!.severe( 'Fatal error occurred when processing $uri.', e, trace);
'Fatal error occurred when processing $uri.', e, trace);
} else {
stderr
..writeln('Fatal error occurred when processing '
'${req.uri}:')
..writeln(e)
..writeln(trace);
}
}); });
}, },
); );
var zone = Zone.current.fork(specification: zoneSpec); var zone = Zone.current.fork(specification: zoneSpec);
req.container!.registerSingleton<Zone>(zone); req.container?.registerSingleton<Zone>(zone);
req.container!.registerSingleton<ZoneSpecification>(zoneSpec); req.container?.registerSingleton<ZoneSpecification>(zoneSpec);
// If a synchronous error is thrown, it's not caught by `zone.run`, // If a synchronous error is thrown, it's not caught by `zone.run`,
// so use a try/catch, and recover when need be. // so use a try/catch, and recover when need be.
@ -291,12 +261,12 @@ abstract class Driver<
{bool ignoreFinalizers = false}) { {bool ignoreFinalizers = false}) {
if (req == null || res == null) { if (req == null || res == null) {
try { try {
app.logger?.severe('500 Internal Server Error', e, st); app.logger.severe('500 Internal Server Error', e, st);
setStatusCode(response, 500); setStatusCode(response, 500);
writeStringToResponse(response, '500 Internal Server Error'); writeStringToResponse(response, '500 Internal Server Error');
closeResponse(response); closeResponse(response);
} catch (e) { } catch (e) {
app.logger?.severe('500 Internal Server Error', e); app.logger.severe('500 Internal Server Error', e);
} }
return Future.value(); return Future.value();
} }
@ -322,11 +292,9 @@ abstract class Driver<
ResponseContext res, ResponseContext res,
{bool ignoreFinalizers = false}) { {bool ignoreFinalizers = false}) {
Future<void> _cleanup(_) { Future<void> _cleanup(_) {
if (!app.environment.isProduction && if (!app.environment.isProduction && req.container!.has<Stopwatch>()) {
app.logger != null &&
req.container!.has<Stopwatch>()) {
var sw = req.container!.make<Stopwatch>(); var sw = req.container!.make<Stopwatch>();
app.logger?.info( app.logger.info(
"${res.statusCode} ${req.method} ${req.uri} (${sw.elapsedMilliseconds} ms)"); "${res.statusCode} ${req.method} ${req.uri} (${sw.elapsedMilliseconds} ms)");
} }
return req.close(); return req.close();

View file

@ -9,7 +9,6 @@ import 'dart:typed_data';
import 'package:angel3_route/angel3_route.dart'; import 'package:angel3_route/angel3_route.dart';
import 'package:file/file.dart'; import 'package:file/file.dart';
import 'package:http_parser/http_parser.dart'; import 'package:http_parser/http_parser.dart';
import 'package:logging/logging.dart';
import 'package:mime/mime.dart'; import 'package:mime/mime.dart';
import 'controller.dart'; import 'controller.dart';
@ -25,7 +24,7 @@ abstract class ResponseContext<RawResponse>
final CaseInsensitiveMap<String> _headers = CaseInsensitiveMap<String>.from( final CaseInsensitiveMap<String> _headers = CaseInsensitiveMap<String>.from(
{'content-type': 'text/plain', 'server': 'Angel3'}); {'content-type': 'text/plain', 'server': 'Angel3'});
final log = Logger('ResponseContext'); //final log = Logger('ResponseContext');
Completer? _done; Completer? _done;
int _statusCode = 200; int _statusCode = 200;
@ -351,7 +350,7 @@ abstract class ResponseContext<RawResponse>
if (stackTrace != null) { if (stackTrace != null) {
Zone.current.handleUncaughtError(error, stackTrace); Zone.current.handleUncaughtError(error, stackTrace);
} else { } else {
log.warning('[ResponseContext] stackTrace is null'); app?.logger.warning('[ResponseContext] stackTrace is null');
} }
} }
} }

View file

@ -46,6 +46,11 @@ RequestHandler chain(Iterable<RequestHandler> handlers) {
class Routable extends Router<RequestHandler> { class Routable extends Router<RequestHandler> {
final Map<Pattern, Service> _services = {}; final Map<Pattern, Service> _services = {};
final Map<Pattern, Service?> _serviceLookups = {}; final Map<Pattern, Service?> _serviceLookups = {};
/// A [Map] of application-specific data that can be accessed.
///
/// Packages like `package:angel3_configuration` populate this map
/// for you.
final Map configuration = {}; final Map configuration = {};
final Container _container; final Container _container;

View file

@ -55,6 +55,18 @@ Future<bool> _defaultErrorHandler(
} }
} }
Logger _defaultLogger() {
Logger logger = Logger('SERVER')
//..level = Level.WARNING
..onRecord.listen((rec) {
print(rec);
if (rec.error != null) print(rec.error);
if (rec.stackTrace != null) print(rec.stackTrace);
});
return logger;
}
/// A powerful real-time/REST/MVC server class. /// A powerful real-time/REST/MVC server class.
class Angel extends Routable { class Angel extends Routable {
static Future<String> _noViewEngineConfigured(String view, [Map? data]) => static Future<String> _noViewEngineConfigured(String view, [Map? data]) =>
@ -99,19 +111,6 @@ class Angel extends Routable {
/// A set of [Controller] objects that have been loaded into the application. /// A set of [Controller] objects that have been loaded into the application.
Map<Pattern, Controller> get controllers => _controllers; Map<Pattern, Controller> get controllers => _controllers;
/// Now *deprecated*, in favor of [AngelEnv] and [angelEnv]. Use `app.environment.isProduction`
/// instead.
///
/// Indicates whether the application is running in a production environment.
///
/// The criteria for this is the `ANGEL_ENV` environment variable being set to
/// `'production'`.
///
/// This value is memoized the first time you call it, so do not change environment
/// configuration at runtime!
//@deprecated
//bool get isProduction => environment.isProduction;
/// The [AngelEnvironment] in which the application is running. /// The [AngelEnvironment] in which the application is running.
/// ///
/// By default, it is automatically inferred. /// By default, it is automatically inferred.
@ -121,7 +120,7 @@ class Angel extends Routable {
Angel? get parent => _parent; Angel? get parent => _parent;
/// Outputs diagnostics and debug messages. /// Outputs diagnostics and debug messages.
Logger? logger; Logger logger = _defaultLogger();
/// Plug-ins to be called right before server startup. /// Plug-ins to be called right before server startup.
/// ///
@ -138,14 +137,6 @@ class Angel extends Routable {
/// These will only not run if a response's `willCloseItself` is set to `true`. /// These will only not run if a response's `willCloseItself` is set to `true`.
final List<RequestHandler> responseFinalizers = []; final List<RequestHandler> responseFinalizers = [];
/// A [Map] of application-specific data that can be accessed by any
/// piece of code that can see this [Angel] instance.
///
/// Packages like `package:angel3_configuration` populate this map
/// for you.
@override
final Map configuration = {};
/// A function that renders views. /// A function that renders views.
/// ///
/// Called by [ResponseContext]@`render`. /// Called by [ResponseContext]@`render`.
@ -158,11 +149,10 @@ class Angel extends Routable {
Route<RequestHandler> addRoute( Route<RequestHandler> addRoute(
String method, String path, RequestHandler handler, String method, String path, RequestHandler handler,
{Iterable<RequestHandler> middleware = const []}) { {Iterable<RequestHandler> middleware = const []}) {
//middleware ??= [];
if (_flattened != null) { if (_flattened != null) {
logger?.warning( logger.warning(
'WARNING: You added a route ($method $path) to the router, after it had been optimized.'); 'WARNING: You added a route ($method $path) to the router, after it had been optimized.');
logger?.warning( logger.warning(
'This route will be ignored, and no requests will ever reach it.'); 'This route will be ignored, and no requests will ever reach it.');
} }
@ -173,9 +163,9 @@ class Angel extends Routable {
SymlinkRoute<RequestHandler> mount( SymlinkRoute<RequestHandler> mount(
String path, Router<RequestHandler> router) { String path, Router<RequestHandler> router) {
if (_flattened != null) { if (_flattened != null) {
logger?.warning( logger.warning(
'WARNING: You added mounted a child router ($path) on the router, after it had been optimized.'); 'WARNING: You added mounted a child router ($path) on the router, after it had been optimized.');
logger?.warning( logger.warning(
'This route will be ignored, and no requests will ever reach it.'); 'This route will be ignored, and no requests will ever reach it.');
} }
@ -202,7 +192,7 @@ class Angel extends Routable {
/// ///
/// The server will be **COMPLETELY DEFUNCT** after this operation! /// The server will be **COMPLETELY DEFUNCT** after this operation!
@override @override
Future close() { Future<void> close() {
Future.forEach(services.values, (Service service) { Future.forEach(services.values, (Service service) {
service.close(); service.close();
}); });
@ -212,14 +202,13 @@ class Angel extends Routable {
_preContained.clear(); _preContained.clear();
handlerCache.clear(); handlerCache.clear();
encoders.clear(); encoders.clear();
//_serializer = json.encode;
_children.clear(); _children.clear();
_parent = null; //_parent = null;
logger = null; //logger = null;
//_flattened = null;
startupHooks.clear(); startupHooks.clear();
shutdownHooks.clear(); shutdownHooks.clear();
responseFinalizers.clear(); responseFinalizers.clear();
_flattened = null;
return Future.value(); return Future.value();
} }
@ -312,7 +301,7 @@ class Angel extends Routable {
void optimizeForProduction({bool force = false}) { void optimizeForProduction({bool force = false}) {
if (environment.isProduction || force == true) { if (environment.isProduction || force == true) {
_flattened ??= flatten(this); _flattened ??= flatten(this);
logger?.info('Angel is running in production mode.'); logger.info('Angel is running in production mode.');
} }
} }
@ -391,16 +380,21 @@ class Angel extends Routable {
{Reflector reflector = {Reflector reflector =
const ThrowingReflector(errorMessage: _reflectionErrorMessage), const ThrowingReflector(errorMessage: _reflectionErrorMessage),
this.environment = angelEnv, this.environment = angelEnv,
this.logger, Logger? logger,
this.allowMethodOverrides = true, this.allowMethodOverrides = true,
this.serializer, this.serializer,
this.viewGenerator}) this.viewGenerator})
: super(reflector) { : super(reflector) {
// Override default logger
if (logger != null) {
this.logger = logger;
}
if (reflector is EmptyReflector || reflector is ThrowingReflector) { if (reflector is EmptyReflector || reflector is ThrowingReflector) {
var msg = var msg =
'No `reflector` was passed to the Angel constructor, so reflection will not be available.\n' + 'No `reflector` was passed to the Angel constructor, so reflection will not be available.\n' +
_reflectionInfo; _reflectionInfo;
logger?.warning(msg); this.logger.warning(msg);
} }
bootstrapContainer(); bootstrapContainer();

View file

@ -103,15 +103,10 @@ class AngelHttp extends Driver<HttpRequest, HttpResponse, HttpServer,
Future<HttpResponseContext> createResponseContext( Future<HttpResponseContext> createResponseContext(
HttpRequest request, HttpResponse response, HttpRequest request, HttpResponse response,
[HttpRequestContext? correspondingRequest]) { [HttpRequestContext? correspondingRequest]) {
// TODO: Refactored to overcome NNBD migration error
var context = HttpResponseContext(response, app, correspondingRequest); var context = HttpResponseContext(response, app, correspondingRequest);
context.serializer = (app.serializer ?? json.encode); context.serializer = (app.serializer ?? json.encode);
context.encoders.addAll(app.encoders); context.encoders.addAll(app.encoders);
return Future<HttpResponseContext>.value(context); return Future<HttpResponseContext>.value(context);
// return Future<HttpResponseContext>.value(
// HttpResponseContext(response, app, correspondingRequest)
// ..serializer = (app.serializer ?? json.encode)
// ..encoders.addAll(app.encoders ?? {}));
} }
@override @override

View file

@ -24,7 +24,7 @@ class HttpRequestContext extends RequestContext<HttpRequest?> {
@override @override
List<Cookie> get cookies { List<Cookie> get cookies {
return rawRequest!.cookies; return rawRequest?.cookies ?? [];
} }
@override @override
@ -51,7 +51,7 @@ class HttpRequestContext extends RequestContext<HttpRequest?> {
@override @override
String get originalMethod { String get originalMethod {
return rawRequest!.method; return rawRequest?.method ?? '';
} }
@override @override
@ -61,17 +61,18 @@ class HttpRequestContext extends RequestContext<HttpRequest?> {
@override @override
InternetAddress get remoteAddress { InternetAddress get remoteAddress {
return rawRequest!.connectionInfo!.remoteAddress; return rawRequest?.connectionInfo?.remoteAddress ??
InternetAddress("127.0.0.1");
} }
@override @override
HttpSession get session { HttpSession? get session {
return rawRequest!.session; return rawRequest?.session;
} }
@override @override
Uri get uri { Uri get uri {
return rawRequest!.uri; return rawRequest?.uri ?? Uri();
} }
/// Magically transforms an [HttpRequest] into a [RequestContext]. /// Magically transforms an [HttpRequest] into a [RequestContext].
@ -92,35 +93,6 @@ class HttpRequestContext extends RequestContext<HttpRequest?> {
? MediaType('text', 'plain') ? MediaType('text', 'plain')
: MediaType.parse(request.headers.contentType.toString()); : MediaType.parse(request.headers.contentType.toString());
ctx._override = override; ctx._override = override;
/*
// Faster way to get path
List<int> _path = [];
// Go up until we reach a ?
for (int ch in request.uri.toString().codeUnits) {
if (ch != $question)
_path.add(ch);
else
break;
}
// Remove trailing slashes
int lastSlash = -1;
for (int i = _path.length - 1; i >= 0; i--) {
if (_path[i] == $slash)
lastSlash = i;
else
break;
}
if (lastSlash > -1)
ctx._path = String.fromCharCodes(_path.take(lastSlash));
else
ctx._path = String.fromCharCodes(_path);
*/
ctx._path = path; ctx._path = path;
ctx._io = request; ctx._io = request;

View file

@ -13,15 +13,15 @@ class HttpResponseContext extends ResponseContext<HttpResponse> {
@override @override
final HttpResponse rawResponse; final HttpResponse rawResponse;
@override
Angel? app;
LockableBytesBuilder? _buffer; LockableBytesBuilder? _buffer;
final HttpRequestContext? _correspondingRequest; final HttpRequestContext? _correspondingRequest;
bool _isDetached = false, _isClosed = false, _streamInitialized = false; bool _isDetached = false, _isClosed = false, _streamInitialized = false;
HttpResponseContext(this.rawResponse, this.app, [this._correspondingRequest]); HttpResponseContext(this.rawResponse, Angel? app,
[this._correspondingRequest]) {
this.app = app;
}
@override @override
HttpResponse detach() { HttpResponse detach() {

View file

@ -80,7 +80,7 @@ class AngelHttp2 extends Driver<Socket, ServerTransportStream,
@override @override
Future<void> close() async { Future<void> close() async {
await _artificial!.close(); await _artificial?.close();
await _http.close(); await _http.close();
return await super.close(); return await super.close();
} }
@ -211,7 +211,7 @@ class _AngelHttp2ServerSocket extends Stream<SecureSocket>
}, },
onDone: _ctrl.close, onDone: _ctrl.close,
onError: (e, st) { onError: (e, st) {
driver.app.logger!.warning( driver.app.logger.warning(
'HTTP/2 incoming connection failure: ', e, st as StackTrace); 'HTTP/2 incoming connection failure: ', e, st as StackTrace);
}, },
); );

View file

@ -6,9 +6,6 @@ import 'package:http2/transport.dart';
import 'http2_request_context.dart'; import 'http2_request_context.dart';
class Http2ResponseContext extends ResponseContext<ServerTransportStream> { class Http2ResponseContext extends ResponseContext<ServerTransportStream> {
@override
final Angel? app;
final ServerTransportStream stream; final ServerTransportStream stream;
@override @override
@ -25,8 +22,9 @@ class Http2ResponseContext extends ResponseContext<ServerTransportStream> {
Uri? _targetUri; Uri? _targetUri;
Http2ResponseContext(this.app, this.stream, this._req) { Http2ResponseContext(Angel? app, this.stream, this._req) {
_targetUri = _req!.uri; this.app = app;
_targetUri = _req?.uri;
} }
final List<Http2ResponseContext> _pushes = []; final List<Http2ResponseContext> _pushes = [];

View file

@ -1,6 +1,5 @@
import 'dart:async'; import 'dart:async';
import 'dart:convert'; import 'dart:convert';
import 'dart:io' show BytesBuilder;
import 'dart:io'; import 'dart:io';
import 'package:angel3_container/mirrors.dart'; import 'package:angel3_container/mirrors.dart';

View file

@ -1,6 +1,5 @@
import 'dart:async'; import 'dart:async';
import 'dart:convert'; import 'dart:convert';
import 'dart:io' show stderr;
import 'dart:io'; import 'dart:io';
import 'package:angel3_container/mirrors.dart'; import 'package:angel3_container/mirrors.dart';
@ -92,7 +91,7 @@ void main() {
test('cannot write after close', () async { test('cannot write after close', () async {
try { try {
var rq = MockHttpRequest('GET', Uri.parse('/overwrite')); var rq = MockHttpRequest('GET', Uri.parse('/overwrite'));
await (rq.close()); await rq.close();
await http.handleRequest(rq); await http.handleRequest(rq);
var body = await rq.response.transform(utf8.decoder).join(); var body = await rq.response.transform(utf8.decoder).join();