Skip to content

Instantly share code, notes, and snippets.

@lswith
Last active May 17, 2020 03:05
Show Gist options
  • Save lswith/640b0d46996c94ce38f8c8037582188e to your computer and use it in GitHub Desktop.
Save lswith/640b0d46996c94ce38f8c8037582188e to your computer and use it in GitHub Desktop.
import {
asGlobalInterceptor,
bind,
BindingScope,
ContextTags,
inject,
Interceptor,
InvocationContext,
Provider,
ValueOrPromise,
BindingKey,
} from '@loopback/context';
import {RequestContext, RestBindings} from '@loopback/rest';
import {Logger} from 'winston';
import {LoggingBindings, LoggingComponent} from '@loopback/extension-logging';
import morgan from 'morgan';
import {Request, Response} from 'express';
function jsonFormat(tokens: morgan.TokenIndexer, req: Request, res: Response) {
const empty = (x: Request, y: Response) => {
return '';
};
const emptyExtra = (x: Request, y: Response, z: string) => {
return '';
};
const remoteAddr = tokens['remote-addr'] ?? empty;
const date = tokens['date'] ?? emptyExtra;
const method = tokens['method'] ?? empty;
const url = tokens['url'] ?? empty;
const httpVersion = tokens['http-version'] ?? empty;
const statusCode = tokens['status'] ?? empty;
const contentLength = tokens['res'] ?? emptyExtra;
const referrer = tokens['referrer'] ?? empty;
const userAgent = tokens['user-agent'] ?? empty;
const hostname = tokens['hostname'] ?? empty;
return JSON.stringify({
'remote-address': remoteAddr(req, res),
time: date(req, res, 'iso'),
method: method(req, res),
url: url(req, res),
'http-version': httpVersion(req, res),
'status-code': statusCode(req, res),
'content-length': contentLength(req, res, 'content-length'),
referrer: referrer(req, res),
'user-agent': userAgent(req, res),
hostname: hostname(req, res),
});
}
@bind({
tags: {
[ContextTags.KEY]: BindingKey.create<CustomLoggingComponent>(
'components.CustomLoggingComponent',
),
},
})
export class CustomLoggingComponent extends LoggingComponent {
constructor() {
const loggingConfig = {
enableFluent: false,
enableHttpAccessLog: false,
};
super(loggingConfig);
this.providers[
LoggingBindings.WINSTON_HTTP_ACCESS_LOGGER.key
] = HttpAccessLogInterceptor;
}
}
/**
* A global interceptor that provides logging for http requests/responses.
* (Custom one that parses the morgan format back into json)
*/
@bind(asGlobalInterceptor('logging'), {
tags: {
[ContextTags.KEY]: LoggingBindings.WINSTON_HTTP_ACCESS_LOGGER,
// Only apply to invocations from REST routes
[ContextTags.GLOBAL_INTERCEPTOR_SOURCE]: 'route',
},
scope: BindingScope.SINGLETON,
})
export class HttpAccessLogInterceptor implements Provider<Interceptor> {
constructor(
@inject(LoggingBindings.WINSTON_LOGGER)
private logger: Logger,
) {}
value() {
return this.intercept.bind(this);
}
async intercept<T>(
invocationCtx: InvocationContext,
next: () => ValueOrPromise<T>,
) {
const reqCtx = await invocationCtx.get<RequestContext>(
RestBindings.Http.CONTEXT,
);
const options: morgan.Options = {
stream: {
write: (message: string) => {
try {
const logMessage = JSON.parse(message);
logMessage.level = 'info';
this.logger.log(logMessage);
} catch (err) {
this.logger.info(message);
}
},
},
};
morgan(jsonFormat, options)(reqCtx.request, reqCtx.response, () => {});
return next();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment