Integrating Vercel with Google Cloud Logging

Integrating Vercel with Google Cloud Logging

Learn how to implement a custom log drain solution to send Vercel application logs to Google Cloud Platform's Cloud Logging service, complete with authentication setup and reusable code examples.

5 min read

Published on 14 Apr 2022


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, and project_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 like fs.

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

  1. Navigate to the Google Cloud Console
  2. Go to IAM & AdminService Accounts
  3. Click Create Service Account
  4. Provide a descriptive name (e.g., "vercel-logging-service")
  5. Assign the Logging Admin role to ensure proper write permissions

Step 2: Generate Authentication Keys

  1. Select your newly created service account
  2. Navigate to the Keys tab
  3. Click Add KeyCreate new key
  4. Choose JSON format and download the key file

The downloaded JSON file contains the essential credentials we'll use:

  • client_email: Service account email address
  • private_key: Private key for authentication
  • project_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

1
2
3
yarn add @google-cloud/logging
# or
npm install @google-cloud/logging

Step 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 string
  • VERCEL_ENV: Automatically provided by Vercel (production, preview, or development)

Step 3: CloudLogging Class Implementation

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
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:

  1. Navigate to LoggingLogs Explorer
  2. Filter by your log name (e.g., vercel-production)
  3. Use labels and severity levels to organize and query your logs
  4. Set up log-based metrics and alerts for monitoring