forked from github/docs
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Branch was updated using the 'autoupdate branch' Actions workflow.
- Loading branch information
Showing
5 changed files
with
99 additions
and
121 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,112 +1,34 @@ | ||
import fetch from 'node-fetch' | ||
import got from 'got' | ||
import { Failbot, HTTPBackend, LogBackend } from '@github/failbot' | ||
|
||
export default class FailBot { | ||
constructor({ app, haystackURL, headers }) { | ||
this.app = app | ||
this.headers = headers | ||
const HAYSTACK_APP = 'docs' | ||
|
||
// Since we're using `node-fetch` we can't rely on it deconstructing the | ||
// basic authentication credentials from the URL (e.g. | ||
// https://user:pass@failbotdomain/path) because `node-fetch` will always | ||
// strip it. See https://github.com/node-fetch/node-fetch/issues/1330 | ||
// and it's not a bug. | ||
// The correct thing is to extract it manually and add an `Authorization` | ||
// header based on it from the URL. | ||
const url = new URL(haystackURL) | ||
export function report(error, metadata) { | ||
// If there's no HAYSTACK_URL set, bail early | ||
if (!process.env.HAYSTACK_URL) return | ||
|
||
// remove the basic auth portion of the url since it throws an error in node-fetch | ||
this.haystackURL = `${url.origin}${url.pathname}` | ||
|
||
const { username, password } = url | ||
if (username || password) { | ||
this.headers.Authorization = `Basic ${Buffer.from(`${username}:${password}`).toString( | ||
'base64' | ||
)}` | ||
} else { | ||
console.warn(`The haystack URL does not contain authentication credentials`) | ||
} | ||
} | ||
|
||
/** | ||
* Report an error to Sentry | ||
* @param {Error} error | ||
* @param {any} metadata | ||
* @param {any} [headers] | ||
*/ | ||
static async report(error, metadata, headers = {}) { | ||
// If there's no HAYSTACK_URL set, bail early | ||
if (!process.env.HAYSTACK_URL) return | ||
|
||
const failbot = new FailBot({ | ||
app: 'docs', | ||
const backends = [ | ||
new HTTPBackend({ | ||
haystackURL: process.env.HAYSTACK_URL, | ||
headers, | ||
}) | ||
|
||
return failbot.sendException(error, metadata) | ||
} | ||
|
||
/** | ||
* Create a rollup of this error by generating a base64 representation | ||
* @param {Error} error | ||
*/ | ||
createRollup(error) { | ||
const stackLine = error.stack && error.stack.split('\n')[1] | ||
const str = `${error.name}:${stackLine}`.replace(/=/g, '') | ||
return Buffer.from(str).toString('base64') | ||
} | ||
|
||
/** | ||
* Format the error to a plain JSON object with additional data | ||
* @param {Error} error | ||
* @param {any} metadata | ||
*/ | ||
formatJSON(error, metadata) { | ||
return Object.assign({}, metadata, { | ||
/* eslint-disable camelcase */ | ||
created_at: new Date().toISOString(), | ||
rollup: this.createRollup(error), | ||
class: error.name, | ||
message: error.message, | ||
backtrace: error.stack || '', | ||
js_environment: `Node.js ${process.version}`, | ||
/* eslint-enable camelcase */ | ||
}) | ||
} | ||
|
||
/** | ||
* Populate default context from settings. Since settings commonly comes from | ||
* ENV, this allows setting defaults for the context via the environment. | ||
*/ | ||
getFailbotContext() { | ||
const failbotKeys = {} | ||
|
||
for (const key in process.env) { | ||
if (key.startsWith('FAILBOT_CONTEXT_')) { | ||
const formattedKey = key.replace(/^FAILBOT_CONTEXT_/, '').toLowerCase() | ||
failbotKeys[formattedKey] = process.env[key] | ||
} | ||
} | ||
|
||
return failbotKeys | ||
fetchFn: got, | ||
}), | ||
] | ||
if (process.env.NODE_ENV !== 'test') { | ||
backends.push(new LogBackend({ log: console.log.bind(console) })) | ||
} | ||
const failbot = new Failbot({ | ||
app: HAYSTACK_APP, | ||
backends: backends, | ||
}) | ||
return failbot.report(error, metadata) | ||
} | ||
|
||
/** | ||
* Send the error to Sentry | ||
* @param {Error} error | ||
* @param {any} metadata | ||
*/ | ||
async sendException(error, metadata = {}) { | ||
const data = Object.assign({ app: this.app }, this.getFailbotContext(), metadata) | ||
const body = this.formatJSON(error, Object.assign({ app: this.app }, data)) | ||
|
||
return fetch(this.haystackURL, { | ||
method: 'POST', | ||
body: JSON.stringify(body), | ||
headers: { | ||
...this.headers, | ||
'Content-Type': 'application/json', | ||
}, | ||
}) | ||
} | ||
// Kept for legacy so you can continue to do: | ||
// | ||
// import FailBot from './lib/failbot.js' | ||
// ... | ||
// FailBot.report(myError) | ||
// | ||
export default { | ||
report, | ||
} |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters