nitro-opentelemetry
Version:
Opentelemetry module for the server framework nitro. Compatible with Nuxt.
192 lines (186 loc) • 7.13 kB
JavaScript
import { resolvePathSync, resolvePath } from 'mlly';
import MagicString from 'magic-string';
import { consola } from 'consola';
import { normalize } from 'pathe';
import defu from 'defu';
import { resolveNitroPath } from 'nitropack/kit';
import { resolveModulePath } from 'exsolve';
const presets = {
package: resolvePathSync("nitro-opentelemetry/runtime/utils", {
extensions: [".mjs", ".ts"]
})
};
const logger = consola.withTag("nitro-opentelemetry");
async function getPresetFile(nitro) {
if (nitro.options.otel?.preset === false) {
return "";
}
if (nitro.options.otel?.preset?.name === "custom") {
return await resolvePath(nitro.options.otel.preset.filePath, {
extensions: [".mjs", ".ts"],
url: nitro.options.rootDir
});
}
const nitroPreset = nitro.options.otel?.preset?.name || nitro.options.preset;
switch (nitroPreset) {
case "node":
case "node-cluster":
case "nitro-dev":
case "node-server": {
return await resolvePath("nitro-opentelemetry/runtime/presets/node", {
extensions: [".mjs", ".ts"]
});
}
case "azure-monitor": {
return await resolvePath("nitro-opentelemetry/runtime/presets/azure-monitor", {
extensions: [".mjs", ".ts"]
});
}
case "cf-worker": {
return await resolvePath("nitro-opentelemetry/runtime/presets/cf-worker", {
extensions: [".mjs", ".ts"]
});
}
}
logger.warn(`Initializer file for preset ${nitroPreset} not found. Please provide your own or open an issue on the repository.`);
return "";
}
function isPresetEntry(nitro) {
const preset = (nitro.options.otel?.preset ? nitro.options.otel?.preset.name : void 0) || nitro.options.preset;
return ["cf-worker"].includes(preset);
}
async function module(nitro) {
nitro.options.alias["#nitro-opentelemetry/init"] = await getPresetFile(nitro);
if (isPresetEntry(nitro)) {
nitro.options.alias["#nitro-entry-file"] = nitro.options.entry;
nitro.options.entry = await getPresetFile(nitro);
}
if (nitro.options.otel?.preset !== false) {
nitro.hooks.hook("rollup:before", (nitro2, rollupConfig) => {
if (!rollupConfig.plugins) rollupConfig.plugins = [];
const plugins = rollupConfig.plugins;
if (Array.isArray(plugins)) {
rollupConfig.plugins = plugins.filter((plugin) => {
if (plugin && "name" in plugin) {
return plugin.name !== "impound";
}
return true;
});
}
rollupConfig.plugins.push({
name: "inject-init-plugin",
async transform(code, id) {
const normalizedId = normalize(id);
if (normalizedId.includes("runtime/entries") || this.getModuleInfo(id)?.isEntry) {
const s = new MagicString(code);
s.prepend(`import '#nitro-opentelemetry/init';`);
return {
code: s.toString(),
map: s.generateMap({ hires: true }),
moduleSideEffects: true
};
}
if (normalizedId === nitro2.options.alias["#nitro-opentelemetry/init"]) {
const s = new MagicString(code);
return {
moduleSideEffects: true,
code: s.toString(),
map: s.generateMap({ hires: true })
};
}
}
});
});
nitro.options.virtual["#nitro-otel-options"] = nitro.options.otel?.preset && typeof nitro.options.otel.preset === "object" && "options" in nitro.options.otel.preset ? `export default ${JSON.stringify(nitro.options.otel.preset.options || {})}` : `export default {}`;
}
if (nitro.options.imports) {
nitro.options.imports.presets.push(presets);
}
if (nitro.options.renderer) {
nitro.options.alias["#nitro-renderer"] = nitro.options.renderer;
nitro.options.renderer = await resolvePath("nitro-opentelemetry/runtime/renderer/renderer", {
extensions: [".mjs", ".ts"]
});
nitro.options.externals = defu(nitro.options.externals, {
inline: [nitro.options.renderer]
});
}
if (nitro.options.errorHandler) {
if (typeof nitro.options.errorHandler === "string") {
nitro.options.alias["#nitro-error-handler"] = nitro.options.errorHandler;
nitro.options.errorHandler = await resolvePath("nitro-opentelemetry/runtime/renderer/error", {
extensions: [".mjs", ".ts"]
});
nitro.options.externals = defu(nitro.options.externals, {
inline: [nitro.options.errorHandler]
});
} else if (Array.isArray(nitro.options.errorHandler)) {
nitro.hooks.hook("rollup:before", async (nitro2, rollupConfig) => {
const errorHandlers = await Promise.all(nitro2.options.errorHandler.map((path) => {
return resolveModulePath(resolveNitroPath(path, nitro2.options), {
from: [
import.meta.url,
...nitro2.options.nodeModulesDirs,
nitro2.options.rootDir
],
extensions: [".mjs", ".ts", ".js", ".cjs"]
});
}));
rollupConfig.plugins.push({
name: "nitro-otel:inject-error-handlers",
async transform(code, id) {
if (id.includes("prod")) {
console.log(id, errorHandlers.includes(normalize(id)), errorHandlers);
}
if (errorHandlers.includes(normalize(id))) {
const s = new MagicString(code);
s.prepend(`import { context } from "@opentelemetry/api";
`);
const defaultExport = this.parse(code).body.find((node) => node.type === "ExportDefaultDeclaration");
if (defaultExport) {
s.overwrite(
defaultExport.declaration.start,
defaultExport.declaration.end,
`(...args) => context.with(args?.[1]?.otel?.ctx, ${code.slice(defaultExport.declaration.start, defaultExport.declaration.end)}, undefined, ...args)`
);
return {
code: s.toString(),
map: s.generateMap({ hires: true })
};
}
}
}
});
});
}
}
nitro.options.typescript.tsConfig = defu(nitro.options.typescript.tsConfig, {
compilerOptions: {
types: ["nitro-opentelemetry"]
}
});
nitro.options.plugins.push(await resolvePath("nitro-opentelemetry/runtime/plugin", {
extensions: [".mjs", ".ts"]
}));
nitro.hooks.hook("rollup:before", (_, rollupConfig) => {
const ogModuleCtx = rollupConfig.moduleContext;
rollupConfig.moduleContext = (_id) => {
const id = normalize(_id);
if (id.includes("node_modules/@opentelemetry/api")) {
return "(undefined)";
}
return typeof ogModuleCtx === "object" ? ogModuleCtx[id] : ogModuleCtx?.(id);
};
});
}
async function moduleWithCompat(arg1, arg2) {
if (arg2?.options?.nitro) {
arg2.hooks.hookOnce("nitro:config", (nitroConfig) => {
nitroConfig.modules = nitroConfig.modules || [];
nitroConfig.modules.push(module);
});
} else {
await module(arg1);
}
}
export { moduleWithCompat as default };