@loglayer/transport-sumo-logic 2.1.6 → 2.1.7
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/index.cjs +140 -132
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +78 -71
- package/dist/index.d.ts +78 -71
- package/dist/index.js +117 -133
- package/dist/index.js.map +1 -1
- package/package.json +7 -7
package/dist/index.cjs
CHANGED
|
@@ -1,137 +1,145 @@
|
|
|
1
|
-
|
|
2
|
-
var
|
|
3
|
-
var
|
|
4
|
-
var
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
super(config);
|
|
18
|
-
this.url = config.url;
|
|
19
|
-
this.useCompression = _nullishCoalesce(config.useCompression, () => ( true));
|
|
20
|
-
this.sourceCategory = config.sourceCategory;
|
|
21
|
-
this.sourceName = config.sourceName;
|
|
22
|
-
this.sourceHost = config.sourceHost;
|
|
23
|
-
this.fields = _nullishCoalesce(config.fields, () => ( {}));
|
|
24
|
-
this.headers = _nullishCoalesce(config.headers, () => ( {}));
|
|
25
|
-
this.maxRetries = _nullishCoalesce(_optionalChain([config, 'access', _ => _.retryConfig, 'optionalAccess', _2 => _2.maxRetries]), () => ( 3));
|
|
26
|
-
this.initialRetryMs = _nullishCoalesce(_optionalChain([config, 'access', _3 => _3.retryConfig, 'optionalAccess', _4 => _4.initialRetryMs]), () => ( 1e3));
|
|
27
|
-
this.onError = config.onError;
|
|
28
|
-
this.messageField = _nullishCoalesce(config.messageField, () => ( "message"));
|
|
29
|
-
}
|
|
30
|
-
/**
|
|
31
|
-
* Compresses data using gzip compression.
|
|
32
|
-
*/
|
|
33
|
-
async compressData(data) {
|
|
34
|
-
const stream = new CompressionStream("gzip");
|
|
35
|
-
const writer = stream.writable.getWriter();
|
|
36
|
-
const textEncoder = new TextEncoder();
|
|
37
|
-
const chunks = [];
|
|
38
|
-
await writer.write(textEncoder.encode(data));
|
|
39
|
-
await writer.close();
|
|
40
|
-
const reader = stream.readable.getReader();
|
|
41
|
-
while (true) {
|
|
42
|
-
const { value, done } = await reader.read();
|
|
43
|
-
if (done) break;
|
|
44
|
-
chunks.push(value);
|
|
45
|
-
}
|
|
46
|
-
const totalLength = chunks.reduce((sum, chunk) => sum + chunk.length, 0);
|
|
47
|
-
const result = new Uint8Array(totalLength);
|
|
48
|
-
let offset = 0;
|
|
49
|
-
for (const chunk of chunks) {
|
|
50
|
-
result.set(chunk, offset);
|
|
51
|
-
offset += chunk.length;
|
|
52
|
-
}
|
|
53
|
-
return result;
|
|
54
|
-
}
|
|
55
|
-
/**
|
|
56
|
-
* Handles errors by calling the onError callback if defined
|
|
57
|
-
*/
|
|
58
|
-
handleError(error) {
|
|
59
|
-
if (this.onError) {
|
|
60
|
-
this.onError(error);
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
/**
|
|
64
|
-
* Sends logs to SumoLogic with retry logic, compression, and size validation.
|
|
65
|
-
*/
|
|
66
|
-
async sendToSumoLogic(payload, retryCount = 0) {
|
|
67
|
-
try {
|
|
68
|
-
const rawSize = new TextEncoder().encode(payload).length;
|
|
69
|
-
if (rawSize > MAX_PAYLOAD_SIZE) {
|
|
70
|
-
const error = `Payload size exceeds maximum of ${MAX_PAYLOAD_SIZE} bytes. Size: ${rawSize} bytes`;
|
|
71
|
-
this.handleError(error);
|
|
72
|
-
return;
|
|
73
|
-
}
|
|
74
|
-
let compressedPayload;
|
|
75
|
-
if (this.useCompression) {
|
|
76
|
-
compressedPayload = await this.compressData(payload);
|
|
77
|
-
if (compressedPayload.length > MAX_PAYLOAD_SIZE) {
|
|
78
|
-
const error = `Compressed payload size exceeds maximum of ${MAX_PAYLOAD_SIZE} bytes. Size: ${compressedPayload.length} bytes`;
|
|
79
|
-
this.handleError(error);
|
|
80
|
-
return;
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
const headers = {
|
|
84
|
-
...this.headers,
|
|
85
|
-
"Content-Type": "application/json"
|
|
86
|
-
};
|
|
87
|
-
if (this.sourceCategory) {
|
|
88
|
-
headers["X-Sumo-Category"] = this.sourceCategory;
|
|
89
|
-
}
|
|
90
|
-
if (this.sourceName) {
|
|
91
|
-
headers["X-Sumo-Name"] = this.sourceName;
|
|
92
|
-
}
|
|
93
|
-
if (this.sourceHost) {
|
|
94
|
-
headers["X-Sumo-Host"] = this.sourceHost;
|
|
95
|
-
}
|
|
96
|
-
if (this.useCompression) {
|
|
97
|
-
headers["Content-Encoding"] = "gzip";
|
|
98
|
-
}
|
|
99
|
-
if (Object.keys(this.fields).length > 0) {
|
|
100
|
-
headers["X-Sumo-Fields"] = Object.entries(this.fields).map(([key, value]) => `${key}=${value}`).join(",");
|
|
101
|
-
}
|
|
102
|
-
const response = await fetch(this.url, {
|
|
103
|
-
method: "POST",
|
|
104
|
-
headers,
|
|
105
|
-
body: this.useCompression ? compressedPayload : payload
|
|
106
|
-
});
|
|
107
|
-
if (!response.ok) {
|
|
108
|
-
throw new Error(`HTTP error! status: ${response.status}`);
|
|
109
|
-
}
|
|
110
|
-
} catch (error) {
|
|
111
|
-
if (retryCount < this.maxRetries) {
|
|
112
|
-
const delay = this.initialRetryMs * 2 ** retryCount;
|
|
113
|
-
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
114
|
-
return this.sendToSumoLogic(payload, retryCount + 1);
|
|
115
|
-
}
|
|
116
|
-
this.handleError(error instanceof Error ? error : String(error));
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
shipToLogger({ logLevel, messages, data, hasData }) {
|
|
120
|
-
const stringMessages = messages.filter((msg) => typeof msg === "string");
|
|
121
|
-
const message = stringMessages.join(" ");
|
|
122
|
-
const payload = {
|
|
123
|
-
severity: logLevel,
|
|
124
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
125
|
-
...data && hasData ? data : {}
|
|
126
|
-
};
|
|
127
|
-
if (message) {
|
|
128
|
-
payload[this.messageField] = message;
|
|
129
|
-
}
|
|
130
|
-
this.sendToSumoLogic(JSON.stringify(payload));
|
|
131
|
-
return messages;
|
|
132
|
-
}
|
|
1
|
+
//#region rolldown:runtime
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __copyProps = (to, from, except, desc) => {
|
|
9
|
+
if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
|
|
10
|
+
key = keys[i];
|
|
11
|
+
if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
|
|
12
|
+
get: ((k) => from[k]).bind(null, key),
|
|
13
|
+
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
133
17
|
};
|
|
18
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
|
|
19
|
+
value: mod,
|
|
20
|
+
enumerable: true
|
|
21
|
+
}) : target, mod));
|
|
134
22
|
|
|
23
|
+
//#endregion
|
|
24
|
+
let __loglayer_transport = require("@loglayer/transport");
|
|
25
|
+
__loglayer_transport = __toESM(__loglayer_transport);
|
|
135
26
|
|
|
27
|
+
//#region src/SumoLogicTransport.ts
|
|
28
|
+
const MAX_PAYLOAD_SIZE = 1e6;
|
|
29
|
+
var SumoLogicTransport = class extends __loglayer_transport.LoggerlessTransport {
|
|
30
|
+
url;
|
|
31
|
+
useCompression;
|
|
32
|
+
sourceCategory;
|
|
33
|
+
sourceName;
|
|
34
|
+
sourceHost;
|
|
35
|
+
fields;
|
|
36
|
+
headers;
|
|
37
|
+
maxRetries;
|
|
38
|
+
initialRetryMs;
|
|
39
|
+
onError;
|
|
40
|
+
messageField;
|
|
41
|
+
constructor(config) {
|
|
42
|
+
super(config);
|
|
43
|
+
this.url = config.url;
|
|
44
|
+
this.useCompression = config.useCompression ?? true;
|
|
45
|
+
this.sourceCategory = config.sourceCategory;
|
|
46
|
+
this.sourceName = config.sourceName;
|
|
47
|
+
this.sourceHost = config.sourceHost;
|
|
48
|
+
this.fields = config.fields ?? {};
|
|
49
|
+
this.headers = config.headers ?? {};
|
|
50
|
+
this.maxRetries = config.retryConfig?.maxRetries ?? 3;
|
|
51
|
+
this.initialRetryMs = config.retryConfig?.initialRetryMs ?? 1e3;
|
|
52
|
+
this.onError = config.onError;
|
|
53
|
+
this.messageField = config.messageField ?? "message";
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Compresses data using gzip compression.
|
|
57
|
+
*/
|
|
58
|
+
async compressData(data) {
|
|
59
|
+
const stream = new CompressionStream("gzip");
|
|
60
|
+
const writer = stream.writable.getWriter();
|
|
61
|
+
const textEncoder = new TextEncoder();
|
|
62
|
+
const chunks = [];
|
|
63
|
+
await writer.write(textEncoder.encode(data));
|
|
64
|
+
await writer.close();
|
|
65
|
+
const reader = stream.readable.getReader();
|
|
66
|
+
while (true) {
|
|
67
|
+
const { value, done } = await reader.read();
|
|
68
|
+
if (done) break;
|
|
69
|
+
chunks.push(value);
|
|
70
|
+
}
|
|
71
|
+
const totalLength = chunks.reduce((sum, chunk) => sum + chunk.length, 0);
|
|
72
|
+
const result = new Uint8Array(totalLength);
|
|
73
|
+
let offset = 0;
|
|
74
|
+
for (const chunk of chunks) {
|
|
75
|
+
result.set(chunk, offset);
|
|
76
|
+
offset += chunk.length;
|
|
77
|
+
}
|
|
78
|
+
return result;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Handles errors by calling the onError callback if defined
|
|
82
|
+
*/
|
|
83
|
+
handleError(error) {
|
|
84
|
+
if (this.onError) this.onError(error);
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Sends logs to SumoLogic with retry logic, compression, and size validation.
|
|
88
|
+
*/
|
|
89
|
+
async sendToSumoLogic(payload, retryCount = 0) {
|
|
90
|
+
try {
|
|
91
|
+
const rawSize = new TextEncoder().encode(payload).length;
|
|
92
|
+
if (rawSize > MAX_PAYLOAD_SIZE) {
|
|
93
|
+
const error = `Payload size exceeds maximum of ${MAX_PAYLOAD_SIZE} bytes. Size: ${rawSize} bytes`;
|
|
94
|
+
this.handleError(error);
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
let compressedPayload;
|
|
98
|
+
if (this.useCompression) {
|
|
99
|
+
compressedPayload = await this.compressData(payload);
|
|
100
|
+
if (compressedPayload.length > MAX_PAYLOAD_SIZE) {
|
|
101
|
+
const error = `Compressed payload size exceeds maximum of ${MAX_PAYLOAD_SIZE} bytes. Size: ${compressedPayload.length} bytes`;
|
|
102
|
+
this.handleError(error);
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
const headers = {
|
|
107
|
+
...this.headers,
|
|
108
|
+
"Content-Type": "application/json"
|
|
109
|
+
};
|
|
110
|
+
if (this.sourceCategory) headers["X-Sumo-Category"] = this.sourceCategory;
|
|
111
|
+
if (this.sourceName) headers["X-Sumo-Name"] = this.sourceName;
|
|
112
|
+
if (this.sourceHost) headers["X-Sumo-Host"] = this.sourceHost;
|
|
113
|
+
if (this.useCompression) headers["Content-Encoding"] = "gzip";
|
|
114
|
+
if (Object.keys(this.fields).length > 0) headers["X-Sumo-Fields"] = Object.entries(this.fields).map(([key, value]) => `${key}=${value}`).join(",");
|
|
115
|
+
const response = await fetch(this.url, {
|
|
116
|
+
method: "POST",
|
|
117
|
+
headers,
|
|
118
|
+
body: this.useCompression ? compressedPayload : payload
|
|
119
|
+
});
|
|
120
|
+
if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`);
|
|
121
|
+
} catch (error) {
|
|
122
|
+
if (retryCount < this.maxRetries) {
|
|
123
|
+
const delay = this.initialRetryMs * 2 ** retryCount;
|
|
124
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
125
|
+
return this.sendToSumoLogic(payload, retryCount + 1);
|
|
126
|
+
}
|
|
127
|
+
this.handleError(error instanceof Error ? error : String(error));
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
shipToLogger({ logLevel, messages, data, hasData }) {
|
|
131
|
+
const message = messages.filter((msg) => typeof msg === "string").join(" ");
|
|
132
|
+
const payload = {
|
|
133
|
+
severity: logLevel,
|
|
134
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
135
|
+
...data && hasData ? data : {}
|
|
136
|
+
};
|
|
137
|
+
if (message) payload[this.messageField] = message;
|
|
138
|
+
this.sendToSumoLogic(JSON.stringify(payload));
|
|
139
|
+
return messages;
|
|
140
|
+
}
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
//#endregion
|
|
136
144
|
exports.SumoLogicTransport = SumoLogicTransport;
|
|
137
145
|
//# sourceMappingURL=index.cjs.map
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/home/runner/work/loglayer/loglayer/packages/transports/sumo-logic/dist/index.cjs","../src/SumoLogicTransport.ts"],"names":[],"mappings":"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","file":"/home/runner/work/loglayer/loglayer/packages/transports/sumo-logic/dist/index.cjs","sourcesContent":[null,"import { LoggerlessTransport, type LoggerlessTransportConfig, type LogLayerTransportParams } from \"@loglayer/transport\";\n\ninterface SumoLogicTransportConfig extends LoggerlessTransportConfig {\n /**\n * The URL of your HTTP Source endpoint\n */\n url: string;\n\n /**\n * Whether to use gzip compression (defaults to true)\n */\n useCompression?: boolean;\n\n /**\n * Source category to assign to the logs (optional)\n */\n sourceCategory?: string;\n\n /**\n * Source name to assign to the logs (optional)\n */\n sourceName?: string;\n\n /**\n * Source host to assign to the logs (optional)\n */\n sourceHost?: string;\n\n /**\n * Fields to be added as X-Sumo-Fields header (optional)\n * Will be formatted as a comma-separated list of key=value pairs\n */\n fields?: Record<string, string>;\n\n /**\n * Custom headers to be added to the request (optional)\n */\n headers?: Record<string, string>;\n\n /**\n * Retry configuration (optional)\n */\n retryConfig?: {\n maxRetries?: number;\n initialRetryMs?: number;\n };\n\n /**\n * Optional callback for error handling\n */\n onError?: (error: Error | string) => void;\n\n /**\n * Field name to use for the message (defaults to \"message\")\n */\n messageField?: string;\n}\n\nconst MAX_PAYLOAD_SIZE = 1000000; // 1MB limit as per SumoLogic docs\n\nexport class SumoLogicTransport extends LoggerlessTransport {\n private url: string;\n private useCompression: boolean;\n private sourceCategory?: string;\n private sourceName?: string;\n private sourceHost?: string;\n private fields: Record<string, string>;\n private headers: Record<string, string>;\n private maxRetries: number;\n private initialRetryMs: number;\n private onError?: (error: Error | string) => void;\n private messageField: string;\n\n constructor(config: SumoLogicTransportConfig) {\n super(config);\n this.url = config.url;\n this.useCompression = config.useCompression ?? true;\n this.sourceCategory = config.sourceCategory;\n this.sourceName = config.sourceName;\n this.sourceHost = config.sourceHost;\n this.fields = config.fields ?? {};\n this.headers = config.headers ?? {};\n this.maxRetries = config.retryConfig?.maxRetries ?? 3;\n this.initialRetryMs = config.retryConfig?.initialRetryMs ?? 1000;\n this.onError = config.onError;\n this.messageField = config.messageField ?? \"message\";\n }\n\n /**\n * Compresses data using gzip compression.\n */\n private async compressData(data: string): Promise<Uint8Array> {\n const stream = new CompressionStream(\"gzip\");\n const writer = stream.writable.getWriter();\n const textEncoder = new TextEncoder();\n const chunks: Uint8Array[] = [];\n\n await writer.write(textEncoder.encode(data));\n await writer.close();\n\n const reader = stream.readable.getReader();\n\n while (true) {\n const { value, done } = await reader.read();\n if (done) break;\n chunks.push(value);\n }\n\n const totalLength = chunks.reduce((sum, chunk) => sum + chunk.length, 0);\n const result = new Uint8Array(totalLength);\n let offset = 0;\n\n for (const chunk of chunks) {\n result.set(chunk, offset);\n offset += chunk.length;\n }\n\n return result;\n }\n\n /**\n * Handles errors by calling the onError callback if defined\n */\n private handleError(error: Error | string): void {\n if (this.onError) {\n this.onError(error);\n }\n }\n\n /**\n * Sends logs to SumoLogic with retry logic, compression, and size validation.\n */\n private async sendToSumoLogic(payload: string, retryCount = 0): Promise<void> {\n try {\n // Check payload size before compression\n const rawSize = new TextEncoder().encode(payload).length;\n if (rawSize > MAX_PAYLOAD_SIZE) {\n const error = `Payload size exceeds maximum of ${MAX_PAYLOAD_SIZE} bytes. Size: ${rawSize} bytes`;\n this.handleError(error);\n return;\n }\n\n let compressedPayload: Uint8Array | undefined;\n if (this.useCompression) {\n compressedPayload = await this.compressData(payload);\n if (compressedPayload.length > MAX_PAYLOAD_SIZE) {\n const error = `Compressed payload size exceeds maximum of ${MAX_PAYLOAD_SIZE} bytes. Size: ${compressedPayload.length} bytes`;\n this.handleError(error);\n return;\n }\n }\n\n const headers: Record<string, string> = {\n ...this.headers,\n \"Content-Type\": \"application/json\",\n };\n\n if (this.sourceCategory) {\n headers[\"X-Sumo-Category\"] = this.sourceCategory;\n }\n if (this.sourceName) {\n headers[\"X-Sumo-Name\"] = this.sourceName;\n }\n if (this.sourceHost) {\n headers[\"X-Sumo-Host\"] = this.sourceHost;\n }\n if (this.useCompression) {\n headers[\"Content-Encoding\"] = \"gzip\";\n }\n if (Object.keys(this.fields).length > 0) {\n headers[\"X-Sumo-Fields\"] = Object.entries(this.fields)\n .map(([key, value]) => `${key}=${value}`)\n .join(\",\");\n }\n\n const response = (await fetch(this.url, {\n method: \"POST\",\n headers,\n body: this.useCompression ? compressedPayload : payload,\n })) as Response;\n\n if (!response.ok) {\n throw new Error(`HTTP error! status: ${response.status}`);\n }\n } catch (error) {\n if (retryCount < this.maxRetries) {\n const delay = this.initialRetryMs * 2 ** retryCount;\n await new Promise((resolve) => setTimeout(resolve, delay));\n return this.sendToSumoLogic(payload, retryCount + 1);\n }\n this.handleError(error instanceof Error ? error : String(error));\n }\n }\n\n shipToLogger({ logLevel, messages, data, hasData }: LogLayerTransportParams): any[] {\n // Only use the string messages for content\n const stringMessages = messages.filter((msg) => typeof msg === \"string\");\n const message = stringMessages.join(\" \");\n\n const payload = {\n severity: logLevel,\n timestamp: new Date().toISOString(),\n ...(data && hasData ? data : {}),\n };\n\n if (message) {\n payload[this.messageField] = message;\n }\n\n this.sendToSumoLogic(JSON.stringify(payload));\n\n return messages;\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"index.cjs","names":["LoggerlessTransport","chunks: Uint8Array[]","compressedPayload: Uint8Array | undefined","headers: Record<string, string>"],"sources":["../src/SumoLogicTransport.ts"],"sourcesContent":["import { LoggerlessTransport, type LoggerlessTransportConfig, type LogLayerTransportParams } from \"@loglayer/transport\";\n\ninterface SumoLogicTransportConfig extends LoggerlessTransportConfig {\n /**\n * The URL of your HTTP Source endpoint\n */\n url: string;\n\n /**\n * Whether to use gzip compression (defaults to true)\n */\n useCompression?: boolean;\n\n /**\n * Source category to assign to the logs (optional)\n */\n sourceCategory?: string;\n\n /**\n * Source name to assign to the logs (optional)\n */\n sourceName?: string;\n\n /**\n * Source host to assign to the logs (optional)\n */\n sourceHost?: string;\n\n /**\n * Fields to be added as X-Sumo-Fields header (optional)\n * Will be formatted as a comma-separated list of key=value pairs\n */\n fields?: Record<string, string>;\n\n /**\n * Custom headers to be added to the request (optional)\n */\n headers?: Record<string, string>;\n\n /**\n * Retry configuration (optional)\n */\n retryConfig?: {\n maxRetries?: number;\n initialRetryMs?: number;\n };\n\n /**\n * Optional callback for error handling\n */\n onError?: (error: Error | string) => void;\n\n /**\n * Field name to use for the message (defaults to \"message\")\n */\n messageField?: string;\n}\n\nconst MAX_PAYLOAD_SIZE = 1000000; // 1MB limit as per SumoLogic docs\n\nexport class SumoLogicTransport extends LoggerlessTransport {\n private url: string;\n private useCompression: boolean;\n private sourceCategory?: string;\n private sourceName?: string;\n private sourceHost?: string;\n private fields: Record<string, string>;\n private headers: Record<string, string>;\n private maxRetries: number;\n private initialRetryMs: number;\n private onError?: (error: Error | string) => void;\n private messageField: string;\n\n constructor(config: SumoLogicTransportConfig) {\n super(config);\n this.url = config.url;\n this.useCompression = config.useCompression ?? true;\n this.sourceCategory = config.sourceCategory;\n this.sourceName = config.sourceName;\n this.sourceHost = config.sourceHost;\n this.fields = config.fields ?? {};\n this.headers = config.headers ?? {};\n this.maxRetries = config.retryConfig?.maxRetries ?? 3;\n this.initialRetryMs = config.retryConfig?.initialRetryMs ?? 1000;\n this.onError = config.onError;\n this.messageField = config.messageField ?? \"message\";\n }\n\n /**\n * Compresses data using gzip compression.\n */\n private async compressData(data: string): Promise<Uint8Array> {\n const stream = new CompressionStream(\"gzip\");\n const writer = stream.writable.getWriter();\n const textEncoder = new TextEncoder();\n const chunks: Uint8Array[] = [];\n\n await writer.write(textEncoder.encode(data));\n await writer.close();\n\n const reader = stream.readable.getReader();\n\n while (true) {\n const { value, done } = await reader.read();\n if (done) break;\n chunks.push(value);\n }\n\n const totalLength = chunks.reduce((sum, chunk) => sum + chunk.length, 0);\n const result = new Uint8Array(totalLength);\n let offset = 0;\n\n for (const chunk of chunks) {\n result.set(chunk, offset);\n offset += chunk.length;\n }\n\n return result;\n }\n\n /**\n * Handles errors by calling the onError callback if defined\n */\n private handleError(error: Error | string): void {\n if (this.onError) {\n this.onError(error);\n }\n }\n\n /**\n * Sends logs to SumoLogic with retry logic, compression, and size validation.\n */\n private async sendToSumoLogic(payload: string, retryCount = 0): Promise<void> {\n try {\n // Check payload size before compression\n const rawSize = new TextEncoder().encode(payload).length;\n if (rawSize > MAX_PAYLOAD_SIZE) {\n const error = `Payload size exceeds maximum of ${MAX_PAYLOAD_SIZE} bytes. Size: ${rawSize} bytes`;\n this.handleError(error);\n return;\n }\n\n let compressedPayload: Uint8Array | undefined;\n if (this.useCompression) {\n compressedPayload = await this.compressData(payload);\n if (compressedPayload.length > MAX_PAYLOAD_SIZE) {\n const error = `Compressed payload size exceeds maximum of ${MAX_PAYLOAD_SIZE} bytes. Size: ${compressedPayload.length} bytes`;\n this.handleError(error);\n return;\n }\n }\n\n const headers: Record<string, string> = {\n ...this.headers,\n \"Content-Type\": \"application/json\",\n };\n\n if (this.sourceCategory) {\n headers[\"X-Sumo-Category\"] = this.sourceCategory;\n }\n if (this.sourceName) {\n headers[\"X-Sumo-Name\"] = this.sourceName;\n }\n if (this.sourceHost) {\n headers[\"X-Sumo-Host\"] = this.sourceHost;\n }\n if (this.useCompression) {\n headers[\"Content-Encoding\"] = \"gzip\";\n }\n if (Object.keys(this.fields).length > 0) {\n headers[\"X-Sumo-Fields\"] = Object.entries(this.fields)\n .map(([key, value]) => `${key}=${value}`)\n .join(\",\");\n }\n\n const response = (await fetch(this.url, {\n method: \"POST\",\n headers,\n body: this.useCompression ? compressedPayload : payload,\n })) as Response;\n\n if (!response.ok) {\n throw new Error(`HTTP error! status: ${response.status}`);\n }\n } catch (error) {\n if (retryCount < this.maxRetries) {\n const delay = this.initialRetryMs * 2 ** retryCount;\n await new Promise((resolve) => setTimeout(resolve, delay));\n return this.sendToSumoLogic(payload, retryCount + 1);\n }\n this.handleError(error instanceof Error ? error : String(error));\n }\n }\n\n shipToLogger({ logLevel, messages, data, hasData }: LogLayerTransportParams): any[] {\n // Only use the string messages for content\n const stringMessages = messages.filter((msg) => typeof msg === \"string\");\n const message = stringMessages.join(\" \");\n\n const payload = {\n severity: logLevel,\n timestamp: new Date().toISOString(),\n ...(data && hasData ? data : {}),\n };\n\n if (message) {\n payload[this.messageField] = message;\n }\n\n this.sendToSumoLogic(JSON.stringify(payload));\n\n return messages;\n }\n}\n"],"mappings":"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"}
|
package/dist/index.d.cts
CHANGED
|
@@ -1,77 +1,84 @@
|
|
|
1
|
-
import { LoggerlessTransport, LoggerlessTransportConfig
|
|
1
|
+
import { LogLayerTransportParams, LoggerlessTransport, LoggerlessTransportConfig } from "@loglayer/transport";
|
|
2
2
|
|
|
3
|
+
//#region src/SumoLogicTransport.d.ts
|
|
3
4
|
interface SumoLogicTransportConfig extends LoggerlessTransportConfig {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
5
|
+
/**
|
|
6
|
+
* The URL of your HTTP Source endpoint
|
|
7
|
+
*/
|
|
8
|
+
url: string;
|
|
9
|
+
/**
|
|
10
|
+
* Whether to use gzip compression (defaults to true)
|
|
11
|
+
*/
|
|
12
|
+
useCompression?: boolean;
|
|
13
|
+
/**
|
|
14
|
+
* Source category to assign to the logs (optional)
|
|
15
|
+
*/
|
|
16
|
+
sourceCategory?: string;
|
|
17
|
+
/**
|
|
18
|
+
* Source name to assign to the logs (optional)
|
|
19
|
+
*/
|
|
20
|
+
sourceName?: string;
|
|
21
|
+
/**
|
|
22
|
+
* Source host to assign to the logs (optional)
|
|
23
|
+
*/
|
|
24
|
+
sourceHost?: string;
|
|
25
|
+
/**
|
|
26
|
+
* Fields to be added as X-Sumo-Fields header (optional)
|
|
27
|
+
* Will be formatted as a comma-separated list of key=value pairs
|
|
28
|
+
*/
|
|
29
|
+
fields?: Record<string, string>;
|
|
30
|
+
/**
|
|
31
|
+
* Custom headers to be added to the request (optional)
|
|
32
|
+
*/
|
|
33
|
+
headers?: Record<string, string>;
|
|
34
|
+
/**
|
|
35
|
+
* Retry configuration (optional)
|
|
36
|
+
*/
|
|
37
|
+
retryConfig?: {
|
|
38
|
+
maxRetries?: number;
|
|
39
|
+
initialRetryMs?: number;
|
|
40
|
+
};
|
|
41
|
+
/**
|
|
42
|
+
* Optional callback for error handling
|
|
43
|
+
*/
|
|
44
|
+
onError?: (error: Error | string) => void;
|
|
45
|
+
/**
|
|
46
|
+
* Field name to use for the message (defaults to "message")
|
|
47
|
+
*/
|
|
48
|
+
messageField?: string;
|
|
48
49
|
}
|
|
49
50
|
declare class SumoLogicTransport extends LoggerlessTransport {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
51
|
+
private url;
|
|
52
|
+
private useCompression;
|
|
53
|
+
private sourceCategory?;
|
|
54
|
+
private sourceName?;
|
|
55
|
+
private sourceHost?;
|
|
56
|
+
private fields;
|
|
57
|
+
private headers;
|
|
58
|
+
private maxRetries;
|
|
59
|
+
private initialRetryMs;
|
|
60
|
+
private onError?;
|
|
61
|
+
private messageField;
|
|
62
|
+
constructor(config: SumoLogicTransportConfig);
|
|
63
|
+
/**
|
|
64
|
+
* Compresses data using gzip compression.
|
|
65
|
+
*/
|
|
66
|
+
private compressData;
|
|
67
|
+
/**
|
|
68
|
+
* Handles errors by calling the onError callback if defined
|
|
69
|
+
*/
|
|
70
|
+
private handleError;
|
|
71
|
+
/**
|
|
72
|
+
* Sends logs to SumoLogic with retry logic, compression, and size validation.
|
|
73
|
+
*/
|
|
74
|
+
private sendToSumoLogic;
|
|
75
|
+
shipToLogger({
|
|
76
|
+
logLevel,
|
|
77
|
+
messages,
|
|
78
|
+
data,
|
|
79
|
+
hasData
|
|
80
|
+
}: LogLayerTransportParams): any[];
|
|
75
81
|
}
|
|
76
|
-
|
|
82
|
+
//#endregion
|
|
77
83
|
export { SumoLogicTransport };
|
|
84
|
+
//# sourceMappingURL=index.d.cts.map
|
package/dist/index.d.ts
CHANGED
|
@@ -1,77 +1,84 @@
|
|
|
1
|
-
import { LoggerlessTransport, LoggerlessTransportConfig
|
|
1
|
+
import { LogLayerTransportParams, LoggerlessTransport, LoggerlessTransportConfig } from "@loglayer/transport";
|
|
2
2
|
|
|
3
|
+
//#region src/SumoLogicTransport.d.ts
|
|
3
4
|
interface SumoLogicTransportConfig extends LoggerlessTransportConfig {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
5
|
+
/**
|
|
6
|
+
* The URL of your HTTP Source endpoint
|
|
7
|
+
*/
|
|
8
|
+
url: string;
|
|
9
|
+
/**
|
|
10
|
+
* Whether to use gzip compression (defaults to true)
|
|
11
|
+
*/
|
|
12
|
+
useCompression?: boolean;
|
|
13
|
+
/**
|
|
14
|
+
* Source category to assign to the logs (optional)
|
|
15
|
+
*/
|
|
16
|
+
sourceCategory?: string;
|
|
17
|
+
/**
|
|
18
|
+
* Source name to assign to the logs (optional)
|
|
19
|
+
*/
|
|
20
|
+
sourceName?: string;
|
|
21
|
+
/**
|
|
22
|
+
* Source host to assign to the logs (optional)
|
|
23
|
+
*/
|
|
24
|
+
sourceHost?: string;
|
|
25
|
+
/**
|
|
26
|
+
* Fields to be added as X-Sumo-Fields header (optional)
|
|
27
|
+
* Will be formatted as a comma-separated list of key=value pairs
|
|
28
|
+
*/
|
|
29
|
+
fields?: Record<string, string>;
|
|
30
|
+
/**
|
|
31
|
+
* Custom headers to be added to the request (optional)
|
|
32
|
+
*/
|
|
33
|
+
headers?: Record<string, string>;
|
|
34
|
+
/**
|
|
35
|
+
* Retry configuration (optional)
|
|
36
|
+
*/
|
|
37
|
+
retryConfig?: {
|
|
38
|
+
maxRetries?: number;
|
|
39
|
+
initialRetryMs?: number;
|
|
40
|
+
};
|
|
41
|
+
/**
|
|
42
|
+
* Optional callback for error handling
|
|
43
|
+
*/
|
|
44
|
+
onError?: (error: Error | string) => void;
|
|
45
|
+
/**
|
|
46
|
+
* Field name to use for the message (defaults to "message")
|
|
47
|
+
*/
|
|
48
|
+
messageField?: string;
|
|
48
49
|
}
|
|
49
50
|
declare class SumoLogicTransport extends LoggerlessTransport {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
51
|
+
private url;
|
|
52
|
+
private useCompression;
|
|
53
|
+
private sourceCategory?;
|
|
54
|
+
private sourceName?;
|
|
55
|
+
private sourceHost?;
|
|
56
|
+
private fields;
|
|
57
|
+
private headers;
|
|
58
|
+
private maxRetries;
|
|
59
|
+
private initialRetryMs;
|
|
60
|
+
private onError?;
|
|
61
|
+
private messageField;
|
|
62
|
+
constructor(config: SumoLogicTransportConfig);
|
|
63
|
+
/**
|
|
64
|
+
* Compresses data using gzip compression.
|
|
65
|
+
*/
|
|
66
|
+
private compressData;
|
|
67
|
+
/**
|
|
68
|
+
* Handles errors by calling the onError callback if defined
|
|
69
|
+
*/
|
|
70
|
+
private handleError;
|
|
71
|
+
/**
|
|
72
|
+
* Sends logs to SumoLogic with retry logic, compression, and size validation.
|
|
73
|
+
*/
|
|
74
|
+
private sendToSumoLogic;
|
|
75
|
+
shipToLogger({
|
|
76
|
+
logLevel,
|
|
77
|
+
messages,
|
|
78
|
+
data,
|
|
79
|
+
hasData
|
|
80
|
+
}: LogLayerTransportParams): any[];
|
|
75
81
|
}
|
|
76
|
-
|
|
82
|
+
//#endregion
|
|
77
83
|
export { SumoLogicTransport };
|
|
84
|
+
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.js
CHANGED
|
@@ -1,137 +1,121 @@
|
|
|
1
|
-
// src/SumoLogicTransport.ts
|
|
2
1
|
import { LoggerlessTransport } from "@loglayer/transport";
|
|
3
|
-
|
|
2
|
+
|
|
3
|
+
//#region src/SumoLogicTransport.ts
|
|
4
|
+
const MAX_PAYLOAD_SIZE = 1e6;
|
|
4
5
|
var SumoLogicTransport = class extends LoggerlessTransport {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
this.handleError(error instanceof Error ? error : String(error));
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
shipToLogger({ logLevel, messages, data, hasData }) {
|
|
120
|
-
const stringMessages = messages.filter((msg) => typeof msg === "string");
|
|
121
|
-
const message = stringMessages.join(" ");
|
|
122
|
-
const payload = {
|
|
123
|
-
severity: logLevel,
|
|
124
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
125
|
-
...data && hasData ? data : {}
|
|
126
|
-
};
|
|
127
|
-
if (message) {
|
|
128
|
-
payload[this.messageField] = message;
|
|
129
|
-
}
|
|
130
|
-
this.sendToSumoLogic(JSON.stringify(payload));
|
|
131
|
-
return messages;
|
|
132
|
-
}
|
|
133
|
-
};
|
|
134
|
-
export {
|
|
135
|
-
SumoLogicTransport
|
|
6
|
+
url;
|
|
7
|
+
useCompression;
|
|
8
|
+
sourceCategory;
|
|
9
|
+
sourceName;
|
|
10
|
+
sourceHost;
|
|
11
|
+
fields;
|
|
12
|
+
headers;
|
|
13
|
+
maxRetries;
|
|
14
|
+
initialRetryMs;
|
|
15
|
+
onError;
|
|
16
|
+
messageField;
|
|
17
|
+
constructor(config) {
|
|
18
|
+
super(config);
|
|
19
|
+
this.url = config.url;
|
|
20
|
+
this.useCompression = config.useCompression ?? true;
|
|
21
|
+
this.sourceCategory = config.sourceCategory;
|
|
22
|
+
this.sourceName = config.sourceName;
|
|
23
|
+
this.sourceHost = config.sourceHost;
|
|
24
|
+
this.fields = config.fields ?? {};
|
|
25
|
+
this.headers = config.headers ?? {};
|
|
26
|
+
this.maxRetries = config.retryConfig?.maxRetries ?? 3;
|
|
27
|
+
this.initialRetryMs = config.retryConfig?.initialRetryMs ?? 1e3;
|
|
28
|
+
this.onError = config.onError;
|
|
29
|
+
this.messageField = config.messageField ?? "message";
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Compresses data using gzip compression.
|
|
33
|
+
*/
|
|
34
|
+
async compressData(data) {
|
|
35
|
+
const stream = new CompressionStream("gzip");
|
|
36
|
+
const writer = stream.writable.getWriter();
|
|
37
|
+
const textEncoder = new TextEncoder();
|
|
38
|
+
const chunks = [];
|
|
39
|
+
await writer.write(textEncoder.encode(data));
|
|
40
|
+
await writer.close();
|
|
41
|
+
const reader = stream.readable.getReader();
|
|
42
|
+
while (true) {
|
|
43
|
+
const { value, done } = await reader.read();
|
|
44
|
+
if (done) break;
|
|
45
|
+
chunks.push(value);
|
|
46
|
+
}
|
|
47
|
+
const totalLength = chunks.reduce((sum, chunk) => sum + chunk.length, 0);
|
|
48
|
+
const result = new Uint8Array(totalLength);
|
|
49
|
+
let offset = 0;
|
|
50
|
+
for (const chunk of chunks) {
|
|
51
|
+
result.set(chunk, offset);
|
|
52
|
+
offset += chunk.length;
|
|
53
|
+
}
|
|
54
|
+
return result;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Handles errors by calling the onError callback if defined
|
|
58
|
+
*/
|
|
59
|
+
handleError(error) {
|
|
60
|
+
if (this.onError) this.onError(error);
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Sends logs to SumoLogic with retry logic, compression, and size validation.
|
|
64
|
+
*/
|
|
65
|
+
async sendToSumoLogic(payload, retryCount = 0) {
|
|
66
|
+
try {
|
|
67
|
+
const rawSize = new TextEncoder().encode(payload).length;
|
|
68
|
+
if (rawSize > MAX_PAYLOAD_SIZE) {
|
|
69
|
+
const error = `Payload size exceeds maximum of ${MAX_PAYLOAD_SIZE} bytes. Size: ${rawSize} bytes`;
|
|
70
|
+
this.handleError(error);
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
let compressedPayload;
|
|
74
|
+
if (this.useCompression) {
|
|
75
|
+
compressedPayload = await this.compressData(payload);
|
|
76
|
+
if (compressedPayload.length > MAX_PAYLOAD_SIZE) {
|
|
77
|
+
const error = `Compressed payload size exceeds maximum of ${MAX_PAYLOAD_SIZE} bytes. Size: ${compressedPayload.length} bytes`;
|
|
78
|
+
this.handleError(error);
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
const headers = {
|
|
83
|
+
...this.headers,
|
|
84
|
+
"Content-Type": "application/json"
|
|
85
|
+
};
|
|
86
|
+
if (this.sourceCategory) headers["X-Sumo-Category"] = this.sourceCategory;
|
|
87
|
+
if (this.sourceName) headers["X-Sumo-Name"] = this.sourceName;
|
|
88
|
+
if (this.sourceHost) headers["X-Sumo-Host"] = this.sourceHost;
|
|
89
|
+
if (this.useCompression) headers["Content-Encoding"] = "gzip";
|
|
90
|
+
if (Object.keys(this.fields).length > 0) headers["X-Sumo-Fields"] = Object.entries(this.fields).map(([key, value]) => `${key}=${value}`).join(",");
|
|
91
|
+
const response = await fetch(this.url, {
|
|
92
|
+
method: "POST",
|
|
93
|
+
headers,
|
|
94
|
+
body: this.useCompression ? compressedPayload : payload
|
|
95
|
+
});
|
|
96
|
+
if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`);
|
|
97
|
+
} catch (error) {
|
|
98
|
+
if (retryCount < this.maxRetries) {
|
|
99
|
+
const delay = this.initialRetryMs * 2 ** retryCount;
|
|
100
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
101
|
+
return this.sendToSumoLogic(payload, retryCount + 1);
|
|
102
|
+
}
|
|
103
|
+
this.handleError(error instanceof Error ? error : String(error));
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
shipToLogger({ logLevel, messages, data, hasData }) {
|
|
107
|
+
const message = messages.filter((msg) => typeof msg === "string").join(" ");
|
|
108
|
+
const payload = {
|
|
109
|
+
severity: logLevel,
|
|
110
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
111
|
+
...data && hasData ? data : {}
|
|
112
|
+
};
|
|
113
|
+
if (message) payload[this.messageField] = message;
|
|
114
|
+
this.sendToSumoLogic(JSON.stringify(payload));
|
|
115
|
+
return messages;
|
|
116
|
+
}
|
|
136
117
|
};
|
|
118
|
+
|
|
119
|
+
//#endregion
|
|
120
|
+
export { SumoLogicTransport };
|
|
137
121
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/SumoLogicTransport.ts"],"sourcesContent":["import { LoggerlessTransport, type LoggerlessTransportConfig, type LogLayerTransportParams } from \"@loglayer/transport\";\n\ninterface SumoLogicTransportConfig extends LoggerlessTransportConfig {\n /**\n * The URL of your HTTP Source endpoint\n */\n url: string;\n\n /**\n * Whether to use gzip compression (defaults to true)\n */\n useCompression?: boolean;\n\n /**\n * Source category to assign to the logs (optional)\n */\n sourceCategory?: string;\n\n /**\n * Source name to assign to the logs (optional)\n */\n sourceName?: string;\n\n /**\n * Source host to assign to the logs (optional)\n */\n sourceHost?: string;\n\n /**\n * Fields to be added as X-Sumo-Fields header (optional)\n * Will be formatted as a comma-separated list of key=value pairs\n */\n fields?: Record<string, string>;\n\n /**\n * Custom headers to be added to the request (optional)\n */\n headers?: Record<string, string>;\n\n /**\n * Retry configuration (optional)\n */\n retryConfig?: {\n maxRetries?: number;\n initialRetryMs?: number;\n };\n\n /**\n * Optional callback for error handling\n */\n onError?: (error: Error | string) => void;\n\n /**\n * Field name to use for the message (defaults to \"message\")\n */\n messageField?: string;\n}\n\nconst MAX_PAYLOAD_SIZE = 1000000; // 1MB limit as per SumoLogic docs\n\nexport class SumoLogicTransport extends LoggerlessTransport {\n private url: string;\n private useCompression: boolean;\n private sourceCategory?: string;\n private sourceName?: string;\n private sourceHost?: string;\n private fields: Record<string, string>;\n private headers: Record<string, string>;\n private maxRetries: number;\n private initialRetryMs: number;\n private onError?: (error: Error | string) => void;\n private messageField: string;\n\n constructor(config: SumoLogicTransportConfig) {\n super(config);\n this.url = config.url;\n this.useCompression = config.useCompression ?? true;\n this.sourceCategory = config.sourceCategory;\n this.sourceName = config.sourceName;\n this.sourceHost = config.sourceHost;\n this.fields = config.fields ?? {};\n this.headers = config.headers ?? {};\n this.maxRetries = config.retryConfig?.maxRetries ?? 3;\n this.initialRetryMs = config.retryConfig?.initialRetryMs ?? 1000;\n this.onError = config.onError;\n this.messageField = config.messageField ?? \"message\";\n }\n\n /**\n * Compresses data using gzip compression.\n */\n private async compressData(data: string): Promise<Uint8Array> {\n const stream = new CompressionStream(\"gzip\");\n const writer = stream.writable.getWriter();\n const textEncoder = new TextEncoder();\n const chunks: Uint8Array[] = [];\n\n await writer.write(textEncoder.encode(data));\n await writer.close();\n\n const reader = stream.readable.getReader();\n\n while (true) {\n const { value, done } = await reader.read();\n if (done) break;\n chunks.push(value);\n }\n\n const totalLength = chunks.reduce((sum, chunk) => sum + chunk.length, 0);\n const result = new Uint8Array(totalLength);\n let offset = 0;\n\n for (const chunk of chunks) {\n result.set(chunk, offset);\n offset += chunk.length;\n }\n\n return result;\n }\n\n /**\n * Handles errors by calling the onError callback if defined\n */\n private handleError(error: Error | string): void {\n if (this.onError) {\n this.onError(error);\n }\n }\n\n /**\n * Sends logs to SumoLogic with retry logic, compression, and size validation.\n */\n private async sendToSumoLogic(payload: string, retryCount = 0): Promise<void> {\n try {\n // Check payload size before compression\n const rawSize = new TextEncoder().encode(payload).length;\n if (rawSize > MAX_PAYLOAD_SIZE) {\n const error = `Payload size exceeds maximum of ${MAX_PAYLOAD_SIZE} bytes. Size: ${rawSize} bytes`;\n this.handleError(error);\n return;\n }\n\n let compressedPayload: Uint8Array | undefined;\n if (this.useCompression) {\n compressedPayload = await this.compressData(payload);\n if (compressedPayload.length > MAX_PAYLOAD_SIZE) {\n const error = `Compressed payload size exceeds maximum of ${MAX_PAYLOAD_SIZE} bytes. Size: ${compressedPayload.length} bytes`;\n this.handleError(error);\n return;\n }\n }\n\n const headers: Record<string, string> = {\n ...this.headers,\n \"Content-Type\": \"application/json\",\n };\n\n if (this.sourceCategory) {\n headers[\"X-Sumo-Category\"] = this.sourceCategory;\n }\n if (this.sourceName) {\n headers[\"X-Sumo-Name\"] = this.sourceName;\n }\n if (this.sourceHost) {\n headers[\"X-Sumo-Host\"] = this.sourceHost;\n }\n if (this.useCompression) {\n headers[\"Content-Encoding\"] = \"gzip\";\n }\n if (Object.keys(this.fields).length > 0) {\n headers[\"X-Sumo-Fields\"] = Object.entries(this.fields)\n .map(([key, value]) => `${key}=${value}`)\n .join(\",\");\n }\n\n const response = (await fetch(this.url, {\n method: \"POST\",\n headers,\n body: this.useCompression ? compressedPayload : payload,\n })) as Response;\n\n if (!response.ok) {\n throw new Error(`HTTP error! status: ${response.status}`);\n }\n } catch (error) {\n if (retryCount < this.maxRetries) {\n const delay = this.initialRetryMs * 2 ** retryCount;\n await new Promise((resolve) => setTimeout(resolve, delay));\n return this.sendToSumoLogic(payload, retryCount + 1);\n }\n this.handleError(error instanceof Error ? error : String(error));\n }\n }\n\n shipToLogger({ logLevel, messages, data, hasData }: LogLayerTransportParams): any[] {\n // Only use the string messages for content\n const stringMessages = messages.filter((msg) => typeof msg === \"string\");\n const message = stringMessages.join(\" \");\n\n const payload = {\n severity: logLevel,\n timestamp: new Date().toISOString(),\n ...(data && hasData ? data : {}),\n };\n\n if (message) {\n payload[this.messageField] = message;\n }\n\n this.sendToSumoLogic(JSON.stringify(payload));\n\n return messages;\n }\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.js","names":["chunks: Uint8Array[]","compressedPayload: Uint8Array | undefined","headers: Record<string, string>"],"sources":["../src/SumoLogicTransport.ts"],"sourcesContent":["import { LoggerlessTransport, type LoggerlessTransportConfig, type LogLayerTransportParams } from \"@loglayer/transport\";\n\ninterface SumoLogicTransportConfig extends LoggerlessTransportConfig {\n /**\n * The URL of your HTTP Source endpoint\n */\n url: string;\n\n /**\n * Whether to use gzip compression (defaults to true)\n */\n useCompression?: boolean;\n\n /**\n * Source category to assign to the logs (optional)\n */\n sourceCategory?: string;\n\n /**\n * Source name to assign to the logs (optional)\n */\n sourceName?: string;\n\n /**\n * Source host to assign to the logs (optional)\n */\n sourceHost?: string;\n\n /**\n * Fields to be added as X-Sumo-Fields header (optional)\n * Will be formatted as a comma-separated list of key=value pairs\n */\n fields?: Record<string, string>;\n\n /**\n * Custom headers to be added to the request (optional)\n */\n headers?: Record<string, string>;\n\n /**\n * Retry configuration (optional)\n */\n retryConfig?: {\n maxRetries?: number;\n initialRetryMs?: number;\n };\n\n /**\n * Optional callback for error handling\n */\n onError?: (error: Error | string) => void;\n\n /**\n * Field name to use for the message (defaults to \"message\")\n */\n messageField?: string;\n}\n\nconst MAX_PAYLOAD_SIZE = 1000000; // 1MB limit as per SumoLogic docs\n\nexport class SumoLogicTransport extends LoggerlessTransport {\n private url: string;\n private useCompression: boolean;\n private sourceCategory?: string;\n private sourceName?: string;\n private sourceHost?: string;\n private fields: Record<string, string>;\n private headers: Record<string, string>;\n private maxRetries: number;\n private initialRetryMs: number;\n private onError?: (error: Error | string) => void;\n private messageField: string;\n\n constructor(config: SumoLogicTransportConfig) {\n super(config);\n this.url = config.url;\n this.useCompression = config.useCompression ?? true;\n this.sourceCategory = config.sourceCategory;\n this.sourceName = config.sourceName;\n this.sourceHost = config.sourceHost;\n this.fields = config.fields ?? {};\n this.headers = config.headers ?? {};\n this.maxRetries = config.retryConfig?.maxRetries ?? 3;\n this.initialRetryMs = config.retryConfig?.initialRetryMs ?? 1000;\n this.onError = config.onError;\n this.messageField = config.messageField ?? \"message\";\n }\n\n /**\n * Compresses data using gzip compression.\n */\n private async compressData(data: string): Promise<Uint8Array> {\n const stream = new CompressionStream(\"gzip\");\n const writer = stream.writable.getWriter();\n const textEncoder = new TextEncoder();\n const chunks: Uint8Array[] = [];\n\n await writer.write(textEncoder.encode(data));\n await writer.close();\n\n const reader = stream.readable.getReader();\n\n while (true) {\n const { value, done } = await reader.read();\n if (done) break;\n chunks.push(value);\n }\n\n const totalLength = chunks.reduce((sum, chunk) => sum + chunk.length, 0);\n const result = new Uint8Array(totalLength);\n let offset = 0;\n\n for (const chunk of chunks) {\n result.set(chunk, offset);\n offset += chunk.length;\n }\n\n return result;\n }\n\n /**\n * Handles errors by calling the onError callback if defined\n */\n private handleError(error: Error | string): void {\n if (this.onError) {\n this.onError(error);\n }\n }\n\n /**\n * Sends logs to SumoLogic with retry logic, compression, and size validation.\n */\n private async sendToSumoLogic(payload: string, retryCount = 0): Promise<void> {\n try {\n // Check payload size before compression\n const rawSize = new TextEncoder().encode(payload).length;\n if (rawSize > MAX_PAYLOAD_SIZE) {\n const error = `Payload size exceeds maximum of ${MAX_PAYLOAD_SIZE} bytes. Size: ${rawSize} bytes`;\n this.handleError(error);\n return;\n }\n\n let compressedPayload: Uint8Array | undefined;\n if (this.useCompression) {\n compressedPayload = await this.compressData(payload);\n if (compressedPayload.length > MAX_PAYLOAD_SIZE) {\n const error = `Compressed payload size exceeds maximum of ${MAX_PAYLOAD_SIZE} bytes. Size: ${compressedPayload.length} bytes`;\n this.handleError(error);\n return;\n }\n }\n\n const headers: Record<string, string> = {\n ...this.headers,\n \"Content-Type\": \"application/json\",\n };\n\n if (this.sourceCategory) {\n headers[\"X-Sumo-Category\"] = this.sourceCategory;\n }\n if (this.sourceName) {\n headers[\"X-Sumo-Name\"] = this.sourceName;\n }\n if (this.sourceHost) {\n headers[\"X-Sumo-Host\"] = this.sourceHost;\n }\n if (this.useCompression) {\n headers[\"Content-Encoding\"] = \"gzip\";\n }\n if (Object.keys(this.fields).length > 0) {\n headers[\"X-Sumo-Fields\"] = Object.entries(this.fields)\n .map(([key, value]) => `${key}=${value}`)\n .join(\",\");\n }\n\n const response = (await fetch(this.url, {\n method: \"POST\",\n headers,\n body: this.useCompression ? compressedPayload : payload,\n })) as Response;\n\n if (!response.ok) {\n throw new Error(`HTTP error! status: ${response.status}`);\n }\n } catch (error) {\n if (retryCount < this.maxRetries) {\n const delay = this.initialRetryMs * 2 ** retryCount;\n await new Promise((resolve) => setTimeout(resolve, delay));\n return this.sendToSumoLogic(payload, retryCount + 1);\n }\n this.handleError(error instanceof Error ? error : String(error));\n }\n }\n\n shipToLogger({ logLevel, messages, data, hasData }: LogLayerTransportParams): any[] {\n // Only use the string messages for content\n const stringMessages = messages.filter((msg) => typeof msg === \"string\");\n const message = stringMessages.join(\" \");\n\n const payload = {\n severity: logLevel,\n timestamp: new Date().toISOString(),\n ...(data && hasData ? data : {}),\n };\n\n if (message) {\n payload[this.messageField] = message;\n }\n\n this.sendToSumoLogic(JSON.stringify(payload));\n\n return messages;\n }\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@loglayer/transport-sumo-logic",
|
|
3
3
|
"description": "Sumo Logic transport for the LogLayer logging library.",
|
|
4
|
-
"version": "2.1.
|
|
4
|
+
"version": "2.1.7",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.cjs",
|
|
7
7
|
"module": "./dist/index.js",
|
|
@@ -31,17 +31,17 @@
|
|
|
31
31
|
"transport"
|
|
32
32
|
],
|
|
33
33
|
"dependencies": {
|
|
34
|
-
"@loglayer/transport": "2.3.
|
|
34
|
+
"@loglayer/transport": "2.3.4"
|
|
35
35
|
},
|
|
36
36
|
"devDependencies": {
|
|
37
|
-
"@types/node": "24.
|
|
37
|
+
"@types/node": "24.7.2",
|
|
38
38
|
"serialize-error": "12.0.0",
|
|
39
|
-
"
|
|
39
|
+
"tsdown": "0.15.7",
|
|
40
|
+
"tsx": "4.20.6",
|
|
40
41
|
"typescript": "5.9.3",
|
|
41
42
|
"vitest": "3.2.4",
|
|
42
|
-
"tsx": "4.20.6",
|
|
43
43
|
"@internal/tsconfig": "2.1.0",
|
|
44
|
-
"loglayer": "6.
|
|
44
|
+
"loglayer": "6.9.1"
|
|
45
45
|
},
|
|
46
46
|
"bugs": "https://github.com/loglayer/loglayer/issues",
|
|
47
47
|
"engines": {
|
|
@@ -52,7 +52,7 @@
|
|
|
52
52
|
],
|
|
53
53
|
"homepage": "https://loglayer.dev",
|
|
54
54
|
"scripts": {
|
|
55
|
-
"build": "
|
|
55
|
+
"build": "tsdown src/index.ts",
|
|
56
56
|
"test": "vitest --run",
|
|
57
57
|
"clean": "rm -rf .turbo node_modules dist",
|
|
58
58
|
"lint": "biome check --no-errors-on-unmatched --write --unsafe src",
|