-
-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy patherrors.js
More file actions
117 lines (108 loc) · 3.98 KB
/
Copy patherrors.js
File metadata and controls
117 lines (108 loc) · 3.98 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
import api from 'api'
import { CloudflareLogger } from './logger.js'
/**
* Error handler for Cloudflare Workers.
*
* - Prints to console nicely for Cloudflare logging
* - Responds with nice error response based on ARF
* - Posts to a webhook if configured (ie: into a chat) and only once per 2 days per error.
*
*/
export class ErrorHandler {
constructor(options = {}) {
this.options = options
this.logger = this.options.logger || new CloudflareLogger()
}
/**
* This will log the error, post an alert to a webhook if postTo is set and respond with a JSON error response.
*
* @param {*} c
* @param {*} err
* @returns
*/
async handle(c, err) {
await this.logc(c, err)
return Response.json({ error: { message: err.message } }, { status: err.status || 500 })
}
/**
* This will log and post to webhook, but does not generate a response. Good for use in
* parts where you aren't throwing to exit, but just logging and continuing.
*
* Called it logc to keep log open for the future and stay compatible with regular logging.
*
* @param {*} c
* @param {*} err
* @returns
*/
async logc(c, err) {
// Only log server errors (status >= 500) or crashes (no status).
if (err.status == null || err.status >= 500) {
this.logger.error(err.message, err)
}
c.waitUntil(this.doPost(c, err))
}
async doPost(c, err) {
// Do not post client errors (status < 500) to the webhook.
if (err.status && err.status < 500) return
// console.log('POST TO:', this.options.postTo)
if (this.options.postTo) {
let postTo = this.options.postTo
postTo.options ||= {}
postTo.options.method ||= 'POST'
let options = { ...postTo.options }
let causeStr = ''
let currentCause = err.cause
const seenCauses = new WeakSet()
if (typeof err === 'object' && err !== null) seenCauses.add(err)
while (currentCause) {
if (typeof currentCause === 'object' && currentCause !== null) {
if (seenCauses.has(currentCause)) {
causeStr += '\n\nCaused by: [Circular Reference]'
break
}
seenCauses.add(currentCause)
}
if (currentCause instanceof Error) {
causeStr += `\n\nCaused by: ${currentCause.name}: ${currentCause.message}\n${currentCause.stack || ''}`
currentCause = currentCause.cause
} else if (typeof currentCause === 'object') {
causeStr += `\n\nCaused by: ${JSON.stringify(currentCause, null, ' ')}`
currentCause = currentCause.cause
} else {
causeStr += `\n\nCaused by: ${String(currentCause)}`
break
}
}
let dataStr = this.logger?.data ? '\n\n' + JSON.stringify(this.logger.data, null, ' ') : ''
let message = `${err.name}: ${err.message}${causeStr}${dataStr}\n\n${err.stack}`
if (this.options.appName) message = `${this.options.appName}\n${message}`
if (options.body) {
options.body = options.body(message)
}
let filenameAndLineNumbers = err.stack ? err.stack.split('\n').find((l) => l.match(/.*:\d+:\d+/)) : null
if (!filenameAndLineNumbers) {
filenameAndLineNumbers = err.message.replace(/\s/g, '_')
}
filenameAndLineNumbers = filenameAndLineNumbers.trim()
// console.log('ErrorHandler: filenameAndLineNumbers:', filenameAndLineNumbers)
if (!this.options.force && c.env.KV) {
// console.log('checking KV for duplicates')
let key = `errors/${filenameAndLineNumbers}`
let errorKV = await c.env.KV.get(key)
if (errorKV) {
// console.log('found duplicate error, not posting')
return
}
// console.log("posting to KV so we don't send duplicates")
await c.env.KV.put(key, message, {
expirationTtl: 60 * 60 * 24 * 2, // 2 days
})
}
try {
let r = await api(postTo.url, options)
} catch (e) {
console.log('ErrorHandler: error posting to postTo:', e)
}
}
}
}