我们如何仅使用简单的 JavaScript API 而不使用任何其他库将日志写入谷歌云日志记录?
在文档中我们有这条路线:
POST https://logging.googleapis.com/v2/entries:write
他们描述了可以在体内发送的内容。
在我们的例子中,我们无法使用客户端库,因为我们需要在 remix 应用程序中写入日志,该应用程序托管在 cloudflare 页面上,而我们没有 node.js 环境,因此我们需要仅使用纯 js API 来实现这一点.
没有任何内容描述如何进行身份验证、需要在哪个字段中添加令牌以及最重要的是哪种令牌。 文档说您可以创建一个服务帐户,为其提供适当的权限并生成一个 json 文件密钥。但进一步应该做什么呢? 我真的不喜欢将此文件存储在存储库中的方法。 在文档示例中,他们使用了像 Winston 这样的第三方库,并将路径传递给这个 json:
const {LoggingWinston} = require('@google-cloud/logging-winston');
// Creates a client
const loggingWinston = new LoggingWinston({
projectId: 'your-project-id',
keyFilename: '/path/to/key.json',
});
我们的案例:
每次请求我们网站的任何页面时,它可能是机器人或真实用户,我们需要将日志推送到谷歌日志。对于我们网站的每个页面请求,我们是否需要将此 json 文件密钥交换为某些 access_token?我该怎么办?
当仅使用普通 js API 时,我不知道应该如何处理 json 文件密钥。
Google Cloud Logging APIs require authentication via OAuth 2.0, and when using them from a custom environment without the official client libraries, you must:
1. Obtain a short-lived access token from your service account credentials (the JSON key file).
2. Include this token in the Authorization: Bearer <access_token> header when making requests to the Logging API.
Overview of the Steps
• Don’t send raw service account keys from the browser:
Exposing your private key to the client-side is a security risk. Typically, you would do this server-side. If you must run this code in a Cloudflare Worker (which is a sort of serverless edge environment rather than a traditional browser environment), you can store the service account’s private key in a secure KV store or as environment variables and perform the token exchange there.
• From the service account key file:
The JSON file contains client_email and private_key. These are used to create a signed JWT which you exchange with Google’s OAuth endpoint to get an access token. This token is then used in the Authorization header for your Logging API requests.
• What you do with the key file:
You don’t have to (and generally shouldn’t) store the JSON file directly in your repository. Instead, extract the needed credentials (client_email and private_key) and put them in a secure storage environment that Cloudflare Pages or Workers can provide (e.g., environment variables or Cloudflare KV). Then reconstruct them at runtime to sign a JWT.
• Getting the OAuth token:
You’ll create a JWT and sign it with the private_key. This JWT is sent to Google’s OAuth endpoint (https://oauth2.googleapis.com/token) to obtain a short-lived access token.
• The scope you need for writing logs is: https://www.googleapis.com/auth/logging.write.
• The aud (audience) for the JWT should be https://oauth2.googleapis.com/token.
Once you have the access token, you include it in your Logging API request.
Detailed Steps
1. Store Credentials Securely
Extract client_email and private_key from the service account JSON and store them securely (not in code/repository). For instance, in a Cloudflare Worker, you could use environment variables:
// Example (Cloudflare Worker environment variables)
// CLIENT_EMAIL: service account email from the JSON
// PRIVATE_KEY: service account private key from the JSON
2. Construct and Sign the JWT
You need to create a JWT with the following claims:
• iss: your service account email
• scope: https://www.googleapis.com/auth/logging.write
• aud: https://oauth2.googleapis.com/token
• iat: current time in seconds
• exp: iat + 3600 (usually 1 hour expiration)
Here’s how the process generally looks in code (Cloudflare Workers environment):
// Pseudocode (Cloudflare Worker/Remix on Cloudflare pages):
async function getAccessToken(client_email, private_key) {
const now = Math.floor(Date.now() / 1000);
const header = {
alg: "RS256",
typ: "JWT"
};
const claims = {
iss: client_email,
scope: "https://www.googleapis.com/auth/logging.write",
aud: "https://oauth2.googleapis.com/token",
iat: now,
exp: now + 3600
};
// Encode to base64 URL-safe
function base64url(str) {
return btoa(str)
.replace(/\+/g, '-')
.replace(/\//g, '_')
.replace(/=+$/, '');
}
const encodedHeader = base64url(JSON.stringify(header));
const encodedClaims = base64url(JSON.stringify(claims));
const unsignedJWT = ${encodedHeader}.${encodedClaims};
// Sign using the RS256 algorithm with the private key
// Cloudflare Workers provide a Web Crypto API that can handle this.
const algorithm = { name: "RSASSA-PKCS1-v1_5", hash: "SHA-256" };
// Import the private key
const key = await crypto.subtle.importKey(
"pkcs8",
str2ab(private_key), // you'll need a function to convert PEM to ArrayBuffer
algorithm,
false,
["sign"]
);
const signature = await crypto.subtle.sign(algorithm, key, new TextEncoder().encode(unsignedJWT));
const signedJWT = ${unsignedJWT}.${base64url(String.fromCharCode(...new Uint8Array(signature)))};
return fetch("https://oauth2.googleapis.com/token", {
method: "POST",
headers: { "Content-Type": "application/x-www-form-urlencoded" },
body: new URLSearchParams({
grant_type: "urn:ietf:params:oauth:grant-type:jwt-bearer",
assertion: signedJWT
})
})
.then(res => res.json())
.then(data => data.access_token);
}
// Helper to convert PEM private key to ArrayBuffer
function str2ab(str) {
// You'd need to convert the PEM from base64 into binary.
// This is a bit involved: remove the "-----BEGIN PRIVATE KEY-----" and "-----END PRIVATE KEY-----" lines,
// base64 decode, then convert the result into an ArrayBuffer.
const pem = str.replace(/-----BEGIN PRIVATE KEY-----/, '')
.replace(/-----END PRIVATE KEY-----/, '')
.replace(/\s+/g, '');
const binary = atob(pem);
const array = new Uint8Array(binary.length);
for (let i = 0; i < binary.length; i++) array[i] = binary.charCodeAt(i);
return array.buffer;
}
Note: The above is illustrative. You’ll need to carefully handle PEM keys and possibly adjust for your environment. If running inside a Cloudflare Worker, you have the Web Crypto API, which can handle RSA keys. In a pure browser environment, it’s more complex (and risky) because you would be exposing credentials client-side, which is not recommended.
If you cannot run this server-side and must do it client-side, you should reconsider the approach. Typically, these tokens are fetched server-side and then possibly proxied to the client if absolutely needed. Directly calling Google Cloud APIs from the client is usually not advised because it reveals credentials.
3. Use the Access Token to Write Logs
Once you have the access_token, you can call the Logging API:
async function writeLogEntry(accessToken, projectId, logName, entry) {
const url = "https://logging.googleapis.com/v2/entries:write";
const body = {
logName: projects/${projectId}/logs/${logName},
resource: {
type: "global"
},
entries: [entry]
};
const response = await fetch(url, {
method: "POST",
headers: {
"Authorization": Bearer ${accessToken},
"Content-Type": "application/json"
},
body: JSON.stringify(body)
});
const result = await response.json();
if (!response.ok) {
console.error("Logging error:", result);
} else {
console.log("Log written successfully.");
}
}
Example call:
const accessToken = await getAccessToken(CLIENT_EMAIL, PRIVATE_KEY);
await writeLogEntry(
accessToken,
"your-project-id",
"my_log",
{
textPayload: "This is a log entry",
severity: "INFO"
}
);