Skip to content
This repository was archived by the owner on Mar 11, 2026. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,12 @@ const pumpify = require('pumpify');
import * as streamEvents from 'stream-events';
import * as through from 'through2';
import * as middleware from './middleware';
import {detectServiceContext} from './metadata';
import {StackdriverHttpRequest as HttpRequest} from './http-request';

export {middleware};
export {HttpRequest};
export {detectServiceContext};

const PKG = require('../../package.json');
const v2 = require('./v2');
Expand Down
33 changes: 33 additions & 0 deletions src/metadata.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ import * as gcpMetadata from 'gcp-metadata';
import {GCPEnv, GoogleAuth} from 'google-auth-library';
import * as pify from 'pify';

import {ServiceContext} from './index';

const readFile = pify(fs.readFile);

function zoneFromQualifiedZone(qualified: string): string|undefined {
Expand Down Expand Up @@ -153,3 +155,34 @@ export async function getDefaultResource(auth: GoogleAuth) {
return getGlobalDescriptor();
}
}

/**
* For logged errors, users can provide a service context. This enables errors
* to be picked up Stackdriver Error Reporting. For more information see
* [this guide]{@link
* https://cloud.google.com/error-reporting/docs/formatting-error-messages} and
* the [official documentation]{@link
* https://cloud.google.com/error-reporting/reference/rest/v1beta1/ServiceContext}.
*/
export async function detectServiceContext(auth: GoogleAuth):
Promise<ServiceContext|null> {
const env = await auth.getEnv();
switch (env) {
case GCPEnv.APP_ENGINE:
return {
service: process.env.GAE_SERVICE || process.env.GAE_MODULE_NAME,
version: process.env.GAE_VERSION || process.env.GAE_MODULE_VERSION,
};
case GCPEnv.CLOUD_FUNCTIONS:
return {
service: process.env.FUNCTION_NAME,
};
// One Kubernetes we probably need to use the pod-name to describe the
// service. Currently there isn't a universal way to acquire the pod
// name from within the pod.
case GCPEnv.KUBERNETES_ENGINE:
case GCPEnv.COMPUTE_ENGINE:
default:
return null;
}
}
82 changes: 82 additions & 0 deletions test/metadata.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import {GCPEnv} from 'google-auth-library';
import * as proxyquire from 'proxyquire';

import assertRejects = require('assert-rejects');
import {detectServiceContext} from '../src/metadata';

let instanceOverride;
const fakeGcpMetadata = {
Expand Down Expand Up @@ -360,4 +361,85 @@ describe('metadata', () => {
});
});
});

describe('detectServiceContext', () => {
it('should return the correct descriptor for App Engine', async () => {
const GAE_MODULE_NAME = 'gae-module-name';
const GAE_MODULE_VERSION = 'gae-module-version';
const GAE_SERVICE = 'gae-service';
const GAE_VERSION = 'gae-version';
process.env.GAE_MODULE_NAME = GAE_MODULE_NAME;
process.env.GAE_MODULE_VERSION = GAE_MODULE_VERSION;
process.env.GAE_SERVICE = GAE_SERVICE;
process.env.GAE_VERSION = GAE_VERSION;
const fakeAuth = {
async getEnv() {
return GCPEnv.APP_ENGINE;
}
};

const sc1 = await metadata.detectServiceContext(fakeAuth);
assert.deepStrictEqual(sc1, {
service: GAE_SERVICE,
version: GAE_VERSION,
});

delete process.env.GAE_SERVICE;
const sc2 = await metadata.detectServiceContext(fakeAuth);
assert.deepStrictEqual(sc2, {
service: GAE_MODULE_NAME,
version: GAE_VERSION,
});

delete process.env.GAE_VERSION;
const sc3 = await metadata.detectServiceContext(fakeAuth);
assert.deepStrictEqual(sc3, {
service: GAE_MODULE_NAME,
version: GAE_MODULE_VERSION,
});
});

it('should return the correct descriptor for Cloud Functions', async () => {
const FUNCTION_NAME = process.env.FUNCTION_NAME = 'function-name';

const fakeAuth = {
async getEnv() {
return GCPEnv.CLOUD_FUNCTIONS;
}
};

const sc1 = await metadata.detectServiceContext(fakeAuth);
assert.deepStrictEqual(sc1, {service: FUNCTION_NAME});
});

it('should return null on GKE', async () => {
const fakeAuth = {
async getEnv() {
return GCPEnv.KUBERNETES_ENGINE;
}
};
const serviceContext = await metadata.detectServiceContext(fakeAuth);
assert.strictEqual(serviceContext, null);
});

it('should return null on GCE', async () => {
const fakeAuth = {
async getEnv() {
return GCPEnv.COMPUTE_ENGINE;
}
};
const serviceContext = await metadata.detectServiceContext(fakeAuth);
assert.strictEqual(serviceContext, null);
});

it('should return null elsewhere', async () => {
const fakeAuth = {
async getEnv() {
return GCPEnv.NONE;
}
};
const serviceContext = await metadata.detectServiceContext(fakeAuth);
assert.strictEqual(serviceContext, null);
});
});
});