Skip to content

Commit 8f83afd

Browse files
wescaiyugasun
andauthored
feat: add monitor module (#28)
* feat: add monitor module * feat: add monitor export * fix: lint errors * fix: fix lint err && add exception case Co-authored-by: yugasun <yuga_sun@163.com>
1 parent dd715f1 commit 8f83afd

File tree

11 files changed

+486
-1
lines changed

11 files changed

+486
-1
lines changed

src/index.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ const Scf = require('./baas/scf')
99
const Tag = require('./baas/tag')
1010
const Postgresql = require('./baas/postgresql')
1111
const Vpc = require('./baas/vpc')
12+
const monitor = require('./monitor')
1213

1314
module.exports = {
1415
Apigw,
@@ -21,5 +22,6 @@ module.exports = {
2122
Scf,
2223
Tag,
2324
Postgresql,
24-
Vpc
25+
Vpc,
26+
monitor
2527
}

src/monitor/agent.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
const { EventEmitter } = require('events')
2+
const util = require('util')
3+
4+
function Agent() {}
5+
6+
util.inherits(Agent, EventEmitter)
7+
8+
module.exports = Agent

src/monitor/constants.js

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
/**
2+
* Enumeration of module instrumentation types.
3+
*
4+
* @private
5+
* @readonly
6+
* @enum {string}
7+
*/
8+
const MODULE_TYPE = {
9+
/** Web server framework module, such as Express or Koa. */
10+
WEB_FRAMEWORK: 'web-framework',
11+
PROXY: 'proxy'
12+
}
13+
14+
exports.MODULE_TYPE = MODULE_TYPE
15+
16+
/**
17+
* 请求开始时间存储key值(context内)
18+
*/
19+
const REUQEST_START = '__request_start__'
20+
21+
exports.REUQEST_START_KEY = REUQEST_START

src/monitor/index.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
const shimmer = require('./shimmer')
2+
const Agent = require('./agent')
3+
4+
function initialize() {
5+
const agent = new Agent()
6+
// 封装 module的_load方法,在load时针对基础组件附加探针
7+
shimmer.patchModule()
8+
// 初始化一系列基础组件
9+
shimmer.bootstrapInstrumentation(agent)
10+
}
11+
12+
initialize()
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
const utils = require('../utils')
2+
3+
/**
4+
* Express middleware generates traces where middleware are considered siblings
5+
* (ended on 'next' invocation) and not nested. Middlware are nested below the
6+
* routers they are mounted to.
7+
*/
8+
9+
function wrapRouteMethods(route) {
10+
const methods = ['all', 'delete', 'get', 'head', 'opts', 'post', 'put', 'patch']
11+
utils.wrapMethod(route, methods, function(fn) {
12+
return fn
13+
})
14+
}
15+
16+
module.exports = function initialize(agent, express) {
17+
if (!express || !express.Router) {
18+
return false
19+
}
20+
21+
// wrapExpress4(express)
22+
utils.wrapMethod(express.Router, 'route', function wrapRoute(fn) {
23+
if (!utils.isFunction(fn)) {
24+
return fn
25+
}
26+
27+
return function wrappedRoute() {
28+
const sourceRoute = fn.apply(this, arguments)
29+
// Express should create a new route and layer every time Router#route is
30+
// called, but just to be on the safe side, make sure we haven't wrapped
31+
// this already.
32+
if (!utils.isWrapped(sourceRoute, 'get')) {
33+
wrapRouteMethods(sourceRoute)
34+
35+
const layer = this.stack[this.stack.length - 1]
36+
utils.wrapMethod(layer, 'handle', function(func) {
37+
const { route } = layer
38+
const { path } = route
39+
return function(request, response) {
40+
function finish() {
41+
response.removeListener('finish', finish)
42+
request.removeListener('aborted', finish)
43+
// 状态码
44+
if (response.statusCode != null) {
45+
const responseCode = String(response.statusCode)
46+
if (/^\d+$/.test(responseCode)) {
47+
const context = request.headers['x-apigateway-context']
48+
agent.emit('responseFinish', context, request.method, path, responseCode)
49+
}
50+
}
51+
}
52+
53+
// response结束时上报状态码和耗时
54+
response.once('finish', finish)
55+
request.once('aborted', finish)
56+
57+
const handle = func.apply(this, arguments)
58+
return handle
59+
}
60+
})
61+
}
62+
return sourceRoute
63+
}
64+
})
65+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
const utils = require('../utils')
2+
const { REUQEST_START_KEY } = require('../constants')
3+
const report = require('../report')
4+
5+
module.exports = function initialize(agent, httpProxy) {
6+
utils.wrapMethod(httpProxy, 'proxy', function wrapRoute(fn) {
7+
return function(server, event, context) {
8+
context[REUQEST_START_KEY] = Date.now()
9+
const proxy = fn.apply(this, arguments)
10+
return new Promise(function(resolve) {
11+
agent.on('responseFinish', function(ctx, method, path, responseCode) {
12+
if (ctx) {
13+
report.reportHttp(ctx, method, path, responseCode).then(
14+
function() {
15+
resolve(proxy)
16+
},
17+
function() {
18+
resolve(proxy)
19+
}
20+
)
21+
} else {
22+
resolve(proxy)
23+
}
24+
})
25+
})
26+
}
27+
})
28+
}

src/monitor/instrumentations.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
const { MODULE_TYPE } = require('./constants')
2+
3+
module.exports = function instrumentations() {
4+
return {
5+
express: { type: MODULE_TYPE.WEB_FRAMEWORK },
6+
'tencent-serverless-http': { type: MODULE_TYPE.PROXY }
7+
}
8+
}

src/monitor/logger.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
module.exports = console

src/monitor/report.js

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
const { Capi } = require('@tencent-sdk/capi')
2+
const logger = require('./logger')
3+
const { REUQEST_START_KEY } = require('./constants')
4+
5+
// 字符串转16进制
6+
function str2hex(str) {
7+
if (str === '') {
8+
return ''
9+
}
10+
const arr = []
11+
for (let i = 0; i < str.length; i++) {
12+
arr.push(str.charCodeAt(i).toString(16))
13+
}
14+
return arr.join('')
15+
}
16+
17+
exports.reportHttp = async function(context, method, path, statusCode) {
18+
try {
19+
context = JSON.parse(decodeURIComponent(context))
20+
path = str2hex(path)
21+
const ServiceType = 'monitor'
22+
const {
23+
tencentcloud_region,
24+
function_name: FunctionName,
25+
function_version: Version = '$latest',
26+
namespace: Namespace = 'default'
27+
} = context
28+
const environment = JSON.parse(context.environment || '{}')
29+
const {
30+
TENCENTCLOUD_SECRETID: SecretId,
31+
TENCENTCLOUD_SECRETKEY: SecretKey,
32+
TENCENTCLOUD_SESSIONTOKEN: Token,
33+
TENCENTCLOUD_REGION: envTencentRegion,
34+
REGION: envRegion
35+
} = environment
36+
const Region = tencentcloud_region || envTencentRegion || envRegion || 'ap-guangzhou'
37+
if (!SecretId || !SecretKey) {
38+
logger.warn('No SecretId or SecretKey in environment parameters.')
39+
return
40+
}
41+
const client = new Capi({
42+
Region,
43+
SecretId,
44+
SecretKey,
45+
Token,
46+
ServiceType
47+
})
48+
const commonParams = {
49+
Version: '2018-07-24',
50+
AnnounceInstance: `${Namespace}|${FunctionName}|${Version}`
51+
}
52+
const debugOptions = {
53+
debug: false,
54+
host: 'monitor.tencentcloudapi.com'
55+
}
56+
57+
const latency = Date.now() - context[REUQEST_START_KEY]
58+
const keyPrefix = `${method}_${path}`
59+
const Metrics = [
60+
{ MetricName: 'request', Value: 1 },
61+
{ MetricName: keyPrefix, Value: 1 },
62+
{ MetricName: 'latency', Value: latency },
63+
{ MetricName: keyPrefix + '_latency', Value: latency },
64+
{ MetricName: keyPrefix + '_' + statusCode, Value: 1 }
65+
]
66+
if (statusCode.startsWith('4')) {
67+
Metrics.push({ MetricName: '4xx', Value: 1 })
68+
Metrics.push({ MetricName: 'error', Value: 1 })
69+
} else if (statusCode.startsWith('5')) {
70+
Metrics.push({ MetricName: '5xx', Value: 1 })
71+
Metrics.push({ MetricName: 'error', Value: 1 })
72+
}
73+
74+
return client.request(
75+
{
76+
Action: 'PutMonitorData',
77+
Metrics,
78+
...commonParams
79+
},
80+
debugOptions,
81+
true
82+
)
83+
} catch (e) {}
84+
}

0 commit comments

Comments
 (0)