Skip to content

Commit

Permalink
Capture errors in sentry better (#5128)
Browse files Browse the repository at this point in the history
Fixes #5127 and similar issues.

Rationale: Whenever something that is not an Error object is passed to
Sentry.captureException we get pretty much no useful information in
sentry

![image](https://github.com/compiler-explorer/compiler-explorer/assets/51220084/c7345449-f7a0-46cb-a198-abe0bd80a8b1)
(usually not even a stacktrace)
  • Loading branch information
jeremy-rifkin committed Jun 11, 2023
1 parent 319dee6 commit acdd5ad
Show file tree
Hide file tree
Showing 14 changed files with 178 additions and 59 deletions.
32 changes: 2 additions & 30 deletions app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ import {logger, logToLoki, logToPapertrail, makeLogStream, suppressConsoleLog} f
import {setupMetricsServer} from './lib/metrics-server.js';
import {ClientOptionsHandler} from './lib/options-handler.js';
import * as props from './lib/properties.js';
import {SetupSentry} from './lib/sentry.js';
import {ShortLinkResolver} from './lib/shortener/google.js';
import {sources} from './lib/sources/index.js';
import {loadSponsorsFromString} from './lib/sponsors.js';
Expand Down Expand Up @@ -364,15 +365,6 @@ async function setupStaticMiddleware(router: express.Router) {
};
}

function shouldRedactRequestData(data: string) {
try {
const parsed = JSON.parse(data);
return !parsed['allowStoreCodeDebug'];
} catch (e) {
return true;
}
}

const googleShortUrlResolver = new ShortLinkResolver();

function oldGoogleUrlHandler(req: express.Request, res: express.Response, next: express.NextFunction) {
Expand Down Expand Up @@ -451,26 +443,6 @@ function startListening(server: express.Express) {
}
}

function setupSentry(sentryDsn: string) {
if (!sentryDsn) {
logger.info('Not configuring sentry');
return;
}
const sentryEnv = ceProps('sentryEnvironment');
Sentry.init({
dsn: sentryDsn,
release: releaseBuildNumber || gitReleaseName,
environment: sentryEnv || defArgs.env[0],
beforeSend(event) {
if (event.request && event.request.data && shouldRedactRequestData(event.request.data)) {
event.request.data = JSON.stringify({redacted: true});
}
return event;
},
});
logger.info(`Configured with Sentry endpoint ${sentryDsn}`);
}

const awsProps = props.propsFor('aws');

// eslint-disable-next-line max-statements
Expand All @@ -479,7 +451,7 @@ async function main() {
// Initialise express and then sentry. Sentry as early as possible to catch errors during startup.
const webServer = express(),
router = express.Router();
setupSentry(aws.getConfig('sentryDsn'));
SetupSentry(aws.getConfig('sentryDsn'), ceProps, releaseBuildNumber, gitReleaseName, defArgs);

startWineInit();

Expand Down
5 changes: 2 additions & 3 deletions lib/cache/s3.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,11 @@
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.

import * as Sentry from '@sentry/node';

import type {GetResult} from '../../types/cache.interfaces.js';
import {logger} from '../logger.js';
import {S3Bucket} from '../s3-handler.js';
import type {S3HandlerOptions} from '../s3-handler.interfaces.js';
import {SentryCapture} from '../sentry.js';

import {BaseCache} from './base.js';
import {StorageClass} from '@aws-sdk/client-s3';
Expand All @@ -50,7 +49,7 @@ export class S3Cache extends BaseCache {
this.onError =
onError ||
((e, op) => {
Sentry.captureException(e);
SentryCapture(e, 'S3Cache onError');
logger.error(`Error while trying to ${op} S3 cache: ${messageFor(e)}`);
});
}
Expand Down
3 changes: 2 additions & 1 deletion lib/handlers/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ import {withAssemblyDocumentationProviders} from './assembly-documentation.js';
import {CompileHandler} from './compile.js';
import {FormattingHandler} from './formatting.js';
import {getSiteTemplates} from './site-templates.js';
import {SentryCapture} from '../sentry.js';

function methodNotAllowed(req: express.Request, res: express.Response) {
res.send('Method Not Allowed');
Expand Down Expand Up @@ -166,7 +167,7 @@ export class ApiHandler {
.catch(err => {
logger.warn(`Exception thrown when expanding ${id}: `, err);
logger.warn('Exception value:', err);
Sentry.captureException(err);
SentryCapture(err, 'shortlinkInfoHandler');
next({
statusCode: 404,
message: `ID "${id}" could not be found`,
Expand Down
10 changes: 6 additions & 4 deletions lib/handlers/compile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ import {
} from './compile.interfaces.js';
import {remove} from '../common-utils.js';
import {CompilerOverrideOptions} from '../../types/compilation/compiler-overrides.interfaces.js';
import {SentryCapture} from '../sentry.js';

temp.track();

Expand Down Expand Up @@ -170,8 +171,9 @@ export class CompileHandler {
),
});
} else {
Sentry.captureException(
SentryCapture(
new Error(`Unexpected Content-Type received by /compiler/:compiler/compile: ${contentType}`),
'lib/handlers/compile.ts proxyReq contentType',
);
proxyReq.write('Unexpected Content-Type');
}
Expand All @@ -183,7 +185,7 @@ export class CompileHandler {
proxyReq.write(bodyData);
}
} catch (e: any) {
Sentry.captureException(e);
SentryCapture(e, 'lib/handlers/compile.ts proxyReq.write');
let json = '<json stringify error>';
try {
json = JSON.stringify(bodyData);
Expand Down Expand Up @@ -586,7 +588,7 @@ export class CompileHandler {
}
}
} catch (ex) {
Sentry.captureException(ex);
SentryCapture(ex, 'lib/handlers/compile.ts res.write');
res.write(`Error handling request: ${ex}`);
}
res.end('\n');
Expand All @@ -598,7 +600,7 @@ export class CompileHandler {
} else {
if (error.stack) {
logger.error('Error during compilation 2: ', error);
Sentry.captureException(error);
SentryCapture(error, 'compile failed');
} else if (error.code) {
logger.error('Error during compilation 3: ', error.code);
if (typeof error.stderr === 'string') {
Expand Down
4 changes: 2 additions & 2 deletions lib/handlers/health-check.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,12 @@
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.

import * as Sentry from '@sentry/node';
import express from 'express';
import fs from 'fs-extra';

import {CompilationQueue} from '../compilation-queue.js';
import {logger} from '../logger.js';
import {SentryCapture} from '../sentry.js';

export class HealthCheckHandler {
public readonly handle: (req: any, res: any) => Promise<void>;
Expand Down Expand Up @@ -57,7 +57,7 @@ export class HealthCheckHandler {
res.send(content);
} catch (e) {
logger.error(`*** HEALTH CHECK FAILURE: while reading file '${this.filePath}' got ${e}`);
Sentry.captureException(e);
SentryCapture(e, 'Health check');
res.status(500).end();
}
}
Expand Down
5 changes: 3 additions & 2 deletions lib/handlers/route-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import {StorageBase} from '../storage/index.js';
import * as utils from '../utils.js';

import {ApiHandler} from './api.js';
import {SentryCapture} from '../sentry.js';

export type HandlerConfig = {
compileHandler: any;
Expand Down Expand Up @@ -110,7 +111,7 @@ export class RouteAPI {
.catch(err => {
logger.debug(`Exception thrown when expanding ${id}: `, err);
logger.warn('Exception value:', err);
Sentry.captureException(err);
SentryCapture(err, 'storedCodeHandler');
next({
statusCode: 404,
message: `ID "${id}/${sessionid}" could not be found`,
Expand Down Expand Up @@ -201,7 +202,7 @@ export class RouteAPI {
.catch(err => {
logger.warn(`Exception thrown when expanding ${id}`);
logger.warn('Exception value:', err);
Sentry.captureException(err);
SentryCapture(err, 'storedStateHandlerResetLayout');
next({
statusCode: 404,
message: `ID "${id}" could not be found`,
Expand Down
85 changes: 85 additions & 0 deletions lib/sentry.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
// Copyright (c) 2023, Compiler Explorer Authors
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.

import {logger} from './logger.js';
import {PropertyGetter} from './properties.interfaces.js';
import {parse} from './stacktrace.js';

import * as Sentry from '@sentry/node';

function shouldRedactRequestData(data: string) {
try {
const parsed = JSON.parse(data);
return !parsed['allowStoreCodeDebug'];
} catch (e) {
return true;
}
}

export function SetupSentry(
sentryDsn: string,
ceProps: PropertyGetter,
releaseBuildNumber: string | undefined,
gitReleaseName: string | undefined,
defArgs: any,
) {
if (!sentryDsn) {
logger.info('Not configuring sentry');
return;
}
const sentryEnv = ceProps('sentryEnvironment');
Sentry.init({
dsn: sentryDsn,
release: releaseBuildNumber || gitReleaseName,
environment: sentryEnv || defArgs.env[0],
beforeSend(event) {
if (event.request && event.request.data && shouldRedactRequestData(event.request.data)) {
event.request.data = JSON.stringify({redacted: true});
}
return event;
},
});
logger.info(`Configured with Sentry endpoint ${sentryDsn}`);
}

export function SentryCapture(value: unknown, context?: string) {
if (value instanceof Error) {
if (context) {
value.message += `\nSentryCapture Context: ${context}`;
}
Sentry.captureException(value);
} else {
const e = new Error(); // eslint-disable-line unicorn/error-message
const trace = parse(e);
Sentry.captureMessage(
`Non-Error capture:\n` +
(context ? `Context: ${context}\n` : '') +
`Data:\n${JSON.stringify(value)}\n` +
`Trace:\n` +
trace
.map(frame => `${frame.functionName} ${frame.fileName}:${frame.lineNumber}:${frame.columnNumber}`)
.join('\n'),
);
}
}
10 changes: 2 additions & 8 deletions static/analytics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,9 @@
// POSSIBILITY OF SUCH DAMAGE.

import {options} from './options.js';
import * as Sentry from '@sentry/browser';
import {SetupSentry} from './sentry.js';

if (options.statusTrackingEnabled && options.sentryDsn) {
Sentry.init({
dsn: options.sentryDsn,
release: options.release,
environment: options.sentryEnvironment,
});
}
SetupSentry();

class GAProxy {
private hasBeenEnabled = false;
Expand Down
3 changes: 2 additions & 1 deletion static/compiler-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import {CompilerInfo} from '../types/compiler.interfaces.js';
import {CompilationResult, FiledataPair} from '../types/compilation/compilation.interfaces.js';
import {CompilationStatus} from './compiler-service.interfaces.js';
import {IncludeDownloads, SourceAndFiles} from './download-service.js';
import {SentryCapture} from './sentry.js';

const ASCII_COLORS_RE = new RegExp(/\x1B\[[\d;]*m(.\[K)?/g);

Expand Down Expand Up @@ -123,7 +124,7 @@ export class CompilerService {
}
}
} catch (e) {
Sentry.captureException(e);
SentryCapture(e, 'processFromLangAndCompiler');
}
// TODO: What now? Found no compilers!
return {
Expand Down
3 changes: 2 additions & 1 deletion static/event-hub.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import GoldenLayout from 'golden-layout';

import {type EventMap} from './event-map.js';
import {type Hub} from './hub.js';
import {SentryCapture} from './sentry.js';

export type EventHubCallback<T extends unknown[]> = (...args: T) => void;

Expand Down Expand Up @@ -83,7 +84,7 @@ export class EventHub {
this.layoutEventHub.off(subscription.evt, subscription.fn, subscription.ctx);
} catch (e) {
Sentry.captureMessage(`Can not unsubscribe from ${subscription.evt.toString()}`);
Sentry.captureException(e);
SentryCapture(e, 'event hub unsubscribe');
}
}
this.subscriptions = [];
Expand Down
5 changes: 3 additions & 2 deletions static/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ import {CompilerExplorerOptions} from './global.js';
import {ComponentConfig, EmptyCompilerState, StateWithId, StateWithLanguage} from './components.interfaces.js';

import * as utils from '../lib/common-utils.js';
import {SentryCapture} from './sentry.js';

const logos = require.context('../views/resources/logos', false, /\.(png|svg)$/);

Expand Down Expand Up @@ -212,7 +213,7 @@ function setupButtons(options: CompilerExplorerOptions, hub: Hub) {
$('#ces .ces-icons').html(data);
})
.fail(err => {
Sentry.captureException(err);
SentryCapture(err, '$.get failed');
});

$('#ces').on('click', () => {
Expand Down Expand Up @@ -594,7 +595,7 @@ function start() {
layout = new GoldenLayout(config, root);
hub = new Hub(layout, subLangId, defaultLangId);
} catch (e) {
Sentry.captureException(e);
SentryCapture(e, 'goldenlayout/hub setup');

if (document.URL.includes('/z/')) {
document.location = document.URL.replace('/z/', '/resetlayout/');
Expand Down
3 changes: 2 additions & 1 deletion static/panes/compiler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ import fileSaver = require('file-saver');
import {ICompilerShared} from '../compiler-shared.interfaces.js';
import {CompilerShared} from '../compiler-shared.js';
import type {ActiveTools, CompilationRequest, CompilationRequestOptions} from './compiler-request.interfaces.js';
import {SentryCapture} from '../sentry.js';
import {LLVMIrBackendOptions} from '../compilation/ir.interfaces.js';

const toolIcons = require.context('../../views/resources/logos', false, /\.(png|svg)$/);
Expand Down Expand Up @@ -1030,7 +1031,7 @@ export class Compiler extends MonacoPane<monaco.editor.IStandaloneCodeEditor, Co
};
} else {
// In case this ever stops working, we'll be notified
Sentry.captureException(new Error('Context menu hack did not return valid original method'));
SentryCapture(new Error('Context menu hack did not return valid original method'));
}

this.editor.addAction({
Expand Down
Loading

0 comments on commit acdd5ad

Please sign in to comment.