Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions src/monitor/agent.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
const EventEmitter = require('events').EventEmitter
const { EventEmitter } = require('events')
const util = require('util')

function Agent () { }
function Agent() {}

util.inherits(Agent, EventEmitter)

module.exports = Agent
module.exports = Agent
8 changes: 4 additions & 4 deletions src/monitor/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@
* @enum {string}
*/
const MODULE_TYPE = {
/** Web server framework module, such as Express or Koa. */
WEB_FRAMEWORK: 'web-framework',
PROXY: 'proxy'
/** Web server framework module, such as Express or Koa. */
WEB_FRAMEWORK: 'web-framework',
PROXY: 'proxy'
}

exports.MODULE_TYPE = MODULE_TYPE
Expand All @@ -18,4 +18,4 @@ exports.MODULE_TYPE = MODULE_TYPE
*/
const REUQEST_START = '__request_start__'

exports.REUQEST_START_KEY = REUQEST_START
exports.REUQEST_START_KEY = REUQEST_START
15 changes: 8 additions & 7 deletions src/monitor/index.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
const shimmer = require('./shimmer')
const Agent = require('./agent')

function initialize() {
const agent = new Agent()
// 封装 module的_load方法,在load时针对基础组件附加探针
shimmer.patchModule()
// 初始化一系列基础组件
shimmer.bootstrapInstrumentation(agent)
}

initialize()
function initialize () {
const agent = new Agent()
// 封装 module的_load方法,在load时针对基础组件附加探针
shimmer.patchModule()
// 初始化一系列基础组件
shimmer.bootstrapInstrumentation(agent)
}
102 changes: 50 additions & 52 deletions src/monitor/instrumentation/express.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
'use strict'
const utils = require('../utils')

/**
Expand All @@ -7,61 +6,60 @@ const utils = require('../utils')
* routers they are mounted to.
*/

module.exports = function initialize (agent, express) {

if (!express || !express.Router) {
return false
}

//wrapExpress4(express)
utils.wrapMethod(express.Router, 'route', function wrapRoute (fn) {
if (!utils.isFunction(fn)) {
return fn
}
function wrapRouteMethods(route) {
const methods = ['all', 'delete', 'get', 'head', 'opts', 'post', 'put', 'patch']
utils.wrapMethod(route, methods, function(fn) {
return fn
})
}

return function wrappedRoute () {
const route = fn.apply(this, arguments)
// Express should create a new route and layer every time Router#route is
// called, but just to be on the safe side, make sure we haven't wrapped
// this already.
if (!utils.isWrapped(route, 'get')) {
wrapRouteMethods(route)
module.exports = function initialize(agent, express) {
if (!express || !express.Router) {
return false
}

const layer = this.stack[this.stack.length - 1]
utils.wrapMethod(layer, 'handle', function (fn) {
const { route } = layer
const { path } = route
return function (request, response) {
function finish () {
response.removeListener('finish', finish)
request.removeListener('aborted', finish)
// 状态码
if (response.statusCode != null) {
const responseCode = String(response.statusCode)
if (/^\d+$/.test(responseCode)) {
const context = request.headers['x-apigateway-context']
agent.emit('responseFinish', context, request.method, path, responseCode)
}
}
}
// wrapExpress4(express)
utils.wrapMethod(express.Router, 'route', function wrapRoute(fn) {
if (!utils.isFunction(fn)) {
return fn
}

// response结束时上报状态码和耗时
response.once('finish', finish)
request.once('aborted', finish)
return function wrappedRoute() {
const sourceRoute = fn.apply(this, arguments)
// Express should create a new route and layer every time Router#route is
// called, but just to be on the safe side, make sure we haven't wrapped
// this already.
if (!utils.isWrapped(sourceRoute, 'get')) {
wrapRouteMethods(sourceRoute)

const handle = fn.apply(this, arguments)
return handle
}
})
const layer = this.stack[this.stack.length - 1]
utils.wrapMethod(layer, 'handle', function(func) {
const { route } = layer
const { path } = route
return function(request, response) {
function finish() {
response.removeListener('finish', finish)
request.removeListener('aborted', finish)
// 状态码
if (response.statusCode != null) {
const responseCode = String(response.statusCode)
if (/^\d+$/.test(responseCode)) {
const context = request.headers['x-apigateway-context']
agent.emit('responseFinish', context, request.method, path, responseCode)
}
}
}
return route
}
})
}

function wrapRouteMethods (route) {
const methods = ['all', 'delete', 'get', 'head', 'opts', 'post', 'put', 'patch']
utils.wrapMethod(route, methods, function (fn) {
return fn
})
// response结束时上报状态码和耗时
response.once('finish', finish)
request.once('aborted', finish)

const handle = func.apply(this, arguments)
return handle
}
})
}
return sourceRoute
}
})
}
40 changes: 21 additions & 19 deletions src/monitor/instrumentation/tencent-serverless-http.js
Original file line number Diff line number Diff line change
@@ -1,22 +1,24 @@
'use strict'
const utils = require('../utils')
const REUQEST_START_KEY = require('../constants').REUQEST_START_KEY
const { REUQEST_START_KEY } = require('../constants')
const report = require('../report')

module.exports = function initialize (agent, httpProxy) {
utils.wrapMethod(httpProxy, 'proxy', function wrapRoute (fn) {
return function (server, event, context) {
context[REUQEST_START_KEY] = Date.now()
const proxy = fn.apply(this, arguments)
return new Promise(function (resolve) {
agent.on('responseFinish', function (context, method, path, responseCode) {
report.reportHttp(context, method, path, responseCode).then(function () {
resolve(proxy)
}, function () {
resolve(proxy)
})
})
})
}
})
}
module.exports = function initialize(agent, httpProxy) {
utils.wrapMethod(httpProxy, 'proxy', function wrapRoute(fn) {
return function(server, event, context) {
context[REUQEST_START_KEY] = Date.now()
const proxy = fn.apply(this, arguments)
return new Promise(function(resolve) {
agent.on('responseFinish', function(ctx, method, path, responseCode) {
report.reportHttp(ctx, method, path, responseCode).then(
function() {
resolve(proxy)
},
function() {
resolve(proxy)
}
)
})
})
}
})
}
14 changes: 6 additions & 8 deletions src/monitor/instrumentations.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
'use strict'
const { MODULE_TYPE } = require('./constants')

const MODULE_TYPE = require('./constants').MODULE_TYPE

module.exports = function instrumentations () {
return {
'express': { type: MODULE_TYPE.WEB_FRAMEWORK },
'tencent-serverless-http': { type: MODULE_TYPE.PROXY },
}
module.exports = function instrumentations() {
return {
express: { type: MODULE_TYPE.WEB_FRAMEWORK },
'tencent-serverless-http': { type: MODULE_TYPE.PROXY }
}
}
2 changes: 1 addition & 1 deletion src/monitor/logger.js
Original file line number Diff line number Diff line change
@@ -1 +1 @@
module.exports = console
module.exports = console
152 changes: 75 additions & 77 deletions src/monitor/report.js
Original file line number Diff line number Diff line change
@@ -1,84 +1,82 @@
const { Capi } = require('@tencent-sdk/capi')
const logger = require('./logger')
const REUQEST_START_KEY = require('./constants').REUQEST_START_KEY
const { REUQEST_START_KEY } = require('./constants')


exports.reportHttp = async function (context, method, path, statusCode) {
context = JSON.parse(decodeURIComponent(context))
path = str2hex(path)
const ServiceType = 'monitor'
const {
tencentcloud_region,
function_name: FunctionName,
function_version: Version = '$latest',
namespace: Namespace = 'default'
} = context
const environment = JSON.parse(context.environment || '{}')
const {
TENCENTCLOUD_SECRETID: SecretId,
TENCENTCLOUD_SECRETKEY: SecretKey,
TENCENTCLOUD_SESSIONTOKEN: Token,
TENCENTCLOUD_REGION: envTencentRegion,
REGION: envRegion
} = environment
const Region = tencentcloud_region || envTencentRegion || envRegion || 'ap-guangzhou'
if (!SecretId || !SecretKey) {
logger.warn('No SecretId or SecretKey in environment parameters.')
return
}
const client = new Capi({
Region,
SecretId,
SecretKey,
Token,
ServiceType
})
const commonParams = {
Version: '2018-07-24',
AnnounceInstance: `${Namespace}|${FunctionName}|${Version}`
}
const debugOptions = {
debug: false,
host: 'monitor.tencentcloudapi.com'
}

const latency = Date.now() - context[REUQEST_START_KEY]
const keyPrefix = `${method}_${path}`
const Metrics = [
{ MetricName: 'request', Value: 1 },
{ MetricName: keyPrefix, Value: 1 },
{ MetricName: 'latency', Value: latency },
{ MetricName: keyPrefix + '_latency', Value: latency },
{ MetricName: keyPrefix + '_' + statusCode, Value: 1 }
]
if (statusCode.startsWith('4')) {
Metrics.push({ MetricName: '4xx', Value: 1 })
Metrics.push({ MetricName: 'error', Value: 1 })
} else if (statusCode.startsWith('5')) {
Metrics.push({ MetricName: '5xx', Value: 1 })
Metrics.push({ MetricName: 'error', Value: 1 })
}

await client.request(
{
Action: 'PutMonitorData',
Metrics,
...commonParams
},
debugOptions,
true
)
// 字符串转16进制
function str2hex(str) {
if (str === '') {
return ''
}
const arr = []
for (let i = 0; i < str.length; i++) {
arr.push(str.charCodeAt(i).toString(16))
}
return arr.join('')
}

exports.reportHttp = async function(context, method, path, statusCode) {
context = JSON.parse(decodeURIComponent(context))
path = str2hex(path)
const ServiceType = 'monitor'
const {
tencentcloud_region,
function_name: FunctionName,
function_version: Version = '$latest',
namespace: Namespace = 'default'
} = context
const environment = JSON.parse(context.environment || '{}')
const {
TENCENTCLOUD_SECRETID: SecretId,
TENCENTCLOUD_SECRETKEY: SecretKey,
TENCENTCLOUD_SESSIONTOKEN: Token,
TENCENTCLOUD_REGION: envTencentRegion,
REGION: envRegion
} = environment
const Region = tencentcloud_region || envTencentRegion || envRegion || 'ap-guangzhou'
if (!SecretId || !SecretKey) {
logger.warn('No SecretId or SecretKey in environment parameters.')
return
}
const client = new Capi({
Region,
SecretId,
SecretKey,
Token,
ServiceType
})
const commonParams = {
Version: '2018-07-24',
AnnounceInstance: `${Namespace}|${FunctionName}|${Version}`
}
const debugOptions = {
debug: false,
host: 'monitor.tencentcloudapi.com'
}

// 字符串转16进制
function str2hex (str) {
if (str === '') {
return ''
}
let arr = []
for (let i = 0; i < str.length; i++) {
arr.push(str.charCodeAt(i).toString(16))
}
return arr.join('')
const latency = Date.now() - context[REUQEST_START_KEY]
const keyPrefix = `${method}_${path}`
const Metrics = [
{ MetricName: 'request', Value: 1 },
{ MetricName: keyPrefix, Value: 1 },
{ MetricName: 'latency', Value: latency },
{ MetricName: keyPrefix + '_latency', Value: latency },
{ MetricName: keyPrefix + '_' + statusCode, Value: 1 }
]
if (statusCode.startsWith('4')) {
Metrics.push({ MetricName: '4xx', Value: 1 })
Metrics.push({ MetricName: 'error', Value: 1 })
} else if (statusCode.startsWith('5')) {
Metrics.push({ MetricName: '5xx', Value: 1 })
Metrics.push({ MetricName: 'error', Value: 1 })
}

await client.request(
{
Action: 'PutMonitorData',
Metrics,
...commonParams
},
debugOptions,
true
)
}
Loading