Last active
May 17, 2020 03:05
-
-
Save lswith/640b0d46996c94ce38f8c8037582188e to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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