@procube/pino-transmit-http
Version:
A pino browser transmit that send log statements over HTTP
124 lines • 4.17 kB
JavaScript
import throttle from "lodash.throttle";
import debounce from "lodash.debounce";
import { encode } from "@msgpack/msgpack";
import { gzip } from "pako";
const defaultOptions = {
throttle: 500,
asJson: false,
url: "/log",
};
function exception2string(e) {
if (typeof e === "string") {
return e;
}
if (e instanceof Error) {
if (e.stack) {
return e.stack;
}
return e.message;
}
if (typeof e.toString === "function") {
return e.toString();
}
return "Unknown error";
}
function concatenateUint8Arrays(arrayOfArrays) {
const totalLength = arrayOfArrays.reduce((total, arr) => total + arr.length, 0);
const result = new Uint8Array(totalLength);
let offset = 0;
for (const arr of arrayOfArrays) {
result.set(arr, offset);
offset += arr.length;
}
return result;
}
export default function transmitHttp(inOpts, _logger) {
const opts = { ...defaultOptions, ...inOpts };
if (typeof window === "undefined" ||
!window.navigator ||
!window.navigator.sendBeacon) {
console.error("pino-transmit-http: sendBeacon is not available.");
return undefined;
}
let collection = [];
async function rawSend() {
if (collection.length === 0) {
return;
}
try {
console.log(`sending logs: ${JSON.stringify(collection)}`);
// const collectionData = concatenateUint8Arrays(
// collection.map((item) => encode(item)),
// )
// const gzippedBody = gzip(encode(collectionData))
const body = opts.asJson ? JSON.stringify(collection) : encode(collection);
const gzippedBody = gzip(body);
collection = [];
await fetch(opts.url, {
method: "POST",
mode: "same-origin",
headers: {
"Content-Encoding": "gzip",
"Content-Type": opts.asJson ? "application/json" : "application/msgpack",
},
body: gzippedBody,
});
}
catch (e) {
console.error(`pino-transmit-http: failed to send logs: ${exception2string(e)}`);
}
}
function flush() {
if (collection.length === 0) {
return;
}
console.log(`sending logs as flush: ${JSON.stringify(collection)}`);
const collectionData = concatenateUint8Arrays(collection.map((item) => encode(item)));
collection = [];
const gzippedBody = gzip(collectionData);
window.navigator.sendBeacon(opts.url, gzippedBody);
}
let send;
if (opts.debounce !== null && opts.debounce !== undefined) {
send = debounce(rawSend, opts.debounce);
}
else if (opts.throttle !== null && opts.throttle !== undefined) {
send = throttle(rawSend, opts.throttle, {
trailing: true,
leading: false,
});
}
else {
console.warn("pino-transmit-http: Either throttle or debounce option must be passed to pino-transmit-http. Falling back to throttle by %dms", defaultOptions.throttle);
send = throttle(rawSend, defaultOptions.throttle, {
trailing: true,
leading: false,
});
}
if (typeof window !== "undefined") {
window.addEventListener("unload", function onUnload() {
send = flush;
flush(); // flush logs, no await as we are in an unload event
});
window.addEventListener("resize", function onResize() {
flush();
});
}
return {
level: opts.level,
send: function (_level, logEvent) {
try {
collection.push({
time: logEvent.ts / 1000,
level: logEvent.level.label,
...logEvent.messages[0],
});
send();
}
catch (e) {
console.error(`pino-transmit-http: Failed to transmit logs: ${exception2string(e)}`);
}
},
};
}
//# sourceMappingURL=index.js.map