Yuki Takei 11 месяцев назад
Родитель
Сommit
fe169a31b5

+ 33 - 0
apps/app/src/features/opentelemetry/server/node-sdk-resource.ts

@@ -0,0 +1,33 @@
+import { Resource } from '@opentelemetry/resources';
+import type { NodeSDK } from '@opentelemetry/sdk-node';
+
+/**
+ * Get resource from SDK instance
+ * Note: This uses internal API of NodeSDK
+ */
+export const getResource = (sdk: NodeSDK): Resource => {
+  // This cast is necessary as _resource is a private property
+  const resource = (sdk as any)._resource;
+  if (!(resource instanceof Resource)) {
+    throw new Error('Failed to access SDK resource');
+  }
+  return resource;
+};
+
+/**
+ * Set resource to SDK instance
+ * Note: This uses internal API of NodeSDK
+ * @throws Error if resource cannot be set
+ */
+export const setResource = (sdk: NodeSDK, resource: Resource): void => {
+  // Verify that we can access the _resource property
+  try {
+    getResource(sdk);
+  }
+  catch (e) {
+    throw new Error('Failed to access SDK resource');
+  }
+
+  // This cast is necessary as _resource is a private property
+  (sdk as any)._resource = resource;
+};

+ 9 - 4
apps/app/src/features/opentelemetry/server/node-sdk.spec.ts

@@ -7,6 +7,7 @@ import {
 import { configManager } from '~/server/service/config-manager';
 
 import { detectServiceInstanceId, initInstrumentation } from './node-sdk';
+import { getResource } from './node-sdk-resource';
 import { getSdkInstance, resetSdkInstance } from './node-sdk.testing';
 
 // Only mock configManager as it's external to what we're testing
@@ -42,8 +43,10 @@ describe('node-sdk', () => {
       expect(sdkInstance).toBeDefined();
       expect(sdkInstance).toBeInstanceOf(NodeSDK);
 
+      assert(sdkInstance != null);
+
       // Verify initial state (service.instance.id should not be set)
-      const resource = (sdkInstance as any)._resource;
+      const resource = getResource(sdkInstance);
       expect(resource).toBeInstanceOf(Resource);
       expect(resource.attributes['service.instance.id']).toBeUndefined();
 
@@ -51,7 +54,7 @@ describe('node-sdk', () => {
       await detectServiceInstanceId();
 
       // Verify that resource was updated with app:serviceInstanceId
-      const updatedResource = (sdkInstance as any)._resource;
+      const updatedResource = getResource(sdkInstance);
       expect(updatedResource.attributes['service.instance.id']).toBe('test-instance-id');
     });
 
@@ -69,14 +72,16 @@ describe('node-sdk', () => {
 
       // Verify initial state
       const sdkInstance = getSdkInstance();
-      const resource = (sdkInstance as any)._resource;
+      assert(sdkInstance != null);
+
+      const resource = getResource(sdkInstance);
       expect(resource.attributes['service.instance.id']).toBeUndefined();
 
       // Call detectServiceInstanceId
       await detectServiceInstanceId();
 
       // Verify that otel:serviceInstanceId was used
-      const updatedResource = (sdkInstance as any)._resource;
+      const updatedResource = getResource(sdkInstance);
       expect(updatedResource.attributes['service.instance.id']).toBe('otel-instance-id');
     });
 

+ 5 - 14
apps/app/src/features/opentelemetry/server/node-sdk.ts

@@ -1,10 +1,11 @@
 import { ConfigSource } from '@growi/core/dist/interfaces';
-import { Resource } from '@opentelemetry/resources';
 import type { NodeSDK } from '@opentelemetry/sdk-node';
 
 import { configManager } from '~/server/service/config-manager';
 import loggerFactory from '~/utils/logger';
 
+import { setResource } from './node-sdk-resource';
+
 const logger = loggerFactory('growi:opentelemetry:server');
 
 let sdkInstance: NodeSDK | undefined;
@@ -73,11 +74,7 @@ For more information, see https://docs.growi.org/en/admin-guide/admin-cookbook/t
 export const detectServiceInstanceId = async(): Promise<void> => {
   const instrumentationEnabled = configManager.getConfig('otel:enabled', ConfigSource.env);
 
-  if (instrumentationEnabled) {
-    if (sdkInstance == null) {
-      throw new Error('OpenTelemetry SDK instance is not initialized');
-    }
-
+  if (instrumentationEnabled && sdkInstance != null) {
     const { generateNodeSDKConfiguration } = await import('./node-sdk-configuration');
 
     const serviceInstanceId = configManager.getConfig('otel:serviceInstanceId')
@@ -85,20 +82,14 @@ export const detectServiceInstanceId = async(): Promise<void> => {
 
     // Update resource with new service instance id
     const newConfig = generateNodeSDKConfiguration(serviceInstanceId);
-    if (newConfig.resource instanceof Resource) {
-      (sdkInstance as any)._resource = newConfig.resource;
-    }
+    setResource(sdkInstance, newConfig.resource);
   }
 };
 
 export const startOpenTelemetry = (): void => {
   const instrumentationEnabled = configManager.getConfig('otel:enabled', ConfigSource.env);
 
-  if (instrumentationEnabled) {
-    if (sdkInstance == null) {
-      throw new Error('OpenTelemetry SDK instance is not initialized');
-    }
-
+  if (instrumentationEnabled && sdkInstance != null) {
     sdkInstance.start();
   }
 };