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

feat: add request logging #716

Merged
merged 8 commits into from Jul 30, 2022
4 changes: 2 additions & 2 deletions source/main.ts
Expand Up @@ -126,11 +126,11 @@ for (const endpoint of args['--listen']) {
let message = chalk.green('Serving!');
if (local) {
const prefix = network ? '- ' : '';
const space = network ? ' ' : ' ';
const space = network ? ' ' : ' ';

message += `\n\n${chalk.bold(`${prefix}Local:`)}${space}${local}`;
}
if (network) message += `\n${chalk.bold('- On Your Network:')} ${network}`;
if (network) message += `\n${chalk.bold('- Network:')} ${network}`;
if (previous)
message += chalk.red(
`\n\nThis port was picked because ${chalk.underline(
Expand Down
1 change: 1 addition & 0 deletions source/types.ts
Expand Up @@ -72,6 +72,7 @@ export declare interface Options {
'--single': boolean;
'--debug': boolean;
'--config': Path;
'--no-request-logging': boolean;
'--no-clipboard': boolean;
'--no-compression': boolean;
'--no-etag': boolean;
Expand Down
8 changes: 6 additions & 2 deletions source/utilities/cli.ts
Expand Up @@ -14,6 +14,7 @@ const options = {
'--single': Boolean,
'--debug': Boolean,
'--config': String,
'--no-request-logging': Boolean,
'--no-clipboard': Boolean,
'--no-compression': Boolean,
'--no-etag': Boolean,
Expand All @@ -30,6 +31,7 @@ const options = {
'-s': '--single',
'-d': '--debug',
'-c': '--config',
'-L': '--no-request-logging',
'-n': '--no-clipboard',
'-u': '--no-compression',
'-S': '--symlinks',
Expand Down Expand Up @@ -66,12 +68,14 @@ const helpText = chalk`

-p Specify custom port

-d, --debug Show debugging information

-s, --single Rewrite all not-found requests to \`index.html\`

-d, --debug Show debugging information

-c, --config Specify custom path to \`serve.json\`

-L, --no-request-logging Do not log any request information to the console.

-C, --cors Enable CORS, sets \`Access-Control-Allow-Origin\` to \`*\`

-n, --no-clipboard Do not copy the local address to the clipboard
Expand Down
10 changes: 6 additions & 4 deletions source/utilities/logger.ts
Expand Up @@ -5,12 +5,14 @@

import chalk from 'chalk';

const http = (...message: string[]) =>
console.info(chalk.bgBlue.bold(' HTTP '), ...message);
const info = (...message: string[]) =>
console.error(chalk.magenta('INFO:', ...message));
console.info(chalk.bgMagenta.bold(' INFO '), ...message);
const warn = (...message: string[]) =>
console.error(chalk.yellow('WARNING:', ...message));
console.error(chalk.bgYellow.bold(' WARN '), ...message);
const error = (...message: string[]) =>
console.error(chalk.red('ERROR:', ...message));
console.error(chalk.bgRed.bold(' ERROR '), ...message);
const log = console.log;

export const logger = { info, warn, error, log };
export const logger = { http, info, warn, error, log };
26 changes: 26 additions & 0 deletions source/utilities/server.ts
Expand Up @@ -7,8 +7,10 @@ import { readFile } from 'node:fs/promises';
import handler from 'serve-handler';
import compression from 'compression';
import isPortReachable from 'is-port-reachable';
import chalk from 'chalk';
import { getNetworkAddress, registerCloseListener } from './http.js';
import { promisify } from './promise.js';
import { logger } from './logger.js';
import type { IncomingMessage, ServerResponse } from 'node:http';
import type { AddressInfo } from 'node:net';
import type {
Expand Down Expand Up @@ -46,13 +48,37 @@ export const startServer = async (
type ExpressRequest = Parameters<typeof compress>[0];
type ExpressResponse = Parameters<typeof compress>[1];

// Log the request.
const requestTime = new Date();
const formattedTime = `${requestTime.toLocaleDateString()} ${requestTime.toLocaleTimeString()}`;
const ipAddress =
request.socket.remoteAddress?.replace('::ffff:', '') ?? 'unknown';
const requestUrl = `${request.method ?? 'GET'} ${request.url ?? '/'}`;
if (!args['--no-request-logging'])
logger.http(
chalk.dim(formattedTime),
chalk.yellow(ipAddress),
chalk.cyan(requestUrl),
);

if (args['--cors'])
response.setHeader('Access-Control-Allow-Origin', '*');
if (!args['--no-compression'])
await compress(request as ExpressRequest, response as ExpressResponse);

// Let the `serve-handler` module do the rest.
await handler(request, response, config);

// Before returning the response, log the status code and time taken.
const responseTime = Date.now() - requestTime.getTime();
if (!args['--no-request-logging'])
logger.http(
chalk.dim(formattedTime),
chalk.yellow(ipAddress),
chalk[response.statusCode < 400 ? 'green' : 'red'](
`Returned ${response.statusCode} in ${responseTime} ms`,
),
);
};

// Then we run the async function, and re-throw any errors.
Expand Down