-
Notifications
You must be signed in to change notification settings - Fork 57
feat: Create logging service #1137
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: development
Are you sure you want to change the base?
Changes from all commits
732d2f0
fc4681b
7ff3c41
28b8892
0b3dc81
c095cbd
6fe5d08
7e41e15
2b829ce
9934b81
1d1ebfa
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,15 +1,21 @@ | ||
| import { LogLevelType, SDKInitConfig, SDKLoggerApi } from './sdkRuntimeModels'; | ||
| import { ReportingLogger } from './logging/reportingLogger'; | ||
| import { ErrorCodes } from './logging/types'; | ||
|
|
||
| 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; | ||
| private reportingLogger: ReportingLogger; | ||
|
|
||
| constructor(config: ILoggerConfig) { | ||
| constructor(config: ILoggerConfig, | ||
| reportingLogger?: ReportingLogger, | ||
| ) { | ||
| this.logLevel = config.logLevel ?? LogLevelType.Warning; | ||
| this.logger = config.logger ?? new ConsoleLogger(); | ||
| this.reportingLogger = reportingLogger; | ||
| } | ||
|
|
||
| public verbose(msg: string): void { | ||
|
|
@@ -22,21 +28,24 @@ export class Logger { | |
| } | ||
|
|
||
| public warning(msg: string): void { | ||
| if(this.logLevel === LogLevelType.None) | ||
| if(this.logLevel === LogLevelType.None) | ||
| return; | ||
|
|
||
| if (this.logger.warning && | ||
| 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) | ||
| public error(msg: string, code?: ErrorCodes): void { | ||
| if(this.logLevel === LogLevelType.None) | ||
| return; | ||
|
|
||
| if (this.logger.error) { | ||
| this.logger.error(msg); | ||
| if (code) { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm unsure if this is a good way to determine whether to report the error to our backend.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. +1 on renaming param name to |
||
| this.reportingLogger?.error(msg, code); | ||
| } | ||
| } | ||
| } | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,185 @@ | ||||||||||||||||||||||||||||||||||||||||||
| import { ErrorCodes, LogRequestBody, WSDKErrorSeverity } from "./types"; | ||||||||||||||||||||||||||||||||||||||||||
| import { FetchUploader, IFetchPayload } from "../uploaders"; | ||||||||||||||||||||||||||||||||||||||||||
| import { IStore, SDKConfig } from "../store"; | ||||||||||||||||||||||||||||||||||||||||||
| import { SDKInitConfig } from "../sdkRuntimeModels"; | ||||||||||||||||||||||||||||||||||||||||||
| import Constants from "../constants"; | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| interface IReportingLoggerPayload extends IFetchPayload { | ||||||||||||||||||||||||||||||||||||||||||
| headers: IFetchPayload['headers'] & { | ||||||||||||||||||||||||||||||||||||||||||
| 'rokt-launcher-instance-guid'?: string; | ||||||||||||||||||||||||||||||||||||||||||
| 'rokt-launcher-version': string; | ||||||||||||||||||||||||||||||||||||||||||
| 'rokt-wsdk-version': string; | ||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||
| body: string; | ||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| export class ReportingLogger { | ||||||||||||||||||||||||||||||||||||||||||
| private readonly isEnabled: boolean; | ||||||||||||||||||||||||||||||||||||||||||
| private readonly reporter: string = 'mp-wsdk'; | ||||||||||||||||||||||||||||||||||||||||||
| private readonly integration: string = 'mp-wsdk'; | ||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+18
to
+19
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Would these ver differ and if not should we make them one string? or have a constant and set each to the constant?
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @mattbodle what is the actual difference between these two? |
||||||||||||||||||||||||||||||||||||||||||
| private readonly rateLimiter: IRateLimiter; | ||||||||||||||||||||||||||||||||||||||||||
| private store: IStore | null; | ||||||||||||||||||||||||||||||||||||||||||
| private readonly loggingUrl: string; | ||||||||||||||||||||||||||||||||||||||||||
| private readonly errorUrl: string; | ||||||||||||||||||||||||||||||||||||||||||
| private readonly isWebSdkLoggingEnabled: boolean; | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| constructor( | ||||||||||||||||||||||||||||||||||||||||||
| config: SDKConfig | SDKInitConfig | any, | ||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||
| private readonly sdkVersion: string, | ||||||||||||||||||||||||||||||||||||||||||
| store?: IStore, | ||||||||||||||||||||||||||||||||||||||||||
| private readonly launcherInstanceGuid?: string, | ||||||||||||||||||||||||||||||||||||||||||
| rateLimiter?: IRateLimiter, | ||||||||||||||||||||||||||||||||||||||||||
| ) { | ||||||||||||||||||||||||||||||||||||||||||
| this.loggingUrl = `https://${config.loggingUrl || Constants.DefaultBaseUrls.loggingUrl}`; | ||||||||||||||||||||||||||||||||||||||||||
| this.errorUrl = `https://${config.errorUrl || Constants.DefaultBaseUrls.errorUrl}`; | ||||||||||||||||||||||||||||||||||||||||||
| this.isWebSdkLoggingEnabled = config.isWebSdkLoggingEnabled || false; | ||||||||||||||||||||||||||||||||||||||||||
| this.store = store ?? null; | ||||||||||||||||||||||||||||||||||||||||||
| this.isEnabled = this.isReportingEnabled(); | ||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||
| this.rateLimiter = rateLimiter ?? new RateLimiter(); | ||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| public setStore(store: IStore): void { | ||||||||||||||||||||||||||||||||||||||||||
| this.store = store; | ||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| public info(msg: string, code?: ErrorCodes) { | ||||||||||||||||||||||||||||||||||||||||||
| this.sendLog(WSDKErrorSeverity.INFO, msg, code); | ||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| public error(msg: string, code?: ErrorCodes, stackTrace?: string) { | ||||||||||||||||||||||||||||||||||||||||||
| this.sendError(WSDKErrorSeverity.ERROR, msg, code, stackTrace); | ||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| public warning(msg: string, code?: ErrorCodes) { | ||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||
| this.sendError(WSDKErrorSeverity.WARNING, msg, code); | ||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| private sendToServer(url: string, severity: WSDKErrorSeverity, msg: string, code?: ErrorCodes, stackTrace?: string): void { | ||||||||||||||||||||||||||||||||||||||||||
| if (!this.canSendLog(severity)) | ||||||||||||||||||||||||||||||||||||||||||
| return; | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||||||||||||
| const logRequest = this.buildLogRequest(severity, msg, code, stackTrace); | ||||||||||||||||||||||||||||||||||||||||||
| const uploader = new FetchUploader(url); | ||||||||||||||||||||||||||||||||||||||||||
| const payload: IReportingLoggerPayload = { | ||||||||||||||||||||||||||||||||||||||||||
| method: 'POST', | ||||||||||||||||||||||||||||||||||||||||||
| headers: this.getHeaders(), | ||||||||||||||||||||||||||||||||||||||||||
| body: JSON.stringify(logRequest), | ||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||
| uploader.upload(payload).catch((error) => { | ||||||||||||||||||||||||||||||||||||||||||
| console.error('ReportingLogger: Failed to send log', error); | ||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||
| } catch (error) { | ||||||||||||||||||||||||||||||||||||||||||
| console.error('ReportingLogger: Failed to send log', error); | ||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+72
to
+74
|
||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| private sendLog(severity: WSDKErrorSeverity, msg: string, code?: ErrorCodes, stackTrace?: string): void { | ||||||||||||||||||||||||||||||||||||||||||
| this.sendToServer(this.loggingUrl, severity, msg, code, stackTrace); | ||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| private sendError(severity: WSDKErrorSeverity, msg: string, code?: ErrorCodes, stackTrace?: string): void { | ||||||||||||||||||||||||||||||||||||||||||
| this.sendToServer(this.errorUrl, severity, msg, code, stackTrace); | ||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| private buildLogRequest(severity: WSDKErrorSeverity, msg: string, code?: ErrorCodes, stackTrace?: string): LogRequestBody { | ||||||||||||||||||||||||||||||||||||||||||
| return { | ||||||||||||||||||||||||||||||||||||||||||
| additionalInformation: { | ||||||||||||||||||||||||||||||||||||||||||
| message: msg, | ||||||||||||||||||||||||||||||||||||||||||
| version: this.getVersion(), | ||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||
| severity: severity, | ||||||||||||||||||||||||||||||||||||||||||
| code: code ?? ErrorCodes.UNKNOWN_ERROR, | ||||||||||||||||||||||||||||||||||||||||||
alexs-mparticle marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||||||||||||||||||||||||||
| url: this.getUrl(), | ||||||||||||||||||||||||||||||||||||||||||
| deviceInfo: this.getUserAgent(), | ||||||||||||||||||||||||||||||||||||||||||
| stackTrace: stackTrace, | ||||||||||||||||||||||||||||||||||||||||||
| reporter: this.reporter, | ||||||||||||||||||||||||||||||||||||||||||
| integration: this.integration | ||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| private getVersion(): string { | ||||||||||||||||||||||||||||||||||||||||||
| return this.store?.getIntegrationName?.() ?? `mParticle_wsdkv_${this.sdkVersion}`; | ||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||
| return this.store?.getIntegrationName?.() ?? `mParticle_wsdkv_${this.sdkVersion}`; | |
| return `mParticle_wsdkv_${this.sdkVersion}`; |
Copilot
AI
Feb 13, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Trailing whitespace on this line. Remove the trailing whitespace for consistency with code style.
Copilot
AI
Feb 13, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The optional chaining across multiple lines (116-120) is fragile and hard to read. Consider simplifying this check by extracting the query parameter check into a separate helper function, or by checking for window.location first and then checking the search parameter. This would make the code more maintainable and testable.
| return ( | |
| typeof window !== 'undefined' && | |
| (window. | |
| location?. | |
| search?. | |
| toLowerCase()?. | |
| includes('mp_enable_logging=true') ?? false) | |
| ); | |
| } | |
| return this.isDebugModeEnabledViaQueryParam(); | |
| } | |
| private isDebugModeEnabledViaQueryParam(): boolean { | |
| if (typeof window === 'undefined' || !window.location || !window.location.search) { | |
| return false; | |
| } | |
| const search = window.location.search.toLowerCase(); | |
| return search.includes('mp_enable_logging=true'); | |
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We don't use a header keys var/enum for these?
Copilot
AI
Feb 13, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Trailing whitespace on this line. Remove the trailing whitespace for consistency with code style.
| const newCount = count + 1; | |
| this.logCount.set(severity, newCount); | |
| const newCount = count + 1; | |
| this.logCount.set(severity, newCount); |
alexs-mparticle marked this conversation as resolved.
Show resolved
Hide resolved
alexs-mparticle marked this conversation as resolved.
Show resolved
Hide resolved
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,32 @@ | ||
| import { valueof } from '../utils'; | ||
|
|
||
| export const ErrorCodes = { | ||
| UNKNOWN_ERROR: 'UNKNOWN_ERROR', | ||
| UNHANDLED_EXCEPTION: 'UNHANDLED_EXCEPTION', | ||
| IDENTITY_REQUEST: 'IDENTITY_REQUEST', | ||
| } as const; | ||
|
|
||
| export type ErrorCodes = valueof<typeof ErrorCodes>; | ||
|
|
||
| export type ErrorCode = ErrorCodes | string; | ||
alexs-mparticle marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| export const WSDKErrorSeverity = { | ||
| ERROR: 'ERROR', | ||
| INFO: 'INFO', | ||
| WARNING: 'WARNING', | ||
| } as const; | ||
|
|
||
| export type WSDKErrorSeverity = (typeof WSDKErrorSeverity)[keyof typeof WSDKErrorSeverity]; | ||
|
|
||
| export type ErrorsRequestBody = { | ||
| additionalInformation?: Record<string, string>; | ||
| code: ErrorCode; | ||
| severity: WSDKErrorSeverity; | ||
| stackTrace?: string; | ||
| deviceInfo?: string; | ||
| integration?: string; | ||
| reporter?: string; | ||
| url?: string; | ||
| }; | ||
|
|
||
| export type LogRequestBody = ErrorsRequestBody; | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The string concatenation here uses both the ' + ' operator and string concatenation with quotes. For consistency and readability, consider using template literals instead:
Error sending identity request to servers - ${errorMessage}