Skip to content

Commit fbd8a44

Browse files
committed
[#355] feat: Implement AgentBuilder pattern for flexible agent configuration (#355)
- Add AgentBuilder with fluent interface for configurable agent creation - Set agentStartTime from Node.js process start time (#361) - Make Ping, StatsMonitor and CommandService configurable (#362) - Add disablePingScheduler() and disableStatsScheduler() builder methods - Simplify context management for Node.js 18+ support - Remove AsyncHooks/LocalStorage detection code - Delete obsolete test-helper.js utility - Update test cases to use AsyncLocalStorage directly - Update MockAgent to use AgentBuilder pattern - Enable scheduler control in functional tests
1 parent 7545974 commit fbd8a44

25 files changed

+260
-304
lines changed

index.js

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,14 @@
66

77
'use strict'
88

9-
const Agent = require('./lib/agent')
10-
const agent = new Agent()
9+
const AgentBuilder = require('./lib/agent-builder')
10+
const AgentInfo = require('./lib/data/dto/agent-info')
11+
const { getConfig } = require('./lib/config')
12+
13+
const config = getConfig()
14+
const agentInfo = AgentInfo.make(config)
15+
const agent = new AgentBuilder(agentInfo)
16+
.setConfig(config)
17+
.build()
18+
agent.start()
1119
module.exports = agent

lib/agent-builder.js

Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
/**
2+
* Pinpoint Node.js Agent
3+
* Copyright 2020-present NAVER Corp.
4+
* Apache License v2.0
5+
*/
6+
7+
'use strict'
8+
9+
const { LogBuilder } = require('./utils/log/log-builder')
10+
const log = require('./utils/log/logger')
11+
const dataSenderFactory = require('./client/data-sender-factory')
12+
const stringMetaService = require('./context/string-meta-service')
13+
const apiMetaService = require('./context/api-meta-service')
14+
const sqlMetadataService = require('./instrumentation/sql/sql-metadata-service')
15+
const TraceContext = require('./context/trace-context')
16+
const { ModuleHook } = require('./instrumentation/module-hook')
17+
const Scheduler = require('./utils/scheduler')
18+
const AgentStatsMonitor = require('./metric/agent-stats-monitor')
19+
const PingScheduler = require('./metric/ping-scheduler')
20+
const { getConfig } = require('./config')
21+
22+
class Agent {
23+
constructor(agentInfo, config, logger) {
24+
this.agentInfo = agentInfo
25+
this.config = config
26+
this.logger = logger
27+
}
28+
29+
start() {
30+
this.logger.warn('[Pinpoint Agent] Configuration', this.config)
31+
32+
if (typeof this.config?.enable === 'boolean' && !this.config.enable) {
33+
this.logger.warn('[Pinpoint Agent][' + this.config.agentId + '] Disabled')
34+
return
35+
}
36+
37+
this.dataSender.send(this.agentInfo)
38+
stringMetaService.init(this.dataSender)
39+
apiMetaService.init(this.dataSender)
40+
sqlMetadataService.setDataSender(this.dataSender)
41+
42+
this.traceContext = new TraceContext(this.agentInfo, this.dataSender, this.config)
43+
this.moduleHook = new ModuleHook(this)
44+
this.moduleHook.start()
45+
46+
this.services.forEach(service => {
47+
service.call(this)
48+
})
49+
50+
this.logger.warn('[Pinpoint Agent][' + this.config.agentId + '] Initialized', this.agentInfo)
51+
}
52+
53+
createTraceObject() {
54+
return this.traceContext.newTraceObject2()
55+
}
56+
57+
currentTraceObject() {
58+
return this.traceContext.currentTraceObject()
59+
}
60+
61+
completeTraceObject(trace) {
62+
this.traceContext.completeTraceObject(trace)
63+
}
64+
65+
getAgentInfo() {
66+
return this.agentInfo
67+
}
68+
69+
getTraceContext() {
70+
return this.traceContext
71+
}
72+
73+
shutdown() {
74+
this.mainScheduler?.stop()
75+
this.pingScheduler?.stop()
76+
this.moduleHook?.stop()
77+
}
78+
}
79+
80+
class AgentBuilder {
81+
constructor(agentInfo) {
82+
this.agentInfo = agentInfo
83+
this.services = []
84+
this.enableStatsMonitor = true
85+
this.enablePingScheduler = true
86+
this.enableServiceCommand = true
87+
}
88+
89+
setConfig(config) {
90+
this.config = config
91+
return this
92+
}
93+
94+
setDataSender(dataSender) {
95+
this.dataSender = dataSender
96+
return this
97+
}
98+
99+
addService(service) {
100+
this.services.push(service)
101+
return this
102+
}
103+
104+
disableStatsScheduler() {
105+
this.enableStatsMonitor = false
106+
return this
107+
}
108+
109+
disablePingScheduler() {
110+
this.enablePingScheduler = false
111+
return this
112+
}
113+
114+
disableServiceCommand() {
115+
this.enableServiceCommand = false
116+
return this
117+
}
118+
119+
build() {
120+
if (!this.config) {
121+
this.config = getConfig()
122+
}
123+
log.setRootLogger(LogBuilder.createDefaultLogBuilder().setConfig(this.config).build())
124+
125+
const agent = new Agent(this.agentInfo, this.config, log)
126+
if (!this.dataSender) {
127+
this.dataSender = dataSenderFactory.create(this.config, this.agentInfo)
128+
}
129+
agent.dataSender = this.dataSender
130+
131+
if (this.enableStatsMonitor || this.config.enabledStatsMonitor) {
132+
this.addService(() => {
133+
this.mainScheduler = new Scheduler(5000)
134+
const agentStatsMonitor = new AgentStatsMonitor(this.dataSender, this.agentInfo.getAgentId(), this.agentInfo.getAgentStartTime())
135+
this.mainScheduler.addJob(() => { agentStatsMonitor.run() })
136+
this.mainScheduler.start()
137+
})
138+
}
139+
140+
if (this.enablePingScheduler) {
141+
this.addService(() => {
142+
this.pingScheduler = new PingScheduler(this.dataSender)
143+
})
144+
}
145+
146+
if (this.enableServiceCommand) {
147+
this.addService(() => {
148+
this.dataSender.sendSupportedServicesCommand()
149+
})
150+
}
151+
152+
agent.services = this.services
153+
return agent
154+
}
155+
}
156+
157+
module.exports = AgentBuilder

lib/agent.js

Lines changed: 0 additions & 116 deletions
This file was deleted.

lib/config.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -326,7 +326,7 @@ function hasDockerCGroup() {
326326
}
327327
}
328328

329-
let agentConfig = readConfigJson(defaultConfig)
329+
let agentConfig
330330

331331
const loadedConfigCallbacks = []
332332
function registerLoadedConfig(propertyName, callback) {

lib/data/dto/agent-info.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,8 @@ class AgentInfo {
2828
this.vmVerson = ''
2929
}
3030

31-
static create(config, agentStartTime) {
31+
static make(config) {
32+
const agentStartTime = String(Date.now() - Math.floor(process.uptime() * 1000))
3233
return new AgentInfo({
3334
agentId: config.agentId,
3435
agentName: config.agentName,

lib/instrumentation/context/local-storage.js

Lines changed: 1 addition & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -6,25 +6,10 @@
66

77
'use strict'
88

9-
const semver = require('semver')
109
const AsyncContextLocalStorage = require('./async-context-local-storage')
11-
const AsyncHooksLocalStorage = require('./async-hooks-local-storage')
12-
const { getConfig } = require('../../config')
1310
class LocalStorage {
1411
constructor() {
15-
this.storage = this.createLocalStorage()
16-
}
17-
18-
createLocalStorage() {
19-
const config = getConfig()
20-
if (config && !config.enable) {
21-
return new EmptyLocalStorage()
22-
}
23-
24-
if (!semver.satisfies(process.version, '>=16.4.0')) {
25-
return new AsyncHooksLocalStorage()
26-
}
27-
return new AsyncContextLocalStorage()
12+
this.storage = new AsyncContextLocalStorage()
2813
}
2914

3015
run(store, callback) {

test/agent.test.js

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,10 @@
66

77
const test = require('tape')
88

9-
const { enableDataSending } = require('./test-helper')
10-
enableDataSending()
11-
129
test('Should initialize agent', function (t) {
1310
t.plan(2)
1411

1512
const agent = require('./support/agent-singleton-mock')
1613
t.ok(agent)
17-
t.equal(agent.pinpointClient.agentInfo.agentVersion, '1.3.0', 'agent version from package.json')
14+
t.equal(agent.agentInfo.agentVersion, '1.3.0', 'agent version from package.json')
1815
})

test/client/data-sender.test.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,10 @@
77
const test = require('tape')
88

99
const agent = require('../support/agent-singleton-mock')
10-
const { fixture } = require('../test-helper')
1110
const dataSenderMock = require('../support/data-sender-mock')
1211
const dataSender = dataSenderMock()
1312
const AgentInfo = require('../../lib/data/dto/agent-info')
14-
const agentInfo = AgentInfo.create(fixture.config, Date.now())
13+
const agentInfo = AgentInfo.make(agent.config)
1514
const ApiMetaInfo = require('../../lib/data/dto/api-meta-info')
1615
const StringMetaInfo = require('../../lib/data/dto/string-meta-info')
1716
const defaultPredefinedMethodDescriptorRegistry = require('../../lib/constant/default-predefined-method-descriptor-registry')

test/client/grpc-stream.test.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,10 @@
66

77
const test = require('tape')
88
const grpc = require('@grpc/grpc-js')
9-
9+
const log = require('../../lib/utils/log/logger')
1010
const services = require('../../lib/data/v1/Service_grpc_pb')
1111
const dataConvertor = require('../../lib/data/grpc-data-convertor')
1212
const { Empty } = require('google-protobuf/google/protobuf/empty_pb')
13-
const { log } = require('../test-helper')
1413

1514
// https://github.com/agreatfool/grpc_tools_node_protoc_ts/blob/7caf9fb3a650fe7cf7a04c0c65201997874a5f38/examples/src/grpcjs/server.ts#L53
1615
const messageCount = 11

0 commit comments

Comments
 (0)