refactor progress
This commit is contained in:
54
packages/api/src/telemetry/BusinessSpans.tsx
Normal file
54
packages/api/src/telemetry/BusinessSpans.tsx
Normal file
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Fluxer Contributors
|
||||
*
|
||||
* This file is part of Fluxer.
|
||||
*
|
||||
* Fluxer is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Fluxer is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import {type SpanOptions, withSpan} from '@fluxer/api/src/telemetry/Tracing';
|
||||
import {recordCounter} from '@fluxer/telemetry/src/Metrics';
|
||||
|
||||
export async function withBusinessSpan<T>(
|
||||
spanName: string,
|
||||
metricName: string,
|
||||
attributes: Record<string, string> = {},
|
||||
fn: () => Promise<T>,
|
||||
): Promise<T> {
|
||||
const spanOptions: SpanOptions = {
|
||||
name: spanName,
|
||||
attributes,
|
||||
};
|
||||
|
||||
try {
|
||||
const result = await withSpan(spanOptions, fn);
|
||||
recordCounter({
|
||||
name: metricName,
|
||||
dimensions: {...attributes, status: 'success'},
|
||||
value: 1,
|
||||
});
|
||||
return result;
|
||||
} catch (error) {
|
||||
recordCounter({
|
||||
name: metricName,
|
||||
dimensions: {
|
||||
...attributes,
|
||||
status: 'error',
|
||||
error_type: error instanceof Error ? error.name : 'unknown',
|
||||
},
|
||||
value: 1,
|
||||
});
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
157
packages/api/src/telemetry/CsamTelemetry.tsx
Normal file
157
packages/api/src/telemetry/CsamTelemetry.tsx
Normal file
@@ -0,0 +1,157 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Fluxer Contributors
|
||||
*
|
||||
* This file is part of Fluxer.
|
||||
*
|
||||
* Fluxer is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Fluxer is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import type {CsamResourceType} from '@fluxer/api/src/csam/CsamTypes';
|
||||
import {recordCounter, recordHistogram} from '@fluxer/api/src/Telemetry';
|
||||
|
||||
export type CsamScanStatus = 'success' | 'error' | 'skipped' | 'disabled';
|
||||
export type CsamApiStatus = 'success' | 'error' | 'timeout';
|
||||
export type NcmecSubmissionStatus = 'success' | 'error' | 'disabled';
|
||||
export type EvidenceStorageStatus = 'success' | 'error';
|
||||
|
||||
export function recordCsamScan(params: {
|
||||
resourceType: CsamResourceType;
|
||||
mediaType: string;
|
||||
status: CsamScanStatus;
|
||||
}): void {
|
||||
recordCounter({
|
||||
name: 'fluxer.csam.scans.total',
|
||||
dimensions: {
|
||||
resource_type: params.resourceType,
|
||||
media_type: params.mediaType,
|
||||
status: params.status,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function recordCsamScanDuration(params: {resourceType: CsamResourceType; durationMs: number}): void {
|
||||
recordHistogram({
|
||||
name: 'fluxer.csam.scan.duration_ms',
|
||||
valueMs: params.durationMs,
|
||||
dimensions: {
|
||||
resource_type: params.resourceType,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function recordCsamMatch(params: {resourceType: CsamResourceType; source: string; matchCount: number}): void {
|
||||
recordCounter({
|
||||
name: 'fluxer.csam.matches.total',
|
||||
value: params.matchCount,
|
||||
dimensions: {
|
||||
resource_type: params.resourceType,
|
||||
source: params.source,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function recordPhotoDnaApiCall(params: {
|
||||
operation: 'hash' | 'match';
|
||||
status: CsamApiStatus;
|
||||
hashCount?: number;
|
||||
}): void {
|
||||
recordCounter({
|
||||
name: 'fluxer.csam.photodna.api.total',
|
||||
dimensions: {
|
||||
operation: params.operation,
|
||||
status: params.status,
|
||||
hash_count: String(params.hashCount ?? 0),
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function recordPhotoDnaApiDuration(params: {operation: 'hash' | 'match'; durationMs: number}): void {
|
||||
recordHistogram({
|
||||
name: 'fluxer.csam.photodna.api.duration_ms',
|
||||
valueMs: params.durationMs,
|
||||
dimensions: {
|
||||
operation: params.operation,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export type NcmecSubmissionOperation = 'report' | 'evidence' | 'fileinfo' | 'finish' | 'retract';
|
||||
|
||||
export function recordNcmecSubmission(params: {
|
||||
operation: NcmecSubmissionOperation;
|
||||
status: NcmecSubmissionStatus;
|
||||
}): void {
|
||||
recordCounter({
|
||||
name: 'fluxer.csam.ncmec.submissions',
|
||||
dimensions: {
|
||||
operation: params.operation,
|
||||
status: params.status,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function recordCsamEvidenceStorage(params: {
|
||||
status: EvidenceStorageStatus;
|
||||
evidenceType: 'package' | 'attachment' | 'frame';
|
||||
}): void {
|
||||
recordCounter({
|
||||
name: 'fluxer.csam.evidence.stored',
|
||||
dimensions: {
|
||||
status: params.status,
|
||||
evidence_type: params.evidenceType,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function recordCsamQueueProcessed(params: {status: 'success' | 'error' | 'timeout'; batchSize: number}): void {
|
||||
recordCounter({
|
||||
name: 'fluxer.csam.queue.processed',
|
||||
dimensions: {
|
||||
status: params.status,
|
||||
batch_size: String(params.batchSize),
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function recordCsamQueueWaitTime(params: {waitTimeMs: number}): void {
|
||||
recordHistogram({
|
||||
name: 'fluxer.csam.queue.wait_time_ms',
|
||||
valueMs: params.waitTimeMs,
|
||||
});
|
||||
}
|
||||
|
||||
export function recordCsamQueueDepth(params: {depth: number}): void {
|
||||
recordCounter({
|
||||
name: 'fluxer.csam.queue.depth',
|
||||
value: params.depth,
|
||||
});
|
||||
}
|
||||
|
||||
export type ArachnidApiStatus = 'success' | 'error' | 'timeout';
|
||||
|
||||
export function recordArachnidApiCall(params: {status: ArachnidApiStatus}): void {
|
||||
recordCounter({
|
||||
name: 'fluxer.csam.arachnid.api.total',
|
||||
dimensions: {
|
||||
status: params.status,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function recordArachnidApiDuration(params: {durationMs: number}): void {
|
||||
recordHistogram({
|
||||
name: 'fluxer.csam.arachnid.api.duration_ms',
|
||||
valueMs: params.durationMs,
|
||||
});
|
||||
}
|
||||
115
packages/api/src/telemetry/MessageTelemetry.tsx
Normal file
115
packages/api/src/telemetry/MessageTelemetry.tsx
Normal file
@@ -0,0 +1,115 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Fluxer Contributors
|
||||
*
|
||||
* This file is part of Fluxer.
|
||||
*
|
||||
* Fluxer is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Fluxer is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import {recordCounter, recordHistogram} from '@fluxer/api/src/Telemetry';
|
||||
|
||||
export type MessageDeleteType = 'single' | 'bulk' | 'moderation';
|
||||
export type AttachmentOperation = 'upload' | 'process' | 'delete';
|
||||
export type AttachmentStatus = 'success' | 'error';
|
||||
|
||||
export function recordMessageSent(params: {
|
||||
channelType: string;
|
||||
hasAttachments: string;
|
||||
hasEmbeds: string;
|
||||
messageType: string;
|
||||
}): void {
|
||||
recordCounter({
|
||||
name: 'fluxer.messages.sent.total',
|
||||
dimensions: {
|
||||
channel_type: params.channelType,
|
||||
has_attachments: params.hasAttachments,
|
||||
has_embeds: params.hasEmbeds,
|
||||
message_type: params.messageType,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function recordMessageSendDuration(params: {channelType: string; durationMs: number}): void {
|
||||
recordHistogram({
|
||||
name: 'fluxer.messages.send.duration_ms',
|
||||
valueMs: params.durationMs,
|
||||
dimensions: {
|
||||
channel_type: params.channelType,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function recordMessageEdited(params: {channelType: number}): void {
|
||||
recordCounter({
|
||||
name: 'fluxer.messages.edited.total',
|
||||
dimensions: {
|
||||
channel_type: String(params.channelType),
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function recordMessageDeleted(params: {channelType: number; deleteType: MessageDeleteType}): void {
|
||||
recordCounter({
|
||||
name: 'fluxer.messages.deleted.total',
|
||||
dimensions: {
|
||||
channel_type: String(params.channelType),
|
||||
delete_type: params.deleteType,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function recordMessageRetrieved(params: {channelType: number; count: number}): void {
|
||||
recordCounter({
|
||||
name: 'fluxer.messages.retrieved.total',
|
||||
dimensions: {
|
||||
channel_type: String(params.channelType),
|
||||
count: String(params.count),
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function recordMessageRetrievalDuration(params: {channelType: number; durationMs: number}): void {
|
||||
recordHistogram({
|
||||
name: 'fluxer.messages.retrieval.duration_ms',
|
||||
valueMs: params.durationMs,
|
||||
dimensions: {
|
||||
channel_type: String(params.channelType),
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function recordAttachmentOperation(params: {
|
||||
operation: AttachmentOperation;
|
||||
contentType: string;
|
||||
status: AttachmentStatus;
|
||||
}): void {
|
||||
recordCounter({
|
||||
name: 'fluxer.messages.attachments.total',
|
||||
dimensions: {
|
||||
operation: params.operation,
|
||||
content_type: params.contentType,
|
||||
status: params.status,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function recordAttachmentUploadDuration(params: {contentType: string; durationMs: number}): void {
|
||||
recordHistogram({
|
||||
name: 'fluxer.messages.attachment.upload.duration_ms',
|
||||
valueMs: params.durationMs,
|
||||
dimensions: {
|
||||
content_type: params.contentType,
|
||||
},
|
||||
});
|
||||
}
|
||||
80
packages/api/src/telemetry/Tracing.tsx
Normal file
80
packages/api/src/telemetry/Tracing.tsx
Normal file
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Fluxer Contributors
|
||||
*
|
||||
* This file is part of Fluxer.
|
||||
*
|
||||
* Fluxer is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Fluxer is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import {getActiveSpan, getTracer} from '@fluxer/telemetry/src/Tracing';
|
||||
import type {Attributes, SpanKind} from '@opentelemetry/api';
|
||||
import {SpanStatusCode} from '@opentelemetry/api';
|
||||
|
||||
const tracer = getTracer('fluxer-api');
|
||||
|
||||
export interface SpanOptions {
|
||||
name: string;
|
||||
attributes?: Attributes;
|
||||
kind?: SpanKind;
|
||||
}
|
||||
|
||||
export async function withSpan<T>(options: SpanOptions, fn: () => Promise<T>): Promise<T> {
|
||||
return await tracer.startActiveSpan(
|
||||
options.name,
|
||||
{
|
||||
attributes: options.attributes ?? {},
|
||||
kind: options.kind,
|
||||
},
|
||||
async (span) => {
|
||||
try {
|
||||
const result = await fn();
|
||||
span.setStatus({code: SpanStatusCode.OK});
|
||||
return result;
|
||||
} catch (error) {
|
||||
const attributes: Attributes = {
|
||||
error: true,
|
||||
'error.message': error instanceof Error ? error.message : String(error),
|
||||
};
|
||||
if (error instanceof Error) {
|
||||
attributes['error.type'] = error.name;
|
||||
if (error.stack) {
|
||||
attributes['error.stack'] = error.stack;
|
||||
}
|
||||
}
|
||||
span.setAttributes(attributes);
|
||||
span.setStatus({
|
||||
code: SpanStatusCode.ERROR,
|
||||
message: error instanceof Error ? error.message : String(error),
|
||||
});
|
||||
throw error;
|
||||
} finally {
|
||||
span.end();
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
export function setSpanAttributes(attributes: Attributes): void {
|
||||
const span = getActiveSpan();
|
||||
if (span) {
|
||||
span.setAttributes(attributes);
|
||||
}
|
||||
}
|
||||
|
||||
export function addSpanEvent(name: string, attributes?: Attributes): void {
|
||||
const span = getActiveSpan();
|
||||
if (span) {
|
||||
span.addEvent(name, attributes);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user