| // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file | |
| // for details. All rights reserved. Use of this source code is governed by a | |
| // BSD-style license that can be found in the LICENSE file. | |
| import 'dart:async'; | |
| import 'package:stack_trace/stack_trace.dart'; | |
| import '../hijack_exception.dart'; | |
| import '../middleware.dart'; | |
| /// Middleware which prints the time of the request, the elapsed time for the | |
| /// inner handlers, the response's status code and the request URI. | |
| /// | |
| /// If [logger] is passed, it's called for each request. The `msg` parameter is | |
| /// a formatted string that includes the request time, duration, request method, | |
| /// and requested path. When an exception is thrown, it also includes the | |
| /// exception's string and stack trace; otherwise, it includes the status code. | |
| /// The `isError` parameter indicates whether the message is caused by an error. | |
| /// | |
| /// If [logger] is not passed, the message is just passed to [print]. | |
| Middleware logRequests({void Function(String message, bool isError)? logger}) => | |
| (innerHandler) { | |
| final theLogger = logger ?? _defaultLogger; | |
| return (request) { | |
| var startTime = DateTime.now(); | |
| var watch = Stopwatch()..start(); | |
| return Future.sync(() => innerHandler(request)).then((response) { | |
| var msg = _message(startTime, response.statusCode, | |
| request.requestedUri, request.method, watch.elapsed); | |
| theLogger(msg, false); | |
| return response; | |
| }, onError: (Object error, StackTrace stackTrace) { | |
| if (error is HijackException) throw error; | |
| var msg = _errorMessage(startTime, request.requestedUri, | |
| request.method, watch.elapsed, error, stackTrace); | |
| theLogger(msg, true); | |
| // ignore: only_throw_errors | |
| throw error; | |
| }); | |
| }; | |
| }; | |
| String _formatQuery(String query) { | |
| return query == '' ? '' : '?$query'; | |
| } | |
| String _message(DateTime requestTime, int statusCode, Uri requestedUri, | |
| String method, Duration elapsedTime) { | |
| return '${requestTime.toIso8601String()} ' | |
| '${elapsedTime.toString().padLeft(15)} ' | |
| '${method.padRight(7)} [$statusCode] ' // 7 - longest standard HTTP method | |
| '${requestedUri.path}${_formatQuery(requestedUri.query)}'; | |
| } | |
| String _errorMessage(DateTime requestTime, Uri requestedUri, String method, | |
| Duration elapsedTime, Object error, StackTrace? stack) { | |
| var chain = Chain.current(); | |
| if (stack != null) { | |
| chain = Chain.forTrace(stack) | |
| .foldFrames((frame) => frame.isCore || frame.package == 'shelf') | |
| .terse; | |
| } | |
| var msg = '$requestTime\t$elapsedTime\t$method\t${requestedUri.path}' | |
| '${_formatQuery(requestedUri.query)}\n$error'; | |
| return '$msg\n$chain'; | |
| } | |
| void _defaultLogger(String msg, bool isError) { | |
| if (isError) { | |
| print('[ERROR] $msg'); | |
| } else { | |
| print(msg); | |
| } | |
| } |