next
Version:
The React Framework
385 lines (384 loc) • 14.3 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
0 && (module.exports = {
createLocalSpan: null,
getActiveLocalSpan: null,
isLocalRecordingSpan: null,
isLocalSpanRecordingEnabled: null,
isOpenTelemetryIsolatedSpan: null,
registerLocalSpanRecorder: null,
traceLocalSpan: null,
withLocalSpan: null
});
function _export(target, all) {
for(var name in all)Object.defineProperty(target, name, {
enumerable: true,
get: all[name]
});
}
_export(exports, {
createLocalSpan: function() {
return createLocalSpan;
},
getActiveLocalSpan: function() {
return getActiveLocalSpan;
},
isLocalRecordingSpan: function() {
return isLocalRecordingSpan;
},
isLocalSpanRecordingEnabled: function() {
return _spanstore.isLocalSpanRecordingEnabled;
},
isOpenTelemetryIsolatedSpan: function() {
return isOpenTelemetryIsolatedSpan;
},
registerLocalSpanRecorder: function() {
return registerLocalSpanRecorder;
},
traceLocalSpan: function() {
return traceLocalSpan;
},
withLocalSpan: function() {
return withLocalSpan;
}
});
const _api = require("next/dist/compiled/@opentelemetry/api");
const _spanstore = require("./span-store");
const TRACE_ID_HEX_LENGTH = 32;
const SPAN_ID_HEX_LENGTH = 16;
let lastLocalTraceId = 0;
let lastLocalSpanId = 0;
let localSpanAsyncStorage;
const getLocalTraceId = ()=>(++lastLocalTraceId).toString(16).padStart(TRACE_ID_HEX_LENGTH, '0');
const getLocalSpanId = ()=>(++lastLocalSpanId).toString(16).padStart(SPAN_ID_HEX_LENGTH, '0');
function createLocalSpan({ name, attributes, links, startTime, traceId, spanId, parentSpanId, delegateSpan, isolateOpenTelemetry }) {
return new LocalRecordingSpan({
name,
attributes,
links,
startTime,
delegateSpan,
traceId: traceId ?? getLocalTraceId(),
spanId: spanId ?? getLocalSpanId(),
parentSpanId,
requestIdentity: getCurrentRequestIdentity(),
isolateOpenTelemetry: isolateOpenTelemetry ?? false
});
}
function getActiveLocalSpan() {
return localSpanAsyncStorage == null ? void 0 : localSpanAsyncStorage.getStore();
}
function isLocalRecordingSpan(span) {
return span instanceof LocalRecordingSpan;
}
function isOpenTelemetryIsolatedSpan(span) {
return span instanceof LocalRecordingSpan && span.isOpenTelemetryIsolated();
}
function withLocalSpan(span, fn) {
return getLocalSpanAsyncStorage().run(span, fn);
}
async function traceLocalSpan({ parentSpan, ...options }, fn) {
const resolvedParentSpan = parentSpan === undefined ? getActiveLocalSpan() : parentSpan;
const parentSpanContext = resolvedParentSpan == null ? void 0 : resolvedParentSpan.spanContext();
const span = createLocalSpan({
...options,
traceId: parentSpanContext == null ? void 0 : parentSpanContext.traceId,
parentSpanId: parentSpanContext == null ? void 0 : parentSpanContext.spanId,
isolateOpenTelemetry: true
});
return withLocalSpan(span, async ()=>{
try {
return await fn();
} catch (err) {
span.recordException(err);
span.setStatus({
code: _api.SpanStatusCode.ERROR
});
throw err;
} finally{
span.end();
}
});
}
function registerLocalSpanRecorder() {
const key = Symbol.for('@next/local-span-recorder');
globalThis[key] = {
createLocalSpan,
getActiveLocalSpan,
isLocalRecordingSpan,
isOpenTelemetryIsolatedSpan,
isLocalSpanRecordingEnabled: _spanstore.isLocalSpanRecordingEnabled,
isRequestInsightsEnabled: _spanstore.isRequestInsightsEnabled,
traceLocalSpan,
withLocalSpan
};
}
function getLocalSpanAsyncStorage() {
if (!localSpanAsyncStorage) {
const { createAsyncLocalStorage } = require('../../app-render/async-local-storage');
localSpanAsyncStorage = createAsyncLocalStorage();
}
return localSpanAsyncStorage;
}
class LocalRecordingSpan {
constructor({ name, attributes, links, startTime, delegateSpan, traceId, spanId, parentSpanId, requestIdentity, isolateOpenTelemetry }){
this.name = name;
this.attributes = cleanSpanStoreAttributes(attributes);
this.events = [];
this.delegateSpan = delegateSpan;
this.openTelemetryIsolated = isolateOpenTelemetry;
this.spanContextValue = (delegateSpan == null ? void 0 : delegateSpan.spanContext()) ?? {
traceId,
spanId,
traceFlags: 0
};
this.links = getSpanStoreLinks(links);
this.parentSpanId = parentSpanId;
this.requestIdentity = requestIdentity;
this.startTime = getTimestamp(startTime);
this.statusCode = undefined;
this.statusMessage = undefined;
this.exception = undefined;
this.ended = false;
}
spanContext() {
return this.spanContextValue;
}
isOpenTelemetryIsolated() {
return this.openTelemetryIsolated;
}
setAttribute(key, value) {
var _this_delegateSpan;
if (this.ended) {
return this;
}
this.attributes[key] = value;
(_this_delegateSpan = this.delegateSpan) == null ? void 0 : _this_delegateSpan.setAttribute(key, value);
return this;
}
setAttributes(attributes) {
var _this_delegateSpan;
if (this.ended) {
return this;
}
// OpenTelemetry attributes may be undefined. Ignore them instead of
// overwriting an existing value as Object.assign would.
for (const key of Object.keys(attributes)){
const value = attributes[key];
if (value !== undefined) {
this.attributes[key] = value;
}
}
(_this_delegateSpan = this.delegateSpan) == null ? void 0 : _this_delegateSpan.setAttributes(attributes);
return this;
}
addEvent(name, attributesOrStartTime, startTime) {
var _this_delegateSpan;
if (this.ended) {
return this;
}
const eventTime = startTime === undefined && isTimestampInput(attributesOrStartTime) ? attributesOrStartTime : startTime;
this.events.push({
name,
timestamp: getTimestamp(eventTime),
attributes: isSpanStoreAttributes(attributesOrStartTime) ? cleanSpanStoreAttributes(attributesOrStartTime) : undefined
});
(_this_delegateSpan = this.delegateSpan) == null ? void 0 : _this_delegateSpan.addEvent(name, attributesOrStartTime, startTime);
return this;
}
setStatus(status) {
var _this_delegateSpan;
if (this.ended) {
return this;
}
this.statusCode = status.code;
this.statusMessage = status.message;
(_this_delegateSpan = this.delegateSpan) == null ? void 0 : _this_delegateSpan.setStatus(status);
return this;
}
updateName(name) {
var _this_delegateSpan;
if (this.ended) {
return this;
}
this.name = name;
(_this_delegateSpan = this.delegateSpan) == null ? void 0 : _this_delegateSpan.updateName(name);
return this;
}
end(endTime) {
if (this.ended) {
return;
}
this.ended = true;
try {
var _this_delegateSpan;
(_this_delegateSpan = this.delegateSpan) == null ? void 0 : _this_delegateSpan.end(endTime);
} finally{
try {
this.record(endTime);
} finally{
this.releaseReferences();
}
}
}
isRecording() {
return !this.ended;
}
recordException(exception, time) {
var _this_delegateSpan;
if (this.ended) {
return;
}
this.exception = getSpanStoreException(exception);
this.events.push({
name: 'exception',
timestamp: getTimestamp(time),
attributes: getSpanStoreExceptionAttributes(this.exception)
});
(_this_delegateSpan = this.delegateSpan) == null ? void 0 : _this_delegateSpan.recordException(exception, time);
}
record(endTime) {
const recordAttributes = Object.keys(this.attributes).length > 0 ? this.attributes : undefined;
(0, _spanstore.recordSpan)({
name: this.name,
startTime: this.startTime,
durationMs: Math.max(0, getTimestamp(endTime) - this.startTime),
status: this.statusCode === _api.SpanStatusCode.ERROR ? 'error' : 'ok',
traceId: this.spanContextValue.traceId,
spanId: this.spanContextValue.spanId,
parentSpanId: this.parentSpanId,
requestId: this.requestIdentity.requestId,
requestInsightKind: this.requestIdentity.requestInsightKind,
htmlRequestId: this.requestIdentity.htmlRequestId,
route: getStringAttribute(recordAttributes, 'next.route') ?? getStringAttribute(recordAttributes, 'http.route') ?? this.requestIdentity.route,
url: getStringAttribute(recordAttributes, 'http.url') ?? this.requestIdentity.url,
attributes: recordAttributes,
links: this.links,
events: this.events.length > 0 ? this.events : undefined,
error: this.getRecordError()
});
}
releaseReferences() {
// AsyncLocalStorage can keep an ended span reachable when work spawned
// inside the span outlives it. Keep only the immutable span context and
// primitive timing/identity fields needed by the Span API after end.
this.name = '';
this.attributes = {};
this.events = [];
this.delegateSpan = undefined;
this.links = undefined;
this.requestIdentity = {};
this.statusMessage = undefined;
this.exception = undefined;
}
getRecordError() {
if (this.exception) {
return this.exception;
}
if (this.statusCode === _api.SpanStatusCode.ERROR && this.statusMessage) {
return {
message: this.statusMessage
};
}
return undefined;
}
}
function getSpanStoreLinks(links) {
const spanStoreLinks = links == null ? void 0 : links.map((link)=>({
traceId: link.context.traceId,
spanId: link.context.spanId,
attributes: cleanSpanStoreAttributes(link.attributes)
}));
return (spanStoreLinks == null ? void 0 : spanStoreLinks.length) ? spanStoreLinks : undefined;
}
function cleanSpanStoreAttributes(attributes) {
const cleanedAttributes = {};
if (attributes) {
for (const key of Object.keys(attributes)){
const value = attributes[key];
if (value !== undefined) {
cleanedAttributes[key] = value;
}
}
}
return cleanedAttributes;
}
function getTimestamp(time) {
if (time instanceof Date) {
return time.getTime();
}
if (Array.isArray(time)) {
return time[0] * 1000 + time[1] / 1000000;
}
if (typeof time === 'number') {
return time < performance.timeOrigin / 2 ? performance.timeOrigin + time : time;
}
return performance.timeOrigin + performance.now();
}
function getStringAttribute(attributes, key) {
const value = attributes == null ? void 0 : attributes[key];
return typeof value === 'string' ? value : undefined;
}
function isSpanStoreAttributes(value) {
return typeof value === 'object' && value !== null && !Array.isArray(value) && !(value instanceof Date);
}
function isTimestampInput(value) {
return typeof value === 'number' || Array.isArray(value) || value instanceof Date;
}
function getSpanStoreException(exception) {
if (exception instanceof Error) {
return {
type: exception.name,
message: exception.message
};
}
if (typeof exception === 'string') {
return {
message: exception
};
}
if (exception && typeof exception === 'object') {
var _exception_constructor;
return {
type: (_exception_constructor = exception.constructor) == null ? void 0 : _exception_constructor.name,
message: 'message' in exception ? String(exception.message) : undefined
};
}
return undefined;
}
function getSpanStoreExceptionAttributes(exception) {
if (!exception) {
return undefined;
}
const attributes = {};
if (exception.type !== undefined) {
attributes['exception.type'] = exception.type;
}
if (exception.message !== undefined) {
attributes['exception.message'] = exception.message;
}
return Object.keys(attributes).length > 0 ? attributes : undefined;
}
function getCurrentRequestIdentity() {
try {
const { getRequestInsightsIdentity } = require('./request-insights-identity');
const { workAsyncStorage } = require('../../app-render/work-async-storage.external');
const { workUnitAsyncStorage } = require('../../app-render/work-unit-async-storage.external');
const workStore = workAsyncStorage.getStore();
const workUnitStore = workUnitAsyncStorage.getStore();
const requestInsightsIdentity = getRequestInsightsIdentity();
const url = workUnitStore && 'url' in workUnitStore ? workUnitStore.url : undefined;
return {
requestId: (requestInsightsIdentity == null ? void 0 : requestInsightsIdentity.requestId) ?? (workStore == null ? void 0 : workStore.requestId),
requestInsightKind: requestInsightsIdentity == null ? void 0 : requestInsightsIdentity.kind,
htmlRequestId: (requestInsightsIdentity == null ? void 0 : requestInsightsIdentity.htmlRequestId) ?? (workStore == null ? void 0 : workStore.htmlRequestId),
route: workStore == null ? void 0 : workStore.route,
url: url ? `${url.pathname}${url.search}` : requestInsightsIdentity == null ? void 0 : requestInsightsIdentity.url
};
} catch {
return {};
}
}
//# sourceMappingURL=local-span-recorder.js.map