forked from pinpoint-apm/pinpoint-node-agent
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathexpress.js
151 lines (131 loc) · 5.53 KB
/
express.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
/**
* Pinpoint Node.js Agent
* Copyright 2020-present NAVER Corp.
* Apache License v2.0
*/
'use strict'
const shimmer = require('shimmer')
const ServiceTypeCode = require('../../constant/service-type').ServiceTypeCode
const log = require('../../utils/logger')
const MethodDescriptor = require('../../context/method-descriptor')
const apiMetaService = require('../../context/api-meta-service')
const StringUtils = require('../../utils/string-utils')
module.exports = function (agent, version, express) {
const MODULE_NAME = 'express'
const methodNames = ['GET', 'POST', 'PUT', 'HEAD', 'DELETE', 'OPTIONS', 'PATCH', 'ALL']
const names = methodNames.map(method => ['route', method])
const methodDescriptorMap = names.reduce((descMap, [objectName, methodName]) => {
const descriptor = MethodDescriptor.create(MODULE_NAME, objectName, methodName)
apiMetaService.cacheApi(descriptor)
descMap[objectName + methodName] = descriptor
return descMap
}, {})
function getMethodDescriptor(objectPath, methodName, httpMethod, callstack) {
const stacks = callstack.stack.split(/\n */)
const expressMethodDescriptor = makeHttpMethodMethodDescriptor(stacks)
const method = objectPath === 'route' ? httpMethod : methodName
return methodDescriptorMap[objectPath + method]
}
function getDescriptionText(objectPath, methodName, httpMethod) {
return MODULE_NAME + '.' + objectPath + '.' + (methodName ? StringUtils.encodeMethodName(methodName) : 'AnonymousFunction')
}
// shimmer.wrap(express.application, 'use', function (original) {
// return function () {
// log.info('>> [Express] express.application.use ', this.stack)
// return original.apply(this, arguments)
// }
// })
shimmer.wrap(express.Router, 'use', function (original) {
return function () {
const result = original.apply(this, arguments)
const fn = arguments && arguments[1]
if (fn && fn.name !== 'router' && this.stack && this.stack.length) {
// log.debug('>> [Express] express.Router.use ', this.stack[this.stack.length - 1])
const layer = this.stack[this.stack.length - 1]
doPatchLayer(layer, 'middleware', layer.name)
}
return result
}
})
shimmer.wrap(express.Router, 'route', function (original) {
return function () {
const result = original.apply(this, arguments)
if (this.stack && this.stack.length) {
const callstack = {}
Error.captureStackTrace(callstack)
const layer = this.stack[this.stack.length - 1]
doPatchLayer(layer, 'route', layer.name, callstack)
}
return result
}
})
function doPatchLayer(layer, moduleName, methodName, callstack) {
shimmer.wrap(layer, 'handle', function (original) {
return (original.length === 4)
? recordErrorHandle(original, moduleName, methodName)
: recordHandle(original, moduleName, methodName, callstack)
})
}
function recordHandle(original, moduleName, methodName, callstack) {
return function (req, res, next) {
// log.debug('recordHandle start', getMethodDescriptor(moduleName, methodName, req.method))
const trace = agent.traceContext.currentTraceObject()
let spanEventRecorder = null
if (trace) {
spanEventRecorder = trace.traceBlockBegin()
spanEventRecorder.recordServiceType(ServiceTypeCode.express)
const methodDescriptor = getMethodDescriptor(moduleName, methodName, req.method, callstack)
if (methodDescriptor) {
spanEventRecorder.recordApi(getMethodDescriptor(moduleName, methodName, req.method, callstack))
} else {
spanEventRecorder.recordApiDesc(getDescriptionText(moduleName, methodName, req.method))
}
}
const result = original.apply(this, arguments)
if (trace) {
trace.traceBlockEnd(spanEventRecorder)
}
// log.debug('recordHandle end', getMethodDescriptor(moduleName, methodName, req.method))
return result
}
}
function recordErrorHandle(original, moduleName, methodName) {
return function (err, req, res, next) {
log.debug('recordErrorHandle start', getMethodDescriptor(moduleName, methodName, req.method))
const trace = agent.traceContext.currentTraceObject()
let spanEventRecorder = null
if (err && trace) {
spanEventRecorder = trace.traceBlockBegin()
spanEventRecorder.recordServiceType(ServiceTypeCode.express)
spanEventRecorder.recordException(err, true)
const methodDescriptor = getMethodDescriptor(moduleName, methodName, req.method)
if (methodDescriptor) {
spanEventRecorder.recordApi(getMethodDescriptor(moduleName, methodName, req.method))
} else {
spanEventRecorder.recordApiDesc(getDescriptionText(moduleName, methodName, req.method))
}
}
const result = original.apply(this, arguments)
if (trace) {
trace.traceBlockEnd(spanEventRecorder)
}
log.debug('recordErrorHandle end', getMethodDescriptor(moduleName, methodName, req.method))
return result
}
}
function resolveApiName(moduleName, httpMethod) {
if (moduleName === 'route') {
return moduleName + httpMethod
}
return moduleName
}
return express
}
function makeHttpMethodMethodDescriptor(stacks) {
if (!stacks || stacks.length < 4) {
return
}
const httpMethod = stacks[2]
const groups = httpMethod.match(/(?:Function\.(?<functionName>\w+)\..+)?(?:\s\[as\s(?<method>\w+)\])?\s\(.+\/(?<fileName>[^/]+):(?<lineNumber>[0-9]+):(?<columnNumber>[0-9]+)\)$/)
return new MethodDescriptor()
}