-
-
Notifications
You must be signed in to change notification settings - Fork 346
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
improve api telemetry #2769
improve api telemetry #2769
Conversation
Reviewer's Guide by SourceryThis pull request introduces New Relic telemetry to track the usage and performance of various features, and refactors email sending logic into a private method. Sequence diagram for creating a querysequenceDiagram
participant QueriesService
participant PrismaService
participant EventService
QueriesService->>QueriesService: create(userId, createQueryDto)
QueriesService->>PrismaService: queryItem.create(data)
PrismaService-->>QueriesService: res
QueriesService->>EventService: emit(EVENTS.QUERY_UPDATE, { id: res.id })
QueriesService->>QueriesService: agent?.incrementMetric('query.create')
EventService-->>QueriesService:
PrismaService-->>QueriesService: res
Sequence diagram for creating a query collectionsequenceDiagram
participant QueryCollectionsService
participant PrismaService
participant EventService
QueryCollectionsService->>QueryCollectionsService: create(userId, createQueryCollectionDto)
QueryCollectionsService->>PrismaService: queryCollection.create(data)
PrismaService-->>QueryCollectionsService: res
QueryCollectionsService->>EventService: emit(EVENTS.COLLECTION_UPDATE, { id: res.id })
QueryCollectionsService->>QueryCollectionsService: agent?.incrementMetric('query_collection.create')
EventService-->>QueryCollectionsService:
PrismaService-->>QueryCollectionsService: res
Sequence diagram for adding a team membershipsequenceDiagram
participant TeamMembershipsService
participant PrismaService
TeamMembershipsService->>TeamMembershipsService: create(userId, createTeamMembershipDto)
TeamMembershipsService->>PrismaService: teamMembership.create(data)
PrismaService-->>TeamMembershipsService: res
TeamMembershipsService->>TeamMembershipsService: updateSubscriptionQuantity(userId)
TeamMembershipsService->>TeamMembershipsService: agent?.incrementMetric('team.membership.added')
PrismaService-->>TeamMembershipsService: res
Sequence diagram for creating a teamsequenceDiagram
participant TeamsService
participant PrismaService
TeamsService->>TeamsService: create(userId, createTeamDto)
TeamsService->>PrismaService: team.create(data)
PrismaService-->>TeamsService: res
TeamsService->>TeamsService: agent?.incrementMetric('team.created')
PrismaService-->>TeamsService: res
Sequence diagram for listing all teamssequenceDiagram
participant TeamsService
participant PrismaService
TeamsService->>TeamsService: findAll(userId)
TeamsService->>PrismaService: team.findMany(where)
PrismaService-->>TeamsService: res
TeamsService->>TeamsService: agent?.recordMetric('team.list.count', res.length)
PrismaService-->>TeamsService: res
Sequence diagram for refilling monthly creditssequenceDiagram
participant CreditService
participant UserService
participant PrismaService
CreditService->>CreditService: refillMonthlyCredits()
CreditService->>UserService: getProUsers()
UserService-->>CreditService: proUsers
loop for each proUser
CreditService->>PrismaService: creditBalance.update(where, data)
PrismaService-->>CreditService: creditBalanceRecords
end
CreditService->>CreditService: agent?.recordMetric('credit.monthly.refill_count', proUsers.length)
PrismaService-->>CreditService: creditBalanceRecords
Sequence diagram for getting pro userssequenceDiagram
participant UserService
participant PrismaService
UserService->>UserService: getProUsers()
UserService->>PrismaService: user.findMany(where)
PrismaService-->>UserService: proUsers
UserService->>UserService: agent?.recordMetric('users.pro.count', proUsers.length)
PrismaService-->>UserService: proUsers
Sequence diagram for generating events tokensequenceDiagram
participant AuthService
AuthService->>AuthService: getShortLivedEventsToken(userId)
AuthService->>AuthService: agent?.incrementMetric('auth.events_token.generate')
File-Level Changes
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hey @imolorhe - I've reviewed your changes - here's some feedback:
Overall Comments:
- Consider creating a base class or shared utility for accessing the NewRelic agent to avoid duplication.
- The agent should be initialized once and then injected, rather than calling
getAgent()
in each service.
Here's what I looked at during the review
- 🟢 General issues: all looks good
- 🟢 Security: all looks good
- 🟢 Testing: all looks good
- 🟡 Complexity: 1 issue found
- 🟢 Documentation: all looks good
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
@@ -85,6 +87,8 @@ export class QueriesService { | |||
|
|||
this.eventService.emit(EVENTS.QUERY_UPDATE, { id: res.id }); | |||
|
|||
this.agent?.incrementMetric('query.create'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
issue (complexity): Consider centralizing the metric reporting logic using a decorator to wrap service methods.
Consider centralizing the metric reporting to reduce the interleaved instrumentation code. For example, you might create a decorator to wrap your service methods. This way, you can remove repetitive metric calls from the business logic while keeping the behavior intact. For instance:
function withMetric(metricName: string, action: "increment" | "record", countFn?: (result: any) => number) {
return function (_target: any, _propertyKey: string, descriptor: PropertyDescriptor) {
const originalMethod = descriptor.value;
descriptor.value = async function (...args: any[]) {
const result = await originalMethod.apply(this, args);
if (action === "increment") {
this.agent?.incrementMetric(metricName);
} else if (action === "record") {
const count = countFn ? countFn(result) : (Array.isArray(result) ? result.length : 0);
this.agent?.recordMetric(metricName, count);
}
return result;
}
return descriptor;
}
}
Then annotate your service methods accordingly:
class QueriesService {
private readonly agent = getAgent();
constructor( /* ... */ ) {}
@withMetric('query.create', 'increment')
async create(userId: string, createQueryDto: CreateQueryDto) {
// business logic...
}
@withMetric('query.list.count', 'record', res => res.length)
async findAll(userId: string) {
return await this.prisma.queryItem.findMany({
where: { ...queryItemWhereOwnerOrMember(userId) },
});
}
@withMetric('query.revision.list.count', 'record', res => res.length)
async listRevisions(userId: string, queryId: string) {
return await this.prisma.queryItemRevision.findMany({
// query...
});
}
@withMetric('query.revision.create', 'increment')
async createRevision(/* ... */) {
// revision logic...
}
}
This decorator-based approach reduces duplication and clearly separates concerns while keeping functionality unchanged.
Visit the preview URL for this PR (updated for commit d5691c2): https://altair-gql--pr2769-imolorhe-improve-api-780eu871.web.app (expires Tue, 25 Feb 2025 19:28:57 GMT) 🔥 via Firebase Hosting GitHub Action 🌎 Sign: 02d6323d75a99e532a38922862e269d63351a6cf |
Fixes
Checks
yarn test-build
Changes proposed in this pull request:
Summary by Sourcery
This pull request introduces telemetry to track the usage of various API endpoints using New Relic. It adds metrics for email sending, query creation, query listing, query collection creation, team membership changes, team creation, credit balance, and authentication events. The New Relic agent is now initialized and accessed through the
getAgent
function, allowing for conditional usage based on theNEW_RELIC_APP_NAME
environment variable.Enhancements:
Chores:
newrelic
module to encapsulate New Relic agent functionality.getAgent
function, allowing for conditional usage based on theNEW_RELIC_APP_NAME
environment variable.