Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Visualises requests when in development. #104

Closed
wants to merge 8 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:

<img width="261" alt="Production Logging" src="https://cloud.githubusercontent.com/assets/22048/21488276/7fba8ea0-cb92-11e6-8b8c-c2f280026630.png">

### Testing

Micro makes tests compact and a pleasure to read and write.
Expand Down
8 changes: 5 additions & 3 deletions bin/micro
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ const args = parse(process.argv, {
alias: {
H: 'host',
h: 'help',
p: 'port'
p: 'port',
L: 'log'
},
boolean: ['h'],
default: {
Expand All @@ -28,6 +29,7 @@ const help = () => {
console.log(`Usage: micro [opts] <file>
-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`);
}

Expand Down Expand Up @@ -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)
Expand Down
62 changes: 58 additions & 4 deletions lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It'd be cool to document in the README that we do the nicer logging upon explicit NODE_ENV = development setting

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

Expand All @@ -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
Expand All @@ -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)
Expand Down Expand Up @@ -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
}
Expand Down Expand Up @@ -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)
}
})
}
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
67 changes: 67 additions & 0 deletions test/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,24 @@ test('send(<Number>)', async t => {
}
})

test('send(200, <Object>) with log', async t => {
const fn = async (req, res) => {
send(res, 200, {
a: 'b'
})
}

const url = await listen(fn, {log: 'dev'})

const res = await request(url, {
json: true
})

t.deepEqual(res, {
a: 'b'
})
})

test('return <String>', async t => {
const fn = async () => 'woot'

Expand Down Expand Up @@ -294,6 +312,55 @@ test('json', async t => {
t.deepEqual(body.response, 'json')
})

test('json parses twice', async t => {
const fn = async (req, res) => {
await json(req)
const body = await json(req)

send(res, 200, {
response: body.some.cool
})
}

const url = await listen(fn)

const body = await request(url, {
method: 'POST',
body: {
some: {
cool: 'json'
}
},
json: true
})

t.deepEqual(body.response, 'json')
})

test('json with log', async t => {
const fn = async (req, res) => {
const body = await json(req)

send(res, 200, {
response: body.some.cool
})
}

const url = await listen(fn, {log: 'dev'})

const body = await request(url, {
method: 'POST',
body: {
some: {
cool: 'json'
}
},
json: true
})

t.deepEqual(body.response, 'json')
})

test('json limit (below)', async t => {
const fn = async (req, res) => {
const body = await json(req, {
Expand Down
Loading