Skip to content

Commit d3b0fc5

Browse files
szegediwatson
authored andcommitted
chore: make as much of profiler internals private as possible (#6108)
1 parent 03f579e commit d3b0fc5

File tree

2 files changed

+88
-73
lines changed

2 files changed

+88
-73
lines changed

packages/dd-trace/src/profiling/profiler.js

Lines changed: 85 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -42,17 +42,20 @@ function findWebSpan (startedSpans, spanId) {
4242
}
4343

4444
class Profiler extends EventEmitter {
45+
#compressionFn
46+
#compressionOptions
47+
#enabled = false
48+
#endpointCounts = new Map()
49+
#lastStart
50+
#logger
4551
#profileSeq = 0
52+
#spanFinishListener
53+
#timer
4654

4755
constructor () {
4856
super()
49-
this._enabled = false
50-
this._logger = undefined
5157
this._config = undefined
52-
this._timer = undefined
53-
this._lastStart = undefined
5458
this._timeoutInterval = undefined
55-
this.endpointCounts = new Map()
5659
}
5760

5861
start (options) {
@@ -63,17 +66,21 @@ class Profiler extends EventEmitter {
6366
})
6467
}
6568

66-
_logError (err) {
67-
logError(this._logger, err)
69+
get enabled () {
70+
return this.#enabled
71+
}
72+
73+
#logError (err) {
74+
logError(this.#logger, err)
6875
}
6976

7077
async _start (options) {
71-
if (this._enabled) return true
78+
if (this.enabled) return true
7279

7380
const config = this._config = new Config(options)
7481

75-
this._logger = config.logger
76-
this._enabled = true
82+
this.#logger = config.logger
83+
this.#enabled = true
7784
this._setInterval()
7885

7986
// Log errors if the source map finder fails, but don't prevent the rest
@@ -85,7 +92,7 @@ class Profiler extends EventEmitter {
8592

8693
mapper = await maybeSourceMap(config.sourceMap, SourceMapper, config.debugSourceMaps)
8794
if (config.sourceMap && config.debugSourceMaps) {
88-
this._logger.debug(() => {
95+
this.#logger.debug(() => {
8996
return mapper.infoMap.size === 0
9097
? 'Found no source maps'
9198
: `Found source maps for following files: [${[...mapper.infoMap.keys()].join(', ')}]`
@@ -95,18 +102,18 @@ class Profiler extends EventEmitter {
95102
const clevel = config.uploadCompression.level
96103
switch (config.uploadCompression.method) {
97104
case 'gzip':
98-
this._compressionFn = promisify(zlib.gzip)
105+
this.#compressionFn = promisify(zlib.gzip)
99106
if (clevel !== undefined) {
100-
this._compressionOptions = {
107+
this.#compressionOptions = {
101108
level: clevel
102109
}
103110
}
104111
break
105112
case 'zstd':
106113
if (typeof zlib.zstdCompress === 'function') {
107-
this._compressionFn = promisify(zlib.zstdCompress)
114+
this.#compressionFn = promisify(zlib.zstdCompress)
108115
if (clevel !== undefined) {
109-
this._compressionOptions = {
116+
this.#compressionOptions = {
110117
params: {
111118
[zlib.constants.ZSTD_c_compressionLevel]: clevel
112119
}
@@ -115,44 +122,44 @@ class Profiler extends EventEmitter {
115122
} else {
116123
const zstdCompress = require('@datadog/libdatadog').load('datadog-js-zstd').zstd_compress
117124
const level = clevel ?? 0 // 0 is zstd default compression level
118-
this._compressionFn = (buffer) => Promise.resolve(Buffer.from(zstdCompress(buffer, level)))
125+
this.#compressionFn = (buffer) => Promise.resolve(Buffer.from(zstdCompress(buffer, level)))
119126
}
120127
break
121128
}
122129
} catch (err) {
123-
this._logError(err)
130+
this.#logError(err)
124131
}
125132

126133
try {
127134
const start = new Date()
128-
const nearOOMCallback = this._nearOOMExport.bind(this)
135+
const nearOOMCallback = this.#nearOOMExport.bind(this)
129136
for (const profiler of config.profilers) {
130137
// TODO: move this out of Profiler when restoring sourcemap support
131138
profiler.start({
132139
mapper,
133140
nearOOMCallback
134141
})
135-
this._logger.debug(`Started ${profiler.type} profiler in ${threadNamePrefix} thread`)
142+
this.#logger.debug(`Started ${profiler.type} profiler in ${threadNamePrefix} thread`)
136143
}
137144

138145
if (config.endpointCollectionEnabled) {
139-
this._spanFinishListener = this._onSpanFinish.bind(this)
140-
spanFinishedChannel.subscribe(this._spanFinishListener)
146+
this.#spanFinishListener = this.#onSpanFinish.bind(this)
147+
spanFinishedChannel.subscribe(this.#spanFinishListener)
141148
}
142149

143150
this._capture(this._timeoutInterval, start)
144151
return true
145152
} catch (e) {
146-
this._logError(e)
147-
this._stop()
153+
this.#logError(e)
154+
this.#stop()
148155
return false
149156
}
150157
}
151158

152-
_nearOOMExport (profileType, encodedProfile) {
153-
const start = this._lastStart
159+
#nearOOMExport (profileType, encodedProfile) {
160+
const start = this.#lastStart
154161
const end = new Date()
155-
this._submit({
162+
this.#submit({
156163
[profileType]: encodedProfile
157164
}, start, end, snapshotKinds.ON_OUT_OF_MEMORY)
158165
}
@@ -162,45 +169,45 @@ class Profiler extends EventEmitter {
162169
}
163170

164171
stop () {
165-
if (!this._enabled) return
172+
if (!this.enabled) return
166173

167174
// collect and export current profiles
168175
// once collect returns, profilers can be safely stopped
169176
this._collect(snapshotKinds.ON_SHUTDOWN, false)
170-
this._stop()
177+
this.#stop()
171178
}
172179

173-
_stop () {
174-
if (!this._enabled) return
180+
#stop () {
181+
if (!this.enabled) return
175182

176-
this._enabled = false
183+
this.#enabled = false
177184

178-
if (this._spanFinishListener !== undefined) {
179-
spanFinishedChannel.unsubscribe(this._spanFinishListener)
180-
this._spanFinishListener = undefined
185+
if (this.#spanFinishListener !== undefined) {
186+
spanFinishedChannel.unsubscribe(this.#spanFinishListener)
187+
this.#spanFinishListener = undefined
181188
}
182189

183190
for (const profiler of this._config.profilers) {
184191
profiler.stop()
185-
this._logger.debug(`Stopped ${profiler.type} profiler in ${threadNamePrefix} thread`)
192+
this.#logger.debug(`Stopped ${profiler.type} profiler in ${threadNamePrefix} thread`)
186193
}
187194

188-
clearTimeout(this._timer)
189-
this._timer = undefined
195+
clearTimeout(this.#timer)
196+
this.#timer = undefined
190197
}
191198

192199
_capture (timeout, start) {
193-
if (!this._enabled) return
194-
this._lastStart = start
195-
if (!this._timer || timeout !== this._timeoutInterval) {
196-
this._timer = setTimeout(() => this._collect(snapshotKinds.PERIODIC), timeout)
197-
this._timer.unref()
200+
if (!this.enabled) return
201+
this.#lastStart = start
202+
if (!this.#timer || timeout !== this._timeoutInterval) {
203+
this.#timer = setTimeout(() => this._collect(snapshotKinds.PERIODIC), timeout)
204+
this.#timer.unref()
198205
} else {
199-
this._timer.refresh()
206+
this.#timer.refresh()
200207
}
201208
}
202209

203-
_onSpanFinish (span) {
210+
#onSpanFinish (span) {
204211
const context = span.context()
205212
const tags = context._tags
206213
if (!isWebServerSpan(tags)) return
@@ -211,19 +218,19 @@ class Profiler extends EventEmitter {
211218
// Make sure this is the outermost web span, just in case so we don't overcount
212219
if (findWebSpan(getStartedSpans(context), context._parentId)) return
213220

214-
let counter = this.endpointCounts.get(endpointName)
221+
let counter = this.#endpointCounts.get(endpointName)
215222
if (counter === undefined) {
216223
counter = { count: 1 }
217-
this.endpointCounts.set(endpointName, counter)
224+
this.#endpointCounts.set(endpointName, counter)
218225
} else {
219226
counter.count++
220227
}
221228
}
222229

223230
async _collect (snapshotKind, restart = true) {
224-
if (!this._enabled) return
231+
if (!this.enabled) return
225232

226-
const startDate = this._lastStart
233+
const startDate = this.#lastStart
227234
const endDate = new Date()
228235
const profiles = []
229236
const encodedProfiles = {}
@@ -238,7 +245,7 @@ class Profiler extends EventEmitter {
238245
for (const profiler of this._config.profilers) {
239246
const profile = profiler.profile(restart, startDate, endDate)
240247
if (!restart) {
241-
this._logger.debug(`Stopped ${profiler.type} profiler in ${threadNamePrefix} thread`)
248+
this.#logger.debug(`Stopped ${profiler.type} profiler in ${threadNamePrefix} thread`)
242249
}
243250
if (!profile) continue
244251
profiles.push({ profiler, profile })
@@ -255,11 +262,11 @@ class Profiler extends EventEmitter {
255262
await Promise.all(profiles.map(async ({ profiler, profile }) => {
256263
try {
257264
const encoded = await profiler.encode(profile)
258-
const compressed = encoded instanceof Buffer && this._compressionFn !== undefined
259-
? await this._compressionFn(encoded, this._compressionOptions)
265+
const compressed = encoded instanceof Buffer && this.#compressionFn !== undefined
266+
? await this.#compressionFn(encoded, this.#compressionOptions)
260267
: encoded
261268
encodedProfiles[profiler.type] = compressed
262-
this._logger.debug(() => {
269+
this.#logger.debug(() => {
263270
const profileJson = JSON.stringify(profile, (key, value) => {
264271
return typeof value === 'bigint' ? value.toString() : value
265272
})
@@ -269,38 +276,38 @@ class Profiler extends EventEmitter {
269276
} catch (err) {
270277
// If encoding one of the profile types fails, we should still try to
271278
// encode and submit the other profile types.
272-
this._logError(err)
279+
this.#logError(err)
273280
}
274281
}))
275282

276283
if (hasEncoded) {
277-
await this._submit(encodedProfiles, startDate, endDate, snapshotKind)
284+
await this.#submit(encodedProfiles, startDate, endDate, snapshotKind)
278285
profileSubmittedChannel.publish()
279-
this._logger.debug('Submitted profiles')
286+
this.#logger.debug('Submitted profiles')
280287
}
281288
} catch (err) {
282-
this._logError(err)
283-
this._stop()
289+
this.#logError(err)
290+
this.#stop()
284291
}
285292
}
286293

287-
_submit (profiles, start, end, snapshotKind) {
294+
#submit (profiles, start, end, snapshotKind) {
288295
const { tags } = this._config
289296

290297
// Flatten endpoint counts
291298
const endpointCounts = {}
292-
for (const [endpoint, { count }] of this.endpointCounts) {
299+
for (const [endpoint, { count }] of this.#endpointCounts) {
293300
endpointCounts[endpoint] = count
294301
}
295-
this.endpointCounts.clear()
302+
this.#endpointCounts.clear()
296303

297304
tags.snapshot = snapshotKind
298305
tags.profile_seq = this.#profileSeq++
299306
const exportSpec = { profiles, start, end, tags, endpointCounts }
300307
const tasks = this._config.exporters.map(exporter =>
301308
exporter.export(exportSpec).catch(err => {
302-
if (this._logger) {
303-
this._logger.warn(err)
309+
if (this.#logger) {
310+
this.#logger.warn(err)
304311
}
305312
})
306313
)
@@ -310,24 +317,32 @@ class Profiler extends EventEmitter {
310317
}
311318

312319
class ServerlessProfiler extends Profiler {
320+
#profiledIntervals = 0
321+
#interval = 1 // seconds
322+
#flushAfterIntervals
323+
313324
constructor () {
314325
super()
315-
this._profiledIntervals = 0
316-
this._interval = 1
317-
this._flushAfterIntervals = undefined
326+
this.#profiledIntervals = 0
327+
this.#interval = 1
328+
this.#flushAfterIntervals = undefined
329+
}
330+
331+
get profiledIntervals () {
332+
return this.#profiledIntervals
318333
}
319334

320335
_setInterval () {
321-
this._timeoutInterval = this._interval * 1000
322-
this._flushAfterIntervals = this._config.flushInterval / 1000
336+
this._timeoutInterval = this.#interval * 1000
337+
this.#flushAfterIntervals = this._config.flushInterval / 1000
323338
}
324339

325340
async _collect (snapshotKind, restart = true) {
326-
if (this._profiledIntervals >= this._flushAfterIntervals || !restart) {
327-
this._profiledIntervals = 0
341+
if (this.#profiledIntervals >= this.#flushAfterIntervals || !restart) {
342+
this.#profiledIntervals = 0
328343
await super._collect(snapshotKind, restart)
329344
} else {
330-
this._profiledIntervals += 1
345+
this.#profiledIntervals += 1
331346
this._capture(this._timeoutInterval, new Date())
332347
// Don't submit profile until 65 (flushAfterIntervals) intervals have elapsed
333348
}

packages/dd-trace/test/profiling/profiler.spec.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -395,7 +395,7 @@ describe('profiler', function () {
395395
sourceMapCreate.rejects(error)
396396
await profiler._start({ profilers, exporters, logger, sourceMap: true })
397397
expect(consoleLogger.error.args[0][0]).to.equal(error)
398-
expect(profiler._enabled).to.equal(true)
398+
expect(profiler.enabled).to.equal(true)
399399
})
400400
})
401401

@@ -430,11 +430,11 @@ describe('profiler', function () {
430430

431431
it('should increment profiled intervals after one interval elapses', async () => {
432432
await profiler._start({ profilers, exporters })
433-
expect(profiler._profiledIntervals).to.equal(0)
433+
expect(profiler.profiledIntervals).to.equal(0)
434434

435435
clock.tick(interval)
436436

437-
expect(profiler._profiledIntervals).to.equal(1)
437+
expect(profiler.profiledIntervals).to.equal(1)
438438
sinon.assert.notCalled(exporter.export)
439439
})
440440

0 commit comments

Comments
 (0)