feat(middleware-logger): filter requests + silent mode
MODIFY
packages/@integration/src/app.ts
packages/@integration/src/app.ts
import { httpListener } from '@marblejs/core';
import { bodyParser$ } from '@marblejs/middleware-body';
-import { logger$ } from '@marblejs/middleware-logger';
+import { loggerWithOpts$ } from '@marblejs/middleware-logger';
import { api$ } from './effects/api.effects';
const middlewares = [
- logger$,
+ loggerWithOpts$({ silent: false }),
bodyParser$,
];

ADD
packages/middleware-logger/src/console.factory.ts
+import chalk from 'chalk';
+
+export const formatStatusCode = (statusCode: number): string =>
+ statusCode >= 400
+ ? chalk.yellow(statusCode.toString())
+ : chalk.green(statusCode.toString());
+
+export const printLog = (
+ method: string,
+ url: string,
+ statusCode: string,
+ formattedTime: string
+) => {
+ console.info(`%s %s %s %s`, method, url, statusCode, formattedTime);
+};

MODIFY
packages/middleware-logger/src/index.ts
packages/middleware-logger/src/index.ts
-import { Middleware } from '@marblejs/core';
-import { compose } from '@marblejs/core/dist/+internal';
-import chalk from 'chalk';
-import { tap } from 'rxjs/operators';
-import { formatTime, getTimeDifferenceMs } from './time.factory';
-
-const getStatusCode = (statusCode: number): string =>
- statusCode >= 400
- ? chalk.yellow(statusCode.toString())
- : chalk.green(statusCode.toString());
-
-const printLog = (
- method: string,
- url: string,
- statusCode: string,
- formattedTime: string
-) => {
- console.info(`%s %s %s %s`, method, url, statusCode, formattedTime);
-};
-
-export const logger$: Middleware = (req$, res) =>
- req$.pipe(
- tap(req => {
- const startTime = new Date();
-
- res.on('finish', () =>
- printLog(
- req.method,
- req.url,
- getStatusCode(res.statusCode),
- compose(formatTime, getTimeDifferenceMs)(startTime)
- )
- );
- })
- );
+export { logger$, loggerWithOpts$ } from './logger.middleware';

ADD
packages/middleware-logger/src/logger.middleware.ts
+import { Middleware, HttpRequest, HttpResponse } from '@marblejs/core';
+import { compose, Maybe } from '@marblejs/core/dist/+internal';
+import { WriteStream } from 'fs';
+import { fromEvent, Timestamp } from 'rxjs';
+import { timestamp, tap, map, take, filter, mapTo } from 'rxjs/operators';
+import { printLog, formatStatusCode } from './console.factory';
+import { formatTime, getTimeDifferenceMs } from './time.factory';
+
+export interface LoggerOptions {
+ silent?: boolean;
+ stream?: WriteStream;
+ filter?: (req: HttpRequest) => boolean;
+}
+
+const isNotSilent = (opts: LoggerOptions) => () =>
+ !opts.silent;
+
+const isRequest = (opts: LoggerOptions) => ({ value: req }: Timestamp<HttpRequest>) =>
+ Maybe.of(opts.filter)
+ .map(filter => filter(req))
+ .valueOr(true);
+
+const handleFinishEvent = (res: HttpResponse, opts: LoggerOptions) => (stamp: Timestamp<HttpRequest>) =>
+ fromEvent(res, 'finish')
+ .pipe(
+ take(1),
+ mapTo(stamp),
+ filter(isNotSilent(opts)),
+ filter(isRequest(opts)),
+ )
+ .subscribe(({ value: req, timestamp }) => {
+ printLog(
+ req.method,
+ req.url,
+ formatStatusCode(res.statusCode),
+ compose(formatTime, getTimeDifferenceMs)(new Date(timestamp))
+ );
+ });
+
+/**
+ * @deprecated [#1] since version 1.1,
+ * [#2] will be deleted in version 2.0,
+ * [#3] use loggerWithOpts$ instead,
+ */
+export const logger$: Middleware = (req$, res) => {
+ // tslint:disable-next-line:max-line-length
+ console.warn('Deprecation warning: logger$ is deprecated since v1.1 and will be removed in v2.0. Use loggerWithOpts$ instead.');
+ return loggerWithOpts$()(req$, res, null);
+};
+
+export const loggerWithOpts$ = (opts: LoggerOptions = {}): Middleware => (req$, res) =>
+ req$.pipe(
+ timestamp(),
+ tap(handleFinishEvent(res, opts)),
+ map(({ value: req }) => req),
+ );

RENAME
packages/middleware-logger/src/index.spec.ts
packages/middleware-logger/src/spec/logger-middleware.spec.ts
import { HttpRequest, HttpResponse } from '@marblejs/core';
import { Marbles } from '@marblejs/core/dist/+internal';
import { EventEmitter } from 'events';
-import { logger$ } from './index';
+import { logger$ } from '../logger.middleware';
const createMockReq = (url: string, method: string) => ({ url, method } as HttpRequest);
const createMockRes = (status: number) => new class extends EventEmitter {
@@ -16,7 +16,7 @@ describe('Logger middleware', () => {
spyOn(console, 'error').and.stub();
});
- it('reacts to 200 status on the console', () => {
+ test('reacts to 200 status on the console', () => {
const request = createMockReq('/', 'GET');
const response = createMockRes(200);
@@ -29,7 +29,7 @@ describe('Logger middleware', () => {
expect(console.info).toHaveBeenCalled();
});
- it('reacts to 400 status on the console', () => {
+ test('reacts to 400 status on the console', () => {
const request = createMockReq('/test', 'POST');
const response = createMockRes(403);

RENAME
packages/middleware-logger/src/time.factory.spec.ts
packages/middleware-logger/src/spec/time.factory.spec.ts
-import { formatTime, getTimeDifferenceMs } from './time.factory';
+import { formatTime, getTimeDifferenceMs } from '../time.factory';
describe('TimeFactory', () => {

feat(middleware-logger): filter requests + silent mode