@sentry/node
Version:
Sentry Node SDK using OpenTelemetry for performance instrumentation
392 lines (388 loc) • 12.2 kB
JavaScript
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
const node_child_process = require('node:child_process');
const node_fs = require('node:fs');
const os = require('node:os');
const node_path = require('node:path');
const util = require('node:util');
const core = require('@sentry/core');
const readFileAsync = util.promisify(node_fs.readFile);
const readDirAsync = util.promisify(node_fs.readdir);
const INTEGRATION_NAME = "Context";
const _nodeContextIntegration = ((options = {}) => {
const _options = {
app: true,
os: true,
device: true,
culture: true,
cloudResource: true,
...options
};
const appContext = _options.app ? getAppContext() : void 0;
const deviceContext = _options.device ? getDeviceContext(_options.device) : void 0;
const cultureContext = _options.culture ? getCultureContext() : void 0;
const cloudResourceContext = _options.cloudResource ? getCloudResourceContext() : void 0;
const osContextPromise = _options.os ? getOsContext() : void 0;
const cachedSpanAttributes = {
"process.runtime.engine.name": "v8",
"process.runtime.engine.version": process.versions.v8,
...contextsToSpanAttributes({
app: appContext,
device: deviceContext,
culture: cultureContext,
cloud_resource: cloudResourceContext
})
};
if (osContextPromise) {
osContextPromise.then((osCtx) => Object.assign(cachedSpanAttributes, contextsToSpanAttributes({ os: osCtx }))).catch(() => {
});
}
const contextsPromise = (async () => {
const contexts = {};
if (osContextPromise) {
contexts.os = await osContextPromise;
}
if (appContext) {
contexts.app = appContext;
}
if (deviceContext) {
contexts.device = deviceContext;
}
if (cultureContext) {
contexts.culture = cultureContext;
}
if (cloudResourceContext) {
contexts.cloud_resource = cloudResourceContext;
}
return contexts;
})();
async function addContext(event) {
const updatedContext = _updateContext(await contextsPromise);
event.contexts = {
...event.contexts,
app: { ...updatedContext.app, ...event.contexts?.app },
os: { ...updatedContext.os, ...event.contexts?.os },
device: { ...updatedContext.device, ...event.contexts?.device },
culture: { ...updatedContext.culture, ...event.contexts?.culture },
cloud_resource: { ...updatedContext.cloud_resource, ...event.contexts?.cloud_resource }
};
return event;
}
return {
name: INTEGRATION_NAME,
processEvent(event) {
return addContext(event);
},
processSegmentSpan(span) {
core.safeSetSpanJSONAttributes(span, cachedSpanAttributes);
core.safeSetSpanJSONAttributes(span, getDynamicSpanAttributes(appContext, deviceContext));
}
};
});
const nodeContextIntegration = core.defineIntegration(_nodeContextIntegration);
function _updateContext(contexts) {
if (contexts.app?.app_memory) {
contexts.app.app_memory = process.memoryUsage().rss;
}
if (contexts.app?.free_memory && typeof process.availableMemory === "function") {
const freeMemory = process.availableMemory?.();
if (freeMemory != null) {
contexts.app.free_memory = freeMemory;
}
}
if (contexts.device?.free_memory) {
contexts.device.free_memory = os.freemem();
}
return contexts;
}
function contextsToSpanAttributes(contexts) {
const attrs = {};
const { app, device, os: osCtx, culture, cloud_resource } = contexts;
if (app) {
if (app.app_start_time) {
attrs["app.start_time"] = app.app_start_time;
}
}
if (device) {
if (device.arch) {
attrs["device.archs"] = [device.arch];
}
if (device.boot_time) {
attrs["device.boot_time"] = device.boot_time;
}
if (device.memory_size != null) {
attrs["device.memory_size"] = device.memory_size;
}
if (device.processor_count != null) {
attrs["device.processor_count"] = device.processor_count;
}
if (device.cpu_description) {
attrs["device.cpu_description"] = device.cpu_description;
}
if (device.processor_frequency != null) {
attrs["device.processor_frequency"] = device.processor_frequency;
}
}
if (osCtx) {
if (osCtx.name) {
attrs["os.name"] = osCtx.name;
}
if (osCtx.version) {
attrs["os.version"] = osCtx.version;
}
if (osCtx.kernel_version) {
attrs["os.kernel_version"] = osCtx.kernel_version;
}
if (osCtx.build) {
attrs["os.build"] = osCtx.build;
}
}
if (culture) {
if (culture.locale) {
attrs["culture.locale"] = culture.locale;
}
if (culture.timezone) {
attrs["culture.timezone"] = culture.timezone;
}
}
if (cloud_resource) {
for (const [key, value] of Object.entries(cloud_resource)) {
if (value != null) {
attrs[key] = value;
}
}
}
return attrs;
}
function getDynamicSpanAttributes(appContext, deviceContext) {
const attrs = {};
if (appContext) {
attrs["app.memory"] = process.memoryUsage().rss;
if (typeof process.availableMemory === "function") {
const freeMemory = process.availableMemory?.();
if (freeMemory != null) {
attrs["app.free_memory"] = freeMemory;
}
}
}
if (deviceContext?.free_memory != null) {
attrs["device.free_memory"] = os.freemem();
}
return attrs;
}
async function getOsContext() {
const platformId = os.platform();
switch (platformId) {
case "darwin":
return getDarwinInfo();
case "linux":
return getLinuxInfo();
default:
return {
name: PLATFORM_NAMES[platformId] || platformId,
version: os.release()
};
}
}
function getCultureContext() {
try {
if (typeof process.versions.icu !== "string") {
return;
}
const january = /* @__PURE__ */ new Date(9e8);
const spanish = new Intl.DateTimeFormat("es", { month: "long" });
if (spanish.format(january) === "enero") {
const options = Intl.DateTimeFormat().resolvedOptions();
return {
locale: options.locale,
timezone: options.timeZone
};
}
} catch {
}
return;
}
function getAppContext() {
const app_memory = process.memoryUsage().rss;
const app_start_time = new Date(Date.now() - process.uptime() * 1e3).toISOString();
const appContext = { app_start_time, app_memory };
if (typeof process.availableMemory === "function") {
const freeMemory = process.availableMemory?.();
if (freeMemory != null) {
appContext.free_memory = freeMemory;
}
}
return appContext;
}
function getDeviceContext(deviceOpt) {
const device = {};
let uptime;
try {
uptime = os.uptime();
} catch {
}
if (typeof uptime === "number") {
device.boot_time = new Date(Date.now() - uptime * 1e3).toISOString();
}
device.arch = os.arch();
if (deviceOpt === true || deviceOpt.memory) {
device.memory_size = os.totalmem();
device.free_memory = os.freemem();
}
if (deviceOpt === true || deviceOpt.cpu) {
const cpuInfo = os.cpus();
const firstCpu = cpuInfo?.[0];
if (firstCpu) {
device.processor_count = cpuInfo.length;
device.cpu_description = firstCpu.model;
device.processor_frequency = firstCpu.speed;
}
}
return device;
}
const PLATFORM_NAMES = {
aix: "IBM AIX",
freebsd: "FreeBSD",
openbsd: "OpenBSD",
sunos: "SunOS",
win32: "Windows",
ohos: "OpenHarmony",
android: "Android"
};
const LINUX_DISTROS = [
{ name: "fedora-release", distros: ["Fedora"] },
{ name: "redhat-release", distros: ["Red Hat Linux", "Centos"] },
{ name: "redhat_version", distros: ["Red Hat Linux"] },
{ name: "SuSE-release", distros: ["SUSE Linux"] },
{ name: "lsb-release", distros: ["Ubuntu Linux", "Arch Linux"] },
{ name: "debian_version", distros: ["Debian"] },
{ name: "debian_release", distros: ["Debian"] },
{ name: "arch-release", distros: ["Arch Linux"] },
{ name: "gentoo-release", distros: ["Gentoo Linux"] },
{ name: "novell-release", distros: ["SUSE Linux"] },
{ name: "alpine-release", distros: ["Alpine Linux"] }
];
const LINUX_VERSIONS = {
alpine: (content) => content,
arch: (content) => matchFirst(/distrib_release=(.*)/, content),
centos: (content) => matchFirst(/release ([^ ]+)/, content),
debian: (content) => content,
fedora: (content) => matchFirst(/release (..)/, content),
mint: (content) => matchFirst(/distrib_release=(.*)/, content),
red: (content) => matchFirst(/release ([^ ]+)/, content),
suse: (content) => matchFirst(/VERSION = (.*)\n/, content),
ubuntu: (content) => matchFirst(/distrib_release=(.*)/, content)
};
function matchFirst(regex, text) {
const match = regex.exec(text);
return match ? match[1] : void 0;
}
async function getDarwinInfo() {
const darwinInfo = {
kernel_version: os.release(),
name: "Mac OS X",
version: `10.${Number(os.release().split(".")[0]) - 4}`
};
try {
const output = await new Promise((resolve, reject) => {
node_child_process.execFile("/usr/bin/sw_vers", (error, stdout) => {
if (error) {
reject(error);
return;
}
resolve(stdout);
});
});
darwinInfo.name = matchFirst(/^ProductName:\s+(.*)$/m, output);
darwinInfo.version = matchFirst(/^ProductVersion:\s+(.*)$/m, output);
darwinInfo.build = matchFirst(/^BuildVersion:\s+(.*)$/m, output);
} catch {
}
return darwinInfo;
}
function getLinuxDistroId(name) {
return name.split(" ")[0].toLowerCase();
}
async function getLinuxInfo() {
const linuxInfo = {
kernel_version: os.release(),
name: "Linux"
};
try {
const etcFiles = await readDirAsync("/etc");
const distroFile = LINUX_DISTROS.find((file) => etcFiles.includes(file.name));
if (!distroFile) {
return linuxInfo;
}
const distroPath = node_path.join("/etc", distroFile.name);
const contents = (await readFileAsync(distroPath, { encoding: "utf-8" })).toLowerCase();
const { distros } = distroFile;
linuxInfo.name = distros.find((d) => contents.indexOf(getLinuxDistroId(d)) >= 0) || distros[0];
const id = getLinuxDistroId(linuxInfo.name);
linuxInfo.version = LINUX_VERSIONS[id]?.(contents);
} catch {
}
return linuxInfo;
}
function getCloudResourceContext() {
if (process.env.VERCEL) {
return {
"cloud.provider": "vercel",
"cloud.region": process.env.VERCEL_REGION
};
} else if (process.env.AWS_REGION) {
return {
"cloud.provider": "aws",
"cloud.region": process.env.AWS_REGION,
"cloud.platform": process.env.AWS_EXECUTION_ENV
};
} else if (process.env.GCP_PROJECT) {
return {
"cloud.provider": "gcp"
};
} else if (process.env.ALIYUN_REGION_ID) {
return {
"cloud.provider": "alibaba_cloud",
"cloud.region": process.env.ALIYUN_REGION_ID
};
} else if (process.env.WEBSITE_SITE_NAME && process.env.REGION_NAME) {
return {
"cloud.provider": "azure",
"cloud.region": process.env.REGION_NAME
};
} else if (process.env.IBM_CLOUD_REGION) {
return {
"cloud.provider": "ibm_cloud",
"cloud.region": process.env.IBM_CLOUD_REGION
};
} else if (process.env.TENCENTCLOUD_REGION) {
return {
"cloud.provider": "tencent_cloud",
"cloud.region": process.env.TENCENTCLOUD_REGION,
"cloud.account.id": process.env.TENCENTCLOUD_APPID,
"cloud.availability_zone": process.env.TENCENTCLOUD_ZONE
};
} else if (process.env.NETLIFY) {
return {
"cloud.provider": "netlify"
};
} else if (process.env.FLY_REGION) {
return {
"cloud.provider": "fly.io",
"cloud.region": process.env.FLY_REGION
};
} else if (process.env.DYNO) {
return {
"cloud.provider": "heroku"
};
} else {
return void 0;
}
}
exports.contextsToSpanAttributes = contextsToSpanAttributes;
exports.getAppContext = getAppContext;
exports.getDeviceContext = getDeviceContext;
exports.getDynamicSpanAttributes = getDynamicSpanAttributes;
exports.nodeContextIntegration = nodeContextIntegration;
exports.readDirAsync = readDirAsync;
exports.readFileAsync = readFileAsync;
//# sourceMappingURL=context.js.map