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
# or
npm 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 Example
export 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