Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
4cc5aa8
feat: Migrate Logger to Typescript (#1120)
gtamanaha Nov 25, 2025
7358464
feat: Create Logging Service
gtamanaha Nov 24, 2025
7abe3fe
fix(apiClient): Update upload URL construction in APIClient and clean…
gtamanaha Nov 25, 2025
c646b0c
fix(tests): Update reportingLogger tests to use globalThis instead of…
gtamanaha Nov 25, 2025
5cef096
refactor(mParticleInstance): Simplify initialization by removing kitB…
gtamanaha Nov 27, 2025
8fa5adb
test(apiClient): Add unit tests for sendLogToServer method with Fetch…
gtamanaha Nov 27, 2025
85f99db
refactor(reportingLogger): Add return types to private methods for im…
gtamanaha Nov 27, 2025
dcd3d84
refactor(reportingLogger): Allow optional rate limiter in constructor…
gtamanaha Nov 27, 2025
04c9e79
refactor(reportingLogger): Change rateLimiter type to IRateLimiter an…
gtamanaha Nov 27, 2025
cb0222e
fix(tests): Replace global references from 'global' to 'globalThis' i…
gtamanaha Nov 27, 2025
c8dc013
refactor(logging): Move sendLogToServer method from APIClient to Repo…
gtamanaha Dec 2, 2025
8746bd1
refactor(apiClient): Remove unused LogRequest import to clean up code
gtamanaha Dec 2, 2025
cac1c3c
feat(reportingLogger): Add 'rokt-account-id' header and update constr…
gtamanaha Dec 5, 2025
5581588
refactor(errorCodes, reportingLogger): Replace ErrorCodes type defini…
gtamanaha Dec 9, 2025
42a67c0
feat(reportingLogger): Introduce default user agent and URL handling …
gtamanaha Dec 10, 2025
4de0bc7
fix(reportingLogger): Update default account ID to '0' and adjust cor…
gtamanaha Dec 10, 2025
c268646
refactor(reportingLogger): Replace LogRequest with IWSDKError and upd…
gtamanaha Jan 5, 2026
4e7e0cb
refactor(reportingLogger, wsdk-error): Update error logging structure…
gtamanaha Jan 5, 2026
0d48826
feat(reportingLogger): Add feature flag support to ReportingLogger co…
gtamanaha Jan 5, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions src/batchUploader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -259,15 +259,15 @@ export class BatchUploader {
return;
}

const { verbose } = this.mpInstance.Logger;
const { Logger } = this.mpInstance;

this.eventsQueuedForProcessing.push(event);
if (this.offlineStorageEnabled && this.eventVault) {
this.eventVault.store(this.eventsQueuedForProcessing);
}

verbose(`Queuing event: ${JSON.stringify(event)}`);
verbose(`Queued event count: ${this.eventsQueuedForProcessing.length}`);
Logger.verbose(`Queuing event: ${JSON.stringify(event)}`);
Logger.verbose(`Queued event count: ${this.eventsQueuedForProcessing.length}`);

if (this.shouldTriggerImmediateUpload(event.EventDataType)) {
this.prepareAndUpload(false, false);
Expand Down
23 changes: 11 additions & 12 deletions src/identityApiClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,12 +88,12 @@ export default function IdentityAPIClient(
aliasRequest: IAliasRequest,
aliasCallback: IAliasCallback
) {
const { verbose, error } = mpInstance.Logger;
const { Logger } = mpInstance;
const { invokeAliasCallback } = mpInstance._Helpers;
const { aliasUrl } = mpInstance._Store.SDKConfig;
const { devToken: apiKey } = mpInstance._Store;

verbose(Messages.InformationMessages.SendAliasHttp);
Logger.verbose(Messages.InformationMessages.SendAliasHttp);

// https://go.mparticle.com/work/SQDSDKS-6750
const uploadUrl = `https://${aliasUrl}${apiKey}/Alias`;
Expand Down Expand Up @@ -136,7 +136,7 @@ export default function IdentityAPIClient(
try {
aliasResponseBody = await response.json();
} catch (e) {
verbose('The request has no response body');
Logger.verbose('The request has no response body');
}
} else {
// https://go.mparticle.com/work/SQDSDKS-6568
Expand Down Expand Up @@ -171,11 +171,11 @@ export default function IdentityAPIClient(

}

verbose(message);
Logger.verbose(message);
invokeAliasCallback(aliasCallback, response.status, errorMessage);
} catch (e) {
const errorMessage = (e as Error).message || e.toString();
error('Error sending alias request to mParticle servers. ' + errorMessage);
Logger.error('Error sending alias request to mParticle servers. ' + errorMessage);
invokeAliasCallback(
aliasCallback,
HTTPCodes.noHttpCoverage,
Expand All @@ -193,15 +193,14 @@ export default function IdentityAPIClient(
mpid: MPID,
knownIdentities: UserIdentities
) {
const { verbose, error } = mpInstance.Logger;
const { invokeCallback } = mpInstance._Helpers;

verbose(Messages.InformationMessages.SendIdentityBegin);
const { Logger } = mpInstance;
Logger.verbose(Messages.InformationMessages.SendIdentityBegin);
if (!identityApiRequest) {
error(Messages.ErrorMessages.APIRequestEmpty);
Logger.error(Messages.ErrorMessages.APIRequestEmpty);
return;
}
verbose(Messages.InformationMessages.SendIdentityHttp);
Logger.verbose(Messages.InformationMessages.SendIdentityHttp);

if (mpInstance._Store.identityCallInFlight) {
invokeCallback(
Expand Down Expand Up @@ -289,7 +288,7 @@ export default function IdentityAPIClient(

mpInstance._Store.identityCallInFlight = false;

verbose(message);
Logger.verbose(message);
parseIdentityResponse(
identityResponse,
previousMPID,
Expand All @@ -304,7 +303,7 @@ export default function IdentityAPIClient(

const errorMessage = (err as Error).message || err.toString();

error('Error sending identity request to servers' + ' - ' + errorMessage);
Logger.error('Error sending identity request to servers' + ' - ' + errorMessage);
invokeCallback(
callback,
HTTPCodes.noHttpCoverage,
Expand Down
61 changes: 0 additions & 61 deletions src/logger.js

This file was deleted.

66 changes: 66 additions & 0 deletions src/logger.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { LogLevelType, SDKInitConfig, SDKLoggerApi } from './sdkRuntimeModels';

export type ILoggerConfig = Pick<SDKInitConfig, 'logLevel' | 'logger'>;
export type IConsoleLogger = Partial<Pick<SDKLoggerApi, 'error' | 'warning' | 'verbose'>>;

export class Logger {
private logLevel: LogLevelType;
private logger: IConsoleLogger;

constructor(config: ILoggerConfig) {
this.logLevel = config.logLevel ?? LogLevelType.Warning;
this.logger = config.logger ?? new ConsoleLogger();
}

public verbose(msg: string): void {
if(this.logLevel === LogLevelType.None)
return;

if (this.logger.verbose && this.logLevel === LogLevelType.Verbose) {
this.logger.verbose(msg);
}
}

public warning(msg: string): void {
if(this.logLevel === LogLevelType.None)
return;

if (this.logger.warning &&
(this.logLevel === LogLevelType.Verbose || this.logLevel === LogLevelType.Warning)) {
this.logger.warning(msg);
}
}

public error(msg: string): void {
if(this.logLevel === LogLevelType.None)
return;

if (this.logger.error) {
this.logger.error(msg);
}
}

public setLogLevel(newLogLevel: LogLevelType): void {
this.logLevel = newLogLevel;
}
}

export class ConsoleLogger implements IConsoleLogger {
public verbose(msg: string): void {
if (console && console.info) {
console.info(msg);
}
}

public error(msg: string): void {
if (console && console.error) {
console.error(msg);
}
}

public warning(msg: string): void {
if (console && console.warn) {
console.warn(msg);
}
}
}
7 changes: 7 additions & 0 deletions src/logging/errorCodes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { valueof } from '../utils';

export type ErrorCodes = valueof<typeof ErrorCodes>;

export const ErrorCodes = {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this should be ErrorMessages. ErrorCodes sounds like they'd be something numerical like HTTP Error Codes (404, 429, etc).

Are you naming this ErrorMessages because that's something defined on the server side?

UNHANDLED_EXCEPTION: 'UNHANDLED_EXCEPTION',
} as const;
140 changes: 140 additions & 0 deletions src/logging/reportingLogger.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@

import { ErrorCodes } from "./errorCodes";
import { IWSDKError, WSDKErrorSeverity } from "./wsdk-error";
import { FetchUploader, XHRUploader } from "../uploaders";

export interface IReportingLogger {
error(msg: string, code?: ErrorCodes, stack?: string): void;
warning(msg: string, code?: ErrorCodes): void;
}

export class ReportingLogger implements IReportingLogger {
private readonly isEnabled: boolean;
private readonly reporter: string = 'mp-wsdk';
private readonly integration: string = 'mp-wsdk';
private readonly rateLimiter: IRateLimiter;

constructor(
private baseUrl: string,
private readonly isFeatureFlagEnabled: boolean,
private readonly sdkVersion: string,
private readonly accountId: string,
private readonly roktLauncherInstanceGuid: string,
rateLimiter?: IRateLimiter,
) {
this.isEnabled = this.isReportingEnabled();
this.rateLimiter = rateLimiter ?? new RateLimiter();
}

public error(msg: string, code: ErrorCodes, name?: string, stack?: string) {
this.sendLog(WSDKErrorSeverity.ERROR, msg, code, name, stack);
};

public warning(msg: string, code: ErrorCodes, name?: string) {
this.sendLog(WSDKErrorSeverity.WARNING, msg, code, name);
};

private sendLog(
severity: WSDKErrorSeverity,
msg: string,
code: ErrorCodes,
name?: string,
stack?: string
): void {
if(!this.canSendLog(severity))
return;

const wsdkError: IWSDKError = {
name: name,
message: msg,
additionalInformation: {
message: msg,
version: this.sdkVersion,

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should version be here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see logs using this for version. Open to suggestion

url: window?.location?.href,
},
severity: severity,
code: code,
stack: stack,
reporter: this.reporter,
integration: this.integration,
};

this.sendLogToServer(wsdkError);
}

private isReportingEnabled(): boolean {
return (
this.isRoktDomainPresent() &&
(this.isFeatureFlagEnabled ||
this.isDebugModeEnabled())
);
}

private isRoktDomainPresent(): boolean {
return Boolean(window['ROKT_DOMAIN']);
}

private isDebugModeEnabled(): boolean {
return (
window.
location?.
search?.
toLowerCase()?.
includes('mp_enable_logging=true') ?? false
);
}

private canSendLog(severity: WSDKErrorSeverity): boolean {
return this.isEnabled && !this.isRateLimited(severity);
}

private isRateLimited(severity: WSDKErrorSeverity): boolean {
return this.rateLimiter.incrementAndCheck(severity);
}

private sendLogToServer(wsdkError: IWSDKError) {
const uploadUrl = `${this.baseUrl}/v1/log`;
const uploader = window.fetch
? new FetchUploader(uploadUrl)
: new XHRUploader(uploadUrl);

const headers = {
Accept: 'text/plain;charset=UTF-8',
'Content-Type': 'text/plain;charset=UTF-8',
'rokt-launcher-instance-guid': this.roktLauncherInstanceGuid,
};

if (this.accountId) {
headers['rokt-account-id'] = this.accountId;
}

uploader.upload({
headers: headers,
method: 'POST',
body: JSON.stringify(wsdkError),
});
};
}

export interface IRateLimiter {
incrementAndCheck(severity: WSDKErrorSeverity): boolean;
}

export class RateLimiter implements IRateLimiter {
private readonly rateLimits: Map<WSDKErrorSeverity, number> = new Map([
[WSDKErrorSeverity.ERROR, 10],
[WSDKErrorSeverity.WARNING, 10],
[WSDKErrorSeverity.INFO, 10],
]);
private logCount: Map<WSDKErrorSeverity, number> = new Map();

public incrementAndCheck(severity: WSDKErrorSeverity): boolean {
const count = this.logCount.get(severity) || 0;
const limit = this.rateLimits.get(severity) || 10;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we want 10 to be the default rate limit, we can then reuse a constant here.


const newCount = count + 1;
this.logCount.set(severity, newCount);

return newCount > limit;
}
}
20 changes: 20 additions & 0 deletions src/logging/wsdk-error.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { ErrorCodes } from "./errorCodes";

export type WSDKErrorSeverity = (typeof WSDKErrorSeverity)[keyof typeof WSDKErrorSeverity];
export const WSDKErrorSeverity = {
ERROR: 'ERROR',
INFO: 'INFO',
WARNING: 'WARNING',
} as const;

export interface IWSDKError {
name: string;
message: string;
stack?: string;
code?: ErrorCodes;
reporter?: string;
integration?: string;
severity?: WSDKErrorSeverity;
additionalInformation?: Record<string, string>;
handled?: boolean;
}
Loading
Loading