diff --git a/README.md b/README.md
index 2d0a755c..4f32ac2c 100644
--- a/README.md
+++ b/README.md
@@ -249,6 +249,16 @@ function handleErrors (fn) {
}
```
+### Logging
+
+Micro provides built-in logging to help visualise requests. When `NODE_ENV` is set to `development`, micro will display the request along with any json body like this:
+
+![Development Logging](https://cloud.githubusercontent.com/assets/22048/19218565/2c6081ba-8db2-11e6-93df-9e256a8354fb.png)
+
+This behavior can be overridden by using the cli argument `--log` which can be set to `dev`, `prod`, or `off`. The `prod` version is a condensed log that will not show the json body:
+
+
+
### Testing
Micro makes tests compact and a pleasure to read and write.
diff --git a/bin/micro b/bin/micro
index 12811762..cdf9c0c0 100755
--- a/bin/micro
+++ b/bin/micro
@@ -14,7 +14,8 @@ const args = parse(process.argv, {
alias: {
H: 'host',
h: 'help',
- p: 'port'
+ p: 'port',
+ L: 'log'
},
boolean: ['h'],
default: {
@@ -28,6 +29,7 @@ const help = () => {
console.log(`Usage: micro [opts]
-H, --host Host to listen on [0.0.0.0]
-p, --port Port to listen on [3000]
+ -L, --log dev|prod|off [off]
-h, --help Show this help message`);
}
@@ -79,8 +81,8 @@ if ('function' !== typeof mod) {
process.exit(1)
}
-const { port, host } = args
-serve(mod).listen(port, host, err => {
+const { port, host, log } = args
+serve(mod, {log}).listen(port, host, err => {
if (err) {
console.error('micro:', err.stack)
process.exit(1)
diff --git a/lib/index.js b/lib/index.js
index b016f24c..1837111a 100644
--- a/lib/index.js
+++ b/lib/index.js
@@ -5,8 +5,22 @@ const server = require('http').Server
const getRawBody = require('raw-body')
const typer = require('media-typer')
const isStream = require('isstream')
+const chalk = require('chalk')
+const jsome = require('jsome')
const DEV = 'development' === process.env.NODE_ENV
+jsome.colors = {
+ num: 'cyan',
+ str: 'green',
+ bool: 'red',
+ regex: 'blue',
+ undef: 'grey',
+ null: 'grey',
+ attr: 'reset',
+ quot: 'reset',
+ punc: 'reset',
+ brack: 'reset'
+}
module.exports = exports = serve
@@ -16,18 +30,26 @@ exports.send = send
exports.sendError = sendError
exports.createError = createError
-function serve(fn, {onError = null} = {}) {
+function serve(fn, {onError = null, log} = {}) {
+ if (typeof log === 'undefined' && DEV) {
+ log = 'dev'
+ }
+
if (onError) {
console.warn('[DEPRECATED] onError is deprecated and will be removed in a future release. Please use your own try/catch as needed.')
}
return server((req, res) => {
- run(req, res, fn, onError || sendError)
+ run(req, res, fn, onError || sendError, {log})
})
}
-async function run(req, res, fn, onError) {
+async function run(req, res, fn, onError, {log}) {
try {
+ if (log === 'dev' || log === 'prod') {
+ await logRequest(req, res, log)
+ }
+
const val = await fn(req, res)
// return a non-null value -> send
@@ -44,7 +66,8 @@ async function json(req, {limit = '1mb'} = {}) {
const type = req.headers['content-type']
const length = req.headers['content-length']
const encoding = typer.parse(type).parameters.charset
- const str = await getRawBody(req, {limit, length, encoding})
+ req.rawBody = req.rawBody || getRawBody(req, {limit, length, encoding})
+ const str = await req.rawBody
try {
return JSON.parse(str)
@@ -94,6 +117,7 @@ function send(res, code, obj = null) {
str = JSON.stringify(obj)
}
res.setHeader('Content-Type', 'application/json')
+ res._logBody = obj
} else {
str = obj
}
@@ -122,3 +146,33 @@ function createError(code, msg, orig) {
err.originalError = orig
return err
}
+
+let requestCounter = 0
+
+async function logRequest(req, res, log) {
+ const start = new Date()
+ const requestIndex = ++requestCounter
+ const dateString = `${chalk.grey(start.toLocaleTimeString())}`
+ console.log(`> #${requestIndex} ${chalk.bold(req.method)} ${req.url}\t\t${dateString}`)
+
+ if (log === 'dev') {
+ try {
+ const parsedJson = await json(req)
+ jsome(parsedJson)
+ } catch (err) {
+ console.log(`JSON body could not be parsed: ${err.message}`)
+ }
+ }
+
+ res.once('finish', () => {
+ const delta = new Date() - start
+ const time = delta < 10000 ? `${delta}ms` : `${Math.round(delta / 1000)}s`
+ const endDateString = `${chalk.grey(new Date().toLocaleTimeString())}`
+
+ console.log(`< #${requestIndex} ${chalk.bold(res.statusCode)} [+${time}]\t${endDateString}`)
+
+ if (log === 'dev' && res._logBody) {
+ jsome(res._logBody)
+ }
+ })
+}
diff --git a/package.json b/package.json
index fbbbd17f..b09e97c4 100644
--- a/package.json
+++ b/package.json
@@ -69,8 +69,10 @@
},
"dependencies": {
"async-to-gen": "1.3.0",
+ "chalk": "^1.1.3",
"is-async-supported": "1.2.0",
"isstream": "0.1.2",
+ "jsome": "^2.3.25",
"media-typer": "0.3.0",
"minimist": "1.2.0",
"raw-body": "2.2.0"
diff --git a/test/index.js b/test/index.js
index 795aace0..40769921 100644
--- a/test/index.js
+++ b/test/index.js
@@ -90,6 +90,24 @@ test('send()', async t => {
}
})
+test('send(200,