blob: 5985644d1d6a3811ebb0fc4d77209580a1155393 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { updateAuthHeader } from "./utils";
import { updateCustomHeaders } from "./utils/headers";
let sendIntervalId = null;
/**
* Initializes the log queue processors.
* @param {Array} logs Array of logs to append to.
* @param {Object} config Configuration object to use when logging.
*/
export function initSender(logs, config) {
if (sendIntervalId !== null) {
clearInterval(sendIntervalId);
}
sendIntervalId = sendOnInterval(logs, config);
sendOnClose(logs, config);
}
/**
* Checks the provided log array on an interval, flushing the logs
* if the queue has reached the threshold specified by the provided config.
* @param {Array} logs Array of logs to read from.
* @param {Object} config Configuration object to be read from.
* @return {Number} The newly created interval id.
*/
export function sendOnInterval(logs, config) {
return setInterval(function () {
if (!config.on) {
return;
}
if (logs.length >= config.logCountThreshold) {
sendLogs(logs.slice(0), config, 0); // Send a copy
logs.splice(0); // Clear array reference (no reassignment)
}
}, config.transmitInterval);
}
/**
* Attempts to flush the remaining logs when the window is closed.
* @param {Array} logs Array of logs to be flushed.
* @param {Object} config Configuration object to be read from.
*/
export function sendOnClose(logs, config) {
window.addEventListener("pagehide", function () {
if (config.on && logs.length > 0) {
// NOTE: sendBeacon does not support auth headers,
// so this will fail if auth is required.
// The alternative is to use fetch() with keepalive: true
// https://developer.mozilla.org/en-US/docs/Web/API/Navigator/sendBeacon#description
// https://stackoverflow.com/a/73062712/9263449
navigator.sendBeacon(config.url, JSON.stringify(logs));
logs.splice(0); // clear log queue
}
});
}
/**
* Sends the provided array of logs to the specified url,
* retrying the request up to the specified number of retries.
* @param {Array} logs Array of logs to send.
* @param {string} config configuration parameters (e.g., to extract URL from & send the POST request to).
* @param {Number} retries Maximum number of attempts to send the logs.
*/
// @todo expose config object to sendLogs replate url with config.url
export function sendLogs(logs, config, retries) {
const req = new XMLHttpRequest();
const data = JSON.stringify(logs);
req.open("POST", config.url);
// Update headers
updateAuthHeader(config);
if (config.authHeader) {
req.setRequestHeader("Authorization", config.authHeader);
}
req.setRequestHeader("Content-type", "application/json;charset=UTF-8");
// Update custom headers last to allow them to over-write the defaults. This assumes
// the user knows what they are doing and may want to over-write the defaults.
updateCustomHeaders(config);
if (config.headers) {
Object.entries(config.headers).forEach(([header, value]) => {
req.setRequestHeader(header, value);
});
}
req.onreadystatechange = function () {
if (req.readyState === 4 && req.status !== 200) {
if (retries > 0) {
sendLogs(logs, config, retries--);
}
}
};
req.send(data);
}