UNPKG

3.29 kBJavaScriptView Raw
1"use strict"
2
3const uuid = require("uuid/v4")
4const { ErrorReporting } = require("@google-cloud/error-reporting")
5
6const reqIdHeader = "X-Request-Id"
7
8module.exports = function createCtxLogger(logger, options={}, fetchUser=null) {
9 const gcloudErrorReporting = new ErrorReporting(gcloudOptions(options))
10 const errorHandler = initErrorHandler(gcloudErrorReporting, fetchUser || (async () => {}))
11 const logMiddleware = initLogMiddleware(logger, errorHandler)
12
13 return logMiddleware
14}
15
16function initLogMiddleware(logger, errorHandler) {
17 return async (ctx, next) => {
18 let reqId = ctx.reqId = ctx.request.get(reqIdHeader) || uuid()
19 let log = logger.child(reqId)
20 let requestTimer = log.timer()
21
22 ctx.log = log
23 ctx.requestTimer = requestTimer
24 ctx.errorReporting = errorHandler.gcloudErrorReporting
25
26 try {
27 await next()
28 } catch (e) {
29 await errorHandler(ctx, e)
30 } finally {
31 ctx.response.set("X-Server-Request-Id", ctx.reqId)
32 ctx.requestTimer.end("%s %s > %d", ctx.request.method, ctx.request.url, ctx.response.status)
33 }
34 }
35}
36
37function initErrorHandler(gcloudErrorReporting, fetchUser) {
38 const handler = async (ctx, e) => {
39 if (e instanceof Error === false) {
40 e = new Error(e)
41 }
42
43 if (!e.status) e.status = 500
44 if (!e.jsonBody) e.jsonBody = {}
45
46 ctx.type = "json"
47 ctx.status = e.status
48
49 ctx.log.debug("%s %s < params: %O, body: %O", ctx.request.method, ctx.request.url, ctx.params, ctx.request.body)
50
51 if (!e.expose) {
52 let user = await fetchUser(ctx)
53
54 let requestInformation = {
55 method: ctx.method,
56 url: ctx.url,
57 userAgent: ctx.request.get("user-agent"),
58 referrer: ctx.request.get("referrer"),
59 statusCode: ctx.status,
60 remoteAddress: ctx.ip,
61 requestId: ctx.request.get("x-request-id") }
62
63 let errorMessage = gcloudErrorReporting.event()
64 .setMessage(`${e.stack}\n\nreqId:${ctx.reqId}`)
65 .consumeRequestInformation(requestInformation)
66
67 if (user) errorMessage.setUser(user)
68
69 gcloudErrorReporting.report(errorMessage, (err) => {
70 if (err) return ctx.log.trace(`Error ${err} while reporting to google cloud`)
71 ctx.log.trace("Error reported to GCloud")
72 })
73
74 ctx.log.error(e)
75 }
76
77 e.jsonBody.status = e.status
78 e.jsonBody.message = e.expose ? e.message : "Error"
79
80 ctx.body = {
81 error: e.jsonBody,
82 reqId: ctx.reqId
83 }
84 }
85
86 handler.gcloudErrorReporting = gcloudErrorReporting
87 return handler
88}
89
90function gcloudOptions(options) {
91 let pckg = require("package.json")
92 let serviceContext = options.serviceContext || {
93 service: pckg.name,
94 version: pckg.version }
95
96 let gcloudOptions = {
97 serviceContext,
98 projectId: options.projectId,
99 reportUnhandledRejections: true,
100 logLevel: 2 || options.logLevel,
101 reportMode: options.reportMode || (options.ignoreEnvironmentCheck ? "always" : "production")
102 }
103 if (options.credentials) {
104 gcloudOptions.credentials = options.credentials
105 }
106
107 return gcloudOptions
108}