Skip to content

Commit

Permalink
feat(gcf-utils): Add @type property for unhandled errors. (#4438)
Browse files Browse the repository at this point in the history
* feat(gcf-utils): Add @type property for unhandled errors.

This should make Cloud Error Reporting great again.

Co-authored-by: Jeff Ching <chingor@google.com>
  • Loading branch information
Takashi Matsuo and chingor13 authored Sep 21, 2022
1 parent a462df8 commit d7b1a70
Show file tree
Hide file tree
Showing 3 changed files with 48 additions and 3 deletions.
13 changes: 12 additions & 1 deletion packages/gcf-utils/src/gcf-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@ import {
export {TriggerType} from './bot-request';
export {GCFLogger} from './logging/gcf-logger';

export const ERROR_REPORTING_TYPE_NAME =
'type.googleapis.com/google.devtools.clouderrorreporting.v1beta1.ReportedErrorEvent';

// On Cloud Functions, rawBody is automatically added.
// It's not guaranteed on other platform.
export interface RequestWithRawBody extends express.Request {
Expand Down Expand Up @@ -1310,7 +1313,15 @@ function parseRateLimitError(e: Error): RateLimits | undefined {
* @param {GCFLogger} logger The logger to log to
* @param {Error} e The error to log
*/
function logErrors(logger: GCFLogger, e: Error) {
export function logErrors(logger: GCFLogger, e: Error) {
// Add "@type" bindings so that Cloud Error Reporting will capture these logs.
const bindings = logger.getBindings();
if (bindings['@type'] !== ERROR_REPORTING_TYPE_NAME) {
logger = logger.child({
'@type': ERROR_REPORTING_TYPE_NAME,
...bindings,
});
}
if (e instanceof AggregateError) {
for (const inner of e) {
// AggregateError should not contain an AggregateError, but
Expand Down
4 changes: 2 additions & 2 deletions packages/gcf-utils/test/gcf-bootstrapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -482,7 +482,7 @@ describe('GCFBootstrapper', () => {
it('logs errors for single handler errors', async () => {
const fakeLogger = new GCFLogger();
sandbox.stub(loggerModule, 'buildRequestLogger').returns(fakeLogger);
const errorStub = sandbox.stub(fakeLogger, 'error');
const errorStub = sandbox.stub(GCFLogger.prototype, 'error');
await mockBootstrapper(undefined, async app => {
app.on('issues', async () => {
throw new SyntaxError('Some error message');
Expand Down Expand Up @@ -513,7 +513,7 @@ describe('GCFBootstrapper', () => {
it('logs errors for multiple handler errors', async () => {
const fakeLogger = new GCFLogger();
sandbox.stub(loggerModule, 'buildRequestLogger').returns(fakeLogger);
const errorStub = sandbox.stub(fakeLogger, 'error');
const errorStub = sandbox.stub(GCFLogger.prototype, 'error');
await mockBootstrapper(undefined, async app => {
app.on('issues', async () => {
throw new SyntaxError('Some error message');
Expand Down
34 changes: 34 additions & 0 deletions packages/gcf-utils/test/gcf-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,20 +17,54 @@
import {
addOrUpdateIssueComment,
getAuthenticatedOctokit,
ERROR_REPORTING_TYPE_NAME,
logErrors,
} from '../src/gcf-utils';
import {GCFLogger} from '../src/logging/gcf-logger';
import * as gcfUtilsModule from '../src/gcf-utils';
import {Octokit} from '@octokit/rest';
import {resolve} from 'path';
import snapshot from 'snap-shot-it';
import {ObjectWritableMock} from 'stream-mock';
import {Probot, ProbotOctokit} from 'probot';
import {describe, beforeEach, afterEach, it} from 'mocha';
import nock from 'nock';
import * as sinon from 'sinon';
import * as assert from 'assert';
import {LogLine} from './test-helpers';

nock.disableNetConnect();

const fixturesPath = resolve(__dirname, '../../test/fixtures');

describe('logErrors', () => {
let destination: ObjectWritableMock;
let logger: GCFLogger & {[key: string]: Function};

function readLogsAsObjects(writeStream: ObjectWritableMock): LogLine[] {
try {
writeStream.end();
const lines: string[] = writeStream.data;
return lines.map(line => JSON.parse(line));
} catch (error) {
throw new Error(`Failed to read stream: ${error}`);
}
}

beforeEach(() => {
destination = new ObjectWritableMock();
logger = new GCFLogger(destination) as GCFLogger & {
[key: string]: Function;
};
});

it('adds @type property to the log entry', () => {
logErrors(logger, new Error('An error happened'));
const loggedLines: LogLine[] = readLogsAsObjects(destination);
assert.strictEqual(loggedLines[0]['@type'], ERROR_REPORTING_TYPE_NAME);
});
});

// Test app
const app = (app: Probot) => {
app.on('issues', async context => {
Expand Down

0 comments on commit d7b1a70

Please sign in to comment.