UNPKG

@graphql-mesh/plugin-opentelemetry

Version:
394 lines (388 loc) • 15 kB
'use strict'; 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;