pinpoint-node-agent
Version:
Pinpoint APM agent for Node.js — distributed tracing, error analysis, and URI statistics
119 lines (99 loc) • 3.61 kB
JavaScript
/**
* Pinpoint Node.js Agent
* Copyright 2020-present NAVER Corp.
* Apache License v2.0
*/
'use strict'
const SpanEventBuilder = require('./span-event-builder')
const { ExceptionMetaDataBuilder } = require('./exception-meta-data-builder')
// bufferSize in DefaultContextConfig.java
const bufferSize = 20
// BufferedStorage.java
class SpanRepository {
constructor(spanChunkBuilder, dataSender) {
this.spanChunkBuilder = spanChunkBuilder
this.dataSender = dataSender
this.buffer = []
this.exceptions = []
}
storeSpanEvent(spanEvent) {
if (!spanEvent) {
return
}
this.buffer.push(spanEvent)
if (this.isOverflow()) {
const buffer = this.bufferDrain()
this.sendSpanChunk(buffer)
}
}
isOverflow() {
return this.buffer.length >= bufferSize
}
bufferDrain() {
const copy = this.buffer
this.buffer = []
return copy
}
sendSpanChunk(spanEventBuilders) {
if (spanEventBuilders.length < 1) {
return
}
// GrpcSpanProcessorV2.java: postProcess(SpanChunk spanChunk, pSpanChunk)
const sortedSpanEventBuilders = calculateStartElapsedTime(spanEventBuilders)
const spanEvents = sortedSpanEventBuilders.map(builder => builder.build())
this.exceptions = this.exceptions.concat(this.filterExceptions(spanEvents))
this.spanChunkBuilder.setKeyTime(spanEvents[0]?.getStartTime())
this.dataSender.send(this.spanChunkBuilder.build(spanEvents))
}
filterExceptions(spanEvents) {
if (!Array.isArray(spanEvents) || spanEvents.length === 0) {
return []
}
const exceptions = spanEvents
.map(spanEvent => spanEvent?.exception)
.filter(Boolean)
.flat()
if (exceptions.length === 0) {
return []
}
return exceptions
}
storeSpan(spanBuilder) {
if (!spanBuilder) {
return
}
// GrpcSpanProcessorV2.java: postProcess(Span span, pSpan)
const sortedSpanEventBuilders = calculateStartElapsedTime(this.bufferDrain(), spanBuilder.getStartTime())
const spanEvents = sortedSpanEventBuilders.map(builder => builder.build())
spanBuilder.setSpanEventList(spanEvents)
this.dataSender.send(spanBuilder.build())
const exceptions = this.exceptions.concat(this.filterExceptions(spanEvents))
if (exceptions.length < 1) {
return
}
const builder = new ExceptionMetaDataBuilder(spanBuilder.getTraceRoot())
const uriTemplate = spanBuilder.getTraceRoot().getEnricher('uriStats.uriTemplate')
if (uriTemplate) {
builder.setUriTemplate(uriTemplate)
}
exceptions.forEach(exception => builder.addException(exception))
this.dataSender.send(builder.build())
}
flush() {
const buffer = this.bufferDrain()
this.sendSpanChunk(buffer)
}
}
module.exports = SpanRepository
function calculateStartElapsedTime(spanEvents, keyTime) {
if (spanEvents.length === 0) {
return spanEvents
}
const sorted = Array.from(spanEvents).sort(SpanEventBuilder.comparator)
sorted.reduce((previousStartTime, spanEventBuilder) => {
const startElapsedTime = spanEventBuilder.getStartTime() - previousStartTime
spanEventBuilder.setStartElapsedTime(startElapsedTime)
return spanEventBuilder.getStartTime()
}, keyTime ?? sorted[0].getStartTime())
return sorted
}