-
Notifications
You must be signed in to change notification settings - Fork 57
feat: Create Logging Service SDKE-511 #1123
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: blackout2025
Are you sure you want to change the base?
Changes from all commits
4cc5aa8
7358464
7abe3fe
c646b0c
5cef096
8fa5adb
85f99db
dcd3d84
04c9e79
cb0222e
c8dc013
8746bd1
cac1c3c
5581588
42a67c0
4de0bc7
c268646
4e7e0cb
0d48826
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
This file was deleted.
| 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); | ||
| } | ||
| } | ||
| } |
| 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 = { | ||
| UNHANDLED_EXCEPTION: 'UNHANDLED_EXCEPTION', | ||
| } as const; | ||
| 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, | ||
|
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. Should version be here?
Contributor
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. 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. | ||
gtamanaha marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| 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; | ||
|
Collaborator
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. 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; | ||
| } | ||
| } | ||
| 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; | ||
| } |
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.
I think this should be
ErrorMessages.ErrorCodessounds like they'd be something numerical like HTTP Error Codes (404, 429, etc).Are you naming this
ErrorMessagesbecause that's something defined on the server side?