@splunk/otel
Version:
The Splunk distribution of OpenTelemetry Node Instrumentation provides a Node agent that automatically instruments your Node application to capture and report distributed traces to Splunk APM.
168 lines • 6.4 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.encode = exports.serialize = exports.StringTable = void 0;
exports.serializeHeapProfile = serializeHeapProfile;
/*
* Copyright Splunk Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
const zlib_1 = require("zlib");
const util_1 = require("util");
const profile_1 = require("./proto/profile");
const gzipPromise = (0, util_1.promisify)(zlib_1.gzip);
class StringTable {
constructor() {
this._stringMap = new Map();
this.getIndex('');
}
getIndex(str) {
let idx = this._stringMap.get(str);
if (idx !== undefined) {
return idx;
}
idx = this._stringMap.size;
this._stringMap.set(str, idx);
return idx;
}
serialize() {
return [...this._stringMap.keys()];
}
}
exports.StringTable = StringTable;
class Serializer {
constructor() {
this.stringTable = new StringTable();
this.locationsMap = new Map();
this.functionsMap = new Map();
}
getLocation(fileName, functionName, lineNumber) {
const key = `${fileName}:${functionName}:${lineNumber}`;
let location = this.locationsMap.get(key);
if (!location) {
location = new profile_1.perftools.profiles.Location({
id: this.locationsMap.size + 1,
line: [this.getLine(fileName, functionName, lineNumber)],
});
this.locationsMap.set(key, location);
}
return location;
}
getFunction(fileName, functionName) {
const key = `${fileName}:${functionName}`;
let fun = this.functionsMap.get(key);
if (!fun) {
const functionNameId = this.stringTable.getIndex(functionName);
fun = new profile_1.perftools.profiles.Function({
id: this.functionsMap.size + 1,
name: functionNameId,
systemName: functionNameId,
filename: this.stringTable.getIndex(fileName),
});
this.functionsMap.set(key, fun);
}
return fun;
}
getLine(fileName, functionName, lineNumber) {
return new profile_1.perftools.profiles.Line({
functionId: this.getFunction(fileName, functionName).id,
line: lineNumber !== 0 ? lineNumber : -1,
});
}
serializeHeapProfile(profile) {
const SOURCE_EVENT_TIME = this.stringTable.getIndex('source.event.time');
const label = [{ key: SOURCE_EVENT_TIME, num: profile.timestamp }];
const sample = [];
const { samples, treeMap } = profile;
for (const s of samples) {
let node = treeMap[s.nodeId];
const path = [];
while (node !== undefined) {
const location = this.getLocation(node.scriptName, node.name, node.lineNumber);
path.push(location.id);
node = treeMap[node.parentId];
}
sample.push({
locationId: path,
value: [s.size],
label,
});
}
return profile_1.perftools.profiles.Profile.create({
sample,
location: [...this.locationsMap.values()],
function: [...this.functionsMap.values()],
stringTable: this.stringTable.serialize(),
});
}
serializeCpuProfile(profile, options) {
const { stacktraces } = profile;
const STR = {
TIMESTAMP: this.stringTable.getIndex('source.event.time'),
TRACE_ID: this.stringTable.getIndex('trace_id'),
SPAN_ID: this.stringTable.getIndex('span_id'),
SOURCE_EVENT_PERIOD: this.stringTable.getIndex('source.event.period'),
};
const eventPeriodLabel = new profile_1.perftools.profiles.Label({
key: STR.SOURCE_EVENT_PERIOD,
num: options.samplingPeriodMillis,
});
const samples = stacktraces.map(({ stacktrace, timestamp, spanId, traceId }) => {
const labels = [
new profile_1.perftools.profiles.Label({
key: STR.TIMESTAMP,
num: Number(BigInt(timestamp) / BigInt(1000000)),
}),
eventPeriodLabel,
];
if (traceId) {
labels.push(new profile_1.perftools.profiles.Label({
key: STR.TRACE_ID,
str: this.stringTable.getIndex(traceId.toString('hex')),
}));
}
if (spanId) {
labels.push(new profile_1.perftools.profiles.Label({
key: STR.SPAN_ID,
str: this.stringTable.getIndex(spanId.toString('hex')),
}));
}
return new profile_1.perftools.profiles.Sample({
locationId: stacktrace.map(([fileName, functionName, lineNumber]) => {
return this.getLocation(fileName, functionName, lineNumber).id;
}),
value: [],
label: labels,
});
});
return profile_1.perftools.profiles.Profile.create({
sample: samples,
location: [...this.locationsMap.values()],
function: [...this.functionsMap.values()],
stringTable: this.stringTable.serialize(),
});
}
}
const serialize = (profile, options) => {
return new Serializer().serializeCpuProfile(profile, options);
};
exports.serialize = serialize;
function serializeHeapProfile(profile) {
return new Serializer().serializeHeapProfile(profile);
}
const encode = async function encode(profile) {
const buffer = profile_1.perftools.profiles.Profile.encode(profile).finish();
return gzipPromise(buffer);
};
exports.encode = encode;
//# sourceMappingURL=utils.js.map