settings for metrics

This commit is contained in:
Daniel Dietzler
2023-12-28 19:06:35 +01:00
parent db997f9173
commit 902d4d0275
18 changed files with 486 additions and 45 deletions
+54 -12
View File
@@ -1,17 +1,59 @@
export class MetricsServerInfoDto {
cpuCount?: number;
cpuModel?: string;
memoryCount?: number;
version?: string;
import { IsBoolean } from 'class-validator';
// TODO I feel like it must be possible to generate those from MetricsServerInfo and MetricsAssetCount
export class MetricServerInfoConfig {
@IsBoolean()
cpuCount!: boolean;
@IsBoolean()
cpuModel!: boolean;
@IsBoolean()
memory!: boolean;
@IsBoolean()
version!: boolean;
}
export class MetricsAssetCountDto {
image?: number;
video?: number;
total?: number;
export class MetricsAssetCountConfig {
@IsBoolean()
image!: boolean;
@IsBoolean()
video!: boolean;
@IsBoolean()
total!: boolean;
}
export class MetricsDto {
serverInfo!: MetricsServerInfoDto;
assetCount!: MetricsAssetCountDto;
class MetricsServerInfo {
cpuCount!: number;
cpuModel!: string;
memory!: number;
version!: string;
}
class MetricsAssetCount {
image!: number;
video!: number;
total!: number;
}
export interface Metrics {
serverInfo: {
cpuCount: number;
cpuModel: string;
memory: number;
version: string;
};
assetCount: {
image: number;
video: number;
total: number;
};
}
export class MetricsDto implements Metrics {
serverInfo!: MetricsServerInfo;
assetCount!: MetricsAssetCount;
}
+53 -21
View File
@@ -1,39 +1,71 @@
import { Inject, Injectable } from '@nestjs/common';
import _ from 'lodash';
import { serverVersion } from '../domain.constant';
import { JobName } from '../job';
import { ISystemConfigRepository } from '../repositories';
import { IJobRepository } from '../repositories/job.repository';
import { IMetricsRepository, SharedMetrics } from '../repositories/metrics.repository';
import { MetricsDto } from './metrics.dto';
import { IMetricsRepository } from '../repositories/metrics.repository';
import { FeatureFlag, SystemConfigCore, SystemConfigMetricsDto } from '../system-config';
import { Metrics, MetricsDto } from './metrics.dto';
@Injectable()
export class MetricsService {
private configCore: SystemConfigCore;
constructor(
@Inject(IJobRepository) private jobRepository: IJobRepository,
@Inject(IMetricsRepository) private repository: IMetricsRepository,
) {}
@Inject(ISystemConfigRepository) systemConfigRepository: ISystemConfigRepository,
) {
this.configCore = SystemConfigCore.create(systemConfigRepository);
}
async handleQueueMetrics() {
// TODO config for what metrics should be fetched and if any at all
await this.jobRepository.queue({ name: JobName.METRICS, data: { assetCount: true, serverInfo: true } });
if (await this.configCore.hasFeature(FeatureFlag.METRICS)) {
await this.jobRepository.queue({ name: JobName.METRICS });
}
}
async handleMetrics(metrics: SharedMetrics) {
const metricsPayload = new MetricsDto();
if (metrics.serverInfo) {
metricsPayload.serverInfo.version = serverVersion.toString();
metricsPayload.serverInfo.cpuCount = this.repository.getCpuCount();
metricsPayload.serverInfo.cpuModel = this.repository.getCpuModel();
metricsPayload.serverInfo.memoryCount = this.repository.getMemoryCount();
}
async handleSendMetrics() {
const metricsConfig = await this.configCore.getConfig().then((config) => config.metrics);
const metrics = await this.getMetrics(metricsConfig);
if (metrics.assetCount) {
metricsPayload.assetCount.image = await this.repository.getImageCount();
metricsPayload.assetCount.video = await this.repository.getVideoCount();
metricsPayload.assetCount.total = await this.repository.getAssetCount();
}
await this.repository.sendMetrics(metricsPayload);
await this.repository.sendMetrics(metrics);
return true;
}
async getMetrics(config: SystemConfigMetricsDto) {
const metrics: Metrics = new MetricsDto();
metrics.serverInfo = {
cpuCount: this.repository.getCpuCount(),
cpuModel: this.repository.getCpuModel(),
memory: this.repository.getMemory(),
version: serverVersion.toString(),
};
metrics.assetCount = {
image: await this.repository.getImageCount(),
video: await this.repository.getVideoCount(),
total: await this.repository.getAssetCount(),
};
return _.pick(metrics, this.getKeys(config));
}
private getKeys(config: SystemConfigMetricsDto) {
const result = [];
const keys = _.keys(config) as Array<keyof SystemConfigMetricsDto>;
for (const key of keys) {
const subConfig = _.get(config, key);
if (typeof subConfig === 'boolean') {
continue;
}
const keys = _.keys(_.pickBy(subConfig)).map((value) => `${key}.${value}`);
result.push(...keys);
}
return result;
}
}
@@ -9,7 +9,6 @@ import {
ILibraryRefreshJob,
ISidecarWriteJob,
} from '../job/job.interface';
import { SharedMetrics } from './metrics.repository';
export interface JobCounts {
active: number;
@@ -93,7 +92,7 @@ export type JobItem =
| { name: JobName.LIBRARY_QUEUE_CLEANUP; data: IBaseJob }
// Metrics
| { name: JobName.METRICS; data: SharedMetrics };
| { name: JobName.METRICS; data?: IBaseJob };
export type JobHandler<T = any> = (data: T) => boolean | Promise<boolean>;
export type JobItemHandler = (item: JobItem) => Promise<void>;
@@ -2,17 +2,12 @@ import { MetricsDto } from '../metrics';
export const IMetricsRepository = 'IMetricsRepository';
export interface SharedMetrics {
serverInfo: boolean;
assetCount: boolean;
}
export interface IMetricsRepository {
getAssetCount(): Promise<number>;
getCpuCount(): number;
getCpuModel(): string;
getMemoryCount(): number;
getMemory(): number;
getImageCount(): Promise<number>;
getVideoCount(): Promise<number>;
sendMetrics(payload: MetricsDto): Promise<void>;
sendMetrics(payload: Partial<MetricsDto>): Promise<void>;
}
@@ -93,6 +93,7 @@ export class ServerFeaturesDto implements FeatureFlags {
configFile!: boolean;
facialRecognition!: boolean;
map!: boolean;
metrics!: boolean;
trash!: boolean;
reverseGeocoding!: boolean;
oauth!: boolean;
@@ -1,5 +1,6 @@
export * from './system-config-ffmpeg.dto';
export * from './system-config-library.dto';
export * from './system-config-metrics.dto';
export * from './system-config-oauth.dto';
export * from './system-config-password-login.dto';
export * from './system-config-storage-template.dto';
@@ -0,0 +1,18 @@
import { MetricServerInfoConfig, MetricsAssetCountConfig } from '@app/domain';
import { Type } from 'class-transformer';
import { IsBoolean, IsObject, ValidateNested } from 'class-validator';
export class SystemConfigMetricsDto {
@IsBoolean()
enabled!: boolean;
@Type(() => MetricServerInfoConfig)
@ValidateNested()
@IsObject()
serverInfo!: MetricServerInfoConfig;
@Type(() => MetricsAssetCountConfig)
@ValidateNested()
@IsObject()
assetCount!: MetricsAssetCountConfig;
}
@@ -1,6 +1,7 @@
import { SystemConfig } from '@app/infra/entities';
import { Type } from 'class-transformer';
import { IsObject, ValidateNested } from 'class-validator';
import { SystemConfigMetricsDto } from '.';
import { SystemConfigFFmpegDto } from './system-config-ffmpeg.dto';
import { SystemConfigJobDto } from './system-config-job.dto';
import { SystemConfigLibraryDto } from './system-config-library.dto';
@@ -37,6 +38,11 @@ export class SystemConfigDto implements SystemConfig {
@IsObject()
map!: SystemConfigMapDto;
@Type(() => SystemConfigMetricsDto)
@ValidateNested()
@IsObject()
metrics!: SystemConfigMetricsDto;
@Type(() => SystemConfigNewVersionCheckDto)
@ValidateNested()
@IsObject()
@@ -82,6 +82,20 @@ export const defaults = Object.freeze<SystemConfig>({
lightStyle: '',
darkStyle: '',
},
metrics: {
enabled: false,
serverInfo: {
cpuCount: true,
cpuModel: true,
memory: true,
version: true,
},
assetCount: {
image: true,
video: true,
total: true,
},
},
reverseGeocoding: {
enabled: true,
},
@@ -132,6 +146,7 @@ export enum FeatureFlag {
CLIP_ENCODE = 'clipEncode',
FACIAL_RECOGNITION = 'facialRecognition',
MAP = 'map',
METRICS = 'metrics',
REVERSE_GEOCODING = 'reverseGeocoding',
SIDECAR = 'sidecar',
SEARCH = 'search',
@@ -204,6 +219,7 @@ export class SystemConfigCore {
[FeatureFlag.CLIP_ENCODE]: mlEnabled && config.machineLearning.clip.enabled,
[FeatureFlag.FACIAL_RECOGNITION]: mlEnabled && config.machineLearning.facialRecognition.enabled,
[FeatureFlag.MAP]: config.map.enabled,
[FeatureFlag.METRICS]: config.metrics.enabled,
[FeatureFlag.REVERSE_GEOCODING]: config.reverseGeocoding.enabled,
[FeatureFlag.SIDECAR]: true,
[FeatureFlag.SEARCH]: true,