Skip to content

Debugging Course Enrollments (Tracing, Logs)

We can search for courses now! But now when we try to enroll, we’re getting some unresponsive controls.

Enroll Error

In Sentry, we’re seeing the error pop up - and as we’ve shown previously, we can use Tracing and Logs to help troubleshoot the issue quicker.

Module 3 Issues

Learning Objectives

By the end of this module, you will:

  • Create custom spans to track the course enrollment flow
  • Use Trace Explorer to search for specific spans that show the enrollment flow and course information
  • Understand how to resolve the issues with enrollment in the application
  • Extend our logs to include context around the enrollment issues in the application

Frontend Implementation

Let’s start by instrumenting the frontend enrollment functionality to track enrollment requests and user data being sent to the backend.

  1. Replace the Complete Frontend Enrollment Method

    Navigate to the /apps/frontend/src/services/api.ts file and replace the create method in the enrollments object with this instrumented version:

    create: (courseId: string, userId: string | undefined) =>
    Sentry.startSpan(
    {
    name: 'enrollment.create.frontend',
    op: 'http.client',
    attributes: {
    'enrollment.course_id': courseId,
    'enrollment.user_id': userId || 'undefined',
    'enrollment.user_id_provided': !!userId,
    },
    },
    () => {
    logger.info(logger.fmt`Creating enrollment for course: ${courseId}, user: ${userId || 'undefined'}`);
    return fetchApi<any>('/enrollments', {
    method: 'POST',
    body: JSON.stringify({ courseId }),
    });
    }
    ),
  2. Test Enrollment Tracing

    Try enrolling in courses again. Navigate to Explore > Traces in Sentry’s left navigation menu, and search using the span.description filter to find your enrollment-related spans using the enrollment.create.server value.

    Trace Explorer FE
  3. Examine the Trace

    Clicking into one of the spans will show you the trace waterfall where you can examine the properties of any of the spans submitted.

    Trace Waterfall
  4. Examine the Logs

    Check the Explore > Logs tab for the log entries created by logger.info calls.

    Logs

Backend Implementation

Now let’s instrument the backend enrollment functionality to trace server-side processing and identify the missing user ID issue.

  1. Add Sentry Import to Backend Enrollment Routes

    Navigate to the /apps/server/src/modules/enrollments/routes.ts file and add the Sentry import at the top:

    import * as Sentry from '@sentry/node';
  2. Set Up Backend Enrollment Logging

    Add the logger destructuring after your imports:

    const { logger } = Sentry
  3. Replace the Complete Enrollment Route

    Replace the entire enrollmentRoutes.post('/enrollments', async (req, res) route with this instrumented version:

    enrollmentRoutes.post('/enrollments', async (req, res) => {
    try {
    const { userId, courseId } = req.body;
    await Sentry.startSpan(
    {
    name: 'enrollment.create.server',
    op: 'enrollment.process',
    attributes: {
    'enrollment.course_id': courseId || 'undefined',
    'enrollment.user_id': userId || 'undefined',
    'enrollment.user_id_provided': !!userId,
    },
    },
    async (span) => {
    console.log('🔍 Checking enrollment request:', { userId, courseId });
    logger.info(logger.fmt`Processing enrollment request for course: ${courseId || 'undefined'}, user: ${userId || 'undefined'}`);
    // Add initial request validation attributes
    span.setAttributes({
    'enrollment.request.course_id_provided': !!courseId,
    'enrollment.request.user_id_provided': !!userId,
    });
    // First: Validate course ID is provided
    if (!courseId) {
    span.setAttributes({
    'enrollment.validation.course_id': 'missing',
    'enrollment.validation.result': 'failed',
    'enrollment.error': 'course_id_required',
    });
    console.error('❌ Course ID is missing');
    res.status(400).json({ error: 'Course ID is required.' });
    return;
    }
    logger.info(logger.fmt`Verifying course exists: ${courseId}`);
    const courseCheck = await db
    .select()
    .from(courses)
    .where(eq(courses.id, courseId))
    .limit(1);
    logger.info('📚 Course check result:', courseCheck);
    if (courseCheck.length === 0) {
    span.setAttributes({
    'enrollment.validation.course_exists': false,
    'enrollment.validation.result': 'failed',
    'enrollment.error': 'course_not_found',
    });
    console.error('❌ Course not found:', courseId);
    res.status(404).json({ error: `Course with id ${courseId} not found` });
    return;
    }
    // Add course details to span
    const course = courseCheck[0];
    span.setAttributes({
    'enrollment.validation.course_exists': true,
    'enrollment.course.title': course.title,
    'enrollment.course.category': course.category || 'unknown',
    'enrollment.course.level': course.level || 'unknown',
    'enrollment.course.instructor_id': course.instructorId || 'unknown',
    });
    logger.info(logger.fmt`Course found: "${course.title}" (${course.category})`);
    // Third: Validate user ID is provided
    if (!userId) {
    span.setAttributes({
    'enrollment.validation.user_id': 'missing',
    'enrollment.validation.result': 'failed',
    'enrollment.error': 'user_id_missing',
    });
    console.error('❌ User ID is missing');
    throw new Error('User ID is missing');
    }
    // Add final validation success attributes
    span.setAttributes({
    'enrollment.validation.user_id': 'provided',
    'enrollment.validation.result': 'passed',
    'enrollment.process.success': true,
    });
    logger.info(logger.fmt`Enrollment validation successful for user ${userId} in course "${course.title}"`);
    console.log('✅ All validation successful, enrollment approved');
    res.json({
    success: true,
    message: 'Enrollment validation successful',
    courseId,
    userId
    });
    }
    );
    } catch (error: any) {
    Sentry.captureException(error, {
    tags: {
    operation: 'enrollment.create.backend',
    course_id: req.body.courseId || 'undefined',
    user_id: req.body.userId || 'undefined',
    },
    extra: {
    requestBody: req.body,
    courseId: req.body.courseId,
    userId: req.body.userId,
    hasUserId: !!req.body.userId,
    hasCourseId: !!req.body.courseId,
    },
    });
    logger.error(logger.fmt`Enrollment error for course ${req.body.courseId || 'undefined'}, user ${req.body.userId || 'undefined'}: ${error.message}`);
    console.error('💥 Error during enrollment:', error);
    console.error('📊 Error details:', {
    message: error.message,
    stack: error.stack,
    userId: req.body.userId,
    courseId: req.body.courseId,
    });
    throw new Error('Failed to enroll in course');
    }
    });
  4. Test Enrollment Tracing

    Try enrolling in courses again. Navigate to Explore > Traces in Sentry’s left navigation menu, and search using the span.description filter to find your enrollment-related spans using the enrollment.create.server value.

    Trace Explorer BE
  5. Examine the Trace

    Trace Waterfall
  6. Examine the Logs

    Check the Explore > Logs tab for the log entries created by logger.info and logger.error calls.

    Logs

Analyzing the Issue

When we look now, we can see that the User ID is not being passed to the backend. This is visible both in the Trace Explorer’s Span views as well as clearly visible in the logs view.

Fixing the Issue

The fix for this is simple - need to update the frontend client to successfully pass the userId to the backend as well.

  1. Update Frontend to Include User ID

    In the frontend enrollment request, update the enrollments object in /apps/frontend/src/services/api.ts from:

    return fetchApi<any>('/enrollments', {
    method: 'POST',
    body: JSON.stringify({ courseId }),
    });

    to the following:

    return fetchApi<any>('/enrollments', {
    method: 'POST',
    body: JSON.stringify({ courseId, userId }),
    });

Once this change is made, submit your enrollment again and you’ll see successful enrollment of your courses!

Enroll Success