• Blog
  • Documentation
  • Courses
  • Changelog
  • AI Starters
  • UI Kit
  • FAQ
  • Supamode
    New
  • Pricing

Launch your next SaaS in record time with Makerkit, a React SaaS Boilerplate for Next.js and Supabase.

Makerkit is a product of Makerkit Pte Ltd (registered in the Republic of Singapore)Company Registration No: 202407149CFor support or inquiries, please contact us

About
  • FAQ
  • Contact
  • Verify your Discord
  • Consultation
  • Open Source
  • Become an Affiliate
Product
  • Documentation
  • Blog
  • Changelog
  • UI Blocks
  • Figma UI Kit
  • AI SaaS Starters
License
  • Activate License
  • Upgrade License
  • Invite Member
Legal
  • Terms of License
  • Auth Overview
  • Global Configuration
    • Setting up your Firebase Project
    • Setting up Firebase Functions
  • Writing data to Firestore
  • Commands
  • Introduction
  • Production Checklist
  • Introduction
  • Overview
  • Stripe Configuration
  • Running Tests
  • Introduction
  • Setting up Firebase Auth
  • Fetching data from Firestore
  • Technical Details
  • Extending Organizations
  • Stripe Webhooks
  • CI Tests
  • Initial Setup
  • React Hooks
  • Auth Flow
  • API requests
  • Code Style
  • Clone the repository
  • Security Rules
  • User Permissions
  • Limitations
  • Project Structure
  • Third-Party Providers
  • Reading data from Storage
  • Running the application
  • Subscription Permissions
  • One-Time Payments
  • Running the App
  • Email Link Authentication
  • Uploading data to Storage
  • Security Rules
  • Migrate to Lemon Squeezy
  • Project Configuration
  • Multi-Factor Authentication
  • Writing your own Fetch
  • Translations and Locales
  • Coding Conventions
  • Environment Variables
  • Architecture and Folder Structure
    • Structure your Application
    • Data Model
  • Requiring Email verification
  • Sending Emails
  • Tailwind CSS and Styling
  • Validating API payload with Zod
  • Authentication
  • Onboarding Flow
  • Logging
  • Development: adding custom features
  • Prevent abuse with AppCheck
  • Enable CORS
  • Encrypting Secrets
  • User Roles
  • Firestore: Data Fetching
  • Custom React Hooks
  • Custom React Hooks
  • Firestore: Data Writing
  • Troubleshooting
  • Forms
  • Application Pages
  • API Routes
  • API Routes Validation
  • Translations
  • Adding pages to the Marketing Site
  • Deploying to Production
  • Updating to the latest version
This kit is no longer maintained.

Logging | Remix Firebase SaaS Kit

Logging is a fundamental part for your SaaS to understand and monitor the behavior of your code at runtime. Let's learn how to add logging to your Makerkit application.

The Makerkit Boilerplate uses pino as logging library. Pino is simple and lightweight, and it's used across the API functions to log important information that helps you debug your code.

Generally speaking, you will find that we log every function, especially for asynchronous operations that could fail for a number of reasons: network issues, API exceptions, and so on.

So, how to log your API functions effectively? Our recommendation is to log before executing an operation and then log the result of the operation, without leaking important data.

Furthermore, we want to log information such as:

  • which user is performing the operation?
  • which organization is the user part of?
  • any other information that can help you understand and debug what is happening.

Let's assume you're writing an API function to write to your Firestore database:

tsx
function addIntegrationHandler() {
return writeToFirestore(data);
}

Let's rewrite the above by adding logging to your function:

tsx
import logger from '~/core/logger';
async function addIntegrationHandler(
userId: string,
organizationId: number,
integrationId: number,
) {
// this is the context that every log will print out
const loggingContext = {
integrationId,
organizationId,
userId,
};
// Here we log what we're doing
logger.log(loggingContext, `Adding new integration to organization`);
try {
await writeToFirestore(data);
// Here we log that the result of the operation
// was successful
logger.log(loggingContext, `Integration successfully added`);
// return successful response
return res.json({
integrationId,
success: true
});
} catch (e) {
// Here we log that the operation failed
logger.error(loggingContext, `Encountered an error while adding integration`);
// Logging errors can be okay but
// ensure not to leak important information!
logger.debug(e);
// return 500
return res.status(500).json({
integrationId,
success: false
});
}
}

If you use the filter withExceptionFilter, it will automatically log eventual exceptions thrown by the function, which makes the try/catch block optional.

NB: If you're using Vercel, logs are not persisted by default! Check out the Vercel integrations for adding persisting logs to your projects.