@loglayer/transport-sumo-logic 2.1.5 → 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 CHANGED
@@ -1,137 +1,145 @@
1
- "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }// src/SumoLogicTransport.ts
2
- var _transport = require('@loglayer/transport');
3
- var MAX_PAYLOAD_SIZE = 1e6;
4
- var SumoLogicTransport = class extends _transport.LoggerlessTransport {
5
-
6
-
7
-
8
-
9
-
10
-
11
-
12
-
13
-
14
-
15
-
16
- constructor(config) {
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
@@ -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, LogLayerTransportParams } from '@loglayer/transport';
1
+ import { LogLayerTransportParams, LoggerlessTransport, LoggerlessTransportConfig } from "@loglayer/transport";
2
2
 
3
+ //#region src/SumoLogicTransport.d.ts
3
4
  interface SumoLogicTransportConfig extends LoggerlessTransportConfig {
4
- /**
5
- * The URL of your HTTP Source endpoint
6
- */
7
- url: string;
8
- /**
9
- * Whether to use gzip compression (defaults to true)
10
- */
11
- useCompression?: boolean;
12
- /**
13
- * Source category to assign to the logs (optional)
14
- */
15
- sourceCategory?: string;
16
- /**
17
- * Source name to assign to the logs (optional)
18
- */
19
- sourceName?: string;
20
- /**
21
- * Source host to assign to the logs (optional)
22
- */
23
- sourceHost?: string;
24
- /**
25
- * Fields to be added as X-Sumo-Fields header (optional)
26
- * Will be formatted as a comma-separated list of key=value pairs
27
- */
28
- fields?: Record<string, string>;
29
- /**
30
- * Custom headers to be added to the request (optional)
31
- */
32
- headers?: Record<string, string>;
33
- /**
34
- * Retry configuration (optional)
35
- */
36
- retryConfig?: {
37
- maxRetries?: number;
38
- initialRetryMs?: number;
39
- };
40
- /**
41
- * Optional callback for error handling
42
- */
43
- onError?: (error: Error | string) => void;
44
- /**
45
- * Field name to use for the message (defaults to "message")
46
- */
47
- messageField?: string;
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
- private url;
51
- private useCompression;
52
- private sourceCategory?;
53
- private sourceName?;
54
- private sourceHost?;
55
- private fields;
56
- private headers;
57
- private maxRetries;
58
- private initialRetryMs;
59
- private onError?;
60
- private messageField;
61
- constructor(config: SumoLogicTransportConfig);
62
- /**
63
- * Compresses data using gzip compression.
64
- */
65
- private compressData;
66
- /**
67
- * Handles errors by calling the onError callback if defined
68
- */
69
- private handleError;
70
- /**
71
- * Sends logs to SumoLogic with retry logic, compression, and size validation.
72
- */
73
- private sendToSumoLogic;
74
- shipToLogger({ logLevel, messages, data, hasData }: LogLayerTransportParams): any[];
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, LogLayerTransportParams } from '@loglayer/transport';
1
+ import { LogLayerTransportParams, LoggerlessTransport, LoggerlessTransportConfig } from "@loglayer/transport";
2
2
 
3
+ //#region src/SumoLogicTransport.d.ts
3
4
  interface SumoLogicTransportConfig extends LoggerlessTransportConfig {
4
- /**
5
- * The URL of your HTTP Source endpoint
6
- */
7
- url: string;
8
- /**
9
- * Whether to use gzip compression (defaults to true)
10
- */
11
- useCompression?: boolean;
12
- /**
13
- * Source category to assign to the logs (optional)
14
- */
15
- sourceCategory?: string;
16
- /**
17
- * Source name to assign to the logs (optional)
18
- */
19
- sourceName?: string;
20
- /**
21
- * Source host to assign to the logs (optional)
22
- */
23
- sourceHost?: string;
24
- /**
25
- * Fields to be added as X-Sumo-Fields header (optional)
26
- * Will be formatted as a comma-separated list of key=value pairs
27
- */
28
- fields?: Record<string, string>;
29
- /**
30
- * Custom headers to be added to the request (optional)
31
- */
32
- headers?: Record<string, string>;
33
- /**
34
- * Retry configuration (optional)
35
- */
36
- retryConfig?: {
37
- maxRetries?: number;
38
- initialRetryMs?: number;
39
- };
40
- /**
41
- * Optional callback for error handling
42
- */
43
- onError?: (error: Error | string) => void;
44
- /**
45
- * Field name to use for the message (defaults to "message")
46
- */
47
- messageField?: string;
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
- private url;
51
- private useCompression;
52
- private sourceCategory?;
53
- private sourceName?;
54
- private sourceHost?;
55
- private fields;
56
- private headers;
57
- private maxRetries;
58
- private initialRetryMs;
59
- private onError?;
60
- private messageField;
61
- constructor(config: SumoLogicTransportConfig);
62
- /**
63
- * Compresses data using gzip compression.
64
- */
65
- private compressData;
66
- /**
67
- * Handles errors by calling the onError callback if defined
68
- */
69
- private handleError;
70
- /**
71
- * Sends logs to SumoLogic with retry logic, compression, and size validation.
72
- */
73
- private sendToSumoLogic;
74
- shipToLogger({ logLevel, messages, data, hasData }: LogLayerTransportParams): any[];
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
- var MAX_PAYLOAD_SIZE = 1e6;
2
+
3
+ //#region src/SumoLogicTransport.ts
4
+ const MAX_PAYLOAD_SIZE = 1e6;
4
5
  var SumoLogicTransport = class extends LoggerlessTransport {
5
- url;
6
- useCompression;
7
- sourceCategory;
8
- sourceName;
9
- sourceHost;
10
- fields;
11
- headers;
12
- maxRetries;
13
- initialRetryMs;
14
- onError;
15
- messageField;
16
- constructor(config) {
17
- super(config);
18
- this.url = config.url;
19
- this.useCompression = config.useCompression ?? true;
20
- this.sourceCategory = config.sourceCategory;
21
- this.sourceName = config.sourceName;
22
- this.sourceHost = config.sourceHost;
23
- this.fields = config.fields ?? {};
24
- this.headers = config.headers ?? {};
25
- this.maxRetries = config.retryConfig?.maxRetries ?? 3;
26
- this.initialRetryMs = config.retryConfig?.initialRetryMs ?? 1e3;
27
- this.onError = config.onError;
28
- this.messageField = 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
- }
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":"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","names":[]}
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":";;;AA0DA,MAAM,mBAAmB;AAEzB,IAAa,qBAAb,cAAwC,oBAAoB;CAC1D,AAAQ;CACR,AAAQ;CACR,AAAQ;CACR,AAAQ;CACR,AAAQ;CACR,AAAQ;CACR,AAAQ;CACR,AAAQ;CACR,AAAQ;CACR,AAAQ;CACR,AAAQ;CAER,YAAY,QAAkC;AAC5C,QAAM,OAAO;AACb,OAAK,MAAM,OAAO;AAClB,OAAK,iBAAiB,OAAO,kBAAkB;AAC/C,OAAK,iBAAiB,OAAO;AAC7B,OAAK,aAAa,OAAO;AACzB,OAAK,aAAa,OAAO;AACzB,OAAK,SAAS,OAAO,UAAU,EAAE;AACjC,OAAK,UAAU,OAAO,WAAW,EAAE;AACnC,OAAK,aAAa,OAAO,aAAa,cAAc;AACpD,OAAK,iBAAiB,OAAO,aAAa,kBAAkB;AAC5D,OAAK,UAAU,OAAO;AACtB,OAAK,eAAe,OAAO,gBAAgB;;;;;CAM7C,MAAc,aAAa,MAAmC;EAC5D,MAAM,SAAS,IAAI,kBAAkB,OAAO;EAC5C,MAAM,SAAS,OAAO,SAAS,WAAW;EAC1C,MAAM,cAAc,IAAI,aAAa;EACrC,MAAMA,SAAuB,EAAE;AAE/B,QAAM,OAAO,MAAM,YAAY,OAAO,KAAK,CAAC;AAC5C,QAAM,OAAO,OAAO;EAEpB,MAAM,SAAS,OAAO,SAAS,WAAW;AAE1C,SAAO,MAAM;GACX,MAAM,EAAE,OAAO,SAAS,MAAM,OAAO,MAAM;AAC3C,OAAI,KAAM;AACV,UAAO,KAAK,MAAM;;EAGpB,MAAM,cAAc,OAAO,QAAQ,KAAK,UAAU,MAAM,MAAM,QAAQ,EAAE;EACxE,MAAM,SAAS,IAAI,WAAW,YAAY;EAC1C,IAAI,SAAS;AAEb,OAAK,MAAM,SAAS,QAAQ;AAC1B,UAAO,IAAI,OAAO,OAAO;AACzB,aAAU,MAAM;;AAGlB,SAAO;;;;;CAMT,AAAQ,YAAY,OAA6B;AAC/C,MAAI,KAAK,QACP,MAAK,QAAQ,MAAM;;;;;CAOvB,MAAc,gBAAgB,SAAiB,aAAa,GAAkB;AAC5E,MAAI;GAEF,MAAM,UAAU,IAAI,aAAa,CAAC,OAAO,QAAQ,CAAC;AAClD,OAAI,UAAU,kBAAkB;IAC9B,MAAM,QAAQ,mCAAmC,iBAAiB,gBAAgB,QAAQ;AAC1F,SAAK,YAAY,MAAM;AACvB;;GAGF,IAAIC;AACJ,OAAI,KAAK,gBAAgB;AACvB,wBAAoB,MAAM,KAAK,aAAa,QAAQ;AACpD,QAAI,kBAAkB,SAAS,kBAAkB;KAC/C,MAAM,QAAQ,8CAA8C,iBAAiB,gBAAgB,kBAAkB,OAAO;AACtH,UAAK,YAAY,MAAM;AACvB;;;GAIJ,MAAMC,UAAkC;IACtC,GAAG,KAAK;IACR,gBAAgB;IACjB;AAED,OAAI,KAAK,eACP,SAAQ,qBAAqB,KAAK;AAEpC,OAAI,KAAK,WACP,SAAQ,iBAAiB,KAAK;AAEhC,OAAI,KAAK,WACP,SAAQ,iBAAiB,KAAK;AAEhC,OAAI,KAAK,eACP,SAAQ,sBAAsB;AAEhC,OAAI,OAAO,KAAK,KAAK,OAAO,CAAC,SAAS,EACpC,SAAQ,mBAAmB,OAAO,QAAQ,KAAK,OAAO,CACnD,KAAK,CAAC,KAAK,WAAW,GAAG,IAAI,GAAG,QAAQ,CACxC,KAAK,IAAI;GAGd,MAAM,WAAY,MAAM,MAAM,KAAK,KAAK;IACtC,QAAQ;IACR;IACA,MAAM,KAAK,iBAAiB,oBAAoB;IACjD,CAAC;AAEF,OAAI,CAAC,SAAS,GACZ,OAAM,IAAI,MAAM,uBAAuB,SAAS,SAAS;WAEpD,OAAO;AACd,OAAI,aAAa,KAAK,YAAY;IAChC,MAAM,QAAQ,KAAK,iBAAiB,KAAK;AACzC,UAAM,IAAI,SAAS,YAAY,WAAW,SAAS,MAAM,CAAC;AAC1D,WAAO,KAAK,gBAAgB,SAAS,aAAa,EAAE;;AAEtD,QAAK,YAAY,iBAAiB,QAAQ,QAAQ,OAAO,MAAM,CAAC;;;CAIpE,aAAa,EAAE,UAAU,UAAU,MAAM,WAA2C;EAGlF,MAAM,UADiB,SAAS,QAAQ,QAAQ,OAAO,QAAQ,SAAS,CACzC,KAAK,IAAI;EAExC,MAAM,UAAU;GACd,UAAU;GACV,4BAAW,IAAI,MAAM,EAAC,aAAa;GACnC,GAAI,QAAQ,UAAU,OAAO,EAAE;GAChC;AAED,MAAI,QACF,SAAQ,KAAK,gBAAgB;AAG/B,OAAK,gBAAgB,KAAK,UAAU,QAAQ,CAAC;AAE7C,SAAO"}
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.5",
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.2"
34
+ "@loglayer/transport": "2.3.4"
35
35
  },
36
36
  "devDependencies": {
37
- "@types/node": "24.6.1",
37
+ "@types/node": "24.7.2",
38
38
  "serialize-error": "12.0.0",
39
- "tsup": "8.5.0",
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.8.1"
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": "tsup src/index.ts",
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",