@@ -16,7 +16,7 @@ import {
1616 GraphQLDataSource ,
1717 GraphQLDataSourceRequestKind ,
1818} from './datasources/types' ;
19- import { RemoteGraphQLDataSource } from './datasources/RemoteGraphQLDataSource ' ;
19+ import { RemoteGraphQLDataSource } from './datasources' ;
2020import { getVariableValues } from 'graphql/execution/values' ;
2121import {
2222 QueryPlanner ,
@@ -47,7 +47,7 @@ import {
4747 requestContextSpanAttributes ,
4848 operationContextSpanAttributes ,
4949 recordExceptions ,
50- OpenTelemetryAttributeNames
50+ OpenTelemetryAttributeNames , configureOpenTelemetry ,
5151} from './utilities/opentelemetry' ;
5252import { addExtensions } from './schema-helper/addExtensions' ;
5353import {
@@ -64,7 +64,16 @@ import {
6464 Supergraph ,
6565} from '@apollo/federation-internals' ;
6666import { getDefaultLogger } from './logger' ;
67- import { GatewayInterface , GatewayUnsubscriber , GatewayGraphQLRequestContext , GatewayExecutionResult } from '@apollo/server-gateway-interface' ;
67+ import {
68+ GatewayInterface ,
69+ GatewayUnsubscriber ,
70+ GatewayGraphQLRequestContext ,
71+ GatewayExecutionResult ,
72+ GatewayGraphQLRequest , GatewayGraphQLResponse ,
73+ } from '@apollo/server-gateway-interface' ;
74+ import * as os from 'node:os' ;
75+ import { cpuCountSync } from 'node-cpu-count' ;
76+ import { MeterProvider } from '@opentelemetry/sdk-metrics' ;
6877
6978type DataSourceMap = {
7079 [ serviceName : string ] : { url ?: string ; dataSource : GraphQLDataSource } ;
@@ -148,6 +157,7 @@ export class ApolloGateway implements GatewayInterface {
148157 private compositionId ?: string ;
149158 private state : GatewayState ;
150159 private _supergraphManager ?: SupergraphManager ;
160+ private openTelemetryMeterProvider : MeterProvider ;
151161
152162 // Observe query plan, service info, and operation info prior to execution.
153163 // The information made available here will give insight into the resulting
@@ -215,6 +225,8 @@ export class ApolloGateway implements GatewayInterface {
215225
216226 this . validateConfigAndEmitWarnings ( ) ;
217227
228+ this . openTelemetryMeterProvider = this . initialiseTelemetry ( ) ;
229+
218230 this . logger . debug ( 'Gateway successfully initialized (but not yet loaded)' ) ;
219231 this . state = { phase : 'initialized' } ;
220232 }
@@ -281,6 +293,74 @@ export class ApolloGateway implements GatewayInterface {
281293 }
282294 }
283295
296+ private initialiseTelemetry ( ) : MeterProvider {
297+ const meterProvider = configureOpenTelemetry ( ) ;
298+
299+ const os_type = os . type ( ) ;
300+ const host_arch = os . arch ( ) ;
301+ const meter = meterProvider . getMeter ( "apollo/gateway" ) ;
302+
303+ // gateway.instance
304+
305+ const instanceGauge = meter . createObservableGauge ( "gateway.instance" , {
306+ "description" : "The number of instances of the gateway running"
307+ } )
308+ instanceGauge . addCallback ( ( result ) => {
309+ result . observe ( 1 , {
310+ "os.type" : os_type ,
311+ "host.arch" : host_arch ,
312+ "deployment_type" : "gateway"
313+ } )
314+ } )
315+
316+ // gateway.instance.cpu_freq
317+ const cpuFreqGauge = meter . createObservableGauge ( "gateway.instance.cpu_freq" , {
318+ "description" : "The CPU frequency of the underlying instance the router is deployed to" ,
319+ "unit" : "Mhz"
320+ } )
321+ cpuFreqGauge . addCallback ( ( result ) => {
322+ const cpus = os . cpus ( ) ;
323+ const average_frequency = os . cpus ( ) . map ( ( a ) => a . speed ) . reduce ( ( partialSum , a ) => partialSum + a , 0 ) / cpus . length
324+ result . observe ( average_frequency )
325+ } )
326+
327+ // gateway.instance.cpu_count
328+ const cpuCountGauge = meter . createObservableGauge ( "gateway.instance.cpu_count" , {
329+ "description" : "The number of CPUs reported by the instance the gateway is running on"
330+ } )
331+ cpuCountGauge . addCallback ( ( result ) => {
332+ result . observe ( cpuCountSync ( ) , {
333+ "host.arch" : host_arch ,
334+ } ) ;
335+ } )
336+
337+ // gateway.instance.total_memory
338+ const totalMemoryGauge = meter . createObservableGauge ( "gateway.instance.total_memory" , {
339+ "description" : "The amount of memory reported by the instance the router is running on" ,
340+ "unit" : "bytes"
341+ } )
342+ totalMemoryGauge . addCallback ( ( result ) => {
343+ result . observe ( os . totalmem ( ) , {
344+ "host.arch" : host_arch ,
345+ } ) ;
346+ } )
347+
348+ // gateway.instance.schema
349+ const schemaGauge = meter . createObservableGauge ( "gateway.instance.schema" , {
350+ "description" : "Details about the current in-use schema"
351+ } )
352+ schemaGauge . addCallback ( ( result ) => {
353+ if ( this . supergraphSdl ) {
354+ const hash = this . getIdForSupergraphSdl ( this . supergraphSdl )
355+ result . observe ( 1 , {
356+ schema_hash : hash
357+ } )
358+ }
359+ } )
360+
361+ return meterProvider
362+ }
363+
284364 public async load ( options ?: {
285365 apollo ?: ApolloConfigFromAS2Or3 ;
286366 engine ?: GraphQLServiceEngineConfig ;
@@ -739,6 +819,7 @@ export class ApolloGateway implements GatewayInterface {
739819 ? this . config . buildService ( serviceDef )
740820 : new RemoteGraphQLDataSource ( {
741821 url : serviceDef . url ,
822+ meterProvider : this . openTelemetryMeterProvider
742823 } ) ;
743824 }
744825
@@ -782,6 +863,7 @@ export class ApolloGateway implements GatewayInterface {
782863 public executor = async (
783864 requestContext : GatewayGraphQLRequestContext ,
784865 ) : Promise < GatewayExecutionResult > => {
866+
785867 return tracer . startActiveSpan (
786868 OpenTelemetrySpanNames . REQUEST ,
787869 { attributes : requestContextSpanAttributes ( requestContext , this . config . telemetry ) } ,
@@ -874,6 +956,10 @@ export class ApolloGateway implements GatewayInterface {
874956 } ) ;
875957 }
876958
959+ this . openTelemetryMeterProvider . getMeter ( "apollo/gateway" ) . createCounter ( "apollo.gateway.operations.fetch.request_size" , {
960+ unit : "bytes"
961+ } ) . add ( this . calculate_request_size ( request ) ) ;
962+
877963 const response = await executeQueryPlan (
878964 queryPlan ,
879965 serviceMap ,
@@ -884,6 +970,10 @@ export class ApolloGateway implements GatewayInterface {
884970 this . config . telemetry
885971 ) ;
886972
973+ this . openTelemetryMeterProvider . getMeter ( "apollo/gateway" ) . createCounter ( "apollo.gateway.operations.fetch.response_size" , {
974+ unit : "bytes"
975+ } ) . add ( this . calculate_response_size ( response ) ) ;
976+
887977 const shouldShowQueryPlan =
888978 this . config . __exposeQueryPlanExperimental &&
889979 request . http &&
@@ -948,6 +1038,39 @@ export class ApolloGateway implements GatewayInterface {
9481038 ) ;
9491039 } ;
9501040
1041+ private calculate_request_size ( request : GatewayGraphQLRequest ) {
1042+ let total = 0 ;
1043+
1044+ if ( request . http ?. headers ) {
1045+ total += Array . from ( request . http . headers ) . reduce ( ( size , headerPair ) => size + Buffer . byteLength ( headerPair [ 0 ] ) + Buffer . byteLength ( headerPair [ 1 ] ) , 0 ) ;
1046+ }
1047+ if ( request . query ) {
1048+ total += Buffer . byteLength ( request . query ) ;
1049+ }
1050+ if ( request . variables ) {
1051+ total += Object . entries ( request . variables ) . reduce ( ( accumulator , variable_pair ) =>
1052+ accumulator + Buffer . byteLength ( variable_pair [ 0 ] ) + Buffer . byteLength ( variable_pair [ 1 ] . toString ( ) )
1053+ , 0 ) ;
1054+ }
1055+
1056+ return total
1057+ }
1058+
1059+ private calculate_response_size ( response : GatewayGraphQLResponse ) {
1060+ let total = 0 ;
1061+
1062+ if ( response . http ?. headers ) {
1063+ total += Array . from ( response . http . headers ) . reduce ( ( size , headerPair ) => size + Buffer . byteLength ( headerPair [ 0 ] ) + Buffer . byteLength ( headerPair [ 1 ] ) , 0 ) ;
1064+ }
1065+ if ( response . data ) {
1066+ total += Object . entries ( response . data ) . reduce ( ( accumulator , data_pair ) =>
1067+ accumulator + Buffer . byteLength ( data_pair [ 0 ] ) + Buffer . byteLength ( JSON . stringify ( data_pair [ 1 ] ) )
1068+ , 0 ) ;
1069+ }
1070+
1071+ return total
1072+ }
1073+
9511074 private validateIncomingRequest (
9521075 requestContext : GatewayGraphQLRequestContext ,
9531076 operationContext : OperationContext ,
@@ -1044,6 +1167,8 @@ export class ApolloGateway implements GatewayInterface {
10441167 queryPlanner : this . queryPlanner ,
10451168 } ;
10461169 }
1170+
1171+
10471172}
10481173
10491174ApolloGateway . prototype . onSchemaChange = deprecate (
0 commit comments