contextual_shelf 0.1.0
contextual_shelf: ^0.1.0 copied to clipboard
shelf using contextual logger
example/contextual_shelf_example.dart
import 'package:contextual/contextual.dart';
import 'package:contextual_shelf/contextual_shelf.dart';
import 'package:shelf/shelf.dart' as shelf;
import 'package:shelf/shelf_io.dart' as io;
/// Custom log profile that logs non-GET requests and API requests
class ApiLogProfile implements LogProfile {
@override
bool shouldLogRequest(shelf.Request request) {
return request.method != 'GET' || request.url.path.startsWith('api/');
}
}
/// This example demonstrates how to use contextual_shelf with a Shelf server.
/// It shows request logging, error handling, and custom log formatting.
void main() async {
// Create a logger with multiple channels
final logger = Logger()
..addChannel(
'console',
ConsoleLogDriver(),
formatter: PrettyLogFormatter(),
)
..addChannel(
'file',
DailyFileLogDriver('logs/server.log'),
formatter: JsonLogFormatter(),
);
// Create a custom log writer with sanitization
final logWriter = DefaultLogWriter(
logger,
sanitizer: Sanitizer(mask: '[REDACTED]'),
);
// Create a log profile that determines which requests to log
final logProfile = ApiLogProfile();
// Create the HTTP logger middleware
final httpLogger = HttpLogger(logProfile, logWriter);
// Create a handler pipeline with logging middleware
final handler = const shelf.Pipeline()
.addMiddleware(httpLogger.middleware)
.addHandler(_handleRequest);
// Start the server
final server = await io.serve(handler, 'localhost', 8080);
print('Server running on port ${server.port}');
// Log server startup
logger.info(
'Server started',
Context({
'port': server.port,
'address': server.address.host,
}));
}
/// Example request handler that demonstrates different scenarios
Future<shelf.Response> _handleRequest(shelf.Request request) async {
try {
switch (request.url.path) {
case 'api/users':
// Simulate successful API request
return shelf.Response.ok(
'{"status": "success", "data": ["user1", "user2"]}',
headers: {'content-type': 'application/json'},
);
case 'api/error':
// Simulate error condition
throw Exception('Simulated API error');
case 'api/slow':
// Simulate slow request
await Future.delayed(Duration(seconds: 2));
return shelf.Response.ok('Slow response');
case 'api/auth':
// Simulate authenticated request
final auth = request.headers['authorization'];
if (auth == null) {
return shelf.Response.unauthorized('No authorization provided');
}
return shelf.Response.ok('Authenticated');
default:
// Handle static content or other routes
return shelf.Response.ok('Hello from Shelf!');
}
} catch (e, _) {
// Errors will be logged by the middleware
return shelf.Response.internalServerError(
body: 'Internal Server Error',
);
}
}
String getCurrentFileAndLine() {
// Generate a current stack trace.
final stackTrace = StackTrace.current.toString();
// The stack trace is a multi-line string; each line looks something like:
// #0 MyClass.myMethod (file:///path/to/file.dart:42:16)
// We can split on newlines and parse the line that describes this method call.
final traceLines = stackTrace.split('\n');
// The first frame (#0) usually corresponds to the current line in Dart.
// Adjust the index if you need a different call site.
final frame = traceLines.isNotEmpty ? traceLines[1] : '';
// A quick (naive) pattern match to find parentheses and extract file info.
// You might want to refine this or use a regex for more robust parsing.
final fileInfoStart = frame.indexOf('(');
final fileInfoEnd = frame.indexOf(')');
if (fileInfoStart != -1 && fileInfoEnd != -1 && fileInfoStart < fileInfoEnd) {
final fileInfo = frame.substring(fileInfoStart + 1, fileInfoEnd);
return fileInfo; // something like "file:///path/to/file.dart:42:16"
}
// If parsing fails for some reason, return the raw frame or an empty string.
return frame;
}