@logtape/otel 0.1.0 → 0.3.0-dev.5
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/README.md +23 -3
- package/esm/_dnt.shims.js +57 -0
- package/esm/deno.js +3 -2
- package/esm/mod.js +56 -10
- package/package.json +1 -1
- package/script/_dnt.shims.js +60 -0
- package/script/deno.js +3 -2
- package/script/mod.js +79 -10
- package/types/_dnt.shims.d.ts +2 -0
- package/types/_dnt.shims.d.ts.map +1 -0
- package/types/deno.d.ts +1 -0
- package/types/mod.d.ts +34 -11
- package/types/mod.d.ts.map +1 -1
package/README.md
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
[![npm][npm badge]][npm]
|
|
6
6
|
[![GitHub Actions][GitHub Actions badge]][GitHub Actions]
|
|
7
7
|
|
|
8
|
-
This package provides an OpenTelemetry sink for [LogTape]. It allows you to
|
|
8
|
+
This package provides an [OpenTelemetry] sink for [LogTape]. It allows you to
|
|
9
9
|
send your LogTape logs to OpenTelemetry-compatible backends.
|
|
10
10
|
|
|
11
11
|
[JSR]: https://jsr.io/@logtape/otel
|
|
@@ -14,6 +14,7 @@ send your LogTape logs to OpenTelemetry-compatible backends.
|
|
|
14
14
|
[npm badge]: https://img.shields.io/npm/v/@logtape/otel?logo=npm
|
|
15
15
|
[GitHub Actions]: https://github.com/dahlia/logtape-otel/actions/workflows/main.yaml
|
|
16
16
|
[GitHub Actions badge]: https://github.com/dahlia/logtape-otel/actions/workflows/main.yaml/badge.svg
|
|
17
|
+
[OpenTelemetry]: https://opentelemetry.io/
|
|
17
18
|
[LogTape]: https://github.com/dahlia/logtape
|
|
18
19
|
|
|
19
20
|
|
|
@@ -109,7 +110,11 @@ await configure({
|
|
|
109
110
|
});
|
|
110
111
|
~~~~
|
|
111
112
|
|
|
113
|
+
For more information, see the documentation of the [`getOpenTelemetrySink()`]
|
|
114
|
+
function and [`OpenTelemetrySinkOptions`] type.
|
|
115
|
+
|
|
112
116
|
[`getOpenTelemetrySink()`]: https://jsr.io/@logtape/otel/doc/~/getOpenTelemetrySink
|
|
117
|
+
[`OpenTelemetrySinkOptions`]: https://jsr.io/@logtape/otel/doc/~/OpenTelemetrySinkOptions
|
|
113
118
|
[`LoggerProvider`]: https://open-telemetry.github.io/opentelemetry-js/classes/_opentelemetry_sdk_logs.LoggerProvider.html
|
|
114
119
|
|
|
115
120
|
|
|
@@ -146,7 +151,22 @@ needed.
|
|
|
146
151
|
Changelog
|
|
147
152
|
---------
|
|
148
153
|
|
|
149
|
-
Version 0.
|
|
150
|
-
|
|
154
|
+
### Version 0.3.0
|
|
155
|
+
|
|
156
|
+
To be released.
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
### Version 0.2.0
|
|
160
|
+
|
|
161
|
+
Released on August 26, 2024.
|
|
162
|
+
|
|
163
|
+
- The `OpenTelemetrySinkOptions` type is now an interface.
|
|
164
|
+
- Added `OpenTelemetrySinkOptions.messageType` option.
|
|
165
|
+
- Added `OpenTelemetrySinkOptions.objectRenderer` option. Now non-scalar
|
|
166
|
+
values are rendered using `util.inspect()` in Node.js/Bun and
|
|
167
|
+
`Deno.inspect()` in Deno by default.
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
### Version 0.1.0
|
|
151
171
|
|
|
152
172
|
Released on August 24, 2024. Initial release.
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
const dntGlobals = {};
|
|
2
|
+
export const dntGlobalThis = createMergeProxy(globalThis, dntGlobals);
|
|
3
|
+
function createMergeProxy(baseObj, extObj) {
|
|
4
|
+
return new Proxy(baseObj, {
|
|
5
|
+
get(_target, prop, _receiver) {
|
|
6
|
+
if (prop in extObj) {
|
|
7
|
+
return extObj[prop];
|
|
8
|
+
}
|
|
9
|
+
else {
|
|
10
|
+
return baseObj[prop];
|
|
11
|
+
}
|
|
12
|
+
},
|
|
13
|
+
set(_target, prop, value) {
|
|
14
|
+
if (prop in extObj) {
|
|
15
|
+
delete extObj[prop];
|
|
16
|
+
}
|
|
17
|
+
baseObj[prop] = value;
|
|
18
|
+
return true;
|
|
19
|
+
},
|
|
20
|
+
deleteProperty(_target, prop) {
|
|
21
|
+
let success = false;
|
|
22
|
+
if (prop in extObj) {
|
|
23
|
+
delete extObj[prop];
|
|
24
|
+
success = true;
|
|
25
|
+
}
|
|
26
|
+
if (prop in baseObj) {
|
|
27
|
+
delete baseObj[prop];
|
|
28
|
+
success = true;
|
|
29
|
+
}
|
|
30
|
+
return success;
|
|
31
|
+
},
|
|
32
|
+
ownKeys(_target) {
|
|
33
|
+
const baseKeys = Reflect.ownKeys(baseObj);
|
|
34
|
+
const extKeys = Reflect.ownKeys(extObj);
|
|
35
|
+
const extKeysSet = new Set(extKeys);
|
|
36
|
+
return [...baseKeys.filter((k) => !extKeysSet.has(k)), ...extKeys];
|
|
37
|
+
},
|
|
38
|
+
defineProperty(_target, prop, desc) {
|
|
39
|
+
if (prop in extObj) {
|
|
40
|
+
delete extObj[prop];
|
|
41
|
+
}
|
|
42
|
+
Reflect.defineProperty(baseObj, prop, desc);
|
|
43
|
+
return true;
|
|
44
|
+
},
|
|
45
|
+
getOwnPropertyDescriptor(_target, prop) {
|
|
46
|
+
if (prop in extObj) {
|
|
47
|
+
return Reflect.getOwnPropertyDescriptor(extObj, prop);
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
return Reflect.getOwnPropertyDescriptor(baseObj, prop);
|
|
51
|
+
}
|
|
52
|
+
},
|
|
53
|
+
has(_target, prop) {
|
|
54
|
+
return prop in extObj || prop in baseObj;
|
|
55
|
+
},
|
|
56
|
+
});
|
|
57
|
+
}
|
package/esm/deno.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export default {
|
|
2
2
|
"name": "@logtape/otel",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0-dev.5+7ed92c72",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": "./mod.ts"
|
|
@@ -15,7 +15,8 @@ export default {
|
|
|
15
15
|
"@opentelemetry/otlp-exporter-base": "npm:@opentelemetry/otlp-exporter-base@^0.52.1",
|
|
16
16
|
"@opentelemetry/resources": "npm:@opentelemetry/resources@^1.25.1",
|
|
17
17
|
"@opentelemetry/sdk-logs": "npm:@opentelemetry/sdk-logs@^0.52.1",
|
|
18
|
-
"@opentelemetry/semantic-conventions": "npm:@opentelemetry/semantic-conventions@^1.26.0"
|
|
18
|
+
"@opentelemetry/semantic-conventions": "npm:@opentelemetry/semantic-conventions@^1.26.0",
|
|
19
|
+
"@std/dotenv": "jsr:@std/dotenv@^0.225.1"
|
|
19
20
|
},
|
|
20
21
|
"tasks": {
|
|
21
22
|
"dnt": "deno run -A dnt.ts"
|
package/esm/mod.js
CHANGED
|
@@ -4,6 +4,7 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
|
|
|
4
4
|
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
5
5
|
};
|
|
6
6
|
var _DiagLoggerAdaptor_instances, _DiagLoggerAdaptor_escape;
|
|
7
|
+
import * as dntShim from "./_dnt.shims.js";
|
|
7
8
|
import { getLogger, } from "@logtape/logtape";
|
|
8
9
|
import { diag, DiagLogLevel } from "@opentelemetry/api";
|
|
9
10
|
import { SeverityNumber, } from "@opentelemetry/api-logs";
|
|
@@ -37,6 +38,7 @@ export function getOpenTelemetrySink(options = {}) {
|
|
|
37
38
|
else {
|
|
38
39
|
loggerProvider = options.loggerProvider;
|
|
39
40
|
}
|
|
41
|
+
const objectRenderer = options.objectRenderer ?? "inspect";
|
|
40
42
|
const logger = loggerProvider.getLogger(metadata.name, metadata.version);
|
|
41
43
|
const sink = (record) => {
|
|
42
44
|
const { category, level, message, timestamp, properties } = record;
|
|
@@ -45,13 +47,16 @@ export function getOpenTelemetrySink(options = {}) {
|
|
|
45
47
|
return;
|
|
46
48
|
}
|
|
47
49
|
const severityNumber = mapLevelToSeverityNumber(level);
|
|
48
|
-
const attributes = convertToAttributes(properties);
|
|
50
|
+
const attributes = convertToAttributes(properties, objectRenderer);
|
|
49
51
|
attributes["category"] = [...category];
|
|
50
|
-
const body = convertMessageToBody(message);
|
|
51
52
|
logger.emit({
|
|
52
53
|
severityNumber,
|
|
53
54
|
severityText: level,
|
|
54
|
-
body
|
|
55
|
+
body: typeof options.messageType === "function"
|
|
56
|
+
? convertMessageToCustomBodyFormat(message, objectRenderer, options.messageType)
|
|
57
|
+
: options.messageType === "array"
|
|
58
|
+
? convertMessageToArray(message, objectRenderer)
|
|
59
|
+
: convertMessageToString(message, objectRenderer),
|
|
55
60
|
attributes,
|
|
56
61
|
timestamp: new Date(timestamp),
|
|
57
62
|
});
|
|
@@ -78,7 +83,7 @@ function mapLevelToSeverityNumber(level) {
|
|
|
78
83
|
return SeverityNumber.UNSPECIFIED;
|
|
79
84
|
}
|
|
80
85
|
}
|
|
81
|
-
function convertToAttributes(properties) {
|
|
86
|
+
function convertToAttributes(properties, objectRenderer) {
|
|
82
87
|
const attributes = {};
|
|
83
88
|
for (const [name, value] of Object.entries(properties)) {
|
|
84
89
|
const key = `attributes.${name}`;
|
|
@@ -90,7 +95,7 @@ function convertToAttributes(properties) {
|
|
|
90
95
|
if (v == null)
|
|
91
96
|
continue;
|
|
92
97
|
if (t != null && typeof v !== t) {
|
|
93
|
-
attributes[key] = value.map(convertToString);
|
|
98
|
+
attributes[key] = value.map((v) => convertToString(v, objectRenderer));
|
|
94
99
|
break;
|
|
95
100
|
}
|
|
96
101
|
t = typeof v;
|
|
@@ -98,7 +103,7 @@ function convertToAttributes(properties) {
|
|
|
98
103
|
attributes[key] = value;
|
|
99
104
|
}
|
|
100
105
|
else {
|
|
101
|
-
const encoded = convertToString(value);
|
|
106
|
+
const encoded = convertToString(value, objectRenderer);
|
|
102
107
|
if (encoded == null)
|
|
103
108
|
continue;
|
|
104
109
|
attributes[key] = encoded;
|
|
@@ -106,11 +111,13 @@ function convertToAttributes(properties) {
|
|
|
106
111
|
}
|
|
107
112
|
return attributes;
|
|
108
113
|
}
|
|
109
|
-
function convertToString(value) {
|
|
114
|
+
function convertToString(value, objectRenderer) {
|
|
110
115
|
if (value === null || value === undefined || typeof value === "string") {
|
|
111
116
|
return value;
|
|
112
117
|
}
|
|
113
|
-
|
|
118
|
+
if (objectRenderer === "inspect")
|
|
119
|
+
return inspect(value);
|
|
120
|
+
if (typeof value === "number" || typeof value === "boolean") {
|
|
114
121
|
return value.toString();
|
|
115
122
|
}
|
|
116
123
|
else if (value instanceof Date)
|
|
@@ -118,7 +125,7 @@ function convertToString(value) {
|
|
|
118
125
|
else
|
|
119
126
|
return JSON.stringify(value);
|
|
120
127
|
}
|
|
121
|
-
function
|
|
128
|
+
function convertMessageToArray(message, objectRenderer) {
|
|
122
129
|
const body = [];
|
|
123
130
|
for (let i = 0; i < message.length; i += 2) {
|
|
124
131
|
const msg = message[i];
|
|
@@ -126,10 +133,49 @@ function convertMessageToBody(message) {
|
|
|
126
133
|
if (message.length <= i + 1)
|
|
127
134
|
break;
|
|
128
135
|
const val = message[i + 1];
|
|
129
|
-
body.push(convertToString(val));
|
|
136
|
+
body.push(convertToString(val, objectRenderer));
|
|
130
137
|
}
|
|
131
138
|
return body;
|
|
132
139
|
}
|
|
140
|
+
function convertMessageToString(message, objectRenderer) {
|
|
141
|
+
let body = "";
|
|
142
|
+
for (let i = 0; i < message.length; i += 2) {
|
|
143
|
+
const msg = message[i];
|
|
144
|
+
body += msg;
|
|
145
|
+
if (message.length <= i + 1)
|
|
146
|
+
break;
|
|
147
|
+
const val = message[i + 1];
|
|
148
|
+
const extra = convertToString(val, objectRenderer);
|
|
149
|
+
body += extra ?? JSON.stringify(extra);
|
|
150
|
+
}
|
|
151
|
+
return body;
|
|
152
|
+
}
|
|
153
|
+
function convertMessageToCustomBodyFormat(message, objectRenderer, bodyFormatter) {
|
|
154
|
+
const body = message.map((msg) => convertToString(msg, objectRenderer));
|
|
155
|
+
return bodyFormatter(body);
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* A platform-specific inspect function. In Deno, this is {@link Deno.inspect},
|
|
159
|
+
* and in Node.js/Bun it is {@link util.inspect}. If neither is available, it
|
|
160
|
+
* falls back to {@link JSON.stringify}.
|
|
161
|
+
*
|
|
162
|
+
* @param value The value to inspect.
|
|
163
|
+
* @returns The string representation of the value.
|
|
164
|
+
*/
|
|
165
|
+
const inspect =
|
|
166
|
+
// @ts-ignore: Deno global
|
|
167
|
+
"Deno" in dntShim.dntGlobalThis && "inspect" in globalThis.Deno &&
|
|
168
|
+
// @ts-ignore: Deno global
|
|
169
|
+
typeof globalThis.Deno.inspect === "function"
|
|
170
|
+
// @ts-ignore: Deno global
|
|
171
|
+
? globalThis.Deno.inspect
|
|
172
|
+
// @ts-ignore: Node.js global
|
|
173
|
+
: "util" in dntShim.dntGlobalThis && "inspect" in globalThis.util &&
|
|
174
|
+
// @ts-ignore: Node.js global
|
|
175
|
+
globalThis.util.inspect === "function"
|
|
176
|
+
// @ts-ignore: Node.js global
|
|
177
|
+
? globalThis.util.inspect
|
|
178
|
+
: JSON.stringify;
|
|
133
179
|
class DiagLoggerAdaptor {
|
|
134
180
|
constructor() {
|
|
135
181
|
_DiagLoggerAdaptor_instances.add(this);
|
package/package.json
CHANGED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.dntGlobalThis = void 0;
|
|
4
|
+
const dntGlobals = {};
|
|
5
|
+
exports.dntGlobalThis = createMergeProxy(globalThis, dntGlobals);
|
|
6
|
+
function createMergeProxy(baseObj, extObj) {
|
|
7
|
+
return new Proxy(baseObj, {
|
|
8
|
+
get(_target, prop, _receiver) {
|
|
9
|
+
if (prop in extObj) {
|
|
10
|
+
return extObj[prop];
|
|
11
|
+
}
|
|
12
|
+
else {
|
|
13
|
+
return baseObj[prop];
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
set(_target, prop, value) {
|
|
17
|
+
if (prop in extObj) {
|
|
18
|
+
delete extObj[prop];
|
|
19
|
+
}
|
|
20
|
+
baseObj[prop] = value;
|
|
21
|
+
return true;
|
|
22
|
+
},
|
|
23
|
+
deleteProperty(_target, prop) {
|
|
24
|
+
let success = false;
|
|
25
|
+
if (prop in extObj) {
|
|
26
|
+
delete extObj[prop];
|
|
27
|
+
success = true;
|
|
28
|
+
}
|
|
29
|
+
if (prop in baseObj) {
|
|
30
|
+
delete baseObj[prop];
|
|
31
|
+
success = true;
|
|
32
|
+
}
|
|
33
|
+
return success;
|
|
34
|
+
},
|
|
35
|
+
ownKeys(_target) {
|
|
36
|
+
const baseKeys = Reflect.ownKeys(baseObj);
|
|
37
|
+
const extKeys = Reflect.ownKeys(extObj);
|
|
38
|
+
const extKeysSet = new Set(extKeys);
|
|
39
|
+
return [...baseKeys.filter((k) => !extKeysSet.has(k)), ...extKeys];
|
|
40
|
+
},
|
|
41
|
+
defineProperty(_target, prop, desc) {
|
|
42
|
+
if (prop in extObj) {
|
|
43
|
+
delete extObj[prop];
|
|
44
|
+
}
|
|
45
|
+
Reflect.defineProperty(baseObj, prop, desc);
|
|
46
|
+
return true;
|
|
47
|
+
},
|
|
48
|
+
getOwnPropertyDescriptor(_target, prop) {
|
|
49
|
+
if (prop in extObj) {
|
|
50
|
+
return Reflect.getOwnPropertyDescriptor(extObj, prop);
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
53
|
+
return Reflect.getOwnPropertyDescriptor(baseObj, prop);
|
|
54
|
+
}
|
|
55
|
+
},
|
|
56
|
+
has(_target, prop) {
|
|
57
|
+
return prop in extObj || prop in baseObj;
|
|
58
|
+
},
|
|
59
|
+
});
|
|
60
|
+
}
|
package/script/deno.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.default = {
|
|
4
4
|
"name": "@logtape/otel",
|
|
5
|
-
"version": "0.
|
|
5
|
+
"version": "0.3.0-dev.5+7ed92c72",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"exports": {
|
|
8
8
|
".": "./mod.ts"
|
|
@@ -17,7 +17,8 @@ exports.default = {
|
|
|
17
17
|
"@opentelemetry/otlp-exporter-base": "npm:@opentelemetry/otlp-exporter-base@^0.52.1",
|
|
18
18
|
"@opentelemetry/resources": "npm:@opentelemetry/resources@^1.25.1",
|
|
19
19
|
"@opentelemetry/sdk-logs": "npm:@opentelemetry/sdk-logs@^0.52.1",
|
|
20
|
-
"@opentelemetry/semantic-conventions": "npm:@opentelemetry/semantic-conventions@^1.26.0"
|
|
20
|
+
"@opentelemetry/semantic-conventions": "npm:@opentelemetry/semantic-conventions@^1.26.0",
|
|
21
|
+
"@std/dotenv": "jsr:@std/dotenv@^0.225.1"
|
|
21
22
|
},
|
|
22
23
|
"tasks": {
|
|
23
24
|
"dnt": "deno run -A dnt.ts"
|
package/script/mod.js
CHANGED
|
@@ -1,4 +1,27 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
2
25
|
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
|
|
3
26
|
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
|
4
27
|
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
@@ -10,6 +33,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
10
33
|
var _DiagLoggerAdaptor_instances, _DiagLoggerAdaptor_escape;
|
|
11
34
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
35
|
exports.getOpenTelemetrySink = getOpenTelemetrySink;
|
|
36
|
+
const dntShim = __importStar(require("./_dnt.shims.js"));
|
|
13
37
|
const logtape_1 = require("@logtape/logtape");
|
|
14
38
|
const api_1 = require("@opentelemetry/api");
|
|
15
39
|
const api_logs_1 = require("@opentelemetry/api-logs");
|
|
@@ -43,6 +67,7 @@ function getOpenTelemetrySink(options = {}) {
|
|
|
43
67
|
else {
|
|
44
68
|
loggerProvider = options.loggerProvider;
|
|
45
69
|
}
|
|
70
|
+
const objectRenderer = options.objectRenderer ?? "inspect";
|
|
46
71
|
const logger = loggerProvider.getLogger(deno_js_1.default.name, deno_js_1.default.version);
|
|
47
72
|
const sink = (record) => {
|
|
48
73
|
const { category, level, message, timestamp, properties } = record;
|
|
@@ -51,13 +76,16 @@ function getOpenTelemetrySink(options = {}) {
|
|
|
51
76
|
return;
|
|
52
77
|
}
|
|
53
78
|
const severityNumber = mapLevelToSeverityNumber(level);
|
|
54
|
-
const attributes = convertToAttributes(properties);
|
|
79
|
+
const attributes = convertToAttributes(properties, objectRenderer);
|
|
55
80
|
attributes["category"] = [...category];
|
|
56
|
-
const body = convertMessageToBody(message);
|
|
57
81
|
logger.emit({
|
|
58
82
|
severityNumber,
|
|
59
83
|
severityText: level,
|
|
60
|
-
body
|
|
84
|
+
body: typeof options.messageType === "function"
|
|
85
|
+
? convertMessageToCustomBodyFormat(message, objectRenderer, options.messageType)
|
|
86
|
+
: options.messageType === "array"
|
|
87
|
+
? convertMessageToArray(message, objectRenderer)
|
|
88
|
+
: convertMessageToString(message, objectRenderer),
|
|
61
89
|
attributes,
|
|
62
90
|
timestamp: new Date(timestamp),
|
|
63
91
|
});
|
|
@@ -84,7 +112,7 @@ function mapLevelToSeverityNumber(level) {
|
|
|
84
112
|
return api_logs_1.SeverityNumber.UNSPECIFIED;
|
|
85
113
|
}
|
|
86
114
|
}
|
|
87
|
-
function convertToAttributes(properties) {
|
|
115
|
+
function convertToAttributes(properties, objectRenderer) {
|
|
88
116
|
const attributes = {};
|
|
89
117
|
for (const [name, value] of Object.entries(properties)) {
|
|
90
118
|
const key = `attributes.${name}`;
|
|
@@ -96,7 +124,7 @@ function convertToAttributes(properties) {
|
|
|
96
124
|
if (v == null)
|
|
97
125
|
continue;
|
|
98
126
|
if (t != null && typeof v !== t) {
|
|
99
|
-
attributes[key] = value.map(convertToString);
|
|
127
|
+
attributes[key] = value.map((v) => convertToString(v, objectRenderer));
|
|
100
128
|
break;
|
|
101
129
|
}
|
|
102
130
|
t = typeof v;
|
|
@@ -104,7 +132,7 @@ function convertToAttributes(properties) {
|
|
|
104
132
|
attributes[key] = value;
|
|
105
133
|
}
|
|
106
134
|
else {
|
|
107
|
-
const encoded = convertToString(value);
|
|
135
|
+
const encoded = convertToString(value, objectRenderer);
|
|
108
136
|
if (encoded == null)
|
|
109
137
|
continue;
|
|
110
138
|
attributes[key] = encoded;
|
|
@@ -112,11 +140,13 @@ function convertToAttributes(properties) {
|
|
|
112
140
|
}
|
|
113
141
|
return attributes;
|
|
114
142
|
}
|
|
115
|
-
function convertToString(value) {
|
|
143
|
+
function convertToString(value, objectRenderer) {
|
|
116
144
|
if (value === null || value === undefined || typeof value === "string") {
|
|
117
145
|
return value;
|
|
118
146
|
}
|
|
119
|
-
|
|
147
|
+
if (objectRenderer === "inspect")
|
|
148
|
+
return inspect(value);
|
|
149
|
+
if (typeof value === "number" || typeof value === "boolean") {
|
|
120
150
|
return value.toString();
|
|
121
151
|
}
|
|
122
152
|
else if (value instanceof Date)
|
|
@@ -124,7 +154,7 @@ function convertToString(value) {
|
|
|
124
154
|
else
|
|
125
155
|
return JSON.stringify(value);
|
|
126
156
|
}
|
|
127
|
-
function
|
|
157
|
+
function convertMessageToArray(message, objectRenderer) {
|
|
128
158
|
const body = [];
|
|
129
159
|
for (let i = 0; i < message.length; i += 2) {
|
|
130
160
|
const msg = message[i];
|
|
@@ -132,10 +162,49 @@ function convertMessageToBody(message) {
|
|
|
132
162
|
if (message.length <= i + 1)
|
|
133
163
|
break;
|
|
134
164
|
const val = message[i + 1];
|
|
135
|
-
body.push(convertToString(val));
|
|
165
|
+
body.push(convertToString(val, objectRenderer));
|
|
136
166
|
}
|
|
137
167
|
return body;
|
|
138
168
|
}
|
|
169
|
+
function convertMessageToString(message, objectRenderer) {
|
|
170
|
+
let body = "";
|
|
171
|
+
for (let i = 0; i < message.length; i += 2) {
|
|
172
|
+
const msg = message[i];
|
|
173
|
+
body += msg;
|
|
174
|
+
if (message.length <= i + 1)
|
|
175
|
+
break;
|
|
176
|
+
const val = message[i + 1];
|
|
177
|
+
const extra = convertToString(val, objectRenderer);
|
|
178
|
+
body += extra ?? JSON.stringify(extra);
|
|
179
|
+
}
|
|
180
|
+
return body;
|
|
181
|
+
}
|
|
182
|
+
function convertMessageToCustomBodyFormat(message, objectRenderer, bodyFormatter) {
|
|
183
|
+
const body = message.map((msg) => convertToString(msg, objectRenderer));
|
|
184
|
+
return bodyFormatter(body);
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* A platform-specific inspect function. In Deno, this is {@link Deno.inspect},
|
|
188
|
+
* and in Node.js/Bun it is {@link util.inspect}. If neither is available, it
|
|
189
|
+
* falls back to {@link JSON.stringify}.
|
|
190
|
+
*
|
|
191
|
+
* @param value The value to inspect.
|
|
192
|
+
* @returns The string representation of the value.
|
|
193
|
+
*/
|
|
194
|
+
const inspect =
|
|
195
|
+
// @ts-ignore: Deno global
|
|
196
|
+
"Deno" in dntShim.dntGlobalThis && "inspect" in globalThis.Deno &&
|
|
197
|
+
// @ts-ignore: Deno global
|
|
198
|
+
typeof globalThis.Deno.inspect === "function"
|
|
199
|
+
// @ts-ignore: Deno global
|
|
200
|
+
? globalThis.Deno.inspect
|
|
201
|
+
// @ts-ignore: Node.js global
|
|
202
|
+
: "util" in dntShim.dntGlobalThis && "inspect" in globalThis.util &&
|
|
203
|
+
// @ts-ignore: Node.js global
|
|
204
|
+
globalThis.util.inspect === "function"
|
|
205
|
+
// @ts-ignore: Node.js global
|
|
206
|
+
? globalThis.util.inspect
|
|
207
|
+
: JSON.stringify;
|
|
139
208
|
class DiagLoggerAdaptor {
|
|
140
209
|
constructor() {
|
|
141
210
|
_DiagLoggerAdaptor_instances.add(this);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"_dnt.shims.d.ts","sourceRoot":"","sources":["../src/_dnt.shims.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,aAAa,gCAA2C,CAAC"}
|
package/types/deno.d.ts
CHANGED
package/types/mod.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type Sink } from "@logtape/logtape";
|
|
2
|
-
import { type LoggerProvider as LoggerProviderBase } from "@opentelemetry/api-logs";
|
|
2
|
+
import { type AnyValue, type LoggerProvider as LoggerProviderBase } from "@opentelemetry/api-logs";
|
|
3
3
|
import type { OTLPExporterNodeConfigBase } from "@opentelemetry/otlp-exporter-base";
|
|
4
4
|
import { type LogRecordProcessor } from "@opentelemetry/sdk-logs";
|
|
5
5
|
/**
|
|
@@ -19,38 +19,61 @@ type ILoggerProvider = LoggerProviderBase & {
|
|
|
19
19
|
*/
|
|
20
20
|
shutdown?: () => Promise<void>;
|
|
21
21
|
};
|
|
22
|
+
/**
|
|
23
|
+
* The way to render the object in the log record. If `"json"`,
|
|
24
|
+
* the object is rendered as a JSON string. If `"inspect"`,
|
|
25
|
+
* the object is rendered using `util.inspect` in Node.js/Bun, or
|
|
26
|
+
* `Deno.inspect` in Deno.
|
|
27
|
+
*/
|
|
28
|
+
export type ObjectRenderer = "json" | "inspect";
|
|
29
|
+
type Message = (string | null | undefined)[];
|
|
30
|
+
/**
|
|
31
|
+
* Custom `body` attribute formatter
|
|
32
|
+
*/
|
|
33
|
+
export type BodyFormatter = (message: Message) => AnyValue;
|
|
22
34
|
/**
|
|
23
35
|
* Options for creating an OpenTelemetry sink.
|
|
24
36
|
*/
|
|
25
|
-
export
|
|
37
|
+
export interface OpenTelemetrySinkOptions {
|
|
26
38
|
/**
|
|
27
39
|
* The OpenTelemetry logger provider to use.
|
|
28
40
|
*/
|
|
29
|
-
loggerProvider
|
|
41
|
+
loggerProvider?: ILoggerProvider;
|
|
30
42
|
/**
|
|
31
|
-
*
|
|
32
|
-
* the
|
|
43
|
+
* The way to render the message in the log record. If `"string"`,
|
|
44
|
+
* the message is rendered as a single string with the values are
|
|
45
|
+
* interpolated into the message. If `"array"`, the message is
|
|
46
|
+
* rendered as an array of strings. `"string"` by default.
|
|
47
|
+
* @since 0.2.0
|
|
48
|
+
*
|
|
49
|
+
* Or even fully customizable with a {@link BodyFormatter} function.
|
|
33
50
|
*/
|
|
34
|
-
|
|
35
|
-
} | {
|
|
51
|
+
messageType?: "string" | "array" | BodyFormatter;
|
|
36
52
|
/**
|
|
37
|
-
* The
|
|
53
|
+
* The way to render the object in the log record. If `"json"`,
|
|
54
|
+
* the object is rendered as a JSON string. If `"inspect"`,
|
|
55
|
+
* the object is rendered using `util.inspect` in Node.js/Bun, or
|
|
56
|
+
* `Deno.inspect` in Deno. `"inspect"` by default.
|
|
38
57
|
*/
|
|
39
|
-
|
|
58
|
+
objectRenderer?: ObjectRenderer;
|
|
40
59
|
/**
|
|
41
60
|
* Whether to log diagnostics. Diagnostic logs are logged to
|
|
42
61
|
* the `["logtape", "meta", "otel"]` category.
|
|
62
|
+
* Turned off by default.
|
|
43
63
|
*/
|
|
44
64
|
diagnostics?: boolean;
|
|
45
65
|
/**
|
|
46
66
|
* The OpenTelemetry OTLP exporter configuration to use.
|
|
67
|
+
* Ignored if `loggerProvider` is provided.
|
|
47
68
|
*/
|
|
48
69
|
otlpExporterConfig?: OTLPExporterNodeConfigBase;
|
|
49
70
|
/**
|
|
50
|
-
* The service name to use.
|
|
71
|
+
* The service name to use. If not provided, the service name is
|
|
72
|
+
* taken from the `OTEL_SERVICE_NAME` environment variable.
|
|
73
|
+
* Ignored if `loggerProvider` is provided.
|
|
51
74
|
*/
|
|
52
75
|
serviceName?: string;
|
|
53
|
-
}
|
|
76
|
+
}
|
|
54
77
|
/**
|
|
55
78
|
* Creates a sink that forwards log records to OpenTelemetry.
|
|
56
79
|
* @param options Options for creating the sink.
|
package/types/mod.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mod.d.ts","sourceRoot":"","sources":["../src/mod.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"mod.d.ts","sourceRoot":"","sources":["../src/mod.ts"],"names":[],"mappings":"AACA,OAAO,EAIL,KAAK,IAAI,EACV,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EACL,KAAK,QAAQ,EACb,KAAK,cAAc,IAAI,kBAAkB,EAG1C,MAAM,yBAAyB,CAAC;AAEjC,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,mCAAmC,CAAC;AAEpF,OAAO,EAEL,KAAK,kBAAkB,EAExB,MAAM,yBAAyB,CAAC;AAKjC;;GAEG;AACH,KAAK,eAAe,GAAG,kBAAkB,GAAG;IAC1C;;;OAGG;IACH,qBAAqB,CAAC,SAAS,EAAE,kBAAkB,GAAG,IAAI,CAAC;IAE3D;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAChC,CAAC;AAEF;;;;;GAKG;AACH,MAAM,MAAM,cAAc,GAAG,MAAM,GAAG,SAAS,CAAC;AAEhD,KAAK,OAAO,GAAG,CAAC,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC,EAAE,CAAC;AAE7C;;GAEG;AACH,MAAM,MAAM,aAAa,GAAG,CAAC,OAAO,EAAE,OAAO,KAAK,QAAQ,CAAC;AAE3D;;GAEG;AACH,MAAM,WAAW,wBAAwB;IACvC;;OAEG;IACH,cAAc,CAAC,EAAE,eAAe,CAAC;IAEjC;;;;;;;;OAQG;IACH,WAAW,CAAC,EAAE,QAAQ,GAAG,OAAO,GAAG,aAAa,CAAC;IAEjD;;;;;OAKG;IACH,cAAc,CAAC,EAAE,cAAc,CAAC;IAEhC;;;;OAIG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;IAEtB;;;OAGG;IACH,kBAAkB,CAAC,EAAE,0BAA0B,CAAC;IAEhD;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAClC,OAAO,GAAE,wBAA6B,GACrC,IAAI,CA0DN"}
|