@embrace-io/web-sdk
Version:
145 lines (144 loc) • 5.76 kB
JavaScript
import { getIncrementedCount } from "../../utils/getIncrementedCount.js";
import { KEY_EMB_ERROR_LOG_COUNT, KEY_EMB_EXCEPTION_CAUSE, KEY_EMB_EXCEPTION_HANDLING, KEY_EMB_EXCEPTION_NUMBER, KEY_EMB_JS_EXCEPTION_STACKTRACE, KEY_EMB_JS_FILE_BUNDLE_IDS, KEY_EMB_STATE, KEY_EMB_TYPE, KEY_EMB_UNHANDLED_EXCEPTIONS_COUNT } from "../../constants/attributes.js";
import { getVisibilityState } from "../../utils/getVisibilityState.js";
import { GLOBAL_CONFIG } from "../../utils/globalConfig.js";
import { diag } from "@opentelemetry/api";
import { ATTR_EXCEPTION_MESSAGE, ATTR_EXCEPTION_STACKTRACE, ATTR_EXCEPTION_TYPE } from "@opentelemetry/semantic-conventions";
import { SeverityNumber, logs } from "@opentelemetry/api-logs";
//#region src/managers/EmbraceLogManager/EmbraceLogManager.ts
const EMBRACE_EXCEPTION_NUMBER_STORAGE_KEY = "embrace_exception_number";
/**
* GLOBAL_CONFIG._EmbraceFileBundleIDs is populated at runtime when each file is loaded,
* based on the contents injected by the embrace-web-cli.
*/
const getJSFileBundleIDs = () => JSON.stringify(GLOBAL_CONFIG._EmbraceFileBundleIDs || {});
var EmbraceLogManager = class EmbraceLogManager {
_diag;
_perf;
_logger;
_userSessionManager;
_limitManager;
_visibilityDoc;
_storage;
constructor({ diag: diagParam, perf, userSessionManager, limitManager, loggerProvider: globalLoggerProviderOverride, visibilityDoc, storage }) {
const loggerProvider = globalLoggerProviderOverride ?? logs;
this._diag = diagParam ?? diag.createComponentLogger({ namespace: "EmbraceLogManager" });
this._perf = perf;
this._logger = loggerProvider.getLogger("embrace-web-sdk-logs");
this._userSessionManager = userSessionManager;
this._limitManager = limitManager;
this._visibilityDoc = visibilityDoc;
this._storage = storage;
}
_validateAttributes(attributes) {
if (Object.prototype.toString.call(attributes) !== "[object Object]") {
this._diag.warn("attributes must be a plain object", attributes);
return {};
}
return attributes;
}
static _logSeverityToSeverityNumber(severity) {
switch (severity) {
case "info": return SeverityNumber.INFO;
case "warning": return SeverityNumber.WARN;
default: return SeverityNumber.ERROR;
}
}
logException(error, { handled = true, attributes = {}, timestamp = this._perf.getNowMillis(), handler = "manual" } = {}) {
const validAttrs = this._validateAttributes(attributes);
if (!handled) this._userSessionManager.incrSessionPartCountForKey(KEY_EMB_UNHANDLED_EXCEPTIONS_COUNT);
const type = error?.constructor?.name || typeof error;
let errMessage = "";
let errName = type;
let errStack = "";
let errCause = "";
if (error instanceof Error) {
errMessage = String(error.message || "").trim();
errName = error.name || "";
errStack = error.stack || "";
errCause = error.cause ? "present" : "";
} else if (error && typeof error === "object") try {
errMessage = JSON.stringify(error);
} catch {
errMessage = String(error).trim();
}
else errMessage = String(error).trim();
const limited = this._limitManager.limitException(errMessage, validAttrs);
if (limited === "dropped") return;
this._logger.emit({
timestamp,
severityNumber: SeverityNumber.ERROR,
severityText: "ERROR",
body: limited.message,
attributes: {
...limited.attributes,
[KEY_EMB_TYPE]: "sys.exception",
[KEY_EMB_EXCEPTION_HANDLING]: EmbraceLogManager._exceptionHandlingType(handled, handler),
[KEY_EMB_EXCEPTION_CAUSE]: errCause,
[ATTR_EXCEPTION_TYPE]: type,
["exception.name"]: errName,
[ATTR_EXCEPTION_MESSAGE]: limited.message,
[ATTR_EXCEPTION_STACKTRACE]: errStack,
[KEY_EMB_JS_FILE_BUNDLE_IDS]: getJSFileBundleIDs(),
[KEY_EMB_STATE]: getVisibilityState(this._visibilityDoc),
[KEY_EMB_EXCEPTION_NUMBER]: getIncrementedCount(this._storage, EMBRACE_EXCEPTION_NUMBER_STORAGE_KEY, this._diag)
}
});
}
message(message, severity, { attributes = {}, includeStacktrace = true, stacktrace } = {}) {
if (typeof message !== "string" || !message.trim()) {
this._diag.warn("Message must be a non-empty string");
return;
}
if (![
"info",
"warning",
"error"
].includes(severity)) {
this._diag.warn("Severity must be info, warning, or error");
return;
}
const validAttrs = this._validateAttributes(attributes);
if (severity === "error") this._userSessionManager.incrSessionPartCountForKey(KEY_EMB_ERROR_LOG_COUNT);
let stack = "";
if (severity !== "info") {
if (typeof stacktrace === "string") stack = stacktrace;
else if (includeStacktrace) stack = (/* @__PURE__ */ new Error()).stack || "";
}
this._logMessage({
message: message.trim(),
severity,
timestamp: this._perf.getNowMillis(),
attributes: validAttrs,
stack
});
}
_logMessage({ message, severity, timestamp, attributes = {}, stack }) {
const limitedLog = this._limitManager.limitLog(message, severity, attributes);
if (limitedLog === "dropped") return;
this._logger.emit({
timestamp,
severityNumber: EmbraceLogManager._logSeverityToSeverityNumber(severity),
severityText: severity.toUpperCase(),
body: limitedLog.message,
attributes: {
...limitedLog.attributes,
[KEY_EMB_TYPE]: "sys.log",
...stack ? {
[KEY_EMB_JS_EXCEPTION_STACKTRACE]: stack,
[KEY_EMB_JS_FILE_BUNDLE_IDS]: getJSFileBundleIDs()
} : {},
[KEY_EMB_STATE]: getVisibilityState(this._visibilityDoc)
}
});
}
static _exceptionHandlingType(handled, handler) {
if (handled) return "handled";
if (handler === "global_exception") return "unhandled_error";
if (handler === "promise_rejection") return "unhandled_rejection";
return "unhandled";
}
};
//#endregion
export { EmbraceLogManager };
//# sourceMappingURL=EmbraceLogManager.js.map