-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathSentry.cfc
More file actions
executable file
·244 lines (215 loc) · 6.89 KB
/
Sentry.cfc
File metadata and controls
executable file
·244 lines (215 loc) · 6.89 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
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
/**
* wheels-sentry — Wheels package for Sentry error tracking.
*
* Provides automatic SDK initialization, controller mixin methods for
* capturing errors/messages with Wheels context, and breadcrumb support.
*
* Mixin methods available in controllers:
* sentryCapture(exception, [level], [additionalData])
* sentryMessage(message, [level])
* sentrySetUser(userStruct)
* sentryAddBreadcrumb(message, [category], [data], [level])
*/
component mixin="controller" output="false" {
function init() {
this.version = "3.0";
initSentry();
return this;
}
/**
* Auto-initialize the Sentry SDK if a DSN is available.
* Reads from: (1) Wheels setting sentryDSN, (2) SENTRY_DSN env var.
* Stores the client in application.sentry.
*/
private void function initSentry() {
if (structKeyExists(application, "sentry"))
return;
lock name="wheelsSentryInit" type="exclusive" timeout="10" {
if (structKeyExists(application, "sentry"))
return;
try {
var dsn = "";
try {
dsn = get("sentryDSN");
} catch (any e) {}
if (!len(trim(dsn))) {
var javaEnv = createObject("java", "java.lang.System").getenv("SENTRY_DSN");
if (!isNull(javaEnv) && len(trim(javaEnv)))
dsn = javaEnv;
}
if (!len(trim(dsn)))
return;
// Determine environment and release
var appKey = structKeyExists(application, "wheels") ? "wheels" : "$wheels";
var env = "development";
if (structKeyExists(application, appKey) && structKeyExists(application[appKey], "environment"))
env = application[appKey].environment;
var rel = "unknown";
try {
var javaAppVersion = createObject("java", "java.lang.System").getenv("APP_VERSION");
if (!isNull(javaAppVersion) && len(trim(javaAppVersion)))
rel = javaAppVersion;
} catch (any e) {}
// Read scope settings with defaults
var scopeSettings = {
"sendDefaultPii": $sentryGetSetting("sentrySendDefaultPii", false),
"includeHeaders": $sentryGetSetting("sentryIncludeHeaders", true),
"includeServerContext": $sentryGetSetting("sentryIncludeServerContext", true),
"includeUser": false,
"includeSession": false,
"includeCookies": false
};
// PII settings: individual overrides take precedence, otherwise follow sendDefaultPii
var pii = scopeSettings.sendDefaultPii;
scopeSettings.includeUser = $sentryGetSetting("sentryIncludeUser", pii);
scopeSettings.includeSession = $sentryGetSetting("sentryIncludeSession", pii);
scopeSettings.includeCookies = $sentryGetSetting("sentryIncludeCookies", pii);
application.sentry = new plugins.sentry.SentryClient(
DSN: dsn,
environment: env,
release: rel,
serverName: cgi.server_name,
scopeSettings: scopeSettings
);
writeLog(
text="wheels-sentry initialized (env=#env#, release=#rel#, pii=#pii#)",
type="information",
file="application"
);
} catch (any e) {
writeLog(
text="wheels-sentry initialization failed: #e.message#",
type="error",
file="application"
);
}
} // end lock
}
/**
* Read a Wheels setting with a fallback default. Returns the default
* if the setting doesn't exist or throws.
*/
private any function $sentryGetSetting(required string name, required any defaultValue) {
try {
var val = get(arguments.name);
if (isBoolean(val)) return val;
if (isSimpleValue(val) && len(val)) return val;
} catch (any e) {}
return arguments.defaultValue;
}
/**
* Capture an exception with Wheels controller/action context.
*
* @exception The exception struct to capture.
* @level Sentry level (default: "error").
* @additionalData Optional struct of extra data to attach.
*/
public void function sentryCapture(
required any exception,
string level = "error",
any additionalData
) {
if (!structKeyExists(application, "sentry"))
return;
var eventTags = {};
if (structKeyExists(variables, "params")) {
var p = variables.params;
if (structKeyExists(p, "controller"))
eventTags["wheels.controller"] = p.controller;
if (structKeyExists(p, "action"))
eventTags["wheels.action"] = p.action;
application.sentry.setRequestContext(
controller: structKeyExists(p, "controller") ? p.controller : "",
action: structKeyExists(p, "action") ? p.action : "",
params: p
);
}
var captureArgs = {
exception: arguments.exception,
level: arguments.level,
useThread: true,
showJavaStackTrace: true,
userInfo: $sentryGetUser(),
tags: eventTags
};
if (!isNull(arguments.additionalData))
captureArgs["additionalData"] = arguments.additionalData;
application.sentry.captureException(argumentCollection: captureArgs);
}
/**
* Capture a message with Wheels controller/action context.
*
* @message The message string to send to Sentry.
* @level Sentry level (default: "info").
*/
public void function sentryMessage(
required string message,
string level = "info"
) {
if (!structKeyExists(application, "sentry"))
return;
var eventTags = {};
if (structKeyExists(variables, "params")) {
var p = variables.params;
if (structKeyExists(p, "controller"))
eventTags["wheels.controller"] = p.controller;
if (structKeyExists(p, "action"))
eventTags["wheels.action"] = p.action;
application.sentry.setRequestContext(
controller: structKeyExists(p, "controller") ? p.controller : "",
action: structKeyExists(p, "action") ? p.action : "",
params: p
);
}
application.sentry.captureMessage(
message: arguments.message,
level: arguments.level,
useThread: true,
userInfo: $sentryGetUser(),
tags: eventTags
);
}
/**
* Set the Sentry user context for the current request.
* Call this in a before filter to attach user identity to all events.
*
* @userStruct Struct with id, email, username, ip_address, etc.
*/
public void function sentrySetUser(required struct userStruct) {
request.sentryUserOverride = arguments.userStruct;
}
/**
* Add a breadcrumb for the current request.
*
* @message Breadcrumb message.
* @category Category string (default: "controller").
* @data Optional struct of extra data.
* @level Sentry level (default: "info").
*/
public void function sentryAddBreadcrumb(
required string message,
string category = "controller",
struct data = {},
string level = "info"
) {
if (!structKeyExists(application, "sentry"))
return;
application.sentry.addBreadcrumb(
message: arguments.message,
category: arguments.category,
data: arguments.data,
level: arguments.level
);
}
/**
* Build user info from request override. Returns empty struct if
* no user has been set via sentrySetUser() or if user inclusion is
* disabled. Applications should call sentrySetUser() in a before
* filter to attach user identity.
*/
private struct function $sentryGetUser() {
if (structKeyExists(request, "sentryUserOverride"))
return request.sentryUserOverride;
return {};
}
}