@graphql-mesh/plugin-opentelemetry
Version:
394 lines (388 loc) • 15 kB
JavaScript
var api = require('@opentelemetry/api');
var core = require('@opentelemetry/core');
var resources = require('@opentelemetry/resources');
var sdkTraceBase = require('@opentelemetry/sdk-trace-base');
var semanticConventions = require('@opentelemetry/semantic-conventions');
var exporterTraceOtlpHttp = require('@opentelemetry/exporter-trace-otlp-http');
var plugin = require('./plugin-DMWC0usc.cjs');
var apiLogs = require('@opentelemetry/api-logs');
var sdkLogs = require('@opentelemetry/sdk-logs');
require('@graphql-hive/gateway-runtime');
require('@graphql-hive/logger/request');
require('@graphql-mesh/utils');
require('@graphql-tools/utils');
require('@whatwg-node/promise-helpers');
require('@graphql-hive/core');
require('@graphql-mesh/transport-common');
require('graphql');
class HiveTracingSpanProcessor {
traceStateById = /* @__PURE__ */ new Map();
processor;
constructor(config) {
if (config.processor) {
this.processor = config.processor;
} else {
this.processor = new sdkTraceBase.BatchSpanProcessor(
new exporterTraceOtlpHttp.OTLPTraceExporter({
url: config.endpoint,
headers: {
Authorization: `Bearer ${config.accessToken}`,
"X-Hive-Target-Ref": config.target
}
}),
config.batching
);
}
}
onStart(span, parentContext) {
this.processor.onStart(span, parentContext);
const { spanId, traceId } = span.spanContext();
const parentId = span.parentSpanContext?.spanId;
if (isHttpSpan(span)) {
this.traceStateById.set(traceId, {
traceId,
rootId: spanId,
httpSpan: span,
operationRoots: /* @__PURE__ */ new Map(),
subgraphExecutions: /* @__PURE__ */ new Map()
});
return;
}
const traceState = this.traceStateById.get(traceId);
if (!traceState || !parentId) {
return;
}
if (span.name.startsWith("graphql.operation")) {
traceState?.operationRoots.set(spanId, span);
return;
}
const operationRoot = traceState.operationRoots.get(parentId);
if (operationRoot) {
traceState.operationRoots.set(spanId, operationRoot);
}
if (span.name.startsWith("subgraph.execute")) {
traceState.subgraphExecutions.set(spanId, span);
return;
}
const subgraphExecution = traceState.subgraphExecutions.get(parentId);
if (subgraphExecution) {
traceState.subgraphExecutions.set(spanId, subgraphExecution);
}
}
onEnd(span) {
const { traceId, spanId } = span.spanContext();
const traceState = this.traceStateById.get(traceId);
if (!traceState) {
return;
}
if (traceState.rootId === spanId) {
this.traceStateById.delete(traceId);
for (let operationSpan2 of new Set(traceState.operationRoots.values())) {
operationSpan2.startTime = span.startTime;
operationSpan2.endTime = span.endTime;
operationSpan2._duration = core.hrTimeDuration(
operationSpan2.startTime,
operationSpan2.endTime
);
operationSpan2.parentSpanContext = null;
for (const attr in span.attributes) {
operationSpan2.attributes[attr] ??= span.attributes[attr];
}
this.processor.onEnd(operationSpan2);
}
return;
}
const operationSpan = traceState.operationRoots.get(spanId);
if (!operationSpan) {
return;
}
if (operationSpan === span) {
return;
}
if (span.name === "graphql.execute") {
copyAttribute(span, operationSpan, plugin.SEMATTRS_HIVE_GRAPHQL_ERROR_CODES);
copyAttribute(span, operationSpan, plugin.SEMATTRS_HIVE_GRAPHQL_ERROR_COUNT);
copyAttribute(
span,
operationSpan,
plugin.SEMATTRS_HIVE_GATEWAY_OPERATION_SUBGRAPH_NAMES
);
}
const subgraphExecution = traceState.subgraphExecutions.get(spanId);
if (span.name === "http.fetch" && subgraphExecution) {
for (const attr in span.attributes) {
subgraphExecution.attributes[attr] ??= span.attributes[attr];
}
}
this.processor.onEnd(span);
}
async forceFlush() {
return this.processor.forceFlush();
}
async shutdown() {
await this.forceFlush();
this.traceStateById.clear();
return this.processor.shutdown();
}
}
function isHttpSpan(span) {
return !!span.attributes[semanticConventions.SEMATTRS_HTTP_METHOD];
}
function copyAttribute(source, target, sourceAttrName, targetAttrName = sourceAttrName) {
target.attributes[targetAttrName] = source.attributes[sourceAttrName];
}
class OpenTelemetryLogWriter {
logger;
useContextManager;
constructor(options) {
this.useContextManager = options.useContextManager ?? true;
if ("logger" in options) {
this.logger = options.logger;
return;
}
if ("provider" in options) {
if ("register" in options.provider && typeof options.provider.register === "function") {
options.provider.register();
} else {
apiLogs.logs.setGlobalLoggerProvider(options.provider);
}
} else {
const processors = options.processors ?? [];
if (options.exporter) {
if (options.batching !== false) {
processors.push(
new sdkLogs.BatchLogRecordProcessor(
options.exporter,
options.batching === true ? {} : options.batching
)
);
}
processors.push(new sdkLogs.SimpleLogRecordProcessor(options.exporter));
}
if (options.console) {
processors.push(
new sdkLogs.SimpleLogRecordProcessor(new sdkLogs.ConsoleLogRecordExporter())
);
}
apiLogs.logs.setGlobalLoggerProvider(
new sdkLogs.LoggerProvider({
...options,
processors
})
);
}
this.logger = apiLogs.logs.getLogger("gateway");
}
flush() {
const provider = apiLogs.logs.getLoggerProvider();
if ("forceFlush" in provider && typeof provider.forceFlush === "function") {
provider.forceFlush();
}
}
write(level, attrs, msg) {
const attributes = Array.isArray(attrs) ? { ...attrs } : attrs ?? void 0;
return this.logger.emit({
body: msg,
attributes,
severityNumber: HIVE_LOG_LEVEL_NUMBERS[level],
severityText: level,
context: this.useContextManager ? api.context.active() : getContextForRequest(attributes)
});
}
}
const HIVE_LOG_LEVEL_NUMBERS = {
trace: apiLogs.SeverityNumber.TRACE,
debug: apiLogs.SeverityNumber.DEBUG,
info: apiLogs.SeverityNumber.INFO,
warn: apiLogs.SeverityNumber.WARN,
error: apiLogs.SeverityNumber.ERROR
};
function getContextForRequest(attributes) {
if (!attributes?.requestId) {
return api.ROOT_CONTEXT;
}
return plugin.otelCtxForRequestId.get(attributes.requestId) ?? api.ROOT_CONTEXT;
}
function openTelemetrySetup(options) {
const log = options.log?.child("[OpenTelemetry] ");
if (plugin.getEnvVar("OTEL_SDK_DISABLED", false) === "true") {
log?.warn(
"OpenTelemetry integration is disabled because `OTEL_SDK_DISABLED` environment variable is set to `true`"
);
return;
}
const logAttributes = { registrationResults: {} };
let logMessage = "OpenTelemetry integration is enabled";
if (options.traces) {
if (options.traces.tracerProvider) {
if ("register" in options.traces.tracerProvider && typeof options.traces.tracerProvider.register === "function") {
logAttributes["registrationResults"].tracer = options.traces.tracerProvider.register();
} else {
logAttributes["registrationResults"].tracer = api.trace.setGlobalTracerProvider(options.traces.tracerProvider);
}
logMessage += " and provided TracerProvider has been registered";
} else {
let spanProcessors = options.traces.processors ?? [];
if (options.traces.exporter) {
spanProcessors.push(
resolveBatchingConfig(
options.traces.exporter,
options.traces.batching
)
);
logMessage += " and exporter have been registered";
logAttributes["batching"] = options.traces.batching ?? true;
}
if (options.traces.console) {
spanProcessors.push(new sdkTraceBase.SimpleSpanProcessor(new sdkTraceBase.ConsoleSpanExporter()));
logMessage += " in addition to an stdout debug exporter";
logAttributes["console"] = true;
}
const baseResource = resources.resourceFromAttributes({
[semanticConventions.ATTR_SERVICE_NAME]: options.resource && "serviceName" in options.resource ? options.resource?.serviceName : plugin.getEnvVar(
"OTEL_SERVICE_NAME",
"@graphql-mesh/plugin-opentelemetry"
),
[semanticConventions.ATTR_SERVICE_VERSION]: options.resource && "serviceVersion" in options.resource ? options.resource?.serviceVersion : plugin.getEnvVar(
"OTEL_SERVICE_VERSION",
globalThis.__OTEL_PLUGIN_VERSION__
),
["hive.gateway.version"]: globalThis.__VERSION__,
["hive.otel.version"]: globalThis.__OTEL_PLUGIN_VERSION__
});
const resource = options.resource && !("serviceName" in options.resource) ? baseResource.merge(options.resource) : baseResource;
logAttributes["resource"] = resource.attributes;
logAttributes["sampling"] = options.sampler ? "custom" : options.samplingRate;
logAttributes["registrationResults"].tracerProvider = api.trace.setGlobalTracerProvider(
new sdkTraceBase.BasicTracerProvider({
resource,
sampler: options.sampler ?? (options.samplingRate ? new sdkTraceBase.ParentBasedSampler({
root: new sdkTraceBase.TraceIdRatioBasedSampler(options.samplingRate)
}) : new sdkTraceBase.AlwaysOnSampler()),
spanProcessors,
generalLimits: options.generalLimits,
spanLimits: options.traces.spanLimits
})
);
}
}
if (options.contextManager !== null) {
logAttributes["registrationResults"].contextManager = api.context.setGlobalContextManager(options.contextManager);
}
if (!options.propagators || options.propagators.length !== 0) {
const propagators = options.propagators ?? [
new core.W3CBaggagePropagator(),
new core.W3CTraceContextPropagator()
];
logAttributes["registrationResults"].propagators = api.propagation.setGlobalPropagator(
propagators.length === 1 ? propagators[0] : new core.CompositePropagator({ propagators })
);
}
log?.info(logAttributes, logMessage);
}
function hiveTracingSetup(config) {
const log = config.log?.child("[OpenTelemetry] ");
config.target ??= plugin.getEnvVar("HIVE_TARGET", void 0);
if (!config.target) {
throw new Error(
"You must specify the Hive Registry `target`. Either provide `target` option or `HIVE_TARGET` environment variable."
);
}
const logAttributes = { target: config.target };
if (!config.processor) {
config.accessToken ??= plugin.getEnvVar("HIVE_TRACING_ACCESS_TOKEN", void 0) ?? plugin.getEnvVar("HIVE_ACCESS_TOKEN", void 0);
if (!config.accessToken) {
throw new Error(
"You must specify the Hive Registry `accessToken`. Either provide `accessToken` option or `HIVE_ACCESS_TOKEN`/`HIVE_TRACE_ACCESS_TOKEN` environment variable."
);
}
logAttributes["endpoint"] = config.endpoint;
logAttributes["batching"] = config.batching;
}
openTelemetrySetup({
contextManager: config.contextManager,
resource: resources.resourceFromAttributes({
"hive.target_id": config.target
}),
traces: {
processors: [
new HiveTracingSpanProcessor(config)
]
}
});
log?.info(logAttributes, "Hive Tracing integration has been enabled");
}
function resolveBatchingConfig(exporter, batchingConfig) {
const value = batchingConfig ?? true;
if (value === true) {
return new sdkTraceBase.BatchSpanProcessor(exporter);
} else if (value === false) {
return new sdkTraceBase.SimpleSpanProcessor(exporter);
} else {
return new sdkTraceBase.BatchSpanProcessor(exporter, value);
}
}
Object.defineProperty(exports, "ATTR_SERVICE_NAME", {
enumerable: true,
get: function () { return semanticConventions.ATTR_SERVICE_NAME; }
});
Object.defineProperty(exports, "ATTR_SERVICE_VERSION", {
enumerable: true,
get: function () { return semanticConventions.ATTR_SERVICE_VERSION; }
});
Object.defineProperty(exports, "SEMATTRS_HTTP_CLIENT_IP", {
enumerable: true,
get: function () { return semanticConventions.SEMATTRS_HTTP_CLIENT_IP; }
});
Object.defineProperty(exports, "SEMATTRS_HTTP_HOST", {
enumerable: true,
get: function () { return semanticConventions.SEMATTRS_HTTP_HOST; }
});
Object.defineProperty(exports, "SEMATTRS_HTTP_METHOD", {
enumerable: true,
get: function () { return semanticConventions.SEMATTRS_HTTP_METHOD; }
});
Object.defineProperty(exports, "SEMATTRS_HTTP_ROUTE", {
enumerable: true,
get: function () { return semanticConventions.SEMATTRS_HTTP_ROUTE; }
});
Object.defineProperty(exports, "SEMATTRS_HTTP_SCHEME", {
enumerable: true,
get: function () { return semanticConventions.SEMATTRS_HTTP_SCHEME; }
});
Object.defineProperty(exports, "SEMATTRS_HTTP_SERVER_NAME", {
enumerable: true,
get: function () { return semanticConventions.SEMATTRS_HTTP_SERVER_NAME; }
});
Object.defineProperty(exports, "SEMATTRS_HTTP_STATUS_CODE", {
enumerable: true,
get: function () { return semanticConventions.SEMATTRS_HTTP_STATUS_CODE; }
});
Object.defineProperty(exports, "SEMATTRS_HTTP_URL", {
enumerable: true,
get: function () { return semanticConventions.SEMATTRS_HTTP_URL; }
});
Object.defineProperty(exports, "SEMATTRS_HTTP_USER_AGENT", {
enumerable: true,
get: function () { return semanticConventions.SEMATTRS_HTTP_USER_AGENT; }
});
Object.defineProperty(exports, "SEMATTRS_NET_HOST_NAME", {
enumerable: true,
get: function () { return semanticConventions.SEMATTRS_NET_HOST_NAME; }
});
exports.SEMATTRS_GRAPHQL_DOCUMENT = plugin.SEMATTRS_GRAPHQL_DOCUMENT;
exports.SEMATTRS_GRAPHQL_OPERATION_NAME = plugin.SEMATTRS_GRAPHQL_OPERATION_NAME;
exports.SEMATTRS_GRAPHQL_OPERATION_TYPE = plugin.SEMATTRS_GRAPHQL_OPERATION_TYPE;
exports.SEMATTRS_HIVE_GATEWAY_OPERATION_SUBGRAPH_NAMES = plugin.SEMATTRS_HIVE_GATEWAY_OPERATION_SUBGRAPH_NAMES;
exports.SEMATTRS_HIVE_GATEWAY_UPSTREAM_SUBGRAPH_NAME = plugin.SEMATTRS_HIVE_GATEWAY_UPSTREAM_SUBGRAPH_NAME;
exports.SEMATTRS_HIVE_GRAPHQL_ERROR_CODES = plugin.SEMATTRS_HIVE_GRAPHQL_ERROR_CODES;
exports.SEMATTRS_HIVE_GRAPHQL_ERROR_COUNT = plugin.SEMATTRS_HIVE_GRAPHQL_ERROR_COUNT;
exports.SEMATTRS_HIVE_GRAPHQL_OPERATION_HASH = plugin.SEMATTRS_HIVE_GRAPHQL_OPERATION_HASH;
exports.getEnvVar = plugin.getEnvVar;
exports.HIVE_LOG_LEVEL_NUMBERS = HIVE_LOG_LEVEL_NUMBERS;
exports.HiveTracingSpanProcessor = HiveTracingSpanProcessor;
exports.OpenTelemetryLogWriter = OpenTelemetryLogWriter;
exports.getContextForRequest = getContextForRequest;
exports.hiveTracingSetup = hiveTracingSetup;
exports.openTelemetrySetup = openTelemetrySetup;
;