@logtape/otel 1.0.0-dev.258 → 1.0.0-dev.261

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/deno.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@logtape/otel",
3
- "version": "1.0.0-dev.258+b30f4857",
3
+ "version": "1.0.0-dev.261+9cda8a4b",
4
4
  "license": "MIT",
5
5
  "exports": {
6
6
  ".": "./mod.ts"
@@ -15,6 +15,7 @@
15
15
  "@opentelemetry/semantic-conventions": "npm:@opentelemetry/semantic-conventions@^1.34.0"
16
16
  },
17
17
  "tasks": {
18
- "build": "pnpm build"
18
+ "build": "pnpm build",
19
+ "test": "deno test --allow-net --allow-env"
19
20
  }
20
21
  }
package/dist/deno.cjs CHANGED
@@ -1,7 +1,7 @@
1
1
 
2
2
  //#region deno.json
3
3
  var name = "@logtape/otel";
4
- var version = "1.0.0-dev.258+b30f4857";
4
+ var version = "1.0.0-dev.261+9cda8a4b";
5
5
  var license = "MIT";
6
6
  var exports$1 = { ".": "./mod.ts" };
7
7
  var imports = {
@@ -13,7 +13,10 @@ var imports = {
13
13
  "@opentelemetry/sdk-logs": "npm:@opentelemetry/sdk-logs@^0.202.0",
14
14
  "@opentelemetry/semantic-conventions": "npm:@opentelemetry/semantic-conventions@^1.34.0"
15
15
  };
16
- var tasks = { "build": "pnpm build" };
16
+ var tasks = {
17
+ "build": "pnpm build",
18
+ "test": "deno test --allow-net --allow-env"
19
+ };
17
20
  var deno_default = {
18
21
  name,
19
22
  version,
package/dist/deno.js CHANGED
@@ -1,6 +1,6 @@
1
1
  //#region deno.json
2
2
  var name = "@logtape/otel";
3
- var version = "1.0.0-dev.258+b30f4857";
3
+ var version = "1.0.0-dev.261+9cda8a4b";
4
4
  var license = "MIT";
5
5
  var exports = { ".": "./mod.ts" };
6
6
  var imports = {
@@ -12,7 +12,10 @@ var imports = {
12
12
  "@opentelemetry/sdk-logs": "npm:@opentelemetry/sdk-logs@^0.202.0",
13
13
  "@opentelemetry/semantic-conventions": "npm:@opentelemetry/semantic-conventions@^1.34.0"
14
14
  };
15
- var tasks = { "build": "pnpm build" };
15
+ var tasks = {
16
+ "build": "pnpm build",
17
+ "test": "deno test --allow-net --allow-env"
18
+ };
16
19
  var deno_default = {
17
20
  name,
18
21
  version,
package/dist/deno.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"deno.js","names":[],"sources":["../deno.json"],"sourcesContent":["{\n \"name\": \"@logtape/otel\",\n \"version\": \"1.0.0-dev.258+b30f4857\",\n \"license\": \"MIT\",\n \"exports\": {\n \".\": \"./mod.ts\"\n },\n \"imports\": {\n \"@opentelemetry/api\": \"npm:@opentelemetry/api@^1.9.0\",\n \"@opentelemetry/api-logs\": \"npm:@opentelemetry/api-logs@^0.202.0\",\n \"@opentelemetry/exporter-logs-otlp-http\": \"npm:@opentelemetry/exporter-logs-otlp-http@^0.202.0\",\n \"@opentelemetry/otlp-exporter-base\": \"npm:@opentelemetry/otlp-exporter-base@^0.202.0\",\n \"@opentelemetry/resources\": \"npm:@opentelemetry/resources@^2.0.1\",\n \"@opentelemetry/sdk-logs\": \"npm:@opentelemetry/sdk-logs@^0.202.0\",\n \"@opentelemetry/semantic-conventions\": \"npm:@opentelemetry/semantic-conventions@^1.34.0\"\n },\n \"tasks\": {\n \"build\": \"pnpm build\"\n }\n}\n"],"mappings":";WACU;cACG;cACA;cACA,EACT,KAAK,WACN;cACU;CACT,sBAAsB;CACtB,2BAA2B;CAC3B,0CAA0C;CAC1C,qCAAqC;CACrC,4BAA4B;CAC5B,2BAA2B;CAC3B,uCAAuC;AACxC;YACQ,EACP,SAAS,aACV;mBAlBH;;;;;;;AAmBC"}
1
+ {"version":3,"file":"deno.js","names":[],"sources":["../deno.json"],"sourcesContent":["{\n \"name\": \"@logtape/otel\",\n \"version\": \"1.0.0-dev.261+9cda8a4b\",\n \"license\": \"MIT\",\n \"exports\": {\n \".\": \"./mod.ts\"\n },\n \"imports\": {\n \"@opentelemetry/api\": \"npm:@opentelemetry/api@^1.9.0\",\n \"@opentelemetry/api-logs\": \"npm:@opentelemetry/api-logs@^0.202.0\",\n \"@opentelemetry/exporter-logs-otlp-http\": \"npm:@opentelemetry/exporter-logs-otlp-http@^0.202.0\",\n \"@opentelemetry/otlp-exporter-base\": \"npm:@opentelemetry/otlp-exporter-base@^0.202.0\",\n \"@opentelemetry/resources\": \"npm:@opentelemetry/resources@^2.0.1\",\n \"@opentelemetry/sdk-logs\": \"npm:@opentelemetry/sdk-logs@^0.202.0\",\n \"@opentelemetry/semantic-conventions\": \"npm:@opentelemetry/semantic-conventions@^1.34.0\"\n },\n \"tasks\": {\n \"build\": \"pnpm build\",\n \"test\": \"deno test --allow-net --allow-env\"\n }\n}\n"],"mappings":";WACU;cACG;cACA;cACA,EACT,KAAK,WACN;cACU;CACT,sBAAsB;CACtB,2BAA2B;CAC3B,0CAA0C;CAC1C,qCAAqC;CACrC,4BAA4B;CAC5B,2BAA2B;CAC3B,uCAAuC;AACxC;YACQ;CACP,SAAS;CACT,QAAQ;AACT;mBAnBH;;;;;;;AAoBC"}
package/dist/mod.cjs CHANGED
@@ -7,10 +7,23 @@ const __opentelemetry_exporter_logs_otlp_http = require_rolldown_runtime.__toESM
7
7
  const __opentelemetry_resources = require_rolldown_runtime.__toESM(require("@opentelemetry/resources"));
8
8
  const __opentelemetry_sdk_logs = require_rolldown_runtime.__toESM(require("@opentelemetry/sdk-logs"));
9
9
  const __opentelemetry_semantic_conventions = require_rolldown_runtime.__toESM(require("@opentelemetry/semantic-conventions"));
10
- const node_process = require_rolldown_runtime.__toESM(require("node:process"));
11
10
 
12
11
  //#region mod.ts
13
12
  /**
13
+ * Gets an environment variable value across different JavaScript runtimes.
14
+ * @param name The environment variable name.
15
+ * @returns The environment variable value, or undefined if not found.
16
+ */
17
+ function getEnvironmentVariable(name) {
18
+ if (typeof Deno !== "undefined" && Deno.env) try {
19
+ return Deno.env.get(name);
20
+ } catch {
21
+ return void 0;
22
+ }
23
+ if (typeof globalThis !== "undefined" && "process" in globalThis && typeof globalThis.process !== "undefined" && typeof globalThis.process.env === "object" && globalThis.process.env !== null) return globalThis.process.env[name];
24
+ return void 0;
25
+ }
26
+ /**
14
27
  * Creates a sink that forwards log records to OpenTelemetry.
15
28
  * @param options Options for creating the sink.
16
29
  * @returns The sink.
@@ -19,7 +32,7 @@ function getOpenTelemetrySink(options = {}) {
19
32
  if (options.diagnostics) __opentelemetry_api.diag.setLogger(new DiagLoggerAdaptor(), __opentelemetry_api.DiagLogLevel.DEBUG);
20
33
  let loggerProvider;
21
34
  if (options.loggerProvider == null) {
22
- const resource = (0, __opentelemetry_resources.defaultResource)().merge((0, __opentelemetry_resources.resourceFromAttributes)({ [__opentelemetry_semantic_conventions.ATTR_SERVICE_NAME]: options.serviceName ?? node_process.default.env.OTEL_SERVICE_NAME }));
35
+ const resource = (0, __opentelemetry_resources.defaultResource)().merge((0, __opentelemetry_resources.resourceFromAttributes)({ [__opentelemetry_semantic_conventions.ATTR_SERVICE_NAME]: options.serviceName ?? getEnvironmentVariable("OTEL_SERVICE_NAME") }));
23
36
  loggerProvider = new __opentelemetry_sdk_logs.LoggerProvider({ resource });
24
37
  const otlpExporter = new __opentelemetry_exporter_logs_otlp_http.OTLPLogExporter(options.otlpExporterConfig);
25
38
  loggerProvider.addLogRecordProcessor(new __opentelemetry_sdk_logs.SimpleLogRecordProcessor(otlpExporter));
@@ -1 +1 @@
1
- {"version":3,"file":"mod.d.cts","names":[],"sources":["../mod.ts"],"sourcesContent":[],"mappings":";;;;;;;;;AAuBiC;KAQ5B,eAAA,GAAkB,cAAH,GAAA;EAAA;;;AAaM;EASd,qBAAc,CAAA,SAAA,EAjBS,kBAiBT,CAAA,EAAA,IAAA;EAErB;AAML;;;;AAA0D;EAKzC,QAAA,CAAA,EAAA,GAAA,GAtBE,OAsBF,CAAA,IAAwB,CAAA;CAAA;;;;;AAoCQ;AAejD;AAAoC,KAhExB,cAAA,GAgEwB,MAAA,GAAA,SAAA;KA9D/B,OAAA,GA+DM,CAAA,MAAA,GAAA,IAAA,GAAA,SAAA,CAAA,EAAA;;AACJ;;;KA1DK,aAAA,aAA0B,YAAY;;;;UAKjC,wBAAA;;;;mBAIE;;;;;;;;;;qCAWkB;;;;;;;mBAQlB;;;;;;;;;;;uBAaI;;;;;;;;;;;;;iBAeP,oBAAA,WACL,2BACR"}
1
+ {"version":3,"file":"mod.d.cts","names":[],"sources":["../mod.ts"],"sourcesContent":[],"mappings":";;;;;;;;;AAuBiC;KAyC5B,eAAA,GAAkB,cAAH,GAAA;EAAA;;;AAaM;EASd,qBAAc,CAAA,SAAA,EAjBS,kBAiBT,CAAA,EAAA,IAAA;EAErB;AAML;;;;AAA0D;EAKzC,QAAA,CAAA,EAAA,GAAA,GAtBE,OAsBF,CAAA,IAAwB,CAAA;CAAA;;;;;AAoCQ;AAejD;AAAoC,KAhExB,cAAA,GAgEwB,MAAA,GAAA,SAAA;KA9D/B,OAAA,GA+DM,CAAA,MAAA,GAAA,IAAA,GAAA,SAAA,CAAA,EAAA;;AACJ;;;KA1DK,aAAA,aAA0B,YAAY;;;;UAKjC,wBAAA;;;;mBAIE;;;;;;;;;;qCAWkB;;;;;;;mBAQlB;;;;;;;;;;;uBAaI;;;;;;;;;;;;;iBAeP,oBAAA,WACL,2BACR"}
package/dist/mod.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"mod.d.ts","names":[],"sources":["../mod.ts"],"sourcesContent":[],"mappings":";;;;;;;;;AAuBiC;KAQ5B,eAAA,GAAkB,cAAH,GAAA;EAAA;;;AAaM;EASd,qBAAc,CAAA,SAAA,EAjBS,kBAiBT,CAAA,EAAA,IAAA;EAErB;AAML;;;;AAA0D;EAKzC,QAAA,CAAA,EAAA,GAAA,GAtBE,OAsBF,CAAA,IAAwB,CAAA;CAAA;;;;;AAoCQ;AAejD;AAAoC,KAhExB,cAAA,GAgEwB,MAAA,GAAA,SAAA;KA9D/B,OAAA,GA+DM,CAAA,MAAA,GAAA,IAAA,GAAA,SAAA,CAAA,EAAA;;AACJ;;;KA1DK,aAAA,aAA0B,YAAY;;;;UAKjC,wBAAA;;;;mBAIE;;;;;;;;;;qCAWkB;;;;;;;mBAQlB;;;;;;;;;;;uBAaI;;;;;;;;;;;;;iBAeP,oBAAA,WACL,2BACR"}
1
+ {"version":3,"file":"mod.d.ts","names":[],"sources":["../mod.ts"],"sourcesContent":[],"mappings":";;;;;;;;;AAuBiC;KAyC5B,eAAA,GAAkB,cAAH,GAAA;EAAA;;;AAaM;EASd,qBAAc,CAAA,SAAA,EAjBS,kBAiBT,CAAA,EAAA,IAAA;EAErB;AAML;;;;AAA0D;EAKzC,QAAA,CAAA,EAAA,GAAA,GAtBE,OAsBF,CAAA,IAAwB,CAAA;CAAA;;;;;AAoCQ;AAejD;AAAoC,KAhExB,cAAA,GAgEwB,MAAA,GAAA,SAAA;KA9D/B,OAAA,GA+DM,CAAA,MAAA,GAAA,IAAA,GAAA,SAAA,CAAA,EAAA;;AACJ;;;KA1DK,aAAA,aAA0B,YAAY;;;;UAKjC,wBAAA;;;;mBAIE;;;;;;;;;;qCAWkB;;;;;;;mBAQlB;;;;;;;;;;;uBAaI;;;;;;;;;;;;;iBAeP,oBAAA,WACL,2BACR"}
package/dist/mod.js CHANGED
@@ -6,10 +6,23 @@ import { OTLPLogExporter } from "@opentelemetry/exporter-logs-otlp-http";
6
6
  import { defaultResource, resourceFromAttributes } from "@opentelemetry/resources";
7
7
  import { LoggerProvider as LoggerProvider$1, SimpleLogRecordProcessor } from "@opentelemetry/sdk-logs";
8
8
  import { ATTR_SERVICE_NAME } from "@opentelemetry/semantic-conventions";
9
- import process from "node:process";
10
9
 
11
10
  //#region mod.ts
12
11
  /**
12
+ * Gets an environment variable value across different JavaScript runtimes.
13
+ * @param name The environment variable name.
14
+ * @returns The environment variable value, or undefined if not found.
15
+ */
16
+ function getEnvironmentVariable(name) {
17
+ if (typeof Deno !== "undefined" && Deno.env) try {
18
+ return Deno.env.get(name);
19
+ } catch {
20
+ return void 0;
21
+ }
22
+ if (typeof globalThis !== "undefined" && "process" in globalThis && typeof globalThis.process !== "undefined" && typeof globalThis.process.env === "object" && globalThis.process.env !== null) return globalThis.process.env[name];
23
+ return void 0;
24
+ }
25
+ /**
13
26
  * Creates a sink that forwards log records to OpenTelemetry.
14
27
  * @param options Options for creating the sink.
15
28
  * @returns The sink.
@@ -18,7 +31,7 @@ function getOpenTelemetrySink(options = {}) {
18
31
  if (options.diagnostics) diag.setLogger(new DiagLoggerAdaptor(), DiagLogLevel.DEBUG);
19
32
  let loggerProvider;
20
33
  if (options.loggerProvider == null) {
21
- const resource = defaultResource().merge(resourceFromAttributes({ [ATTR_SERVICE_NAME]: options.serviceName ?? process.env.OTEL_SERVICE_NAME }));
34
+ const resource = defaultResource().merge(resourceFromAttributes({ [ATTR_SERVICE_NAME]: options.serviceName ?? getEnvironmentVariable("OTEL_SERVICE_NAME") }));
22
35
  loggerProvider = new LoggerProvider$1({ resource });
23
36
  const otlpExporter = new OTLPLogExporter(options.otlpExporterConfig);
24
37
  loggerProvider.addLogRecordProcessor(new SimpleLogRecordProcessor(otlpExporter));
package/dist/mod.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"mod.js","names":["options: OpenTelemetrySinkOptions","loggerProvider: ILoggerProvider","LoggerProvider","metadata","record: LogRecord","level: string","properties: Record<string, unknown>","objectRenderer: ObjectRenderer","attributes: Record<string, AnyValue>","v","value: unknown","message: readonly unknown[]","body: (string | null | undefined)[]","bodyFormatter: BodyFormatter","inspect: (value: unknown) => string","msg: string","#escape"],"sources":["../mod.ts"],"sourcesContent":["import {\n getLogger,\n type Logger,\n type LogRecord,\n type Sink,\n} from \"@logtape/logtape\";\nimport { diag, type DiagLogger, DiagLogLevel } from \"@opentelemetry/api\";\nimport {\n type AnyValue,\n type LoggerProvider as LoggerProviderBase,\n type LogRecord as OTLogRecord,\n SeverityNumber,\n} from \"@opentelemetry/api-logs\";\nimport { OTLPLogExporter } from \"@opentelemetry/exporter-logs-otlp-http\";\nimport type { OTLPExporterNodeConfigBase } from \"@opentelemetry/otlp-exporter-base\";\nimport {\n defaultResource,\n resourceFromAttributes,\n} from \"@opentelemetry/resources\";\nimport {\n LoggerProvider,\n type LogRecordProcessor,\n SimpleLogRecordProcessor,\n} from \"@opentelemetry/sdk-logs\";\nimport { ATTR_SERVICE_NAME } from \"@opentelemetry/semantic-conventions\";\nimport process from \"node:process\";\nimport metadata from \"./deno.json\" with { type: \"json\" };\n\n/**\n * The OpenTelemetry logger provider.\n */\ntype ILoggerProvider = LoggerProviderBase & {\n /**\n * Adds a new {@link LogRecordProcessor} to this logger.\n * @param processor the new LogRecordProcessor to be added.\n */\n addLogRecordProcessor(processor: LogRecordProcessor): void;\n\n /**\n * Flush all buffered data and shut down the LoggerProvider and all registered\n * LogRecordProcessor.\n *\n * Returns a promise which is resolved when all flushes are complete.\n */\n shutdown?: () => Promise<void>;\n};\n\n/**\n * The way to render the object in the log record. If `\"json\"`,\n * the object is rendered as a JSON string. If `\"inspect\"`,\n * the object is rendered using `util.inspect` in Node.js/Bun, or\n * `Deno.inspect` in Deno.\n */\nexport type ObjectRenderer = \"json\" | \"inspect\";\n\ntype Message = (string | null | undefined)[];\n\n/**\n * Custom `body` attribute formatter.\n * @since 0.3.0\n */\nexport type BodyFormatter = (message: Message) => AnyValue;\n\n/**\n * Options for creating an OpenTelemetry sink.\n */\nexport interface OpenTelemetrySinkOptions {\n /**\n * The OpenTelemetry logger provider to use.\n */\n loggerProvider?: ILoggerProvider;\n\n /**\n * The way to render the message in the log record. If `\"string\"`,\n * the message is rendered as a single string with the values are\n * interpolated into the message. If `\"array\"`, the message is\n * rendered as an array of strings. `\"string\"` by default.\n *\n * Or even fully customizable with a {@link BodyFormatter} function.\n * @since 0.2.0\n */\n messageType?: \"string\" | \"array\" | BodyFormatter;\n\n /**\n * The way to render the object in the log record. If `\"json\"`,\n * the object is rendered as a JSON string. If `\"inspect\"`,\n * the object is rendered using `util.inspect` in Node.js/Bun, or\n * `Deno.inspect` in Deno. `\"inspect\"` by default.\n */\n objectRenderer?: ObjectRenderer;\n\n /**\n * Whether to log diagnostics. Diagnostic logs are logged to\n * the `[\"logtape\", \"meta\", \"otel\"]` category.\n * Turned off by default.\n */\n diagnostics?: boolean;\n\n /**\n * The OpenTelemetry OTLP exporter configuration to use.\n * Ignored if `loggerProvider` is provided.\n */\n otlpExporterConfig?: OTLPExporterNodeConfigBase;\n\n /**\n * The service name to use. If not provided, the service name is\n * taken from the `OTEL_SERVICE_NAME` environment variable.\n * Ignored if `loggerProvider` is provided.\n */\n serviceName?: string;\n}\n\n/**\n * Creates a sink that forwards log records to OpenTelemetry.\n * @param options Options for creating the sink.\n * @returns The sink.\n */\nexport function getOpenTelemetrySink(\n options: OpenTelemetrySinkOptions = {},\n): Sink {\n if (options.diagnostics) {\n diag.setLogger(new DiagLoggerAdaptor(), DiagLogLevel.DEBUG);\n }\n\n let loggerProvider: ILoggerProvider;\n if (options.loggerProvider == null) {\n const resource = defaultResource().merge(\n resourceFromAttributes({\n [ATTR_SERVICE_NAME]: options.serviceName ??\n process.env.OTEL_SERVICE_NAME,\n }),\n );\n loggerProvider = new LoggerProvider({ resource });\n const otlpExporter = new OTLPLogExporter(options.otlpExporterConfig);\n loggerProvider.addLogRecordProcessor(\n // @ts-ignore: it works anyway...\n new SimpleLogRecordProcessor(otlpExporter),\n );\n } else {\n loggerProvider = options.loggerProvider;\n }\n const objectRenderer = options.objectRenderer ?? \"inspect\";\n const logger = loggerProvider.getLogger(metadata.name, metadata.version);\n const sink = (record: LogRecord) => {\n const { category, level, message, timestamp, properties } = record;\n if (\n category[0] === \"logtape\" && category[1] === \"meta\" &&\n category[2] === \"otel\"\n ) {\n return;\n }\n const severityNumber = mapLevelToSeverityNumber(level);\n const attributes = convertToAttributes(properties, objectRenderer);\n attributes[\"category\"] = [...category];\n logger.emit(\n {\n severityNumber,\n severityText: level,\n body: typeof options.messageType === \"function\"\n ? convertMessageToCustomBodyFormat(\n message,\n objectRenderer,\n options.messageType,\n )\n : options.messageType === \"array\"\n ? convertMessageToArray(message, objectRenderer)\n : convertMessageToString(message, objectRenderer),\n attributes,\n timestamp: new Date(timestamp),\n } satisfies OTLogRecord,\n );\n };\n if (loggerProvider.shutdown != null) {\n const shutdown = loggerProvider.shutdown.bind(loggerProvider);\n sink[Symbol.asyncDispose] = shutdown;\n }\n return sink;\n}\n\nfunction mapLevelToSeverityNumber(level: string): number {\n switch (level) {\n case \"trace\":\n return SeverityNumber.TRACE;\n case \"debug\":\n return SeverityNumber.DEBUG;\n case \"info\":\n return SeverityNumber.INFO;\n case \"warning\":\n return SeverityNumber.WARN;\n case \"error\":\n return SeverityNumber.ERROR;\n case \"fatal\":\n return SeverityNumber.FATAL;\n default:\n return SeverityNumber.UNSPECIFIED;\n }\n}\n\nfunction convertToAttributes(\n properties: Record<string, unknown>,\n objectRenderer: ObjectRenderer,\n): Record<string, AnyValue> {\n const attributes: Record<string, AnyValue> = {};\n for (const [name, value] of Object.entries(properties)) {\n const key = `attributes.${name}`;\n if (value == null) continue;\n if (Array.isArray(value)) {\n let t = null;\n for (const v of value) {\n if (v == null) continue;\n if (t != null && typeof v !== t) {\n attributes[key] = value.map((v) =>\n convertToString(v, objectRenderer)\n );\n break;\n }\n t = typeof v;\n }\n attributes[key] = value;\n } else {\n const encoded = convertToString(value, objectRenderer);\n if (encoded == null) continue;\n attributes[key] = encoded;\n }\n }\n return attributes;\n}\n\nfunction convertToString(\n value: unknown,\n objectRenderer: ObjectRenderer,\n): string | null | undefined {\n if (value === null || value === undefined || typeof value === \"string\") {\n return value;\n }\n if (objectRenderer === \"inspect\") return inspect(value);\n if (typeof value === \"number\" || typeof value === \"boolean\") {\n return value.toString();\n } else if (value instanceof Date) return value.toISOString();\n else return JSON.stringify(value);\n}\n\nfunction convertMessageToArray(\n message: readonly unknown[],\n objectRenderer: ObjectRenderer,\n): AnyValue {\n const body: (string | null | undefined)[] = [];\n for (let i = 0; i < message.length; i += 2) {\n const msg = message[i] as string;\n body.push(msg);\n if (message.length <= i + 1) break;\n const val = message[i + 1];\n body.push(convertToString(val, objectRenderer));\n }\n return body;\n}\n\nfunction convertMessageToString(\n message: readonly unknown[],\n objectRenderer: ObjectRenderer,\n): AnyValue {\n let body = \"\";\n for (let i = 0; i < message.length; i += 2) {\n const msg = message[i] as string;\n body += msg;\n if (message.length <= i + 1) break;\n const val = message[i + 1];\n const extra = convertToString(val, objectRenderer);\n body += extra ?? JSON.stringify(extra);\n }\n return body;\n}\n\nfunction convertMessageToCustomBodyFormat(\n message: readonly unknown[],\n objectRenderer: ObjectRenderer,\n bodyFormatter: BodyFormatter,\n): AnyValue {\n const body = message.map((msg) => convertToString(msg, objectRenderer));\n return bodyFormatter(body);\n}\n\n/**\n * A platform-specific inspect function. In Deno, this is {@link Deno.inspect},\n * and in Node.js/Bun it is {@link util.inspect}. If neither is available, it\n * falls back to {@link JSON.stringify}.\n *\n * @param value The value to inspect.\n * @returns The string representation of the value.\n */\nconst inspect: (value: unknown) => string =\n // @ts-ignore: Deno global\n \"Deno\" in globalThis && \"inspect\" in globalThis.Deno &&\n // @ts-ignore: Deno global\n typeof globalThis.Deno.inspect === \"function\"\n // @ts-ignore: Deno global\n ? globalThis.Deno.inspect\n // @ts-ignore: Node.js global\n : \"util\" in globalThis && \"inspect\" in globalThis.util &&\n // @ts-ignore: Node.js global\n globalThis.util.inspect === \"function\"\n // @ts-ignore: Node.js global\n ? globalThis.util.inspect\n : JSON.stringify;\n\nclass DiagLoggerAdaptor implements DiagLogger {\n logger: Logger;\n\n constructor() {\n this.logger = getLogger([\"logtape\", \"meta\", \"otel\"]);\n }\n\n #escape(msg: string): string {\n return msg.replaceAll(\"{\", \"{{\").replaceAll(\"}\", \"}}\");\n }\n\n error(msg: string, ...values: unknown[]): void {\n this.logger.error(`${this.#escape(msg)}: {values}`, { values });\n }\n\n warn(msg: string, ...values: unknown[]): void {\n this.logger.warn(`${this.#escape(msg)}: {values}`, { values });\n }\n\n info(msg: string, ...values: unknown[]): void {\n this.logger.info(`${this.#escape(msg)}: {values}`, { values });\n }\n\n debug(msg: string, ...values: unknown[]): void {\n this.logger.debug(`${this.#escape(msg)}: {values}`, { values });\n }\n\n verbose(msg: string, ...values: unknown[]): void {\n this.logger.debug(`${this.#escape(msg)}: {values}`, { values });\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"mod.js","names":["name: string","options: OpenTelemetrySinkOptions","loggerProvider: ILoggerProvider","LoggerProvider","metadata","record: LogRecord","level: string","properties: Record<string, unknown>","objectRenderer: ObjectRenderer","attributes: Record<string, AnyValue>","v","value: unknown","message: readonly unknown[]","body: (string | null | undefined)[]","bodyFormatter: BodyFormatter","inspect: (value: unknown) => string","msg: string","#escape"],"sources":["../mod.ts"],"sourcesContent":["import {\n getLogger,\n type Logger,\n type LogRecord,\n type Sink,\n} from \"@logtape/logtape\";\nimport { diag, type DiagLogger, DiagLogLevel } from \"@opentelemetry/api\";\nimport {\n type AnyValue,\n type LoggerProvider as LoggerProviderBase,\n type LogRecord as OTLogRecord,\n SeverityNumber,\n} from \"@opentelemetry/api-logs\";\nimport { OTLPLogExporter } from \"@opentelemetry/exporter-logs-otlp-http\";\nimport type { OTLPExporterNodeConfigBase } from \"@opentelemetry/otlp-exporter-base\";\nimport {\n defaultResource,\n resourceFromAttributes,\n} from \"@opentelemetry/resources\";\nimport {\n LoggerProvider,\n type LogRecordProcessor,\n SimpleLogRecordProcessor,\n} from \"@opentelemetry/sdk-logs\";\nimport { ATTR_SERVICE_NAME } from \"@opentelemetry/semantic-conventions\";\nimport metadata from \"./deno.json\" with { type: \"json\" };\n\n/**\n * Gets an environment variable value across different JavaScript runtimes.\n * @param name The environment variable name.\n * @returns The environment variable value, or undefined if not found.\n */\nfunction getEnvironmentVariable(name: string): string | undefined {\n // Deno runtime\n if (typeof Deno !== \"undefined\" && Deno.env) {\n try {\n return Deno.env.get(name);\n } catch {\n // Deno.env.get() can throw if permissions are not granted\n return undefined;\n }\n }\n\n // Node.js/Bun runtime\n if (\n typeof globalThis !== \"undefined\" && \"process\" in globalThis &&\n // @ts-ignore: process exists in Node.js/Bun\n typeof globalThis.process !== \"undefined\" &&\n // @ts-ignore: process.env exists in Node.js/Bun\n typeof globalThis.process.env === \"object\" &&\n // @ts-ignore: process.env exists in Node.js/Bun\n globalThis.process.env !== null\n ) {\n // @ts-ignore: process.env exists in Node.js/Bun\n return globalThis.process.env[name];\n }\n\n // Browser/other environments - no environment variables available\n return undefined;\n}\n\n/**\n * The OpenTelemetry logger provider.\n */\ntype ILoggerProvider = LoggerProviderBase & {\n /**\n * Adds a new {@link LogRecordProcessor} to this logger.\n * @param processor the new LogRecordProcessor to be added.\n */\n addLogRecordProcessor(processor: LogRecordProcessor): void;\n\n /**\n * Flush all buffered data and shut down the LoggerProvider and all registered\n * LogRecordProcessor.\n *\n * Returns a promise which is resolved when all flushes are complete.\n */\n shutdown?: () => Promise<void>;\n};\n\n/**\n * The way to render the object in the log record. If `\"json\"`,\n * the object is rendered as a JSON string. If `\"inspect\"`,\n * the object is rendered using `util.inspect` in Node.js/Bun, or\n * `Deno.inspect` in Deno.\n */\nexport type ObjectRenderer = \"json\" | \"inspect\";\n\ntype Message = (string | null | undefined)[];\n\n/**\n * Custom `body` attribute formatter.\n * @since 0.3.0\n */\nexport type BodyFormatter = (message: Message) => AnyValue;\n\n/**\n * Options for creating an OpenTelemetry sink.\n */\nexport interface OpenTelemetrySinkOptions {\n /**\n * The OpenTelemetry logger provider to use.\n */\n loggerProvider?: ILoggerProvider;\n\n /**\n * The way to render the message in the log record. If `\"string\"`,\n * the message is rendered as a single string with the values are\n * interpolated into the message. If `\"array\"`, the message is\n * rendered as an array of strings. `\"string\"` by default.\n *\n * Or even fully customizable with a {@link BodyFormatter} function.\n * @since 0.2.0\n */\n messageType?: \"string\" | \"array\" | BodyFormatter;\n\n /**\n * The way to render the object in the log record. If `\"json\"`,\n * the object is rendered as a JSON string. If `\"inspect\"`,\n * the object is rendered using `util.inspect` in Node.js/Bun, or\n * `Deno.inspect` in Deno. `\"inspect\"` by default.\n */\n objectRenderer?: ObjectRenderer;\n\n /**\n * Whether to log diagnostics. Diagnostic logs are logged to\n * the `[\"logtape\", \"meta\", \"otel\"]` category.\n * Turned off by default.\n */\n diagnostics?: boolean;\n\n /**\n * The OpenTelemetry OTLP exporter configuration to use.\n * Ignored if `loggerProvider` is provided.\n */\n otlpExporterConfig?: OTLPExporterNodeConfigBase;\n\n /**\n * The service name to use. If not provided, the service name is\n * taken from the `OTEL_SERVICE_NAME` environment variable.\n * Ignored if `loggerProvider` is provided.\n */\n serviceName?: string;\n}\n\n/**\n * Creates a sink that forwards log records to OpenTelemetry.\n * @param options Options for creating the sink.\n * @returns The sink.\n */\nexport function getOpenTelemetrySink(\n options: OpenTelemetrySinkOptions = {},\n): Sink {\n if (options.diagnostics) {\n diag.setLogger(new DiagLoggerAdaptor(), DiagLogLevel.DEBUG);\n }\n\n let loggerProvider: ILoggerProvider;\n if (options.loggerProvider == null) {\n const resource = defaultResource().merge(\n resourceFromAttributes({\n [ATTR_SERVICE_NAME]: options.serviceName ??\n getEnvironmentVariable(\"OTEL_SERVICE_NAME\"),\n }),\n );\n loggerProvider = new LoggerProvider({ resource });\n const otlpExporter = new OTLPLogExporter(options.otlpExporterConfig);\n loggerProvider.addLogRecordProcessor(\n // @ts-ignore: it works anyway...\n new SimpleLogRecordProcessor(otlpExporter),\n );\n } else {\n loggerProvider = options.loggerProvider;\n }\n const objectRenderer = options.objectRenderer ?? \"inspect\";\n const logger = loggerProvider.getLogger(metadata.name, metadata.version);\n const sink = (record: LogRecord) => {\n const { category, level, message, timestamp, properties } = record;\n if (\n category[0] === \"logtape\" && category[1] === \"meta\" &&\n category[2] === \"otel\"\n ) {\n return;\n }\n const severityNumber = mapLevelToSeverityNumber(level);\n const attributes = convertToAttributes(properties, objectRenderer);\n attributes[\"category\"] = [...category];\n logger.emit(\n {\n severityNumber,\n severityText: level,\n body: typeof options.messageType === \"function\"\n ? convertMessageToCustomBodyFormat(\n message,\n objectRenderer,\n options.messageType,\n )\n : options.messageType === \"array\"\n ? convertMessageToArray(message, objectRenderer)\n : convertMessageToString(message, objectRenderer),\n attributes,\n timestamp: new Date(timestamp),\n } satisfies OTLogRecord,\n );\n };\n if (loggerProvider.shutdown != null) {\n const shutdown = loggerProvider.shutdown.bind(loggerProvider);\n sink[Symbol.asyncDispose] = shutdown;\n }\n return sink;\n}\n\nfunction mapLevelToSeverityNumber(level: string): number {\n switch (level) {\n case \"trace\":\n return SeverityNumber.TRACE;\n case \"debug\":\n return SeverityNumber.DEBUG;\n case \"info\":\n return SeverityNumber.INFO;\n case \"warning\":\n return SeverityNumber.WARN;\n case \"error\":\n return SeverityNumber.ERROR;\n case \"fatal\":\n return SeverityNumber.FATAL;\n default:\n return SeverityNumber.UNSPECIFIED;\n }\n}\n\nfunction convertToAttributes(\n properties: Record<string, unknown>,\n objectRenderer: ObjectRenderer,\n): Record<string, AnyValue> {\n const attributes: Record<string, AnyValue> = {};\n for (const [name, value] of Object.entries(properties)) {\n const key = `attributes.${name}`;\n if (value == null) continue;\n if (Array.isArray(value)) {\n let t = null;\n for (const v of value) {\n if (v == null) continue;\n if (t != null && typeof v !== t) {\n attributes[key] = value.map((v) =>\n convertToString(v, objectRenderer)\n );\n break;\n }\n t = typeof v;\n }\n attributes[key] = value;\n } else {\n const encoded = convertToString(value, objectRenderer);\n if (encoded == null) continue;\n attributes[key] = encoded;\n }\n }\n return attributes;\n}\n\nfunction convertToString(\n value: unknown,\n objectRenderer: ObjectRenderer,\n): string | null | undefined {\n if (value === null || value === undefined || typeof value === \"string\") {\n return value;\n }\n if (objectRenderer === \"inspect\") return inspect(value);\n if (typeof value === \"number\" || typeof value === \"boolean\") {\n return value.toString();\n } else if (value instanceof Date) return value.toISOString();\n else return JSON.stringify(value);\n}\n\nfunction convertMessageToArray(\n message: readonly unknown[],\n objectRenderer: ObjectRenderer,\n): AnyValue {\n const body: (string | null | undefined)[] = [];\n for (let i = 0; i < message.length; i += 2) {\n const msg = message[i] as string;\n body.push(msg);\n if (message.length <= i + 1) break;\n const val = message[i + 1];\n body.push(convertToString(val, objectRenderer));\n }\n return body;\n}\n\nfunction convertMessageToString(\n message: readonly unknown[],\n objectRenderer: ObjectRenderer,\n): AnyValue {\n let body = \"\";\n for (let i = 0; i < message.length; i += 2) {\n const msg = message[i] as string;\n body += msg;\n if (message.length <= i + 1) break;\n const val = message[i + 1];\n const extra = convertToString(val, objectRenderer);\n body += extra ?? JSON.stringify(extra);\n }\n return body;\n}\n\nfunction convertMessageToCustomBodyFormat(\n message: readonly unknown[],\n objectRenderer: ObjectRenderer,\n bodyFormatter: BodyFormatter,\n): AnyValue {\n const body = message.map((msg) => convertToString(msg, objectRenderer));\n return bodyFormatter(body);\n}\n\n/**\n * A platform-specific inspect function. In Deno, this is {@link Deno.inspect},\n * and in Node.js/Bun it is {@link util.inspect}. If neither is available, it\n * falls back to {@link JSON.stringify}.\n *\n * @param value The value to inspect.\n * @returns The string representation of the value.\n */\nconst inspect: (value: unknown) => string =\n // @ts-ignore: Deno global\n \"Deno\" in globalThis && \"inspect\" in globalThis.Deno &&\n // @ts-ignore: Deno global\n typeof globalThis.Deno.inspect === \"function\"\n // @ts-ignore: Deno global\n ? globalThis.Deno.inspect\n // @ts-ignore: Node.js global\n : \"util\" in globalThis && \"inspect\" in globalThis.util &&\n // @ts-ignore: Node.js global\n globalThis.util.inspect === \"function\"\n // @ts-ignore: Node.js global\n ? globalThis.util.inspect\n : JSON.stringify;\n\nclass DiagLoggerAdaptor implements DiagLogger {\n logger: Logger;\n\n constructor() {\n this.logger = getLogger([\"logtape\", \"meta\", \"otel\"]);\n }\n\n #escape(msg: string): string {\n return msg.replaceAll(\"{\", \"{{\").replaceAll(\"}\", \"}}\");\n }\n\n error(msg: string, ...values: unknown[]): void {\n this.logger.error(`${this.#escape(msg)}: {values}`, { values });\n }\n\n warn(msg: string, ...values: unknown[]): void {\n this.logger.warn(`${this.#escape(msg)}: {values}`, { values });\n }\n\n info(msg: string, ...values: unknown[]): void {\n this.logger.info(`${this.#escape(msg)}: {values}`, { values });\n }\n\n debug(msg: string, ...values: unknown[]): void {\n this.logger.debug(`${this.#escape(msg)}: {values}`, { values });\n }\n\n verbose(msg: string, ...values: unknown[]): void {\n this.logger.debug(`${this.#escape(msg)}: {values}`, { values });\n }\n}\n"],"mappings":"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"}
package/mod.test.ts ADDED
@@ -0,0 +1,71 @@
1
+ // TODO: Add substantial tests for OpenTelemetry integration.
2
+ // Current tests only verify basic browser compatibility and that the sink
3
+ // can be created without errors. Future tests should include:
4
+ // - Actual log record processing and OpenTelemetry output verification
5
+ // - Integration with real OpenTelemetry collectors
6
+ // - Message formatting and attribute handling
7
+ // - Error handling scenarios
8
+ // - Performance testing
9
+
10
+ import { suite } from "@alinea/suite";
11
+ import { assertEquals } from "@std/assert";
12
+ import { getOpenTelemetrySink } from "./mod.ts";
13
+
14
+ const test = suite(import.meta);
15
+
16
+ test("getOpenTelemetrySink() creates sink without node:process dependency", () => {
17
+ // This test should pass in all environments (Deno, Node.js, browsers)
18
+ // without throwing errors about missing node:process
19
+ const sink = getOpenTelemetrySink();
20
+
21
+ assertEquals(typeof sink, "function");
22
+ });
23
+
24
+ test("getOpenTelemetrySink() works with explicit serviceName", () => {
25
+ const sink = getOpenTelemetrySink({
26
+ serviceName: "test-service",
27
+ });
28
+
29
+ assertEquals(typeof sink, "function");
30
+ });
31
+
32
+ test("getOpenTelemetrySink() handles missing environment variables gracefully", () => {
33
+ // Should not throw even if OTEL_SERVICE_NAME is not set
34
+ const sink = getOpenTelemetrySink({
35
+ // serviceName not provided, should fall back to env var
36
+ });
37
+
38
+ assertEquals(typeof sink, "function");
39
+ });
40
+
41
+ test("getOpenTelemetrySink() with diagnostics enabled", () => {
42
+ const sink = getOpenTelemetrySink({
43
+ diagnostics: true,
44
+ });
45
+
46
+ assertEquals(typeof sink, "function");
47
+ });
48
+
49
+ test("getOpenTelemetrySink() with custom messageType", () => {
50
+ const sink = getOpenTelemetrySink({
51
+ messageType: "array",
52
+ });
53
+
54
+ assertEquals(typeof sink, "function");
55
+ });
56
+
57
+ test("getOpenTelemetrySink() with custom objectRenderer", () => {
58
+ const sink = getOpenTelemetrySink({
59
+ objectRenderer: "json",
60
+ });
61
+
62
+ assertEquals(typeof sink, "function");
63
+ });
64
+
65
+ test("getOpenTelemetrySink() with custom bodyFormatter", () => {
66
+ const sink = getOpenTelemetrySink({
67
+ messageType: (message) => message.join(" "),
68
+ });
69
+
70
+ assertEquals(typeof sink, "function");
71
+ });
package/mod.ts CHANGED
@@ -23,9 +23,42 @@ import {
23
23
  SimpleLogRecordProcessor,
24
24
  } from "@opentelemetry/sdk-logs";
25
25
  import { ATTR_SERVICE_NAME } from "@opentelemetry/semantic-conventions";
26
- import process from "node:process";
27
26
  import metadata from "./deno.json" with { type: "json" };
28
27
 
28
+ /**
29
+ * Gets an environment variable value across different JavaScript runtimes.
30
+ * @param name The environment variable name.
31
+ * @returns The environment variable value, or undefined if not found.
32
+ */
33
+ function getEnvironmentVariable(name: string): string | undefined {
34
+ // Deno runtime
35
+ if (typeof Deno !== "undefined" && Deno.env) {
36
+ try {
37
+ return Deno.env.get(name);
38
+ } catch {
39
+ // Deno.env.get() can throw if permissions are not granted
40
+ return undefined;
41
+ }
42
+ }
43
+
44
+ // Node.js/Bun runtime
45
+ if (
46
+ typeof globalThis !== "undefined" && "process" in globalThis &&
47
+ // @ts-ignore: process exists in Node.js/Bun
48
+ typeof globalThis.process !== "undefined" &&
49
+ // @ts-ignore: process.env exists in Node.js/Bun
50
+ typeof globalThis.process.env === "object" &&
51
+ // @ts-ignore: process.env exists in Node.js/Bun
52
+ globalThis.process.env !== null
53
+ ) {
54
+ // @ts-ignore: process.env exists in Node.js/Bun
55
+ return globalThis.process.env[name];
56
+ }
57
+
58
+ // Browser/other environments - no environment variables available
59
+ return undefined;
60
+ }
61
+
29
62
  /**
30
63
  * The OpenTelemetry logger provider.
31
64
  */
@@ -127,7 +160,7 @@ export function getOpenTelemetrySink(
127
160
  const resource = defaultResource().merge(
128
161
  resourceFromAttributes({
129
162
  [ATTR_SERVICE_NAME]: options.serviceName ??
130
- process.env.OTEL_SERVICE_NAME,
163
+ getEnvironmentVariable("OTEL_SERVICE_NAME"),
131
164
  }),
132
165
  );
133
166
  loggerProvider = new LoggerProvider({ resource });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@logtape/otel",
3
- "version": "1.0.0-dev.258+b30f4857",
3
+ "version": "1.0.0-dev.261+9cda8a4b",
4
4
  "description": "LogTape OpenTelemetry sink",
5
5
  "keywords": [
6
6
  "LogTape",
@@ -42,7 +42,7 @@
42
42
  },
43
43
  "sideEffects": false,
44
44
  "peerDependencies": {
45
- "@logtape/logtape": "1.0.0-dev.258+b30f4857"
45
+ "@logtape/logtape": "1.0.0-dev.261+9cda8a4b"
46
46
  },
47
47
  "dependencies": {
48
48
  "@opentelemetry/api": "^1.9.0",
@@ -54,12 +54,17 @@
54
54
  "@opentelemetry/semantic-conventions": "^1.34.0"
55
55
  },
56
56
  "devDependencies": {
57
+ "@alinea/suite": "^0.6.3",
58
+ "@std/assert": "npm:@jsr/std__assert@^1.0.13",
57
59
  "@std/dotenv": "npm:@jsr/std__dotenv@^0.225.5",
58
60
  "tsdown": "^0.12.7",
59
61
  "typescript": "^5.8.3"
60
62
  },
61
63
  "scripts": {
62
64
  "build": "tsdown",
63
- "prepublish": "tsdown"
65
+ "prepublish": "tsdown",
66
+ "test": "tsdown && node --experimental-transform-types --test",
67
+ "test:bun": "tsdown && bun test",
68
+ "test:deno": "deno task test"
64
69
  }
65
70
  }
package/tsdown.config.ts CHANGED
@@ -6,6 +6,6 @@ export default defineConfig({
6
6
  sourcemap: true,
7
7
  },
8
8
  format: ["esm", "cjs"],
9
- platform: "node",
9
+ platform: "neutral",
10
10
  unbundle: true,
11
11
  });