Skip to content

Commit 841b4bc

Browse files
committed
feat: add telemetry log management; implement filtering and counting of telemetry logs
1 parent e1a653b commit 841b4bc

File tree

7 files changed

+294
-3
lines changed

7 files changed

+294
-3
lines changed

backend/src/dashboard/dashboard.module.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@ import { ChatModule } from '../chat/chat.module';
1717
import { ProjectModule } from '../project/project.module';
1818
import { JwtCacheModule } from '../jwt-cache/jwt-cache.module';
1919
import { Menu } from 'src/auth/menu/menu.model';
20+
import { InterceptorModule } from 'src/interceptor/interceptor.module';
21+
import { TelemetryLogService } from 'src/interceptor/telemetry-log.service';
22+
import { TelemetryLog } from 'src/interceptor/telemetry-log.model';
2023

2124
@Module({
2225
imports: [
@@ -27,14 +30,16 @@ import { Menu } from 'src/auth/menu/menu.model';
2730
Project,
2831
ProjectPackages,
2932
Menu,
33+
TelemetryLog,
3034
]),
3135
UserModule,
3236
AuthModule,
3337
ChatModule,
3438
ProjectModule,
3539
JwtCacheModule,
40+
InterceptorModule,
3641
],
37-
providers: [DashboardResolver, DashboardService],
42+
providers: [DashboardResolver, DashboardService, TelemetryLogService],
3843
exports: [DashboardService],
3944
})
4045
export class DashboardModule {}

backend/src/dashboard/dashboard.resolver.ts

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { DashboardService } from './dashboard.service';
44
import { User } from '../user/user.model';
55
import { Chat } from '../chat/chat.model';
66
import { Project } from '../project/project.model';
7+
import { TelemetryLog } from '../interceptor/telemetry-log.model';
78
import {
89
CreateUserInput,
910
UpdateUserInput,
@@ -15,22 +16,23 @@ import {
1516
UpdateChatInput,
1617
} from './dto/chat-input';
1718
import { ProjectFilterInput, UpdateProjectInput } from './dto/project-input';
19+
import { TelemetryLogFilterInput } from './dto/telemetry-log-input';
1820
import { RequireRoles } from '../decorator/auth.decorator';
1921
import { JWTAuthGuard } from '../guard/jwt-auth.guard';
20-
import { AuthService } from 'src/auth/auth.service';
2122
import { CreateRoleInput } from 'src/auth/role/dto/create-role.input';
2223
import { UpdateRoleInput } from 'src/auth/role/dto/update-role.input';
2324
import { Role } from 'src/auth/role/role.model';
2425
import { GetUserIdFromToken } from 'src/decorator/get-auth-token.decorator';
2526
import { CreateProjectInput } from 'src/project/dto/project.input';
2627
import { DashboardStats } from './dashboard-stat.model';
28+
import { TelemetryLogService } from 'src/interceptor/telemetry-log.service';
2729

2830
@Resolver()
2931
@UseGuards(JWTAuthGuard)
3032
export class DashboardResolver {
3133
constructor(
3234
private readonly dashboardService: DashboardService,
33-
private readonly authService: AuthService,
35+
private readonly telemetryLogService: TelemetryLogService,
3436
) {}
3537

3638
@RequireRoles('Admin')
@@ -206,4 +208,29 @@ export class DashboardResolver {
206208
async dashboardStats(): Promise<DashboardStats> {
207209
return this.dashboardService.getDashboardStats();
208210
}
211+
212+
// Telemetry Log Management
213+
@RequireRoles('Admin')
214+
@Query(() => [TelemetryLog])
215+
async dashboardTelemetryLogs(
216+
@Args('filter', { nullable: true }) filter?: TelemetryLogFilterInput,
217+
): Promise<TelemetryLog[]> {
218+
return this.telemetryLogService.findFiltered(filter);
219+
}
220+
221+
@RequireRoles('Admin')
222+
@Query(() => TelemetryLog)
223+
async dashboardTelemetryLog(
224+
@Args('id', { type: () => ID }) id: number,
225+
): Promise<TelemetryLog> {
226+
return this.telemetryLogService.findById(id);
227+
}
228+
229+
@RequireRoles('Admin')
230+
@Query(() => Number)
231+
async dashboardTelemetryLogsCount(
232+
@Args('filter', { nullable: true }) filter?: TelemetryLogFilterInput,
233+
): Promise<number> {
234+
return this.telemetryLogService.countTelemetryLogs(filter);
235+
}
209236
}

backend/src/dashboard/dashboard.service.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import { Menu } from 'src/auth/menu/menu.model';
2727
import { ProjectService } from 'src/project/project.service';
2828
import { CreateProjectInput } from 'src/project/dto/project.input';
2929
import { DashboardStats } from './dashboard-stat.model';
30+
import { TelemetryLog } from 'src/interceptor/telemetry-log.model';
3031

3132
@Injectable()
3233
export class DashboardService {
@@ -43,6 +44,8 @@ export class DashboardService {
4344
private readonly projectRepository: Repository<Project>,
4445
@InjectRepository(ProjectPackages)
4546
private readonly packageRepository: Repository<ProjectPackages>,
47+
@InjectRepository(TelemetryLog)
48+
private readonly telemetryLogRepository: Repository<ProjectPackages>,
4649
private readonly projectService: ProjectService,
4750
) {}
4851

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import { Field, InputType } from '@nestjs/graphql';
2+
3+
@InputType()
4+
export class TelemetryLogFilterInput {
5+
@Field(() => Date, { nullable: true })
6+
startDate?: Date;
7+
8+
@Field(() => Date, { nullable: true })
9+
endDate?: Date;
10+
11+
@Field({ nullable: true })
12+
requestMethod?: string;
13+
14+
@Field({ nullable: true })
15+
endpoint?: string;
16+
17+
@Field({ nullable: true })
18+
userId?: string;
19+
20+
@Field({ nullable: true })
21+
handler?: string;
22+
23+
@Field(() => Number, { nullable: true })
24+
minTimeConsumed?: number;
25+
26+
@Field(() => Number, { nullable: true })
27+
maxTimeConsumed?: number;
28+
29+
@Field({ nullable: true })
30+
search?: string;
31+
}

backend/src/interceptor/telemetry-log.service.ts

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { Injectable } from '@nestjs/common';
22
import { InjectRepository } from '@nestjs/typeorm';
33
import { Repository } from 'typeorm';
44
import { TelemetryLog } from './telemetry-log.model';
5+
import { TelemetryLogFilterInput } from 'src/dashboard/dto/telemetry-log-input';
56

67
@Injectable()
78
export class TelemetryLogService {
@@ -25,4 +26,107 @@ export class TelemetryLogService {
2526
async findById(id: number): Promise<TelemetryLog> {
2627
return await this.telemetryLogRepository.findOne({ where: { id } });
2728
}
29+
30+
async findFiltered(filter: TelemetryLogFilterInput): Promise<TelemetryLog[]> {
31+
const query = this.telemetryLogRepository.createQueryBuilder('log');
32+
33+
if (filter.startDate) {
34+
query.andWhere('log.timestamp >= :startDate', {
35+
startDate: filter.startDate,
36+
});
37+
}
38+
if (filter.endDate) {
39+
query.andWhere('log.timestamp <= :endDate', { endDate: filter.endDate });
40+
}
41+
if (filter.requestMethod) {
42+
query.andWhere('log.requestMethod = :requestMethod', {
43+
requestMethod: filter.requestMethod,
44+
});
45+
}
46+
if (filter.endpoint) {
47+
query.andWhere('log.endpoint LIKE :endpoint', {
48+
endpoint: `%${filter.endpoint}%`,
49+
});
50+
}
51+
if (filter.userId) {
52+
query.andWhere('log.userId = :userId', { userId: filter.userId });
53+
}
54+
if (filter.handler) {
55+
query.andWhere('log.handler LIKE :handler', {
56+
handler: `%${filter.handler}%`,
57+
});
58+
}
59+
if (filter.minTimeConsumed !== undefined) {
60+
query.andWhere('log.timeConsumed >= :minTimeConsumed', {
61+
minTimeConsumed: filter.minTimeConsumed,
62+
});
63+
}
64+
if (filter.maxTimeConsumed !== undefined) {
65+
query.andWhere('log.timeConsumed <= :maxTimeConsumed', {
66+
maxTimeConsumed: filter.maxTimeConsumed,
67+
});
68+
}
69+
if (filter.search) {
70+
query.andWhere(
71+
'(log.endpoint LIKE :search OR log.requestMethod LIKE :search)',
72+
{ search: `%${filter.search}%` },
73+
);
74+
}
75+
76+
query.orderBy('log.timestamp', 'DESC');
77+
78+
return await query.getMany();
79+
}
80+
81+
async countTelemetryLogs(filter?: TelemetryLogFilterInput): Promise<number> {
82+
const query = this.telemetryLogRepository.createQueryBuilder('log');
83+
84+
if (filter) {
85+
if (filter.startDate) {
86+
query.andWhere('log.timestamp >= :startDate', {
87+
startDate: filter.startDate,
88+
});
89+
}
90+
if (filter.endDate) {
91+
query.andWhere('log.timestamp <= :endDate', {
92+
endDate: filter.endDate,
93+
});
94+
}
95+
if (filter.requestMethod) {
96+
query.andWhere('log.requestMethod = :requestMethod', {
97+
requestMethod: filter.requestMethod,
98+
});
99+
}
100+
if (filter.endpoint) {
101+
query.andWhere('log.endpoint LIKE :endpoint', {
102+
endpoint: `%${filter.endpoint}%`,
103+
});
104+
}
105+
if (filter.userId) {
106+
query.andWhere('log.userId = :userId', { userId: filter.userId });
107+
}
108+
if (filter.handler) {
109+
query.andWhere('log.handler LIKE :handler', {
110+
handler: `%${filter.handler}%`,
111+
});
112+
}
113+
if (filter.minTimeConsumed !== undefined) {
114+
query.andWhere('log.timeConsumed >= :minTimeConsumed', {
115+
minTimeConsumed: filter.minTimeConsumed,
116+
});
117+
}
118+
if (filter.maxTimeConsumed !== undefined) {
119+
query.andWhere('log.timeConsumed <= :maxTimeConsumed', {
120+
maxTimeConsumed: filter.maxTimeConsumed,
121+
});
122+
}
123+
if (filter.search) {
124+
query.andWhere(
125+
'(log.endpoint LIKE :search OR log.requestMethod LIKE :search)',
126+
{ search: `%${filter.search}%` },
127+
);
128+
}
129+
}
130+
return await query.getCount();
131+
}
28132
}

frontend/src/graphql/schema.gql

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,9 @@ type Query {
264264
dashboardRole(id: ID!): SystemRole!
265265
dashboardRoles: [SystemRole!]!
266266
dashboardStats: DashboardStats!
267+
dashboardTelemetryLog(id: ID!): TelemetryLog!
268+
dashboardTelemetryLogs(filter: TelemetryLogFilterInput): [TelemetryLog!]!
269+
dashboardTelemetryLogsCount(filter: TelemetryLogFilterInput): Float!
267270
dashboardUser(id: ID!): User!
268271
dashboardUsers(filter: UserFilterInput): [User!]!
269272
fetchPublicProjects(input: FetchPublicProjectsInputs!): [Project!]!
@@ -330,6 +333,32 @@ type SystemRole {
330333
users: [User!]
331334
}
332335

336+
type TelemetryLog {
337+
endpoint: String!
338+
handler: String
339+
id: ID!
340+
input: String
341+
inputToken: String
342+
output: String
343+
outputToken: String
344+
requestMethod: String!
345+
timeConsumed: Float!
346+
timestamp: Date!
347+
userId: String
348+
}
349+
350+
input TelemetryLogFilterInput {
351+
endDate: Date
352+
endpoint: String
353+
handler: String
354+
maxTimeConsumed: Float
355+
minTimeConsumed: Float
356+
requestMethod: String
357+
search: String
358+
startDate: Date
359+
userId: String
360+
}
361+
333362
input UpdateChatInput {
334363
isActive: Boolean
335364
title: String

0 commit comments

Comments
 (0)