-
Notifications
You must be signed in to change notification settings - Fork 16
works in node and browser #64
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
2698f5d
796fef1
8e22066
38191ae
7145e73
2c009f0
183369b
ee540a3
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,13 +1,28 @@ | ||
| "use strict"; | ||
| var NodeBlob, NodeFetch, NodeAbortController | ||
| try { | ||
| NodeBlob = require('buffer').Blob | ||
| } catch (error) { | ||
| console.log('missing buffer') | ||
| } | ||
| try { | ||
| NodeFetch = require('node-fetch') | ||
| } catch (error) { | ||
| console.log('missing node-fetch') | ||
| } | ||
| try { | ||
| NodeAbortController = require('abort-controller') | ||
| } catch (error) { | ||
| console.log('missing abort-controller') | ||
| } | ||
|
|
||
| let http = require('http'); | ||
| let https = require('https'); | ||
| let url = require('url'); | ||
| const SafeGlobalBlob = typeof Blob !== 'undefined' ? Blob : NodeBlob; | ||
| const safeGlobalFetch = typeof fetch !== 'undefined' ? fetch : NodeFetch; | ||
| const SafeGlobalAbortController = typeof AbortController !== 'undefined' ? AbortController : NodeAbortController; | ||
|
|
||
| const HEADER = '{"Events":['; | ||
| const FOOTER = "]}"; | ||
| const HEADER_FOOTER_BYTES = Buffer.byteLength(HEADER, 'utf8') + Buffer.byteLength(FOOTER, 'utf8'); | ||
|
|
||
| const HEADER_FOOTER_BYTES = (new SafeGlobalBlob([HEADER])).size + (new SafeGlobalBlob([FOOTER])).size; | ||
|
|
||
| class SeqLogger { | ||
| constructor(config) { | ||
|
|
@@ -27,7 +42,7 @@ class SeqLogger { | |
| if (!serverUrl.endsWith('/')) { | ||
| serverUrl += '/'; | ||
| } | ||
| this._endpoint = url.parse(serverUrl + 'api/events/raw'); | ||
| this._endpoint = serverUrl + 'api/events/raw'; | ||
| this._apiKey = cfg.apiKey || dflt.apiKey; | ||
| this._maxBatchingTime = cfg.maxBatchingTime || dflt.maxBatchingTime; | ||
| this._eventSizeLimit = cfg.eventSizeLimit || dflt.eventSizeLimit; | ||
|
|
@@ -43,12 +58,6 @@ class SeqLogger { | |
| this._activeShipper = null; | ||
| this._onRemoteConfigChange = cfg.onRemoteConfigChange || null; | ||
| this._lastRemoteConfig = null; | ||
|
|
||
| this._httpModule = this._endpoint.protocol === "https:" ? https : http | ||
| this._httpAgent = new this._httpModule.Agent({ | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. the reference to _httpAgent on line 96 causes a crash
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Removed |
||
| keepAlive: true, | ||
| maxTotalSockets: 25, // recommendation from https://docs.aws.amazon.com/sdk-for-javascript/v2/developer-guide/node-configuring-maxsockets.html | ||
| }); | ||
| } | ||
|
|
||
| /** | ||
|
|
@@ -101,9 +110,7 @@ class SeqLogger { | |
|
|
||
| this._closed = true; | ||
| this._clearTimer(); | ||
| return this.flush().then(() => { | ||
| this._httpAgent.destroy(); | ||
| }); | ||
| return this.flush().then(() => {}); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. the
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Correct |
||
| } | ||
|
|
||
| /** | ||
|
|
@@ -250,12 +257,12 @@ class SeqLogger { | |
| messageTemplate: "[seq] Circular structure found" | ||
| }); | ||
| } | ||
| var jsonLen = Buffer.byteLength(json, 'utf8'); | ||
| var jsonLen = new SafeGlobalBlob([json]).size; | ||
| if (jsonLen > this._eventSizeLimit) { | ||
| this._onError("[seq] Event body is larger than " + this._eventSizeLimit + " bytes: " + json); | ||
| this._queue[i] = next = this._eventTooLargeErrorEvent(next); | ||
| json = JSON.stringify(next); | ||
| jsonLen = Buffer.byteLength(json, 'utf8'); | ||
| jsonLen = new SafeGlobalBlob([json]).size; | ||
| } | ||
|
|
||
| // Always try to send a batch of at least one event, even if the batch size is | ||
|
|
@@ -284,78 +291,47 @@ class SeqLogger { | |
|
|
||
| return new Promise((resolve, reject) => { | ||
| const sendRequest = (batch, bytes) => { | ||
| const controller = new SafeGlobalAbortController(); | ||
| attempts++; | ||
| let req = this._httpModule.request({ | ||
| host: this._endpoint.hostname, | ||
| port: this._endpoint.port, | ||
| path: this._endpoint.path, | ||
| protocol: this._endpoint.protocol, | ||
| agent: this._httpAgent, | ||
| headers: { | ||
| "Content-Type": "application/json", | ||
| "X-Seq-ApiKey": this._apiKey ? this._apiKey : null, | ||
| "Content-Length": bytes, | ||
| }, | ||
| method: "POST", | ||
| timeout: this._requestTimeout | ||
| }); | ||
|
|
||
| req.on("socket", (socket) => { | ||
| if (socket.listeners("timeout").length == 0) { | ||
| socket.on("timeout", () => { | ||
| req.destroy(); | ||
| if (attempts > this._maxRetries) { | ||
| return reject('HTTP log shipping failed, reached timeout (' + this._requestTimeout + ' ms)') | ||
| } else { | ||
| return setTimeout(() => sendRequest(batch, bytes), this._retryDelay); | ||
| } | ||
| }); | ||
| } | ||
| }); | ||
|
|
||
| req.on('response', res => { | ||
| var httpErr = null; | ||
| if (res.statusCode !== 200 && res.statusCode !== 201) { | ||
| const timerId = setTimeout(() => { | ||
| controller.abort(); | ||
| if (attempts > this._maxRetries) { | ||
| reject('HTTP log shipping failed, reached timeout (' + this._requestTimeout + ' ms)'); | ||
| } else { | ||
| setTimeout(() => sendRequest(batch, bytes), this._retryDelay); | ||
| } | ||
| }, this._requestTimeout); | ||
|
|
||
| safeGlobalFetch(this._endpoint, { | ||
| keepalive: true, | ||
| method: "POST", | ||
| headers: { | ||
| "Content-Type": "application/json", | ||
| "X-Seq-ApiKey": this._apiKey ? this._apiKey : null, | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In my testing this is not working properly.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How did you test it? |
||
| "Content-Length": bytes, | ||
| }, | ||
| body: `${HEADER}${batch.join(',')}${FOOTER}`, | ||
| signal: controller.signal, | ||
| }) | ||
| .then((res) => { | ||
| clearTimeout(timerId); | ||
| let httpErr = null; | ||
| if (res.status !== 200 && res.status !== 201) { | ||
| httpErr = 'HTTP log shipping failed: ' + res.statusCode; | ||
| } | ||
|
|
||
| res.on('data', (buffer) => { | ||
| let dataRaw = buffer.toString(); | ||
|
|
||
| if (this._onRemoteConfigChange && this._lastRemoteConfig !== dataRaw) { | ||
| this._lastRemoteConfig = dataRaw; | ||
| this._onRemoteConfigChange(JSON.parse(dataRaw)); | ||
| if (httpErr !== null) { | ||
| if (this._httpOrNetworkError(res) && attempts < this._maxRetries) { | ||
| return setTimeout(() => sendRequest(batch, bytes), this._retryDelay); | ||
| } | ||
| }); | ||
|
|
||
| res.on('error', e => { | ||
| return reject(e); | ||
| }); | ||
| res.on('end', () => { | ||
| if (httpErr !== null) { | ||
| if (this._httpOrNetworkError(res) && attempts < this._maxRetries) { | ||
| return setTimeout(() => sendRequest(batch, bytes), this._retryDelay); | ||
| } | ||
| return reject(httpErr); | ||
| } else { | ||
| return resolve(true); | ||
| } | ||
| }); | ||
| }); | ||
|
|
||
| req.on('error', e => { | ||
| return reject(e); | ||
| }); | ||
|
|
||
| req.write(HEADER); | ||
| var delim = ""; | ||
| for (var b = 0; b < batch.length; b++) { | ||
| req.write(delim); | ||
| delim = ","; | ||
| req.write(batch[b]); | ||
| } | ||
| req.write(FOOTER); | ||
| req.end(); | ||
| return reject(httpErr); | ||
| } else { | ||
| return resolve(true); | ||
| } | ||
| }) | ||
| .catch((err) => { | ||
| clearTimeout(timerId); | ||
| reject(err); | ||
| }) | ||
| } | ||
|
|
||
| return sendRequest(batch, bytes); | ||
|
|
@@ -369,8 +345,7 @@ class SeqLogger { | |
|
|
||
| // CORS-safelisted for the Content-Type request header | ||
| const options = { type: 'text/plain' }; | ||
|
|
||
| const endpointWithKey = Object.assign({}, this._endpoint, { query: { 'apiKey': this._apiKey } }); | ||
| const endpointWithKey = Object.assign({}, new URL(this._endpoint), { query: { 'apiKey': this._apiKey } }); | ||
|
|
||
| return { | ||
| dataParts, | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fetchcan be polyfilled by referencing node-fetch@2 and then something like:const fetchApi = typeof(fetch) === 'undefined' ? require('node-fetch') : fetch;then it works in node 16
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Slightly different approach but done!