Move trace event handling into NetworkReporter (#53776)

Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/53776

Refactor and improve how we emit CDP trace events for network requests.

Key changes:
- Split up `PerformanceTracer` methods into discrete trace events.
- Move event calls out of `PerformanceEntryReporter` (upwards) into `NetworkReporter`. This now:
    - Aligns better with the matching source `NetworkReporter` events.
    - Removes metadata pieces from `PerformanceEntryReporter` that weren't part of the Web `PerformanceResourceTiming` API.
- Populate additional fields on `ResourceReceiveResponse`.

Changelog: [Internal]

Reviewed By: hoxyq

Differential Revision: D82433225

fbshipit-source-id: 4fce219f0c86a7257b8df250ef9f2b4ec8bd8de3
This commit is contained in:
Alex Hunt
2025-09-18 03:01:22 -07:00
committed by Facebook GitHub Bot
parent 674fb77d93
commit 6ef351b3ce
14 changed files with 200 additions and 115 deletions
+1 -1
View File
@@ -161,7 +161,7 @@ let reactJsInspectorTracing = RNTarget(
name: .reactJsInspectorTracing,
path: "ReactCommon/jsinspector-modern/tracing",
excludedPaths: ["tests"],
dependencies: [.reactNativeDependencies, .reactFeatureFlags, .jsi, .reactOSCompat]
dependencies: [.reactNativeDependencies, .reactFeatureFlags, .reactJsInspectorNetwork, .jsi, .reactOSCompat]
)
/// React-jsinspectornetwork.podspec
@@ -13,13 +13,11 @@ namespace facebook::react::jsinspector_modern::cdp::network {
namespace {
folly::dynamic headersToDynamic(const std::optional<Headers>& headers) {
folly::dynamic headersToDynamic(const Headers& headers) {
folly::dynamic result = folly::dynamic::object;
if (headers) {
for (const auto& [key, value] : *headers) {
result[key] = value;
}
for (const auto& [key, value] : headers) {
result[key] = value;
}
return result;
@@ -41,14 +39,14 @@ folly::dynamic Request::toDynamic() const {
/* static */ Response Response::fromInputParams(
const std::string& url,
uint16_t status,
const std::optional<Headers>& headers,
const Headers& headers,
int encodedDataLength) {
return {
.url = url,
.status = status,
.statusText = httpReasonPhrase(status),
.headers = headers,
.mimeType = mimeTypeFromHeaders(headers.value_or(Headers())),
.mimeType = mimeTypeFromHeaders(headers),
.encodedDataLength = encodedDataLength,
};
}
@@ -24,7 +24,7 @@ using Headers = std::map<std::string, std::string>;
struct Request {
std::string url;
std::string method;
std::optional<Headers> headers;
Headers headers;
std::optional<std::string> postData;
folly::dynamic toDynamic() const;
@@ -37,7 +37,7 @@ struct Response {
std::string url;
uint16_t status;
std::string statusText;
std::optional<Headers> headers;
Headers headers;
std::string mimeType;
int encodedDataLength;
@@ -48,7 +48,7 @@ struct Response {
static Response fromInputParams(
const std::string& url,
uint16_t status,
const std::optional<Headers>& headers,
const Headers& headers,
int encodedDataLength);
folly::dynamic toDynamic() const;
@@ -12,6 +12,8 @@
namespace facebook::react::jsinspector_modern {
using Headers = std::map<std::string, std::string>;
/**
* Get the HTTP reason phrase for a given status code (RFC 9110).
*/
@@ -21,7 +23,6 @@ std::string httpReasonPhrase(uint16_t status);
* Get the MIME type for a response based on the 'Content-Type' header. If
* the header is not present, returns 'application/octet-stream'.
*/
std::string mimeTypeFromHeaders(
const std::map<std::string, std::string>& headers);
std::string mimeTypeFromHeaders(const Headers& headers);
} // namespace facebook::react::jsinspector_modern
@@ -18,6 +18,7 @@ target_include_directories(jsinspector_tracing PUBLIC ${REACT_COMMON_DIR})
target_link_libraries(jsinspector_tracing
folly_runtime
jsinspector_network
oscompat
react_timing
)
@@ -9,6 +9,8 @@
#include "Timing.h"
#include "TraceEventSerializer.h"
#include <jsinspector-modern/network/CdpNetwork.h>
#include <jsinspector-modern/network/HttpUtils.h>
#include <oscompat/OSCompat.h>
#include <react/timing/primitives.h>
@@ -16,9 +18,12 @@
#include <mutex>
using namespace facebook::react;
namespace facebook::react::jsinspector_modern::tracing {
namespace {
/**
* Instead of validating that the indicated duration is positive, we can ensure
* the value is sensible (less than 1 seccond doesn't make much sense).
@@ -31,6 +36,7 @@ ThreadId getCurrentThreadId() {
oscompat::getCurrentThreadId();
return CURRENT_THREAD_ID;
}
} // namespace
PerformanceTracer& PerformanceTracer::getInstance() {
@@ -232,15 +238,9 @@ void PerformanceTracer::reportEventLoopMicrotasks(
});
}
void PerformanceTracer::reportResourceTiming(
const std::string& requestId,
const std::string& url,
HighResTimeStamp fetchStart,
HighResTimeStamp responseStart,
HighResTimeStamp responseEnd,
int statusCode,
const std::string& requestMethod,
const std::string& resourceType) {
void PerformanceTracer::reportResourceWillSendRequest(
const std::string& devtoolsRequestId,
HighResTimeStamp start) {
if (!tracingAtomic_) {
return;
}
@@ -251,27 +251,82 @@ void PerformanceTracer::reportResourceTiming(
}
enqueueEvent(PerformanceTracerResourceWillSendRequest{
.requestId = requestId,
.start = fetchStart,
.requestId = devtoolsRequestId,
.start = start,
.threadId = getCurrentThreadId(),
});
}
void PerformanceTracer::reportResourceSendRequest(
const std::string& devtoolsRequestId,
HighResTimeStamp start,
const std::string& url,
const std::string& requestMethod,
const Headers& headers) {
if (!tracingAtomic_) {
return;
}
std::lock_guard<std::mutex> lock(mutex_);
if (!tracingAtomic_) {
return;
}
auto resourceType =
jsinspector_modern::cdp::network::resourceTypeFromMimeType(
jsinspector_modern::mimeTypeFromHeaders(headers));
enqueueEvent(PerformanceTracerResourceSendRequest{
.requestId = requestId,
.requestId = devtoolsRequestId,
.url = url,
.start = fetchStart,
.start = start,
.requestMethod = requestMethod,
.resourceType = resourceType,
.threadId = getCurrentThreadId(),
});
}
void PerformanceTracer::reportResourceReceiveResponse(
const std::string& devtoolsRequestId,
HighResTimeStamp start,
int statusCode,
const Headers& headers,
int encodedDataLength) {
if (!tracingAtomic_) {
return;
}
std::lock_guard<std::mutex> lock(mutex_);
if (!tracingAtomic_) {
return;
}
enqueueEvent(PerformanceTracerResourceReceiveResponse{
.requestId = requestId,
.start = responseStart,
.requestId = devtoolsRequestId,
.start = start,
.encodedDataLength = encodedDataLength,
.headers = headers,
.mimeType = jsinspector_modern::mimeTypeFromHeaders(headers),
.protocol = "h2",
.statusCode = statusCode,
.threadId = getCurrentThreadId(),
});
}
void PerformanceTracer::reportResourceFinish(
const std::string& devtoolsRequestId,
HighResTimeStamp start) {
if (!tracingAtomic_) {
return;
}
std::lock_guard<std::mutex> lock(mutex_);
if (!tracingAtomic_) {
return;
}
enqueueEvent(PerformanceTracerResourceFinish{
.requestId = requestId,
.start = responseEnd,
.requestId = devtoolsRequestId,
.start = start,
.threadId = getCurrentThreadId(),
});
}
@@ -567,7 +622,7 @@ void PerformanceTracer::enqueueTraceEventsFromPerformanceTracerEvent(
[&](PerformanceTracerResourceSendRequest&& event) {
folly::dynamic data =
folly::dynamic::object("initiator", folly::dynamic::object())(
"renderBlocking", "non_blocking")(
"priority", "VeryHigh")("renderBlocking", "non_blocking")(
"requestId", std::move(event.requestId))(
"requestMethod", std::move(event.requestMethod))(
"resourceType", std::move(event.resourceType))(
@@ -585,7 +640,15 @@ void PerformanceTracer::enqueueTraceEventsFromPerformanceTracerEvent(
});
},
[&](PerformanceTracerResourceReceiveResponse&& event) {
folly::dynamic data = folly::dynamic::object("protocol", "h2")(
folly::dynamic headersEntries = folly::dynamic::array;
for (const auto& [key, value] : event.headers) {
headersEntries.push_back(
folly::dynamic::object("name", key)("value", value));
}
folly::dynamic data = folly::dynamic::object(
"encodedDataLength", event.encodedDataLength)(
"headers", headersEntries)("mimeType", event.mimeType)(
"protocol", event.protocol)(
"requestId", std::move(event.requestId))(
"statusCode", event.statusCode)(
"timing", folly::dynamic::object());
@@ -15,6 +15,7 @@
#include <folly/dynamic.h>
#include <atomic>
#include <map>
#include <mutex>
#include <optional>
#include <vector>
@@ -24,6 +25,8 @@ namespace facebook::react::jsinspector_modern::tracing {
// TODO: Review how this API is integrated into jsinspector_modern (singleton
// design is copied from earlier FuseboxTracer prototype).
using Headers = std::map<std::string, std::string>;
/**
* [Experimental] An interface for logging performance trace events to the
* modern debugger server.
@@ -109,19 +112,53 @@ class PerformanceTracer {
void reportEventLoopMicrotasks(HighResTimeStamp start, HighResTimeStamp end);
/**
* Record a "ResourceSendRequest"/"ResourceFinish" event pair - a labelled
* duration in the Performance timeline Network track. If not currently
* tracing, this is a no-op.
* Record a "ResourceWillSendRequest" event. Paired with other "Resource*"
* events, renders a network request timeline in the Performance panel Network
* track.
*
* If not currently tracing, this is a no-op.
*/
void reportResourceTiming(
const std::string& requestId,
void reportResourceWillSendRequest(
const std::string& devtoolsRequestId,
HighResTimeStamp start);
/**
* Record a "ResourceSendRequest" event. Paired with other "Resource*"
* events, renders a network request timeline in the Performance panel Network
* track.
*
* If not currently tracing, this is a no-op.
*/
void reportResourceSendRequest(
const std::string& devtoolsRequestId,
HighResTimeStamp start,
const std::string& url,
HighResTimeStamp fetchStart,
HighResTimeStamp responseStart,
HighResTimeStamp responseEnd,
int statusCode,
const std::string& requestMethod,
const std::string& resourceType);
const Headers& headers);
/**
* Record a "ResourceReceiveResponse" event. Paired with other "Resource*"
* events, renders a network request timeline in the Performance panel Network
* track.
*
* If not currently tracing, this is a no-op.
*/
void reportResourceReceiveResponse(
const std::string& devtoolsRequestId,
HighResTimeStamp start,
int statusCode,
const Headers& headers,
int encodedDataLength);
/**
* Record a "ResourceFinish" event. Paired with other "Resource*" events,
* renders a network request timeline in the Performance panel Network track.
*
* If not currently tracing, this is a no-op.
*/
void reportResourceFinish(
const std::string& devtoolsRequestId,
HighResTimeStamp start);
/**
* Creates "Profile" Trace Event.
@@ -223,6 +260,10 @@ class PerformanceTracer {
struct PerformanceTracerResourceReceiveResponse {
std::string requestId;
HighResTimeStamp start;
int encodedDataLength;
Headers headers;
std::string mimeType;
std::string protocol;
int statusCode;
ThreadId threadId;
HighResTimeStamp createdAt = HighResTimeStamp::now();
@@ -43,6 +43,7 @@ Pod::Spec.new do |s|
resolve_use_frameworks(s, header_mappings_dir: "../..", module_name: module_name)
add_dependency(s, "React-jsinspectornetwork", :framework_name => 'jsinspector_modernnetwork')
s.dependency "React-oscompat"
s.dependency "React-timing"
@@ -17,5 +17,7 @@ target_compile_options(react_networking PRIVATE -Wpedantic)
target_include_directories(react_networking PUBLIC ${REACT_COMMON_DIR})
target_link_libraries(react_networking
folly_runtime
jsinspector_network
jsinspector_tracing
react_performance_timeline
react_timing)
@@ -11,6 +11,7 @@
#include <jsinspector-modern/network/CdpNetwork.h>
#include <jsinspector-modern/network/HttpUtils.h>
#include <jsinspector-modern/network/NetworkHandler.h>
#include <jsinspector-modern/tracing/PerformanceTracer.h>
#endif
#include <react/featureflags/ReactNativeFeatureFlags.h>
#include <react/performance/timeline/PerformanceEntryReporter.h>
@@ -35,9 +36,9 @@ void NetworkReporter::reportRequestStart(
const RequestInfo& requestInfo,
int encodedDataLength,
const std::optional<ResponseInfo>& redirectResponse) {
if (ReactNativeFeatureFlags::enableResourceTimingAPI()) {
auto now = HighResTimeStamp::now();
auto now = HighResTimeStamp::now();
if (ReactNativeFeatureFlags::enableResourceTimingAPI()) {
// All builds: Annotate PerformanceResourceTiming metadata
{
std::lock_guard<std::mutex> lock(perfTimingsMutex_);
@@ -45,7 +46,6 @@ void NetworkReporter::reportRequestStart(
requestId,
ResourceTimingData{
.url = requestInfo.url,
.requestMethod = requestInfo.httpMethod,
.fetchStart = now,
.requestStart = now,
});
@@ -53,13 +53,15 @@ void NetworkReporter::reportRequestStart(
}
#ifdef REACT_NATIVE_DEBUGGER_ENABLED
// Debug build: CDP event handling
auto headers = requestInfo.headers.value_or(Headers{});
// Debugger enabled: CDP event handling
jsinspector_modern::NetworkHandler::getInstance().onRequestWillBeSent(
requestId,
{
.url = requestInfo.url,
.method = requestInfo.httpMethod,
.headers = requestInfo.headers,
.headers = headers,
.postData = requestInfo.httpBody,
},
redirectResponse.has_value()
@@ -67,18 +69,25 @@ void NetworkReporter::reportRequestStart(
jsinspector_modern::cdp::network::Response::fromInputParams(
redirectResponse->url,
redirectResponse->statusCode,
redirectResponse->headers,
redirectResponse->headers.value_or(Headers{}),
encodedDataLength))
: std::nullopt);
// Debugger enabled: Add trace events to Performance timeline
auto& performanceTracer =
jsinspector_modern::tracing::PerformanceTracer::getInstance();
performanceTracer.reportResourceWillSendRequest(requestId, now);
performanceTracer.reportResourceSendRequest(
requestId, now, requestInfo.url, requestInfo.httpMethod, headers);
#endif
}
void NetworkReporter::reportConnectionTiming(
const std::string& requestId,
const std::optional<Headers>& headers) {
if (ReactNativeFeatureFlags::enableResourceTimingAPI()) {
auto now = HighResTimeStamp::now();
auto now = HighResTimeStamp::now();
if (ReactNativeFeatureFlags::enableResourceTimingAPI()) {
// All builds: Annotate PerformanceResourceTiming metadata
{
std::lock_guard<std::mutex> lock(perfTimingsMutex_);
@@ -90,7 +99,7 @@ void NetworkReporter::reportConnectionTiming(
}
#ifdef REACT_NATIVE_DEBUGGER_ENABLED
// Debug build: CDP event handling
// Debugger enabled: CDP event handling
jsinspector_modern::NetworkHandler::getInstance()
.onRequestWillBeSentExtraInfo(requestId, headers.value_or(Headers{}));
#endif
@@ -100,9 +109,9 @@ void NetworkReporter::reportResponseStart(
const std::string& requestId,
const ResponseInfo& responseInfo,
int encodedDataLength) {
if (ReactNativeFeatureFlags::enableResourceTimingAPI()) {
auto now = HighResTimeStamp::now();
auto now = HighResTimeStamp::now();
if (ReactNativeFeatureFlags::enableResourceTimingAPI()) {
// All builds: Annotate PerformanceResourceTiming metadata
{
std::lock_guard<std::mutex> lock(perfTimingsMutex_);
@@ -111,26 +120,26 @@ void NetworkReporter::reportResponseStart(
it->second.connectEnd = now;
it->second.responseStart = now;
it->second.responseStatus = responseInfo.statusCode;
#ifdef REACT_NATIVE_DEBUGGER_ENABLED
// Debug build: Compute additional fields to send in CDP trace events
it->second.resourceType =
jsinspector_modern::cdp::network::resourceTypeFromMimeType(
jsinspector_modern::mimeTypeFromHeaders(
responseInfo.headers.value_or(Headers{})));
#endif
}
}
}
#ifdef REACT_NATIVE_DEBUGGER_ENABLED
// Debug build: CDP event handling
auto headers = responseInfo.headers.value_or(Headers{});
// Debugger enabled: CDP event handling
jsinspector_modern::NetworkHandler::getInstance().onResponseReceived(
requestId,
jsinspector_modern::cdp::network::Response::fromInputParams(
responseInfo.url,
responseInfo.statusCode,
responseInfo.headers,
headers,
encodedDataLength));
// Debugger enabled: Add trace event to Performance timeline
jsinspector_modern::tracing::PerformanceTracer::getInstance()
.reportResourceReceiveResponse(
requestId, now, responseInfo.statusCode, headers, encodedDataLength);
#endif
}
@@ -139,7 +148,7 @@ void NetworkReporter::reportDataReceived(
int dataLength,
const std::optional<int>& encodedDataLength) {
#ifdef REACT_NATIVE_DEBUGGER_ENABLED
// Debug build: CDP event handling
// Debugger enabled: CDP event handling
jsinspector_modern::NetworkHandler::getInstance().onDataReceived(
requestId, dataLength, encodedDataLength.value_or(dataLength));
#endif
@@ -148,9 +157,9 @@ void NetworkReporter::reportDataReceived(
void NetworkReporter::reportResponseEnd(
const std::string& requestId,
int encodedDataLength) {
if (ReactNativeFeatureFlags::enableResourceTimingAPI()) {
auto now = HighResTimeStamp::now();
auto now = HighResTimeStamp::now();
if (ReactNativeFeatureFlags::enableResourceTimingAPI()) {
// All builds: Report PerformanceResourceTiming event
{
std::lock_guard<std::mutex> lock(perfTimingsMutex_);
@@ -165,19 +174,20 @@ void NetworkReporter::reportResponseEnd(
eventData.connectEnd.value_or(now),
eventData.responseStart.value_or(now),
now,
eventData.responseStatus,
requestId,
eventData.requestMethod,
eventData.resourceType);
eventData.responseStatus);
perfTimingsBuffer_.erase(requestId);
}
}
}
#ifdef REACT_NATIVE_DEBUGGER_ENABLED
// Debug build: CDP event handling
// Debugger enabled: CDP event handling
jsinspector_modern::NetworkHandler::getInstance().onLoadingFinished(
requestId, encodedDataLength);
// Debugger enabled: Add trace event to Performance timeline
jsinspector_modern::tracing::PerformanceTracer::getInstance()
.reportResourceFinish(requestId, now);
#endif
}
@@ -185,7 +195,7 @@ void NetworkReporter::reportRequestFailed(
const std::string& requestId,
bool cancelled) const {
#ifdef REACT_NATIVE_DEBUGGER_ENABLED
// Debug build: CDP event handling
// Debugger enabled: CDP event handling
jsinspector_modern::NetworkHandler::getInstance().onLoadingFailed(
requestId, cancelled);
#endif
@@ -196,7 +206,7 @@ void NetworkReporter::storeResponseBody(
std::string_view body,
bool base64Encoded) {
#ifdef REACT_NATIVE_DEBUGGER_ENABLED
// Debug build: Store fetched response body for later CDP retrieval
// Debugger enabled: Store fetched response body for later CDP retrieval
jsinspector_modern::NetworkHandler::getInstance().storeResponseBody(
requestId, body, base64Encoded);
#endif
@@ -28,8 +28,6 @@ namespace facebook::react {
*/
struct ResourceTimingData {
std::string url;
std::string requestMethod;
std::optional<std::string> resourceType;
HighResTimeStamp fetchStart;
HighResTimeStamp requestStart;
std::optional<HighResTimeStamp> connectStart;
@@ -56,7 +54,8 @@ class NetworkReporter {
/**
* Report a network request that is about to be sent.
*
* - Corresponds to `Network.requestWillBeSent` in CDP.
* - Corresponds to `Network.requestWillBeSent` and the
* "ResourceWillSendRequest" trace event in CDP.
* - Corresponds to `PerformanceResourceTiming.requestStart` (specifically,
* marking when the native request was initiated).
*
@@ -72,7 +71,8 @@ class NetworkReporter {
* Report timestamp for sending the network request, and (in a debug build)
* provide final headers to be reported via CDP.
*
* - Corresponds to `Network.requestWillBeSentExtraInfo` in CDP.
* - Corresponds to `Network.requestWillBeSentExtraInfo` and the
* "ResourceSendRequest" trace event in CDP.
* - Corresponds to `PerformanceResourceTiming.domainLookupStart`,
* `PerformanceResourceTiming.connectStart`. Defined as "immediately before
* the browser starts to establish the connection to the server".
@@ -87,7 +87,8 @@ class NetworkReporter {
* Report when HTTP response headers have been received, corresponding to
* when the first byte of the response is available.
*
* - Corresponds to `Network.responseReceived` in CDP.
* - Corresponds to `Network.responseReceived` and the
* "ResourceReceiveResponse" trace event in CDP.
* - Corresponds to `PerformanceResourceTiming.responseStart`.
*
* https://w3c.github.io/resource-timing/#dom-performanceresourcetiming-responsestart
@@ -112,7 +113,8 @@ class NetworkReporter {
* Report when a network request is complete and we are no longer receiving
* response data.
*
* - Corresponds to `Network.loadingFinished` in CDP.
* - Corresponds to `Network.loadingFinished` and the "ResourceFinish" trace
* event in CDP.
* - Corresponds to `PerformanceResourceTiming.responseEnd`.
*
* https://w3c.github.io/resource-timing/#dom-performanceresourcetiming-responseend
@@ -43,6 +43,7 @@ Pod::Spec.new do |s|
end
add_dependency(s, "React-featureflags")
add_dependency(s, "React-jsinspectornetwork", :framework_name => 'jsinspector_modernnetwork')
add_dependency(s, "React-jsinspectortracing", :framework_name => 'jsinspector_moderntracing')
s.dependency "React-performancetimeline"
s.dependency "React-timing"
@@ -307,10 +307,7 @@ void PerformanceEntryReporter::reportResourceTiming(
std::optional<HighResTimeStamp> connectEnd,
HighResTimeStamp responseStart,
HighResTimeStamp responseEnd,
const std::optional<int>& responseStatus,
const std::optional<std::string>& devtoolsRequestId,
const std::optional<std::string>& requestMethod,
const std::optional<std::string>& resourceType) {
const std::optional<int>& responseStatus) {
const auto entry = PerformanceResourceTiming{
{.name = url, .startTime = fetchStart},
fetchStart,
@@ -322,8 +319,6 @@ void PerformanceEntryReporter::reportResourceTiming(
responseStatus,
};
traceResourceTiming(entry, devtoolsRequestId, requestMethod, resourceType);
// Add to buffers & notify observers
{
std::unique_lock lock(buffersMutex_);
@@ -375,31 +370,4 @@ void PerformanceEntryReporter::traceMeasure(
}
}
void PerformanceEntryReporter::traceResourceTiming(
const PerformanceResourceTiming& entry,
const std::optional<std::string>& devtoolsRequestId,
const std::optional<std::string>& requestMethod,
const std::optional<std::string>& resourceType) const {
if (!entry.responseStart.has_value() || !entry.responseEnd.has_value() ||
!entry.responseStatus.has_value() || !devtoolsRequestId.has_value() ||
!requestMethod.has_value() || !resourceType.has_value()) {
return;
}
auto& performanceTracer =
jsinspector_modern::tracing::PerformanceTracer::getInstance();
if (performanceTracer.isTracing()) {
performanceTracer.reportResourceTiming(
*devtoolsRequestId,
entry.name,
entry.fetchStart,
*entry.responseStart,
*entry.responseEnd,
*entry.responseStatus,
*requestMethod,
*resourceType);
}
}
} // namespace facebook::react
@@ -118,10 +118,7 @@ class PerformanceEntryReporter {
std::optional<HighResTimeStamp> connectEnd,
HighResTimeStamp responseStart,
HighResTimeStamp responseEnd,
const std::optional<int>& responseStatus,
const std::optional<std::string>& devtoolsRequestId,
const std::optional<std::string>& requestMethod,
const std::optional<std::string>& resourceType);
const std::optional<int>& responseStatus);
private:
std::unique_ptr<PerformanceObserverRegistry> observerRegistry_;