bugsnag / bugsnag-js-performance

Monitor the performance of your JavaScript (web and React Native) and see the results in your BugSnag dashboard.
https://docs.bugsnag.com/performance/integration-guides
MIT License
5 stars 3 forks source link

Do we have any capability by which we can link span of android and react native #419

Closed shubhamguptadream11 closed 8 months ago

shubhamguptadream11 commented 8 months ago

Description

Generally if we talk about app Launch in any react native app, its basically the onCreate of Main Application to first meaningful paint on react native screen. So in order to track this we need to somehow start a span on onCreate of serializable and then we need to link some span in RN world to know what's going on in there. And finally after first meaningful paint we override onDraw function to end the android span. To get a better insights all these should be linked with same traceID.

Describe the solution you'd like There should be a mechanism to pass traceId and spanId across platforms to link them together. Since span is not serializable so we cannot pass this.

Describe alternatives you've considered I tried passing spanID and traceId by making a bridge call and tried linking this in RN world. But its not working.

lemnik commented 8 months ago

While this isn't something we fully support yet, you should be able to achieve cross-layer linking by passing the spanId and traceId from Android to ReactNative. You can create ReactNative spans that are children of an Android span using:

BugsnagPerformance.startSpan('Custom span', {
  parentContext: {id: parentSpanId, traceId, isValid: () => true},
})

and you can retrieve the current context on the Android side using:

val ctx = SpanContext.current
val traceId: UUID = ctx.traceId
val spanId: Long = ctx.spanId

The traceId and spanId will need to be hex encoded for the JavaScript layer.

shubhamguptadream11 commented 8 months ago

@lemnik Will try this and let you know. Thanks!

shubhamguptadream11 commented 8 months ago

@lemnik Hey above solution worked. But I am getting this on first RN span. This span’s parent does not exist in the trace.

Attaching the screenshot for this. Here rn-app-launch is the first point which is taking traceId and spanId from android.

Screenshot 2024-03-12 at 4 28 46 PM
gareththackeray commented 8 months ago

The parentSpanId of the rn-app-launch span is not attached to any span in the trace. I expect you'll be able to fix this by double-checking which ids you are sending. If you continue to have problems please contact support at support@bugsnag.com.

lemnik commented 8 months ago

It's also important that the span (the parent of rn-app-launch) is end()ed by whichever layer created it (in this case Android). Spans are not reported unless they are ended.

shubhamguptadream11 commented 8 months ago

Hey, I am passing traceId and spanId from android itself. Then starting a span in react-native environment. By doing this

    const span = BugsnagPerformance.startSpan('rn-app-launch', {
      parentContext: {
        traceId: uuidToHex(traceId),
        id: spanId,
        isValid: () => true,
      },
     });

    someHeavyOperation(); // Here I am starting a new span in rn world as well with name rn-heavy-operation and ending it inside that function only.
    span.end();

Then making a bridge call after this to end android span as well.

I think there is something wrong in Ids. From android I am passing these ids: traceId: 1d8a320b-db58-4f96-979e-e3d88e759c04 spanId: 9108058140016896339

In RN world I am receiving exactly same thing. traceId: 1d8a320bdb584f96979ee3d88e759c04 spanId: 9108058140016896339

I intercepted the trace which is being exported in RN World as well Here is the logs for this

{
  "resourceSpans": [
    {
      "resource": {
        "attributes": [
          {
            "key": "deployment.environment",
            "value": {
              "stringValue": "production"
            }
          },
          {
            "key": "telemetry.sdk.name",
            "value": {
              "stringValue": "bugsnag.performance.reactnative"
            }
          },
          {
            "key": "telemetry.sdk.version",
            "value": {
              "stringValue": "2.2.0"
            }
          },
          {
            "key": "os.type",
            "value": {
              "stringValue": "linux"
            }
          },
          {
            "key": "os.name",
            "value": {
              "stringValue": "android"
            }
          },
          {
            "key": "bugsnag.app.platform",
            "value": {
              "stringValue": "android"
            }
          },
          {
            "key": "os.version",
            "value": {
              "stringValue": "11"
            }
          },
          {
            "key": "bugsnag.device.android_api_version",
            "value": {
              "stringValue": "30"
            }
          },
          {
            "key": "host.arch",
            "value": {
              "stringValue": "arm64"
            }
          },
          {
            "key": "bugsnag.app.version_code",
            "value": {
              "stringValue": "1"
            }
          },
          {
            "key": "device.model.identifier",
            "value": {
              "stringValue": "LE2111"
            }
          },
          {
            "key": "service.name",
            "value": {
              "stringValue": "com.dream11otel"
            }
          },
          {
            "key": "device.manufacturer",
            "value": {
              "stringValue": "OnePlus"
            }
          },
          {
            "key": "device.id",
            "value": {
              "stringValue": "7eb2ab6b-cbf0-4f8b-a0b2-3182a8c95177"
            }
          }
        ]
      },
      "scopeSpans": [
        {
          "spans": [
            {
              "name": "[AppStart/ReactNativeInit]",
              "kind": 3,
              "spanId": "9ca6502fc2025860",
              "traceId": "16460fa04b6eb7a761275ed785ba6711",
              "startTimeUnixNano": "1710325361728022000",
              "endTimeUnixNano": "1710325362350212900",
              "attributes": [
                {
                  "key": "bugsnag.span.category",
                  "value": {
                    "stringValue": "app_start"
                  }
                },
                {
                  "key": "bugsnag.app_start.type",
                  "value": {
                    "stringValue": "ReactNativeInit"
                  }
                },
                {
                  "key": "net.host.connection.type",
                  "value": {
                    "stringValue": "unknown"
                  }
                },
                {
                  "key": "bugsnag.sampling.p",
                  "value": {
                    "doubleValue": 1
                  }
                }
              ],
              "events": []
            },
            {
              "name": "rn-heavy-operation",
              "kind": 3,
              "spanId": "a5cda220235345e0",
              "traceId": "1d8a320bdb584f96979ee3d88e759c04",
              "parentSpanId": "e4d39bd4e62f3bea",
              "startTimeUnixNano": "1710325362387342300",
              "endTimeUnixNano": "1710325398999003000",
              "attributes": [
                {
                  "key": "bugsnag.span.first_class",
                  "value": {
                    "boolValue": false
                  }
                },
                {
                  "key": "bugsnag.span.category",
                  "value": {
                    "stringValue": "custom"
                  }
                },
                {
                  "key": "net.host.connection.type",
                  "value": {
                    "stringValue": "wifi"
                  }
                },
                {
                  "key": "bugsnag.sampling.p",
                  "value": {
                    "doubleValue": 1
                  }
                }
              ],
              "events": []
            },
            {
              "name": "rn-app-launch",
              "kind": 3,
              "spanId": "e4d39bd4e62f3bea",
              "traceId": "1d8a320bdb584f96979ee3d88e759c04",
              "parentSpanId": "9108058140016896339",
              "startTimeUnixNano": "1710325362386789400",
              "endTimeUnixNano": "1710325398999296500",
              "attributes": [
                {
                  "key": "bugsnag.span.category",
                  "value": {
                    "stringValue": "custom"
                  }
                },
                {
                  "key": "net.host.connection.type",
                  "value": {
                    "stringValue": "wifi"
                  }
                },
                {
                  "key": "bugsnag.sampling.p",
                  "value": {
                    "doubleValue": 1
                  }
                }
              ],
              "events": []
            }
          ]
        }
      ]
    }
  ]
}

If you see in above logs. Then for rn-app-launch span parentSpanId is correctly attached and this is same as what I sent from android side and I ended this span as well.

The only thing I noticed different here is the format of spanId. I mean if we see rn-heavy-operation parentSpanId then its format is e4d39bd4e62f3bea (hex format) which is being generated on RN world itself and linked to rn-app-launch correctly.

But if we see rn-app-launch parentSpanId its coming from android world and its format is 9108058140016896339 (Long format).

I checked all other things starting and ending properly.

lemnik commented 8 months ago

I'm guessing that the spanId in the above snippet was directly captured as a Long on the Android side, and sent over to JS as a number. In which case the encoding will be incorrect. The spanId is a 64bit value, and we store it as a string in the JS layer for convenience and easy debugging. The encoding code used by the Android layer is here, and you can see that when an Android span is serialized to json the value is hex encoded.

shubhamguptadream11 commented 8 months ago

Okay do we have any utility function exposed to do this android side?

shubhamguptadream11 commented 8 months ago

This worked. I used .toHexString functions before passing it to RN world. Thanks!! @lemnik