0
Fork 0
mirror of https://github.com/immich-app/immich.git synced 2025-01-21 00:52:43 -05:00

initial sample implementation of metrics

This commit is contained in:
Daniel Dietzler 2023-12-10 22:13:37 +01:00
parent 8fdd3aaed1
commit 874f707c92
No known key found for this signature in database
GPG key ID: A1C0B97CD8E18DFF
13 changed files with 109 additions and 1 deletions

View file

@ -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,

View file

@ -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';

View file

@ -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, QueueName> = {
[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,

View file

@ -0,0 +1,2 @@
export * from './metrics.dto';
export * from './metrics.service';

View file

@ -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;
}

View file

@ -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;
}
}

View file

@ -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';

View file

@ -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<T = any> = (data: T) => boolean | Promise<boolean>;
export type JobItemHandler = (item: JobItem) => Promise<void>;

View file

@ -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<void>;
}

View file

@ -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 },

View file

@ -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';

View file

@ -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<void> {
await axios.post('IMMICH-DATA-DOMAIN', payload);
}
}

View file

@ -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),