@embrace-io/web-sdk
Version:
324 lines (323 loc) • 14.9 kB
JavaScript
import { NamespacedStorage } from "../utils/NamespacedStorage/NamespacedStorage.js";
import { nsfConfigValidation } from "../utils/nsfConfigValidation.js";
import { OTelPerformanceManager, updateZeroTimeMillis } from "../utils/PerformanceManager/OTelPerformanceManager.js";
import { log } from "../api-logs/logAPI.js";
import { page } from "../api-page/pageAPI.js";
import { session } from "../api-sessions/sessionAPI.js";
import { trace as trace$1 } from "../api-traces/traceAPI.js";
import { user } from "../api-users/userAPI.js";
import { EmbraceLogExporter } from "../exporters/EmbraceLogExporter/EmbraceLogExporter.js";
import { EmbraceTraceExporter } from "../exporters/EmbraceTraceExporter/EmbraceTraceExporter.js";
import { EmbraceDynamicConfigManager } from "../managers/EmbraceConfigManager/EmbraceDynamicConfigManager.js";
import { DEFAULT_LIMITS } from "../managers/EmbraceLimitManager/constants.js";
import { EmbraceLimitManager } from "../managers/EmbraceLimitManager/EmbraceLimitManager.js";
import { EmbraceLogManager } from "../managers/EmbraceLogManager/EmbraceLogManager.js";
import { EmbracePageManager } from "../managers/EmbracePageManager/EmbracePageManager.js";
import { EmbraceSDKFeaturesManager } from "../managers/EmbraceSDKFeaturesManager/EmbraceSDKFeaturesManager.js";
import { EmbraceTraceManager } from "../managers/EmbraceTraceManager/EmbraceTraceManager.js";
import { EmbraceUserManager } from "../managers/EmbraceUserManager/EmbraceUserManager.js";
import { EmbraceUserSessionManager } from "../managers/EmbraceUserSessionManager/EmbraceUserSessionManager.js";
import { BrowserLogRecordProcessor } from "../processors/BrowserLogRecordProcessor/BrowserLogRecordProcessor.js";
import { BrowserSpanProcessor } from "../processors/BrowserSpanProcessor/BrowserSpanProcessor.js";
import { EmbraceLogRecordProcessor } from "../processors/EmbraceLogRecordProcessor/EmbraceLogRecordProcessor.js";
import { EmbraceNetworkSpanProcessor } from "../processors/EmbraceNetworkSpanProcessor/EmbraceNetworkSpanProcessor.js";
import { EmbraceSessionPartBatchedSpanProcessor } from "../processors/EmbraceSessionPartBatchedSpanProcessor/EmbraceSessionPartBatchedSpanProcessor.js";
import { LogRecordScrubProcessor } from "../processors/LogRecordScrubProcessor/LogRecordScrubProcessor.js";
import { PageLogRecordProcessor } from "../processors/PageLogRecordProcessor/PageLogRecordProcessor.js";
import { PageSpanProcessor } from "../processors/PageSpanProcessor/PageSpanProcessor.js";
import { SignalCorrelationLogRecordProcessor } from "../processors/SignalCorrelationLogRecordProcessor/SignalCorrelationLogRecordProcessor.js";
import { SignalCorrelationSpanProcessor } from "../processors/SignalCorrelationSpanProcessor/SignalCorrelationSpanProcessor.js";
import { SpanScrubProcessor } from "../processors/SpanScrubProcessor/SpanScrubProcessor.js";
import { UserLogRecordProcessor } from "../processors/UserLogRecordProcessor/UserLogRecordProcessor.js";
import { UserSessionLogRecordProcessor } from "../processors/UserSessionLogRecordProcessor/UserSessionLogRecordProcessor.js";
import { UserSpanProcessor } from "../processors/UserSpanProcessor/UserSpanProcessor.js";
import { SignalBuffer } from "../processors/utils/SignalBuffer.js";
import { EmbraceW3CTraceContextPropagator } from "../propagators/EmbraceW3CTraceContextPropagator/EmbraceW3CTraceContextPropagator.js";
import { getWebSDKOverridableResource, getWebSDKResource } from "../resources/webSdkResource.js";
import { getDefaultAttributeScrubbers } from "./defaultAttributeScrubbers.js";
import { registry } from "./registry.js";
import { setupDefaultInstrumentations } from "./setupDefaultInstrumentations.js";
import { validateAppID, validateAppVersion } from "./utils.js";
import { DiagConsoleLogger, DiagLogLevel, diag } from "@opentelemetry/api";
import { logs } from "@opentelemetry/api-logs";
import { registerInstrumentations } from "@opentelemetry/instrumentation";
import { emptyResource } from "@opentelemetry/resources";
import { BatchLogRecordProcessor, LoggerProvider } from "@opentelemetry/sdk-logs";
import { BatchSpanProcessor } from "@opentelemetry/sdk-trace";
import { StackContextManager, WebTracerProvider } from "@opentelemetry/sdk-trace-web";
//#region src/sdk/initSDK.ts
const initSDK = ({ appID, appVersion, resource = emptyResource(), spanExporters = [], logExporters = [], spanProcessors = [], propagator = null, defaultInstrumentationConfig, instrumentations = [], contextManager = null, logProcessors = [], attributeScrubbers = [], enableDefaultAttributeScrubbing = true, additionalQueryParamsToScrub = [], logLevel = DiagLogLevel.ERROR, embraceDataURL, embraceConfigURL, diagLogger = diag.createComponentLogger({ namespace: "embrace-sdk" }), dynamicSDKConfigManager: providedDynamicSDKConfigManager, dynamicSDKConfig, registerGlobally = true, blockNetworkSpanForwarding = false, restrictedProtocols = /* @__PURE__ */ new Set(["file:"]), useDocumentTitleAsPageLabel = true } = {}) => {
try {
if (registerGlobally) {
const existingSDK = registry.registered();
if (existingSDK !== null) {
diagLogger.warn("SDK has already been successfully initialized, skipping this invocation of initSDK");
return existingSDK;
}
}
diag.setLogger(new DiagConsoleLogger(), { logLevel });
if (typeof window === "undefined") {
diagLogger.warn("browser not detected, skipping initialization");
return false;
}
const perf = new OTelPerformanceManager();
const initSDKStart = perf.getNowMillis();
window.addEventListener("pageshow", (event) => updateZeroTimeMillis(perf.epochMillisFromOrigin(event.timeStamp)));
const validatedAppID = validateAppID(appID);
const validatedAppVersion = validateAppVersion(appVersion);
const sendingToEmbrace = validatedAppID !== void 0;
if (!sendingToEmbrace && !logExporters.length && !spanExporters.length) throw new Error("when appID is omitted, at least one logExporter or spanExporter must be set");
if (restrictedProtocols.has(window.location.protocol)) throw new Error(`not initializing due to restricted protocol: ${window.location.protocol}`);
if (sendingToEmbrace && typeof CompressionStream === "undefined") throw new Error("CompressionStream is not supported in this browser and required for data compression.");
const namespace = appID ?? void 0;
const sdkLocalStorage = new NamespacedStorage({
namespace,
storage: window.localStorage,
diag: diagLogger
});
const sdkTabStorage = new NamespacedStorage({
namespace,
storage: window.sessionStorage,
diag: diagLogger
});
const resourceWithWebSDKAttributes = getWebSDKOverridableResource().merge(resource).merge(getWebSDKResource({
diagLogger,
appVersion: validatedAppVersion,
tabStorage: sdkTabStorage
}));
const userManager = setupUser({
registerGlobally,
sdkLocalStorage
});
const enduserPseudoID = userManager.getEmbraceUserId();
if (sendingToEmbrace && !enduserPseudoID) throw new Error("userID is required when using Embrace exporter");
const dynamicConfigManager = providedDynamicSDKConfigManager ?? new EmbraceDynamicConfigManager({
appID: validatedAppID,
appVersion: validatedAppVersion,
embraceConfigURL,
defaultConfig: dynamicSDKConfig,
deviceId: enduserPseudoID,
storage: sdkLocalStorage
});
dynamicConfigManager.refreshRemoteConfig();
const sdkFeaturesManager = new EmbraceSDKFeaturesManager({
dynamicConfigManager,
deviceId: enduserPseudoID,
blockNetworkSpanForwarding
});
if (!sdkFeaturesManager.isSDKEnabled()) {
diagLogger.debug("SDK is disabled, skipping initialization.");
return false;
}
const nsfValid = nsfConfigValidation({
featureManager: sdkFeaturesManager,
diag: diagLogger,
registerGlobally,
defaultInstrumentationConfig,
propagator
});
const limitManager = new EmbraceLimitManager(DEFAULT_LIMITS);
const finalAttributeScrubbers = [...enableDefaultAttributeScrubbing ? getDefaultAttributeScrubbers(additionalQueryParamsToScrub) : [], ...attributeScrubbers];
const userSessionManager = setupUserSession({
limitManager,
perf,
registerGlobally,
sdkLocalStorage,
visibilityDoc: window.document,
dynamicConfigManager
});
let embraceSpanProcessor;
let embraceLogProcessor;
if (sendingToEmbrace) {
embraceSpanProcessor = new EmbraceSessionPartBatchedSpanProcessor({
exporter: new EmbraceTraceExporter({
appID: validatedAppID,
embraceDataURL,
userID: enduserPseudoID
}),
limitManager,
userSessionManager
});
embraceLogProcessor = new BatchLogRecordProcessor({ exporter: new EmbraceLogExporter({
appID: validatedAppID,
embraceDataURL,
userID: enduserPseudoID
}) });
}
const pageManager = setupPage({
useDocumentTitleAsPageLabel,
registerGlobally,
userSessionManager,
perf
});
const signalBuffer = !defaultInstrumentationConfig?.omit?.has("soft-navigation-performance") ? new SignalBuffer() : void 0;
const { tracerProvider, embraceTraceManager } = setupTraces({
resource: resourceWithWebSDKAttributes,
userManager,
spanExporters,
spanProcessors,
propagator: nsfValid ? new EmbraceW3CTraceContextPropagator() : propagator,
contextManager,
attributeScrubbers: finalAttributeScrubbers,
registerGlobally,
embraceSpanProcessor,
pageManager,
signalBuffer
});
userSessionManager.setTracerProvider(tracerProvider);
userSessionManager.startSessionPartInternal({ reason: "init" });
const { loggerProvider, embraceLogManager } = setupLogs({
resource: resourceWithWebSDKAttributes,
userManager,
logExporters,
logProcessors,
userSessionManager,
limitManager,
attributeScrubbers: finalAttributeScrubbers,
perf,
registerGlobally,
embraceLogProcessor,
sdkLocalStorage,
pageManager,
visibilityDoc: window.document,
signalBuffer
});
if (!registerGlobally) registerInstrumentations({
tracerProvider,
loggerProvider,
instrumentations: [setupDefaultInstrumentations(defaultInstrumentationConfig, {
logManager: embraceLogManager,
userSessionManager,
pageManager,
limitManager,
signalBuffer
}), ...instrumentations]
});
else registerInstrumentations({ instrumentations: [setupDefaultInstrumentations(defaultInstrumentationConfig, {
pageManager,
limitManager,
signalBuffer
}), ...instrumentations] });
diagLogger.info("successfully initialized the SDK");
const sdkControl = {
setDynamicConfig: (config) => {
dynamicConfigManager.setConfig(config);
},
flush: async () => {
await tracerProvider.forceFlush();
await loggerProvider.forceFlush();
},
log: embraceLogManager,
trace: embraceTraceManager,
session: userSessionManager,
user: userManager,
page: pageManager
};
if (registerGlobally) registry.register(sdkControl);
userSessionManager.recordSDKStartupDuration(perf.getNowMillis() - initSDKStart);
return sdkControl;
} catch (e) {
const message = e instanceof Error ? e.message : "Unknown error.";
diagLogger.error(`failed to initialize the SDK: ${message}`);
return false;
}
};
const setupUser = ({ registerGlobally, sdkLocalStorage }) => {
const embraceUserManager = new EmbraceUserManager({ storage: sdkLocalStorage });
if (registerGlobally) user.setGlobalUserManager(embraceUserManager);
return embraceUserManager;
};
const setupUserSession = ({ limitManager, perf, registerGlobally, sdkLocalStorage, visibilityDoc, dynamicConfigManager }) => {
const embraceUserSessionManager = new EmbraceUserSessionManager({
limitManager,
perf,
storage: sdkLocalStorage,
visibilityDoc,
dynamicConfigManager
});
if (registerGlobally) session.setGlobalUserSessionManager(embraceUserSessionManager);
return embraceUserSessionManager;
};
const setupTraces = ({ resource, userManager, spanExporters, spanProcessors = [], propagator = null, contextManager = null, attributeScrubbers, registerGlobally, embraceSpanProcessor, pageManager, signalBuffer }) => {
const finalSpanProcessors = [
...spanProcessors,
new BrowserSpanProcessor(),
new EmbraceNetworkSpanProcessor(),
new UserSpanProcessor({ userManager }),
new PageSpanProcessor({ pageManager }),
new SpanScrubProcessor({ attributeScrubbers }),
...signalBuffer ? [new SignalCorrelationSpanProcessor({ buffer: signalBuffer })] : []
];
spanExporters?.forEach((exporter) => {
finalSpanProcessors.push(new BatchSpanProcessor({ exporter }));
});
if (embraceSpanProcessor) finalSpanProcessors.push(embraceSpanProcessor);
const tracerProvider = new WebTracerProvider({
resource,
spanProcessors: finalSpanProcessors,
spanLimits: {
attributeCountLimit: DEFAULT_LIMITS.maxAllowed.session_property * 2,
eventCountLimit: DEFAULT_LIMITS.maxAllowed.breadcrumb * 2
}
});
const embraceTraceManager = new EmbraceTraceManager({ tracerProvider: registerGlobally ? void 0 : tracerProvider });
if (registerGlobally) {
trace$1.setGlobalTraceManager(embraceTraceManager);
tracerProvider.register({
contextManager: contextManager || new StackContextManager(),
propagator
});
}
return {
tracerProvider,
embraceTraceManager
};
};
const setupLogs = ({ resource, userManager, logExporters, logProcessors, userSessionManager, limitManager, attributeScrubbers, perf, registerGlobally, embraceLogProcessor, sdkLocalStorage, pageManager, visibilityDoc, signalBuffer }) => {
const finalLogProcessors = [
...logProcessors,
new UserSessionLogRecordProcessor({ userSessionManager }),
new BrowserLogRecordProcessor(),
new EmbraceLogRecordProcessor(),
new UserLogRecordProcessor({ userManager }),
new LogRecordScrubProcessor({ attributeScrubbers }),
new PageLogRecordProcessor({ pageManager }),
...signalBuffer ? [new SignalCorrelationLogRecordProcessor({ buffer: signalBuffer })] : []
];
logExporters?.forEach((exporter) => {
finalLogProcessors.push(new BatchLogRecordProcessor({ exporter }));
});
if (embraceLogProcessor) finalLogProcessors.push(embraceLogProcessor);
const loggerProvider = new LoggerProvider({
resource,
processors: finalLogProcessors
});
const embraceLogManager = new EmbraceLogManager({
userSessionManager,
limitManager,
loggerProvider: registerGlobally ? void 0 : loggerProvider,
perf,
storage: sdkLocalStorage,
visibilityDoc
});
if (registerGlobally) {
logs.setGlobalLoggerProvider(loggerProvider);
log.setGlobalLogManager(embraceLogManager);
}
return {
loggerProvider,
embraceLogManager
};
};
const setupPage = ({ useDocumentTitleAsPageLabel, registerGlobally, userSessionManager, perf }) => {
const embracePageManager = new EmbracePageManager({
useDocumentTitleAsPageLabel,
userSessionManager,
perf
});
if (registerGlobally) page.setGlobalPageManager(embracePageManager);
return embracePageManager;
};
//#endregion
export { initSDK };
//# sourceMappingURL=initSDK.js.map