@logtape/otel 1.2.1 → 1.2.3
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/dist/deno.cjs +1 -1
- package/dist/deno.js +1 -1
- package/dist/deno.js.map +1 -1
- package/package.json +5 -2
- package/deno.json +0 -21
- package/sample.ts +0 -27
- package/src/mod.test.ts +0 -71
- package/src/mod.ts +0 -369
- package/tsdown.config.ts +0 -11
package/dist/deno.cjs
CHANGED
package/dist/deno.js
CHANGED
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.2.
|
|
1
|
+
{"version":3,"file":"deno.js","names":[],"sources":["../deno.json"],"sourcesContent":["{\n \"name\": \"@logtape/otel\",\n \"version\": \"1.2.3\",\n \"license\": \"MIT\",\n \"exports\": {\n \".\": \"./src/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,eACN;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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@logtape/otel",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.3",
|
|
4
4
|
"description": "LogTape OpenTelemetry sink",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"LogTape",
|
|
@@ -41,8 +41,11 @@
|
|
|
41
41
|
"./package.json": "./package.json"
|
|
42
42
|
},
|
|
43
43
|
"sideEffects": false,
|
|
44
|
+
"files": [
|
|
45
|
+
"dist/"
|
|
46
|
+
],
|
|
44
47
|
"peerDependencies": {
|
|
45
|
-
"@logtape/logtape": "^1.2.
|
|
48
|
+
"@logtape/logtape": "^1.2.3"
|
|
46
49
|
},
|
|
47
50
|
"dependencies": {
|
|
48
51
|
"@opentelemetry/api": "^1.9.0",
|
package/deno.json
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@logtape/otel",
|
|
3
|
-
"version": "1.2.1",
|
|
4
|
-
"license": "MIT",
|
|
5
|
-
"exports": {
|
|
6
|
-
".": "./src/mod.ts"
|
|
7
|
-
},
|
|
8
|
-
"imports": {
|
|
9
|
-
"@opentelemetry/api": "npm:@opentelemetry/api@^1.9.0",
|
|
10
|
-
"@opentelemetry/api-logs": "npm:@opentelemetry/api-logs@^0.202.0",
|
|
11
|
-
"@opentelemetry/exporter-logs-otlp-http": "npm:@opentelemetry/exporter-logs-otlp-http@^0.202.0",
|
|
12
|
-
"@opentelemetry/otlp-exporter-base": "npm:@opentelemetry/otlp-exporter-base@^0.202.0",
|
|
13
|
-
"@opentelemetry/resources": "npm:@opentelemetry/resources@^2.0.1",
|
|
14
|
-
"@opentelemetry/sdk-logs": "npm:@opentelemetry/sdk-logs@^0.202.0",
|
|
15
|
-
"@opentelemetry/semantic-conventions": "npm:@opentelemetry/semantic-conventions@^1.34.0"
|
|
16
|
-
},
|
|
17
|
-
"tasks": {
|
|
18
|
-
"build": "pnpm build",
|
|
19
|
-
"test": "deno test --allow-net --allow-env"
|
|
20
|
-
}
|
|
21
|
-
}
|
package/sample.ts
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import { configure, getConsoleSink, getLogger } from "@logtape/logtape";
|
|
2
|
-
import { getOpenTelemetrySink } from "@logtape/otel";
|
|
3
|
-
import "@std/dotenv/load";
|
|
4
|
-
|
|
5
|
-
await configure({
|
|
6
|
-
sinks: {
|
|
7
|
-
console: getConsoleSink(),
|
|
8
|
-
otel: getOpenTelemetrySink({
|
|
9
|
-
messageType: "array",
|
|
10
|
-
diagnostics: true,
|
|
11
|
-
}),
|
|
12
|
-
},
|
|
13
|
-
filters: {},
|
|
14
|
-
loggers: [
|
|
15
|
-
{ category: [], sinks: ["console", "otel"], lowestLevel: "debug" },
|
|
16
|
-
],
|
|
17
|
-
});
|
|
18
|
-
|
|
19
|
-
getLogger(["test", "app"]).debug("hello {world} at {timestamp}", {
|
|
20
|
-
world: "debug",
|
|
21
|
-
timestamp: new Date(),
|
|
22
|
-
});
|
|
23
|
-
getLogger(["test", "app"]).info("hello {world} with {object}", {
|
|
24
|
-
world: "info",
|
|
25
|
-
object: new Uint8Array([1, 2, 3]),
|
|
26
|
-
});
|
|
27
|
-
getLogger(["test", "app"]).warn("hello {world}", { world: "warning" });
|
package/src/mod.test.ts
DELETED
|
@@ -1,71 +0,0 @@
|
|
|
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/src/mod.ts
DELETED
|
@@ -1,369 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
getLogger,
|
|
3
|
-
type Logger,
|
|
4
|
-
type LogRecord,
|
|
5
|
-
type Sink,
|
|
6
|
-
} from "@logtape/logtape";
|
|
7
|
-
import { diag, type DiagLogger, DiagLogLevel } from "@opentelemetry/api";
|
|
8
|
-
import {
|
|
9
|
-
type AnyValue,
|
|
10
|
-
type LoggerProvider as LoggerProviderBase,
|
|
11
|
-
type LogRecord as OTLogRecord,
|
|
12
|
-
SeverityNumber,
|
|
13
|
-
} from "@opentelemetry/api-logs";
|
|
14
|
-
import { OTLPLogExporter } from "@opentelemetry/exporter-logs-otlp-http";
|
|
15
|
-
import type { OTLPExporterNodeConfigBase } from "@opentelemetry/otlp-exporter-base";
|
|
16
|
-
import {
|
|
17
|
-
defaultResource,
|
|
18
|
-
resourceFromAttributes,
|
|
19
|
-
} from "@opentelemetry/resources";
|
|
20
|
-
import {
|
|
21
|
-
LoggerProvider,
|
|
22
|
-
type LogRecordProcessor,
|
|
23
|
-
SimpleLogRecordProcessor,
|
|
24
|
-
} from "@opentelemetry/sdk-logs";
|
|
25
|
-
import { ATTR_SERVICE_NAME } from "@opentelemetry/semantic-conventions";
|
|
26
|
-
import metadata from "../deno.json" with { type: "json" };
|
|
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
|
-
|
|
62
|
-
/**
|
|
63
|
-
* The OpenTelemetry logger provider.
|
|
64
|
-
*/
|
|
65
|
-
type ILoggerProvider = LoggerProviderBase & {
|
|
66
|
-
/**
|
|
67
|
-
* Adds a new {@link LogRecordProcessor} to this logger.
|
|
68
|
-
* @param processor the new LogRecordProcessor to be added.
|
|
69
|
-
*/
|
|
70
|
-
addLogRecordProcessor(processor: LogRecordProcessor): void;
|
|
71
|
-
|
|
72
|
-
/**
|
|
73
|
-
* Flush all buffered data and shut down the LoggerProvider and all registered
|
|
74
|
-
* LogRecordProcessor.
|
|
75
|
-
*
|
|
76
|
-
* Returns a promise which is resolved when all flushes are complete.
|
|
77
|
-
*/
|
|
78
|
-
shutdown?: () => Promise<void>;
|
|
79
|
-
};
|
|
80
|
-
|
|
81
|
-
/**
|
|
82
|
-
* The way to render the object in the log record. If `"json"`,
|
|
83
|
-
* the object is rendered as a JSON string. If `"inspect"`,
|
|
84
|
-
* the object is rendered using `util.inspect` in Node.js/Bun, or
|
|
85
|
-
* `Deno.inspect` in Deno.
|
|
86
|
-
*/
|
|
87
|
-
export type ObjectRenderer = "json" | "inspect";
|
|
88
|
-
|
|
89
|
-
type Message = (string | null | undefined)[];
|
|
90
|
-
|
|
91
|
-
/**
|
|
92
|
-
* Custom `body` attribute formatter.
|
|
93
|
-
* @since 0.3.0
|
|
94
|
-
*/
|
|
95
|
-
export type BodyFormatter = (message: Message) => AnyValue;
|
|
96
|
-
|
|
97
|
-
/**
|
|
98
|
-
* Options for creating an OpenTelemetry sink.
|
|
99
|
-
*/
|
|
100
|
-
export interface OpenTelemetrySinkOptions {
|
|
101
|
-
/**
|
|
102
|
-
* The OpenTelemetry logger provider to use.
|
|
103
|
-
*/
|
|
104
|
-
loggerProvider?: ILoggerProvider;
|
|
105
|
-
|
|
106
|
-
/**
|
|
107
|
-
* The way to render the message in the log record. If `"string"`,
|
|
108
|
-
* the message is rendered as a single string with the values are
|
|
109
|
-
* interpolated into the message. If `"array"`, the message is
|
|
110
|
-
* rendered as an array of strings. `"string"` by default.
|
|
111
|
-
*
|
|
112
|
-
* Or even fully customizable with a {@link BodyFormatter} function.
|
|
113
|
-
* @since 0.2.0
|
|
114
|
-
*/
|
|
115
|
-
messageType?: "string" | "array" | BodyFormatter;
|
|
116
|
-
|
|
117
|
-
/**
|
|
118
|
-
* The way to render the object in the log record. If `"json"`,
|
|
119
|
-
* the object is rendered as a JSON string. If `"inspect"`,
|
|
120
|
-
* the object is rendered using `util.inspect` in Node.js/Bun, or
|
|
121
|
-
* `Deno.inspect` in Deno. `"inspect"` by default.
|
|
122
|
-
*/
|
|
123
|
-
objectRenderer?: ObjectRenderer;
|
|
124
|
-
|
|
125
|
-
/**
|
|
126
|
-
* Whether to log diagnostics. Diagnostic logs are logged to
|
|
127
|
-
* the `["logtape", "meta", "otel"]` category.
|
|
128
|
-
* Turned off by default.
|
|
129
|
-
*/
|
|
130
|
-
diagnostics?: boolean;
|
|
131
|
-
|
|
132
|
-
/**
|
|
133
|
-
* The OpenTelemetry OTLP exporter configuration to use.
|
|
134
|
-
* Ignored if `loggerProvider` is provided.
|
|
135
|
-
*/
|
|
136
|
-
otlpExporterConfig?: OTLPExporterNodeConfigBase;
|
|
137
|
-
|
|
138
|
-
/**
|
|
139
|
-
* The service name to use. If not provided, the service name is
|
|
140
|
-
* taken from the `OTEL_SERVICE_NAME` environment variable.
|
|
141
|
-
* Ignored if `loggerProvider` is provided.
|
|
142
|
-
*/
|
|
143
|
-
serviceName?: string;
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
/**
|
|
147
|
-
* Creates a sink that forwards log records to OpenTelemetry.
|
|
148
|
-
* @param options Options for creating the sink.
|
|
149
|
-
* @returns The sink.
|
|
150
|
-
*/
|
|
151
|
-
export function getOpenTelemetrySink(
|
|
152
|
-
options: OpenTelemetrySinkOptions = {},
|
|
153
|
-
): Sink {
|
|
154
|
-
if (options.diagnostics) {
|
|
155
|
-
diag.setLogger(new DiagLoggerAdaptor(), DiagLogLevel.DEBUG);
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
let loggerProvider: ILoggerProvider;
|
|
159
|
-
if (options.loggerProvider == null) {
|
|
160
|
-
const resource = defaultResource().merge(
|
|
161
|
-
resourceFromAttributes({
|
|
162
|
-
[ATTR_SERVICE_NAME]: options.serviceName ??
|
|
163
|
-
getEnvironmentVariable("OTEL_SERVICE_NAME"),
|
|
164
|
-
}),
|
|
165
|
-
);
|
|
166
|
-
loggerProvider = new LoggerProvider({ resource });
|
|
167
|
-
const otlpExporter = new OTLPLogExporter(options.otlpExporterConfig);
|
|
168
|
-
loggerProvider.addLogRecordProcessor(
|
|
169
|
-
// @ts-ignore: it works anyway...
|
|
170
|
-
new SimpleLogRecordProcessor(otlpExporter),
|
|
171
|
-
);
|
|
172
|
-
} else {
|
|
173
|
-
loggerProvider = options.loggerProvider;
|
|
174
|
-
}
|
|
175
|
-
const objectRenderer = options.objectRenderer ?? "inspect";
|
|
176
|
-
const logger = loggerProvider.getLogger(metadata.name, metadata.version);
|
|
177
|
-
const sink = (record: LogRecord) => {
|
|
178
|
-
const { category, level, message, timestamp, properties } = record;
|
|
179
|
-
if (
|
|
180
|
-
category[0] === "logtape" && category[1] === "meta" &&
|
|
181
|
-
category[2] === "otel"
|
|
182
|
-
) {
|
|
183
|
-
return;
|
|
184
|
-
}
|
|
185
|
-
const severityNumber = mapLevelToSeverityNumber(level);
|
|
186
|
-
const attributes = convertToAttributes(properties, objectRenderer);
|
|
187
|
-
attributes["category"] = [...category];
|
|
188
|
-
logger.emit(
|
|
189
|
-
{
|
|
190
|
-
severityNumber,
|
|
191
|
-
severityText: level,
|
|
192
|
-
body: typeof options.messageType === "function"
|
|
193
|
-
? convertMessageToCustomBodyFormat(
|
|
194
|
-
message,
|
|
195
|
-
objectRenderer,
|
|
196
|
-
options.messageType,
|
|
197
|
-
)
|
|
198
|
-
: options.messageType === "array"
|
|
199
|
-
? convertMessageToArray(message, objectRenderer)
|
|
200
|
-
: convertMessageToString(message, objectRenderer),
|
|
201
|
-
attributes,
|
|
202
|
-
timestamp: new Date(timestamp),
|
|
203
|
-
} satisfies OTLogRecord,
|
|
204
|
-
);
|
|
205
|
-
};
|
|
206
|
-
if (loggerProvider.shutdown != null) {
|
|
207
|
-
const shutdown = loggerProvider.shutdown.bind(loggerProvider);
|
|
208
|
-
sink[Symbol.asyncDispose] = shutdown;
|
|
209
|
-
}
|
|
210
|
-
return sink;
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
function mapLevelToSeverityNumber(level: string): number {
|
|
214
|
-
switch (level) {
|
|
215
|
-
case "trace":
|
|
216
|
-
return SeverityNumber.TRACE;
|
|
217
|
-
case "debug":
|
|
218
|
-
return SeverityNumber.DEBUG;
|
|
219
|
-
case "info":
|
|
220
|
-
return SeverityNumber.INFO;
|
|
221
|
-
case "warning":
|
|
222
|
-
return SeverityNumber.WARN;
|
|
223
|
-
case "error":
|
|
224
|
-
return SeverityNumber.ERROR;
|
|
225
|
-
case "fatal":
|
|
226
|
-
return SeverityNumber.FATAL;
|
|
227
|
-
default:
|
|
228
|
-
return SeverityNumber.UNSPECIFIED;
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
function convertToAttributes(
|
|
233
|
-
properties: Record<string, unknown>,
|
|
234
|
-
objectRenderer: ObjectRenderer,
|
|
235
|
-
): Record<string, AnyValue> {
|
|
236
|
-
const attributes: Record<string, AnyValue> = {};
|
|
237
|
-
for (const [name, value] of Object.entries(properties)) {
|
|
238
|
-
const key = `attributes.${name}`;
|
|
239
|
-
if (value == null) continue;
|
|
240
|
-
if (Array.isArray(value)) {
|
|
241
|
-
let t = null;
|
|
242
|
-
for (const v of value) {
|
|
243
|
-
if (v == null) continue;
|
|
244
|
-
if (t != null && typeof v !== t) {
|
|
245
|
-
attributes[key] = value.map((v) =>
|
|
246
|
-
convertToString(v, objectRenderer)
|
|
247
|
-
);
|
|
248
|
-
break;
|
|
249
|
-
}
|
|
250
|
-
t = typeof v;
|
|
251
|
-
}
|
|
252
|
-
attributes[key] = value;
|
|
253
|
-
} else {
|
|
254
|
-
const encoded = convertToString(value, objectRenderer);
|
|
255
|
-
if (encoded == null) continue;
|
|
256
|
-
attributes[key] = encoded;
|
|
257
|
-
}
|
|
258
|
-
}
|
|
259
|
-
return attributes;
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
function convertToString(
|
|
263
|
-
value: unknown,
|
|
264
|
-
objectRenderer: ObjectRenderer,
|
|
265
|
-
): string | null | undefined {
|
|
266
|
-
if (value === null || value === undefined || typeof value === "string") {
|
|
267
|
-
return value;
|
|
268
|
-
}
|
|
269
|
-
if (objectRenderer === "inspect") return inspect(value);
|
|
270
|
-
if (typeof value === "number" || typeof value === "boolean") {
|
|
271
|
-
return value.toString();
|
|
272
|
-
} else if (value instanceof Date) return value.toISOString();
|
|
273
|
-
else return JSON.stringify(value);
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
function convertMessageToArray(
|
|
277
|
-
message: readonly unknown[],
|
|
278
|
-
objectRenderer: ObjectRenderer,
|
|
279
|
-
): AnyValue {
|
|
280
|
-
const body: (string | null | undefined)[] = [];
|
|
281
|
-
for (let i = 0; i < message.length; i += 2) {
|
|
282
|
-
const msg = message[i] as string;
|
|
283
|
-
body.push(msg);
|
|
284
|
-
if (message.length <= i + 1) break;
|
|
285
|
-
const val = message[i + 1];
|
|
286
|
-
body.push(convertToString(val, objectRenderer));
|
|
287
|
-
}
|
|
288
|
-
return body;
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
function convertMessageToString(
|
|
292
|
-
message: readonly unknown[],
|
|
293
|
-
objectRenderer: ObjectRenderer,
|
|
294
|
-
): AnyValue {
|
|
295
|
-
let body = "";
|
|
296
|
-
for (let i = 0; i < message.length; i += 2) {
|
|
297
|
-
const msg = message[i] as string;
|
|
298
|
-
body += msg;
|
|
299
|
-
if (message.length <= i + 1) break;
|
|
300
|
-
const val = message[i + 1];
|
|
301
|
-
const extra = convertToString(val, objectRenderer);
|
|
302
|
-
body += extra ?? JSON.stringify(extra);
|
|
303
|
-
}
|
|
304
|
-
return body;
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
function convertMessageToCustomBodyFormat(
|
|
308
|
-
message: readonly unknown[],
|
|
309
|
-
objectRenderer: ObjectRenderer,
|
|
310
|
-
bodyFormatter: BodyFormatter,
|
|
311
|
-
): AnyValue {
|
|
312
|
-
const body = message.map((msg) => convertToString(msg, objectRenderer));
|
|
313
|
-
return bodyFormatter(body);
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
/**
|
|
317
|
-
* A platform-specific inspect function. In Deno, this is {@link Deno.inspect},
|
|
318
|
-
* and in Node.js/Bun it is {@link util.inspect}. If neither is available, it
|
|
319
|
-
* falls back to {@link JSON.stringify}.
|
|
320
|
-
*
|
|
321
|
-
* @param value The value to inspect.
|
|
322
|
-
* @returns The string representation of the value.
|
|
323
|
-
*/
|
|
324
|
-
const inspect: (value: unknown) => string =
|
|
325
|
-
// @ts-ignore: Deno global
|
|
326
|
-
"Deno" in globalThis && "inspect" in globalThis.Deno &&
|
|
327
|
-
// @ts-ignore: Deno global
|
|
328
|
-
typeof globalThis.Deno.inspect === "function"
|
|
329
|
-
// @ts-ignore: Deno global
|
|
330
|
-
? globalThis.Deno.inspect
|
|
331
|
-
// @ts-ignore: Node.js global
|
|
332
|
-
: "util" in globalThis && "inspect" in globalThis.util &&
|
|
333
|
-
// @ts-ignore: Node.js global
|
|
334
|
-
globalThis.util.inspect === "function"
|
|
335
|
-
// @ts-ignore: Node.js global
|
|
336
|
-
? globalThis.util.inspect
|
|
337
|
-
: JSON.stringify;
|
|
338
|
-
|
|
339
|
-
class DiagLoggerAdaptor implements DiagLogger {
|
|
340
|
-
logger: Logger;
|
|
341
|
-
|
|
342
|
-
constructor() {
|
|
343
|
-
this.logger = getLogger(["logtape", "meta", "otel"]);
|
|
344
|
-
}
|
|
345
|
-
|
|
346
|
-
#escape(msg: string): string {
|
|
347
|
-
return msg.replaceAll("{", "{{").replaceAll("}", "}}");
|
|
348
|
-
}
|
|
349
|
-
|
|
350
|
-
error(msg: string, ...values: unknown[]): void {
|
|
351
|
-
this.logger.error(`${this.#escape(msg)}: {values}`, { values });
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
warn(msg: string, ...values: unknown[]): void {
|
|
355
|
-
this.logger.warn(`${this.#escape(msg)}: {values}`, { values });
|
|
356
|
-
}
|
|
357
|
-
|
|
358
|
-
info(msg: string, ...values: unknown[]): void {
|
|
359
|
-
this.logger.info(`${this.#escape(msg)}: {values}`, { values });
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
debug(msg: string, ...values: unknown[]): void {
|
|
363
|
-
this.logger.debug(`${this.#escape(msg)}: {values}`, { values });
|
|
364
|
-
}
|
|
365
|
-
|
|
366
|
-
verbose(msg: string, ...values: unknown[]): void {
|
|
367
|
-
this.logger.debug(`${this.#escape(msg)}: {values}`, { values });
|
|
368
|
-
}
|
|
369
|
-
}
|