From 874f707c92d5f76ee271f2477dc73fb07c4db39a Mon Sep 17 00:00:00 2001 From: Daniel Dietzler Date: Sun, 10 Dec 2023 22:13:37 +0100 Subject: [PATCH] initial sample implementation of metrics --- server/src/domain/domain.module.ts | 2 + server/src/domain/index.ts | 1 + server/src/domain/job/job.constants.ts | 4 ++ server/src/domain/metrics/index.ts | 2 + server/src/domain/metrics/metrics.dto.ts | 14 ++++++ server/src/domain/metrics/metrics.service.ts | 50 +++++++++++++++++++ server/src/domain/repositories/index.ts | 1 + .../src/domain/repositories/job.repository.ts | 6 ++- .../domain/repositories/metrics.repository.ts | 12 +++++ server/src/infra/infra.module.ts | 3 ++ server/src/infra/repositories/index.ts | 1 + .../infra/repositories/metrics.repository.ts | 11 ++++ server/src/microservices/app.service.ts | 3 ++ 13 files changed, 109 insertions(+), 1 deletion(-) create mode 100644 server/src/domain/metrics/index.ts create mode 100644 server/src/domain/metrics/metrics.dto.ts create mode 100644 server/src/domain/metrics/metrics.service.ts create mode 100644 server/src/domain/repositories/metrics.repository.ts create mode 100644 server/src/infra/repositories/metrics.repository.ts diff --git a/server/src/domain/domain.module.ts b/server/src/domain/domain.module.ts index ff666d89a2..d8b566ba5b 100644 --- a/server/src/domain/domain.module.ts +++ b/server/src/domain/domain.module.ts @@ -11,6 +11,7 @@ import { JobService } from './job'; import { LibraryService } from './library'; import { MediaService } from './media'; import { MetadataService } from './metadata'; +import { MetricsService } from './metrics'; import { PartnerService } from './partner'; import { PersonService } from './person'; import { SearchService } from './search'; @@ -34,6 +35,7 @@ const providers: Provider[] = [ JobService, MediaService, MetadataService, + MetricsService, LibraryService, PersonService, PartnerService, diff --git a/server/src/domain/index.ts b/server/src/domain/index.ts index ca3d4ced46..060de1c6a7 100644 --- a/server/src/domain/index.ts +++ b/server/src/domain/index.ts @@ -14,6 +14,7 @@ export * from './job'; export * from './library'; export * from './media'; export * from './metadata'; +export * from './metrics'; export * from './partner'; export * from './person'; export * from './repositories'; diff --git a/server/src/domain/job/job.constants.ts b/server/src/domain/job/job.constants.ts index d239d28c2b..c5c4dc625a 100644 --- a/server/src/domain/job/job.constants.ts +++ b/server/src/domain/job/job.constants.ts @@ -81,6 +81,9 @@ export enum JobName { SIDECAR_DISCOVERY = 'sidecar-discovery', SIDECAR_SYNC = 'sidecar-sync', SIDECAR_WRITE = 'sidecar-write', + + // metrics + METRICS = 'metrics', } export const JOBS_ASSET_PAGINATION_SIZE = 1000; @@ -95,6 +98,7 @@ export const JOBS_TO_QUEUE: Record = { [JobName.CLEAN_OLD_AUDIT_LOGS]: QueueName.BACKGROUND_TASK, [JobName.PERSON_CLEANUP]: QueueName.BACKGROUND_TASK, [JobName.PERSON_DELETE]: QueueName.BACKGROUND_TASK, + [JobName.METRICS]: QueueName.BACKGROUND_TASK, // conversion [JobName.QUEUE_VIDEO_CONVERSION]: QueueName.VIDEO_CONVERSION, diff --git a/server/src/domain/metrics/index.ts b/server/src/domain/metrics/index.ts new file mode 100644 index 0000000000..1748fae456 --- /dev/null +++ b/server/src/domain/metrics/index.ts @@ -0,0 +1,2 @@ +export * from './metrics.dto'; +export * from './metrics.service'; diff --git a/server/src/domain/metrics/metrics.dto.ts b/server/src/domain/metrics/metrics.dto.ts new file mode 100644 index 0000000000..b3a85e8cbe --- /dev/null +++ b/server/src/domain/metrics/metrics.dto.ts @@ -0,0 +1,14 @@ +export class MetricsServerInfoDto { + version?: string; + diskUse?: string; +} + +export class MetricsAssetCountDto { + photo?: number; + video?: number; +} + +export class MetricsDto { + serverInfo!: MetricsServerInfoDto; + assetCount!: MetricsAssetCountDto; +} diff --git a/server/src/domain/metrics/metrics.service.ts b/server/src/domain/metrics/metrics.service.ts new file mode 100644 index 0000000000..9f97cabe06 --- /dev/null +++ b/server/src/domain/metrics/metrics.service.ts @@ -0,0 +1,50 @@ +import { Inject, Injectable } from '@nestjs/common'; +import { + ICommunicationRepository, + IServerInfoRepository, + IStorageRepository, + ISystemConfigRepository, + IUserRepository, +} from '../repositories'; +import { IMetricsRepository, SharedMetrics } from '../repositories/metrics.repository'; +import { ServerInfoService } from '../server-info'; +import { MetricsDto } from './metrics.dto'; + +@Injectable() +export class MetricsService { + private serverInfo: ServerInfoService; + + constructor( + @Inject(ICommunicationRepository) communicationRepository: ICommunicationRepository, + @Inject(IMetricsRepository) private repository: IMetricsRepository, + @Inject(IUserRepository) userRepository: IUserRepository, + @Inject(IServerInfoRepository) serverInfoRepository: IServerInfoRepository, + @Inject(IStorageRepository) storageRepository: IStorageRepository, + @Inject(ISystemConfigRepository) configRepository: ISystemConfigRepository, + ) { + this.serverInfo = new ServerInfoService( + communicationRepository, + configRepository, + userRepository, + serverInfoRepository, + storageRepository, + ); + } + + async shareMetrics(metrics: SharedMetrics) { + const metricsPayload = new MetricsDto(); + if (metrics.serverInfo) { + metricsPayload.serverInfo.version = this.serverInfo.getVersion().toString(); + metricsPayload.serverInfo.diskUse = (await this.serverInfo.getInfo()).diskUse; + } + + if (metrics.assetCount) { + const stats = await this.serverInfo.getStatistics(); + metricsPayload.assetCount.photo = stats.photos; + metricsPayload.assetCount.video = stats.videos; + } + + await this.repository.sendMetrics(metricsPayload); + return true; + } +} diff --git a/server/src/domain/repositories/index.ts b/server/src/domain/repositories/index.ts index 997cd4e8b5..7929596a25 100644 --- a/server/src/domain/repositories/index.ts +++ b/server/src/domain/repositories/index.ts @@ -12,6 +12,7 @@ export * from './library.repository'; export * from './machine-learning.repository'; export * from './media.repository'; export * from './metadata.repository'; +export * from './metrics.repository'; export * from './move.repository'; export * from './partner.repository'; export * from './person.repository'; diff --git a/server/src/domain/repositories/job.repository.ts b/server/src/domain/repositories/job.repository.ts index bd6982ca19..e2075eabe7 100644 --- a/server/src/domain/repositories/job.repository.ts +++ b/server/src/domain/repositories/job.repository.ts @@ -9,6 +9,7 @@ import { ILibraryRefreshJob, ISidecarWriteJob, } from '../job/job.interface'; +import { SharedMetrics } from './metrics.repository'; export interface JobCounts { active: number; @@ -89,7 +90,10 @@ export type JobItem = | { name: JobName.LIBRARY_REMOVE_OFFLINE; data: IEntityJob } | { name: JobName.LIBRARY_DELETE; data: IEntityJob } | { name: JobName.LIBRARY_QUEUE_SCAN_ALL; data: IBaseJob } - | { name: JobName.LIBRARY_QUEUE_CLEANUP; data: IBaseJob }; + | { name: JobName.LIBRARY_QUEUE_CLEANUP; data: IBaseJob } + + // Metrics + | { name: JobName.METRICS; data: SharedMetrics }; export type JobHandler = (data: T) => boolean | Promise; export type JobItemHandler = (item: JobItem) => Promise; diff --git a/server/src/domain/repositories/metrics.repository.ts b/server/src/domain/repositories/metrics.repository.ts new file mode 100644 index 0000000000..970d54dc54 --- /dev/null +++ b/server/src/domain/repositories/metrics.repository.ts @@ -0,0 +1,12 @@ +import { MetricsDto } from '../metrics'; + +export const IMetricsRepository = 'IMetricsRepository'; + +export interface SharedMetrics { + serverInfo: boolean; + assetCount: boolean; +} + +export interface IMetricsRepository { + sendMetrics(payload: MetricsDto): Promise; +} diff --git a/server/src/infra/infra.module.ts b/server/src/infra/infra.module.ts index aef41f5627..9a503a9305 100644 --- a/server/src/infra/infra.module.ts +++ b/server/src/infra/infra.module.ts @@ -13,6 +13,7 @@ import { IMachineLearningRepository, IMediaRepository, IMetadataRepository, + IMetricsRepository, IMoveRepository, IPartnerRepository, IPersonRepository, @@ -51,6 +52,7 @@ import { MachineLearningRepository, MediaRepository, MetadataRepository, + MetricsRepository, MoveRepository, PartnerRepository, PersonRepository, @@ -78,6 +80,7 @@ const providers: Provider[] = [ { provide: IKeyRepository, useClass: ApiKeyRepository }, { provide: IMachineLearningRepository, useClass: MachineLearningRepository }, { provide: IMetadataRepository, useClass: MetadataRepository }, + { provide: IMetricsRepository, useClass: MetricsRepository }, { provide: IMoveRepository, useClass: MoveRepository }, { provide: IPartnerRepository, useClass: PartnerRepository }, { provide: IPersonRepository, useClass: PersonRepository }, diff --git a/server/src/infra/repositories/index.ts b/server/src/infra/repositories/index.ts index 63b8f2afb2..404d224e7e 100644 --- a/server/src/infra/repositories/index.ts +++ b/server/src/infra/repositories/index.ts @@ -13,6 +13,7 @@ export * from './library.repository'; export * from './machine-learning.repository'; export * from './media.repository'; export * from './metadata.repository'; +export * from './metrics.repository'; export * from './move.repository'; export * from './partner.repository'; export * from './person.repository'; diff --git a/server/src/infra/repositories/metrics.repository.ts b/server/src/infra/repositories/metrics.repository.ts new file mode 100644 index 0000000000..c4b8a0e010 --- /dev/null +++ b/server/src/infra/repositories/metrics.repository.ts @@ -0,0 +1,11 @@ +import { MetricsDto } from '@app/domain/metrics'; +import { IMetricsRepository } from '@app/domain/repositories/metrics.repository'; +import { Injectable } from '@nestjs/common'; +import axios from 'axios'; + +@Injectable() +export class MetricsRepository implements IMetricsRepository { + async sendMetrics(payload: MetricsDto): Promise { + await axios.post('IMMICH-DATA-DOMAIN', payload); + } +} diff --git a/server/src/microservices/app.service.ts b/server/src/microservices/app.service.ts index ef8af48aca..1c59324191 100644 --- a/server/src/microservices/app.service.ts +++ b/server/src/microservices/app.service.ts @@ -15,6 +15,7 @@ import { SystemConfigService, UserService, } from '@app/domain'; +import { MetricsService } from '@app/domain/metrics'; import { Injectable } from '@nestjs/common'; @Injectable() @@ -27,6 +28,7 @@ export class AppService { private libraryService: LibraryService, private mediaService: MediaService, private metadataService: MetadataService, + private metricsService: MetricsService, private personService: PersonService, private smartInfoService: SmartInfoService, private storageTemplateService: StorageTemplateService, @@ -60,6 +62,7 @@ export class AppService { [JobName.VIDEO_CONVERSION]: (data) => this.mediaService.handleVideoConversion(data), [JobName.QUEUE_METADATA_EXTRACTION]: (data) => this.metadataService.handleQueueMetadataExtraction(data), [JobName.METADATA_EXTRACTION]: (data) => this.metadataService.handleMetadataExtraction(data), + [JobName.METRICS]: (data) => this.metricsService.shareMetrics(data), [JobName.LINK_LIVE_PHOTOS]: (data) => this.metadataService.handleLivePhotoLinking(data), [JobName.QUEUE_RECOGNIZE_FACES]: (data) => this.personService.handleQueueRecognizeFaces(data), [JobName.RECOGNIZE_FACES]: (data) => this.personService.handleRecognizeFaces(data),