Skip to content

Instantly share code, notes, and snippets.

@williamdes
Last active May 4, 2024 13:03
Show Gist options
  • Save williamdes/1d0b02c1bf3b94ee0dbaecdbc26d12c1 to your computer and use it in GitHub Desktop.
Save williamdes/1d0b02c1bf3b94ee0dbaecdbc26d12c1 to your computer and use it in GitHub Desktop.
Pure NodeJs Sentry reporter compatible with AWS lambda
/**
* @source https://github.com/errwischt/stacktrace-parser/blob/9b2d5b6114e7a3b3596f7092f0d1160738125374/src/stack-trace-parser.js
* @copyright Copyright (c) 2014-2019 Georg Tavonius
* @license MIT
* This is the same code but only with removed parsing for all browsers or JS platforms else than NodeJS
*/
const StackTraceParser = {
UNKNOWN_FUNCTION: '<unknown>',
/**
* This parses the different stack traces and puts them into one format
* This borrows heavily from TraceKit (https://github.com/csnover/TraceKit)
*/
parse(stackString) {
const lines = stackString.split('\n');
return lines.reduce((stack, line) => {
const parseResult = StackTraceParser.parseNode(line);
if (parseResult) {
stack.push(parseResult);
}
return stack;
}, []);
},
nodeRe: /^\s*at (?:((?:\[object object\])?[^\\/]+(?: \[as \S+\])?) )?\(?(.*?):(\d+)(?::(\d+))?\)?\s*$/i,
parseNode(line) {
const parts = StackTraceParser.nodeRe.exec(line);
if (!parts) {
return null;
}
return {
file: parts[2],
methodName: parts[1] || StackTraceParser.UNKNOWN_FUNCTION,
arguments: [],
lineNumber: +parts[3],
column: parts[4] ? +parts[4] : null,
};
},
};
const https = require('https');
const crypto = require('crypto');
const os = require('os');
const SENTRY_HOST = 'sentry.io';
const SENTRY_PROJECTID = 123456;
const SENTRY_KEY = 'xxxxxxxxxxx';
const LOGGER_NAME = 'my-logger';
/**
* @copyright Copyright (c) 2024 William Desportes <williamdes@wdes.fr>
* @license MIT
* @version 1.0
* @source https://gist.github.com/williamdes/1d0b02c1bf3b94ee0dbaecdbc26d12c1
*/
const sendToSentry = function (error, URLroute, requestMethod, requestHeaders, userId, userIP, tags, release) {
// See: https://develop.sentry.dev/sdk/envelopes/#data-model
const sentryData = {
// See: https://stackoverflow.com/a/69358886/5155484
event_id: crypto.randomBytes(16).toString('hex'),
timestamp: new Date().getTime(), //.toISOString(),
logger: LOGGER_NAME,
platform: 'javascript',
level: 'error',
message: error.message,
transaction: URLroute,
tags: tags,
release: release,
exception: {
type: error.name,
value: error.message,
handled: true, // Best guess depending on the code
stacktrace: {
frames: StackTraceParser.parse(error.stack).map((traceBit) => {
return {
in_app: traceBit.file.indexOf('node:') === -1,
filename: traceBit.file,
function: traceBit.methodName,
vars: traceBit.arguments,
lineno: traceBit.lineNumber,
};
}),
},
},
user: {
id: userId,
ip_address: userIP,
},
sdk: {
name: 'Mini-Sentry-JS',
version: '1.0',
},
contexts: {
// See: https://github.com/getsentry/develop/blob/24.4.2/src/docs/sdk/event-payloads/contexts.mdx#os-context
os: {
name: process.platform,
type: 'os',
version: os.release(),
},
runtime: {
name: process.release.name,
type: 'runtime',
version: process.version,
},
device: {
arch: process.arch,
memory_size: os.totalmem(),
free_memory: os.freemem(),
},
hostname: os.hostname(),
},
request: {
url: URLroute,
method: requestMethod,
headers: requestHeaders,
},
};
const sentryUrl = 'https://' + SENTRY_HOST + '/api/' + SENTRY_PROJECTID + '/store/';
const data = JSON.stringify(sentryData);
const httpOptions = {
method: 'POST',
headers: {
'User-Agent': 'Mini-Sentry-JS/1.0',
'Content-Type': 'application/json',
'X-Sentry-Auth': 'Sentry sentry_version=7, sentry_key=' + SENTRY_KEY + ', sentry_client=mini-sentry-js/1.0',
Accept: 'application/json',
'Content-Length': data.length, // Thank you https://flaviocopes.com/node-http-post/
},
};
const request = https.request(sentryUrl, httpOptions, (res) => {
//console.log(`statusCode: ${res.statusCode}`);
res.on('data', (d) => {
// Enable to inspect the received data
// console.log('sentry said: ' + d);
});
});
request.on('error', (error) => {
console.error(error);
});
request.write(data);
request.end();
// Enable to inspect the sent data
//console.log(sentryData);
};
// ----------- Example --------------
try {
throw new Error('Invalid foo bar');
} catch (e) {
sendToSentry(e, '/app', 'POST', [['Accept', 'application/json']], '1234', '127.0.0.1', {
fooTag: 'barTag',
}, '1.2.3');
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment