diff --git a/server/src/interfaces/metric.interface.ts b/server/src/interfaces/telemetry.interface.ts similarity index 84% rename from server/src/interfaces/metric.interface.ts rename to server/src/interfaces/telemetry.interface.ts index a87a849833..070014f2e0 100644 --- a/server/src/interfaces/metric.interface.ts +++ b/server/src/interfaces/telemetry.interface.ts @@ -1,6 +1,6 @@ import { MetricOptions } from '@opentelemetry/api'; -export const IMetricRepository = 'IMetricRepository'; +export const ITelemetryRepository = 'ITelemetryRepository'; export interface MetricGroupOptions { enabled: boolean; @@ -13,7 +13,7 @@ export interface IMetricGroupRepository { configure(options: MetricGroupOptions): this; } -export interface IMetricRepository { +export interface ITelemetryRepository { api: IMetricGroupRepository; host: IMetricGroupRepository; jobs: IMetricGroupRepository; diff --git a/server/src/repositories/index.ts b/server/src/repositories/index.ts index 5bf08d0d78..94a0212204 100644 --- a/server/src/repositories/index.ts +++ b/server/src/repositories/index.ts @@ -17,7 +17,6 @@ import { IMapRepository } from 'src/interfaces/map.interface'; import { IMediaRepository } from 'src/interfaces/media.interface'; import { IMemoryRepository } from 'src/interfaces/memory.interface'; import { IMetadataRepository } from 'src/interfaces/metadata.interface'; -import { IMetricRepository } from 'src/interfaces/metric.interface'; import { IMoveRepository } from 'src/interfaces/move.interface'; import { INotificationRepository } from 'src/interfaces/notification.interface'; import { IOAuthRepository } from 'src/interfaces/oauth.interface'; @@ -31,6 +30,7 @@ import { IStackRepository } from 'src/interfaces/stack.interface'; import { IStorageRepository } from 'src/interfaces/storage.interface'; import { ISystemMetadataRepository } from 'src/interfaces/system-metadata.interface'; import { ITagRepository } from 'src/interfaces/tag.interface'; +import { ITelemetryRepository } from 'src/interfaces/telemetry.interface'; import { ITrashRepository } from 'src/interfaces/trash.interface'; import { IUserRepository } from 'src/interfaces/user.interface'; import { IVersionHistoryRepository } from 'src/interfaces/version-history.interface'; @@ -54,7 +54,6 @@ import { MapRepository } from 'src/repositories/map.repository'; import { MediaRepository } from 'src/repositories/media.repository'; import { MemoryRepository } from 'src/repositories/memory.repository'; import { MetadataRepository } from 'src/repositories/metadata.repository'; -import { MetricRepository } from 'src/repositories/metric.repository'; import { MoveRepository } from 'src/repositories/move.repository'; import { NotificationRepository } from 'src/repositories/notification.repository'; import { OAuthRepository } from 'src/repositories/oauth.repository'; @@ -68,6 +67,7 @@ import { StackRepository } from 'src/repositories/stack.repository'; import { StorageRepository } from 'src/repositories/storage.repository'; import { SystemMetadataRepository } from 'src/repositories/system-metadata.repository'; import { TagRepository } from 'src/repositories/tag.repository'; +import { TelemetryRepository } from 'src/repositories/telemetry.repository'; import { TrashRepository } from 'src/repositories/trash.repository'; import { UserRepository } from 'src/repositories/user.repository'; import { VersionHistoryRepository } from 'src/repositories/version-history.repository'; @@ -93,7 +93,6 @@ export const repositories = [ { provide: IMediaRepository, useClass: MediaRepository }, { provide: IMemoryRepository, useClass: MemoryRepository }, { provide: IMetadataRepository, useClass: MetadataRepository }, - { provide: IMetricRepository, useClass: MetricRepository }, { provide: IMoveRepository, useClass: MoveRepository }, { provide: INotificationRepository, useClass: NotificationRepository }, { provide: IOAuthRepository, useClass: OAuthRepository }, @@ -107,6 +106,7 @@ export const repositories = [ { provide: IStorageRepository, useClass: StorageRepository }, { provide: ISystemMetadataRepository, useClass: SystemMetadataRepository }, { provide: ITagRepository, useClass: TagRepository }, + { provide: ITelemetryRepository, useClass: TelemetryRepository }, { provide: ITrashRepository, useClass: TrashRepository }, { provide: IUserRepository, useClass: UserRepository }, { provide: IVersionHistoryRepository, useClass: VersionHistoryRepository }, diff --git a/server/src/repositories/metric.repository.ts b/server/src/repositories/telemetry.repository.ts similarity index 80% rename from server/src/repositories/metric.repository.ts rename to server/src/repositories/telemetry.repository.ts index b59bcf9ed1..d1dc66ae85 100644 --- a/server/src/repositories/metric.repository.ts +++ b/server/src/repositories/telemetry.repository.ts @@ -2,7 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { MetricOptions } from '@opentelemetry/api'; import { MetricService } from 'nestjs-otel'; import { IConfigRepository } from 'src/interfaces/config.interface'; -import { IMetricGroupRepository, IMetricRepository, MetricGroupOptions } from 'src/interfaces/metric.interface'; +import { IMetricGroupRepository, ITelemetryRepository, MetricGroupOptions } from 'src/interfaces/telemetry.interface'; class MetricGroupRepository implements IMetricGroupRepository { private enabled = false; @@ -34,7 +34,7 @@ class MetricGroupRepository implements IMetricGroupRepository { } @Injectable() -export class MetricRepository implements IMetricRepository { +export class TelemetryRepository implements ITelemetryRepository { api: MetricGroupRepository; host: MetricGroupRepository; jobs: MetricGroupRepository; @@ -42,9 +42,11 @@ export class MetricRepository implements IMetricRepository { constructor(metricService: MetricService, @Inject(IConfigRepository) configRepository: IConfigRepository) { const { telemetry } = configRepository.getEnv(); - this.api = new MetricGroupRepository(metricService).configure({ enabled: telemetry.apiMetrics }); - this.host = new MetricGroupRepository(metricService).configure({ enabled: telemetry.hostMetrics }); - this.jobs = new MetricGroupRepository(metricService).configure({ enabled: telemetry.jobMetrics }); - this.repo = new MetricGroupRepository(metricService).configure({ enabled: telemetry.repoMetrics }); + const { apiMetrics, hostMetrics, jobMetrics, repoMetrics } = telemetry; + + this.api = new MetricGroupRepository(metricService).configure({ enabled: apiMetrics }); + this.host = new MetricGroupRepository(metricService).configure({ enabled: hostMetrics }); + this.jobs = new MetricGroupRepository(metricService).configure({ enabled: jobMetrics }); + this.repo = new MetricGroupRepository(metricService).configure({ enabled: repoMetrics }); } } diff --git a/server/src/services/base.service.ts b/server/src/services/base.service.ts index 2bb717b45b..441a81cf91 100644 --- a/server/src/services/base.service.ts +++ b/server/src/services/base.service.ts @@ -20,7 +20,6 @@ import { IMapRepository } from 'src/interfaces/map.interface'; import { IMediaRepository } from 'src/interfaces/media.interface'; import { IMemoryRepository } from 'src/interfaces/memory.interface'; import { IMetadataRepository } from 'src/interfaces/metadata.interface'; -import { IMetricRepository } from 'src/interfaces/metric.interface'; import { IMoveRepository } from 'src/interfaces/move.interface'; import { INotificationRepository } from 'src/interfaces/notification.interface'; import { IOAuthRepository } from 'src/interfaces/oauth.interface'; @@ -34,6 +33,7 @@ import { IStackRepository } from 'src/interfaces/stack.interface'; import { IStorageRepository } from 'src/interfaces/storage.interface'; import { ISystemMetadataRepository } from 'src/interfaces/system-metadata.interface'; import { ITagRepository } from 'src/interfaces/tag.interface'; +import { ITelemetryRepository } from 'src/interfaces/telemetry.interface'; import { ITrashRepository } from 'src/interfaces/trash.interface'; import { IUserRepository } from 'src/interfaces/user.interface'; import { IVersionHistoryRepository } from 'src/interfaces/version-history.interface'; @@ -64,7 +64,6 @@ export class BaseService { @Inject(IMediaRepository) protected mediaRepository: IMediaRepository, @Inject(IMemoryRepository) protected memoryRepository: IMemoryRepository, @Inject(IMetadataRepository) protected metadataRepository: IMetadataRepository, - @Inject(IMetricRepository) protected metricRepository: IMetricRepository, @Inject(IMoveRepository) protected moveRepository: IMoveRepository, @Inject(INotificationRepository) protected notificationRepository: INotificationRepository, @Inject(IOAuthRepository) protected oauthRepository: IOAuthRepository, @@ -78,6 +77,7 @@ export class BaseService { @Inject(IStorageRepository) protected storageRepository: IStorageRepository, @Inject(ISystemMetadataRepository) protected systemMetadataRepository: ISystemMetadataRepository, @Inject(ITagRepository) protected tagRepository: ITagRepository, + @Inject(ITelemetryRepository) protected telemetryRepository: ITelemetryRepository, @Inject(ITrashRepository) protected trashRepository: ITrashRepository, @Inject(IUserRepository) protected userRepository: IUserRepository, @Inject(IVersionHistoryRepository) protected versionRepository: IVersionHistoryRepository, diff --git a/server/src/services/job.service.ts b/server/src/services/job.service.ts index 971509447f..46771ff046 100644 --- a/server/src/services/job.service.ts +++ b/server/src/services/job.service.ts @@ -124,7 +124,7 @@ export class JobService extends BaseService { throw new BadRequestException(`Job is already running`); } - this.metricRepository.jobs.addToCounter(`immich.queues.${snakeCase(name)}.started`, 1); + this.telemetryRepository.jobs.addToCounter(`immich.queues.${snakeCase(name)}.started`, 1); switch (name) { case QueueName.VIDEO_CONVERSION: { @@ -197,19 +197,19 @@ export class JobService extends BaseService { } const queueMetric = `immich.queues.${snakeCase(queueName)}.active`; - this.metricRepository.jobs.addToGauge(queueMetric, 1); + this.telemetryRepository.jobs.addToGauge(queueMetric, 1); try { const status = await handler(data); const jobMetric = `immich.jobs.${name.replaceAll('-', '_')}.${status}`; - this.metricRepository.jobs.addToCounter(jobMetric, 1); + this.telemetryRepository.jobs.addToCounter(jobMetric, 1); if (status === JobStatus.SUCCESS || status == JobStatus.SKIPPED) { await this.onDone(item); } } catch (error: Error | any) { this.logger.error(`Unable to run job handler (${queueName}/${name}): ${error}`, error?.stack, data); } finally { - this.metricRepository.jobs.addToGauge(queueMetric, -1); + this.telemetryRepository.jobs.addToGauge(queueMetric, -1); } }); } diff --git a/server/test/repositories/metric.repository.mock.ts b/server/test/repositories/metric.repository.mock.ts deleted file mode 100644 index e2c3e2aac1..0000000000 --- a/server/test/repositories/metric.repository.mock.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { IMetricRepository } from 'src/interfaces/metric.interface'; -import { Mocked, vitest } from 'vitest'; - -export const newMetricRepositoryMock = (): Mocked => { - return { - api: { - addToCounter: vitest.fn(), - addToGauge: vitest.fn(), - addToHistogram: vitest.fn(), - configure: vitest.fn(), - }, - host: { - addToCounter: vitest.fn(), - addToGauge: vitest.fn(), - addToHistogram: vitest.fn(), - configure: vitest.fn(), - }, - jobs: { - addToCounter: vitest.fn(), - addToGauge: vitest.fn(), - addToHistogram: vitest.fn(), - configure: vitest.fn(), - }, - repo: { - addToCounter: vitest.fn(), - addToGauge: vitest.fn(), - addToHistogram: vitest.fn(), - configure: vitest.fn(), - }, - }; -}; diff --git a/server/test/repositories/telemetry.repository.mock.ts b/server/test/repositories/telemetry.repository.mock.ts new file mode 100644 index 0000000000..737463065c --- /dev/null +++ b/server/test/repositories/telemetry.repository.mock.ts @@ -0,0 +1,20 @@ +import { ITelemetryRepository } from 'src/interfaces/telemetry.interface'; +import { Mocked, vitest } from 'vitest'; + +const newMetricGroupMock = () => { + return { + addToCounter: vitest.fn(), + addToGauge: vitest.fn(), + addToHistogram: vitest.fn(), + configure: vitest.fn(), + }; +}; + +export const newTelemetryRepositoryMock = (): Mocked => { + return { + api: newMetricGroupMock(), + host: newMetricGroupMock(), + jobs: newMetricGroupMock(), + repo: newMetricGroupMock(), + }; +}; diff --git a/server/test/utils.ts b/server/test/utils.ts index 3b7e80994d..9a40a22c2c 100644 --- a/server/test/utils.ts +++ b/server/test/utils.ts @@ -20,7 +20,6 @@ import { newMapRepositoryMock } from 'test/repositories/map.repository.mock'; import { newMediaRepositoryMock } from 'test/repositories/media.repository.mock'; import { newMemoryRepositoryMock } from 'test/repositories/memory.repository.mock'; import { newMetadataRepositoryMock } from 'test/repositories/metadata.repository.mock'; -import { newMetricRepositoryMock } from 'test/repositories/metric.repository.mock'; import { newMoveRepositoryMock } from 'test/repositories/move.repository.mock'; import { newNotificationRepositoryMock } from 'test/repositories/notification.repository.mock'; import { newOAuthRepositoryMock } from 'test/repositories/oauth.repository.mock'; @@ -34,6 +33,7 @@ import { newStackRepositoryMock } from 'test/repositories/stack.repository.mock' import { newStorageRepositoryMock } from 'test/repositories/storage.repository.mock'; import { newSystemMetadataRepositoryMock } from 'test/repositories/system-metadata.repository.mock'; import { newTagRepositoryMock } from 'test/repositories/tag.repository.mock'; +import { newTelemetryRepositoryMock } from 'test/repositories/telemetry.repository.mock'; import { newTrashRepositoryMock } from 'test/repositories/trash.repository.mock'; import { newUserRepositoryMock } from 'test/repositories/user.repository.mock'; import { newVersionHistoryRepositoryMock } from 'test/repositories/version-history.repository.mock'; @@ -73,7 +73,6 @@ export const newTestService = ( const mediaMock = newMediaRepositoryMock(); const memoryMock = newMemoryRepositoryMock(); const metadataMock = (metadataRepository || newMetadataRepositoryMock()) as Mocked; - const metricMock = newMetricRepositoryMock(); const moveMock = newMoveRepositoryMock(); const notificationMock = newNotificationRepositoryMock(); const oauthMock = newOAuthRepositoryMock(); @@ -87,6 +86,7 @@ export const newTestService = ( const storageMock = newStorageRepositoryMock(); const systemMock = newSystemMetadataRepositoryMock(); const tagMock = newTagRepositoryMock(); + const telemetryMock = newTelemetryRepositoryMock(); const trashMock = newTrashRepositoryMock(); const userMock = newUserRepositoryMock(); const versionHistoryMock = newVersionHistoryRepositoryMock(); @@ -112,7 +112,6 @@ export const newTestService = ( mediaMock, memoryMock, metadataMock, - metricMock, moveMock, notificationMock, oauthMock, @@ -126,6 +125,7 @@ export const newTestService = ( storageMock, systemMock, tagMock, + telemetryMock, trashMock, userMock, versionHistoryMock, @@ -153,7 +153,6 @@ export const newTestService = ( mediaMock, memoryMock, metadataMock, - metricMock, moveMock, notificationMock, oauthMock, @@ -167,6 +166,7 @@ export const newTestService = ( storageMock, systemMock, tagMock, + telemetryMock, trashMock, userMock, versionHistoryMock,