diff --git a/src/App.res b/src/App.res index 5a24a650a..b65ae89ef 100644 --- a/src/App.res +++ b/src/App.res @@ -1,5 +1,8 @@ @react.component let make = () => { + let (logger, initTimestamp) = React.useMemo0(() => { + (OrcaLogger.make(), Date.now()) + }) let url = RescriptReactRouter.useUrl() let (integrateError, setIntegrateErrorError) = React.useState(() => false) let setLoggerState = Recoil.useSetRecoilState(RecoilAtoms.loggerAtom) @@ -7,11 +10,6 @@ let make = () => { let paymentMode = CardUtils.getQueryParamsDictforKey(url.search, "componentName") let fullscreenMode = CardUtils.getQueryParamsDictforKey(url.search, "fullscreenType") - let logger = React.useMemo0(() => { - let log = OrcaLogger.make() - log - }) - React.useEffect(() => { setLoggerState(_ => logger) None @@ -41,7 +39,7 @@ let make = () => { | "sepaBankTransfer" => | _ => - + } diff --git a/src/LoaderController.res b/src/LoaderController.res index c9adbab9c..1d354db58 100644 --- a/src/LoaderController.res +++ b/src/LoaderController.res @@ -1,6 +1,6 @@ open Utils @react.component -let make = (~children, ~paymentMode, ~setIntegrateErrorError, ~logger) => { +let make = (~children, ~paymentMode, ~setIntegrateErrorError, ~logger, ~initTimestamp) => { open RecoilAtoms //<...>// let (configAtom, setConfig) = Recoil.useRecoilState(configAtom) @@ -15,6 +15,7 @@ let make = (~children, ~paymentMode, ~setIntegrateErrorError, ~logger) => { let setIsGooglePayReady = Recoil.useSetRecoilState(isGooglePayReady) let setIsApplePayReady = Recoil.useSetRecoilState(isApplePayReady) let (divH, setDivH) = React.useState(_ => 0.0) + let (launchTime, setLaunchTime) = React.useState(_ => 0.0) let {showCardFormByDefault, paymentMethodOrder} = optionsPayment let divRef = React.useRef(Nullable.null) @@ -130,15 +131,28 @@ let make = (~children, ~paymentMode, ~setIntegrateErrorError, ~logger) => { showCardFormByDefault && Utils.checkPriorityList(paymentMethodOrder) ? SemiLoaded : Loading | x => x } + let finalLoadLatency = Js.Date.now() -. launchTime switch updatedState { - | Loaded(_) => logger.setLogInfo(~value="Loaded", ~eventName=LOADER_CHANGED, ()) - | Loading => logger.setLogInfo(~value="Loading", ~eventName=LOADER_CHANGED, ()) + | Loaded(_) => + logger.setLogInfo(~value="Loaded", ~eventName=LOADER_CHANGED, ~latency=finalLoadLatency, ()) + | Loading => + logger.setLogInfo(~value="Loading", ~eventName=LOADER_CHANGED, ~latency=finalLoadLatency, ()) | SemiLoaded => { setList(_ => updatedState) - logger.setLogInfo(~value="SemiLoaded", ~eventName=LOADER_CHANGED, ()) + logger.setLogInfo( + ~value="SemiLoaded", + ~eventName=LOADER_CHANGED, + ~latency=finalLoadLatency, + (), + ) } | LoadError(x) => - logger.setLogError(~value="LoadError: " ++ x->JSON.stringify, ~eventName=LOADER_CHANGED, ()) + logger.setLogError( + ~value="LoadError: " ++ x->JSON.stringify, + ~eventName=LOADER_CHANGED, + ~latency=finalLoadLatency, + (), + ) } Window.addEventListener("click", ev => handleOnClickPostMessage(~targetOrigin=keys.parentURL, ev) @@ -257,8 +271,14 @@ let make = (~children, ~paymentMode, ~setIntegrateErrorError, ~logger) => { }) } } - - logger.setLogInfo(~value=Window.href, ~eventName=APP_RENDERED, ()) + setLaunchTime(_ => dict->Utils.getFloat("launchTime", 0.0)) + let initLoadlatency = Date.now() -. launchTime + logger.setLogInfo( + ~value=Window.href, + ~eventName=APP_RENDERED, + ~latency=initLoadlatency, + (), + ) [ ("iframeId", "no-element"->JSON.Encode.string), ("publishableKey", ""->JSON.Encode.string), @@ -267,8 +287,13 @@ let make = (~children, ~paymentMode, ~setIntegrateErrorError, ~logger) => { ]->Array.forEach(keyPair => { dict->CommonHooks.updateKeys(keyPair, setKeys) }) - - logger.setLogInfo(~eventName=PAYMENT_OPTIONS_PROVIDED, ~value="", ()) + let renderLatency = Date.now() -. initTimestamp + logger.setLogInfo( + ~eventName=PAYMENT_OPTIONS_PROVIDED, + ~latency=renderLatency, + ~value="", + (), + ) } } else if dict->getDictIsSome("paymentOptions") { let paymentOptions = dict->Utils.getDictFromObj("paymentOptions") @@ -340,6 +365,7 @@ let make = (~children, ~paymentMode, ~setIntegrateErrorError, ~logger) => { } if dict->getDictIsSome("paymentMethodList") { let list = dict->getJsonObjectFromDict("paymentMethodList") + let finalLoadlatency = Js.Date.now() -. launchTime let updatedState: PaymentType.loadType = list == Dict.make()->JSON.Encode.object ? LoadError(list) @@ -351,11 +377,18 @@ let make = (~children, ~paymentMode, ~setIntegrateErrorError, ~logger) => { isNonEmptyPaymentMethodList ? Loaded(list) : LoadError(list) } switch updatedState { - | Loaded(_) => logger.setLogInfo(~value="Loaded", ~eventName=LOADER_CHANGED, ()) + | Loaded(_) => + logger.setLogInfo( + ~value="Loaded", + ~eventName=LOADER_CHANGED, + ~latency=finalLoadlatency, + (), + ) | LoadError(x) => logger.setLogError( ~value="LoadError: " ++ x->JSON.stringify, ~eventName=LOADER_CHANGED, + ~latency=finalLoadlatency, (), ) | _ => () diff --git a/src/orca-loader/Elements.res b/src/orca-loader/Elements.res index ea15add96..65c015c37 100644 --- a/src/orca-loader/Elements.res +++ b/src/orca-loader/Elements.res @@ -35,6 +35,7 @@ let make = ( let endpoint = ApiEndpoint.getApiEndPoint(~publishableKey, ()) let appearance = localOptions->Dict.get("appearance")->Option.getOr(Dict.make()->JSON.Encode.object) + let launchTime = localOptions->getFloat("launchTime", 0.0) let fonts = localOptions @@ -247,6 +248,7 @@ let make = ( ("sdkHandleOneClickConfirmPayment", sdkHandleOneClickConfirmPayment->JSON.Encode.bool), ("parentURL", "*"->JSON.Encode.string), ("analyticsMetadata", analyticsMetadata), + ("launchTime", launchTime->JSON.Encode.float), ]->Dict.fromArray let handleApplePayMounted = (event: Types.event) => { diff --git a/src/orca-loader/Hyper.res b/src/orca-loader/Hyper.res index c9756ed76..b584c524a 100644 --- a/src/orca-loader/Hyper.res +++ b/src/orca-loader/Hyper.res @@ -348,12 +348,17 @@ let make = (publishableKey, options: option, analyticsInfo: option { open Promise + let elementsOptionsDict = elementsOptions->JSON.Decode.object + elementsOptionsDict + ->Option.forEach(x => x->Dict.set("launchTime", Date.now()->JSON.Encode.float)) + ->ignore + let clientSecretId = - elementsOptions - ->JSON.Decode.object + elementsOptionsDict ->Option.flatMap(x => x->Dict.get("clientSecret")) ->Option.flatMap(JSON.Decode.string) ->Option.getOr("") + let elementsOptions = elementsOptionsDict->Option.mapOr(elementsOptions, JSON.Encode.object) clientSecret := clientSecretId Js.Promise.make((~resolve, ~reject as _) => { logger.setClientSecret(clientSecretId) diff --git a/src/orca-log-catcher/OrcaLogger.res b/src/orca-log-catcher/OrcaLogger.res index 5ce08c4c1..7845c4ba1 100644 --- a/src/orca-log-catcher/OrcaLogger.res +++ b/src/orca-log-catcher/OrcaLogger.res @@ -160,6 +160,7 @@ type setLogInfo = ( ~internalMetadata: string=?, ~eventName: eventName, ~timestamp: string=?, + ~latency: float=?, ~logType: logType=?, ~logCategory: logCategory=?, ~paymentMethod: string=?, @@ -198,6 +199,7 @@ let defaultLoggerConfig = { ~internalMetadata as _=?, ~eventName as _, ~timestamp as _=?, + ~latency as _=?, ~logType as _=?, ~logCategory as _=?, ~paymentMethod as _=?, @@ -219,6 +221,7 @@ let defaultLoggerConfig = { ~internalMetadata as _=?, ~eventName as _, ~timestamp as _=?, + ~latency as _=?, ~logType as _=?, ~logCategory as _=?, ~paymentMethod as _=?, @@ -558,6 +561,7 @@ let make = ( ~internalMetadata="", ~eventName, ~timestamp=?, + ~latency=?, ~logType=INFO, ~logCategory=USER_EVENT, ~paymentMethod="", @@ -565,7 +569,10 @@ let make = ( ) => { let eventNameStr = eventName->eventNameToStrMapper let firstEvent = events.contents->Dict.get(eventNameStr)->Option.isNone - let latency = calculateLatencyHook(~eventName, ()) + let latency = switch latency { + | Some(lat) => lat->Float.toString + | None => calculateLatencyHook(~eventName, ()) + } let localTimestamp = timestamp->Option.getOr(Date.now()->Belt.Float.toString) let localTimestampFloat = localTimestamp->Belt.Float.fromString->Option.getOr(Date.now()) { @@ -657,6 +664,7 @@ let make = ( ~internalMetadata="", ~eventName, ~timestamp=?, + ~latency=?, ~logType=ERROR, ~logCategory=USER_ERROR, ~paymentMethod="", @@ -664,7 +672,10 @@ let make = ( ) => { let eventNameStr = eventName->eventNameToStrMapper let firstEvent = events.contents->Dict.get(eventNameStr)->Option.isNone - let latency = calculateLatencyHook(~eventName, ()) + let latency = switch latency { + | Some(lat) => lat->Float.toString + | None => calculateLatencyHook(~eventName, ()) + } let localTimestamp = timestamp->Option.getOr(Date.now()->Belt.Float.toString) let localTimestampFloat = localTimestamp->Belt.Float.fromString->Option.getOr(Date.now()) {