Vercel's serverless architecture provides an excellent developer experience with its simplicity and deployment efficiency. However, as your application scales from a hobby project to production, implementing robust logging becomes crucial for monitoring, debugging, and maintaining system reliability.
While Vercel offers built-in log drain integrations with popular services like Datadog and LogDNA, Google Cloud Platform's Cloud Logging isn't available as a native integration. This guide will walk you through implementing a custom solution to stream your Vercel logs directly to Google Cloud Logging.
Prerequisites and Important Considerations
Before implementing this solution, please note the following requirements and limitations:
Authentication Requirements:
- A GCP service account with the necessary credentials (
client_email,private_key, andproject_id). - Appropriate IAM permissions for Cloud Logging.
Technical Limitations:
- Synchronous Operations: Due to Vercel's serverless architecture, logging operations must be awaited to prevent premature function termination.
- Edge Functions Incompatibility: This implementation cannot be used with Vercel Edge Functions (such as
_middleware.ts) because the Edge Runtime operates in a browser-like environment that doesn't support Node.js dependencies likefs.
Setting Up GCP Service Account
To authenticate with Google Cloud Logging, you'll need to create a service account with appropriate permissions:
Step 1: Create the Service Account
- Navigate to the Google Cloud Console
- Go to IAM & Admin → Service Accounts
- Click Create Service Account
- Provide a descriptive name (e.g., "vercel-logging-service")
- Assign the Logging Admin role to ensure proper write permissions
Step 2: Generate Authentication Keys
- Select your newly created service account
- Navigate to the Keys tab
- Click Add Key → Create new key
- Choose JSON format and download the key file
The downloaded JSON file contains the essential credentials we'll use:
client_email: Service account email addressprivate_key: Private key for authenticationproject_id: Your GCP project identifier
Implementation: Creating the CloudLogging Class
Now let's implement a reusable TypeScript class that handles the integration with Google Cloud Logging.
Step 1: Install Dependencies
yarn add @google-cloud/logging# ornpm install @google-cloud/loggingStep 2: Environment Setup
Store your service account credentials as an environment variable in Vercel:
GOOGLE_SERVICE_ACCOUNT: The entire JSON key file contents as a stringVERCEL_ENV: Automatically provided by Vercel (production, preview, or development)
Step 3: CloudLogging Class Implementation
import { Logging } from '@google-cloud/logging'; export class CloudLogging { private logging: Logging; private log: any; constructor() { // Parse the service account credentials from environment const credentials = JSON.parse(process.env.GOOGLE_SERVICE_ACCOUNT!); // Initialize the Google Cloud Logging client this.logging = new Logging({ projectId: credentials.project_id, credentials: { client_email: credentials.client_email, private_key: credentials.private_key, } }); // Create a log instance with environment-specific naming this.log = this.logging.log(`vercel-${process.env.VERCEL_ENV}`, { removeCircular: true }); } /** * Logs data to Google Cloud Logging with specified severity level * @param severity - Log severity (INFO, WARNING, ERROR, etc.) * @param data - Data to log (string, object, or any serializable type) * @param labels - Optional custom labels for log organization */ async logData( severity: string, data: string | object | any, labels: Record<string, string> = {} ): Promise<void> { try { const entry = this.log.entry({ resource: { type: 'global', }, labels: { environment: process.env.VERCEL_ENV || 'unknown', source: 'vercel', ...labels }, severity: severity.toUpperCase(), }, data); await this.log.write(entry); } catch (error) { // Fallback to console logging if GCP logging fails console.error('Failed to write to Google Cloud Logging:', error); console.log('Original log data:', { severity, data }); } } /** * Convenience methods for common log levels */ async info(data: any, labels?: Record<string, string>): Promise<void> { return this.logData('INFO', data, labels); } async warn(data: any, labels?: Record<string, string>): Promise<void> { return this.logData('WARNING', data, labels); } async error(data: any, labels?: Record<string, string>): Promise<void> { return this.logData('ERROR', data, labels); }}Usage Examples
With our CloudLogging class implemented, let's explore various ways to integrate it into your Vercel application.
Basic Usage
import { CloudLogging } from '@/lib/gcp/logging'; const logger = new CloudLogging(); // API Route Exampleexport default async function handler(req: NextApiRequest, res: NextApiResponse) { try { // Your business logic here const result = await processUserData(req.body); // Log successful operation await logger.info({ message: 'User data processed successfully', userId: req.body.userId, timestamp: new Date().toISOString() }, { operation: 'user-processing' }); res.status(200).json(result); } catch (error) { // Log error with context await logger.error({ message: 'Failed to process user data', error: error.message, stack: error.stack, requestBody: req.body }, { operation: 'user-processing' }); res.status(500).json({ error: 'Processing failed' }); }}Advanced Usage with Custom Labels
class UserService { private logger = new CloudLogging(); async createUser(userData: any) { try { const user = await this.saveToDatabase(userData); await this.logger.info({ message: 'New user created', userId: user.id, email: user.email }, { service: 'user-service', operation: 'create-user', version: 'v1' }); return user; } catch (error) { await this.logger.error({ message: 'User creation failed', error: error.message, userData: { ...userData, password: '[REDACTED]' } }, { service: 'user-service', operation: 'create-user' }); throw error; } }}Serverless Function Example
import { VercelRequest, VercelResponse } from '@vercel/node';import { CloudLogging } from '../lib/gcp/logging'; const logger = new CloudLogging(); export default async function handler(req: VercelRequest, res: VercelResponse) { const startTime = Date.now(); try { // Log request start await logger.info({ message: 'Processing webhook', method: req.method, url: req.url, headers: req.headers }, { type: 'webhook', stage: 'start' }); // Process webhook const result = await processWebhook(req.body); // Log success with performance metrics await logger.info({ message: 'Webhook processed successfully', duration: Date.now() - startTime, result: result }, { type: 'webhook', stage: 'complete' }); res.status(200).json({ success: true }); } catch (error) { await logger.error({ message: 'Webhook processing failed', duration: Date.now() - startTime, error: error.message }, { type: 'webhook', stage: 'error' }); res.status(500).json({ error: 'Webhook failed' }); }}Viewing Your Logs
Once implemented, your logs will be available in the Google Cloud Console:
- Navigate to Logging → Logs Explorer
- Filter by your log name (e.g.,
vercel-production) - Use labels and severity levels to organize and query your logs
- Set up log-based metrics and alerts for monitoring
