stick
Version:
JSGI based webapp framework
160 lines (139 loc) • 5.21 kB
JavaScript
/**
* @fileOverview Middleware for collecting log messages issued during execution
* of the current request.
*
* This adds a `requestlog` property to the application object with an `items`
* property. During execution of a request `items` contains an array containing
* all the log messages issued for the request. Log messages are represented
* as arrays in the format `[time, level, name, message]`.
*
* During request execution, the `requestlog` property also defines a
* property called `start` containing the time the execution started.
*
* By default, messages are appended to the response if its Content-Type is
* `text/html`. This can be controlled using the `app.requestlog.append`
* boolean flag.
*/
var strings = require('common-utils/strings');
var Buffer = require('common-utils/buffer').Buffer;
var logging = { on: function() {} }; // require('ringo/logging');
var http = require('../utils/http');
var ResponseFilter = http.ResponseFilter, Headers = http.Header;
var Thread = function() {}; // java.lang.Thread;
var ConcurrentHashMap = function() {}; //java.util.concurrent.ConcurrentHashMap;
var threadMap = new ConcurrentHashMap();
function collect() {
var obj = threadMap.get(Thread.currentThread());
if (obj && obj.items) {
var args = Array.slice(arguments);
args.unshift(new Date()); // inject date
obj.items.push(args);
}
}
logging.on("trace", collect.bind(undefined, "trace"));
logging.on("debug", collect.bind(undefined, "debug"));
logging.on("info", collect.bind(undefined, "info"));
logging.on("warn", collect.bind(undefined, "warn"));
logging.on("error", collect.bind(undefined, "error"));
function RequestLog() {
this.enable = true;
this.append = true;
}
Object.defineProperties(RequestLog.prototype, {
items: {
get: function() {
var obj = threadMap.get(Thread.currentThread());
return obj && obj.items ? obj.items : null;
},
enumerable: true
},
start: {
get: function() {
var obj = threadMap.get(Thread.currentThread());
return obj && obj.start ? obj.start : null;
},
enumerable: true
},
});
/**
* Middleware for collecting log messages issued during execution of the current
* request.
*
* @param {Function} next the wrapped middleware chain
* @param {Object} app the Stick Application object
* @returns {Function} a JSGI middleware function
*/
exports.middleware = function requestlog(next, app) {
app.requestlog = new RequestLog();
return function requestlog(request) {
if (!app.requestlog.enable) {
return next(request);
}
var items = [],
start = Date.now(),
thread = Thread.currentThread(),
response;
try {
var contained = threadMap.containsKey(thread);
if (!contained) {
threadMap.put(thread, {
items: items,
start: start
});
}
response = next(request);
} finally {
if (!contained) threadMap.remove(thread);
}
if (app.requestlog.append) {
var status = response.status, headers = response.headers, body = response.body;
// only do this for ordinary HTML responses
var contentType = Headers(headers).get("content-type");
if (status != 200 && status < 400 || !contentType
|| !strings.startsWith(contentType, "text/html")) {
return response;
}
if (items.length > 0) {
response.body = new ResponseFilter(body, function(part) {
if (typeof part != "string" || part.lastIndexOf("</body>") == -1) {
return part;
}
return appendMessages(part, items, start);
});
}
}
return response;
};
}
function appendMessages(part, items, start) {
var buffer = new Buffer();
for (var i = 0; i < items.length; i++) {
appendMessage(buffer, items[i], start);
}
var insert = part.lastIndexOf("</body>");
return part.substring(0, insert) + buffer + part.substring(insert);
}
function appendMessage(buffer, item, start) {
var time = item[0], level = item[1], name = item[2], message = item[3];
var multiline = message
&& (message.trim().indexOf('\n') > 0 || message.indexOf('\r')> 0);
var bgcolor = colors[level] || '#fff';
buffer.write("<div class='ringo-debug-line' style='background:", bgcolor,
"; color: black; border-top: 1px solid black; clear: both;'>");
var timePassed = time - start; // numbers.format(time - start, "00000");
var formatted = strings.format("{} [{}] {}: {}", timePassed, level, name, message);
if (multiline) {
buffer.write("<pre>", formatted, "</pre>");
} else {
buffer.write(formatted);
}
buffer.writeln("</div>");
}
var colors = {
trace: '#fff',
debug: '#fff',
info: '#ff6',
warn: '#ff0',
error: '#f90',
fatal: '#f30'
};