master
c 286 lines 10.3 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 #include "sentry-native.h"
4 #include "daemon/common.h"
5
6 #include "sentry.h"
7
8 static void nd_sentry_add_deadly_signal_as_breadcrumb(void);
9
10 static char sentry_path[FILENAME_MAX] = "";
11
12 static bool sentry_initialized = false;
13 static bool breadcrumb_added = false;
14 static bool nd_sentry_crash_report_enabled = true;
15
16 const char *nd_sentry_path(void) {
17 return sentry_path;
18 }
19
20 void nd_sentry_crash_report(bool enable) {
21 nd_sentry_crash_report_enabled = enable;
22 }
23
24 // --------------------------------------------------------------------------------------------------------------------
25 // helpers
26
27 static void nd_sentry_set_tag(const char *key, const char *value) {
28 if (!value || !*value)
29 return;
30
31 sentry_set_tag(key, value);
32 }
33
34 static void nd_sentry_set_tag_int64(const char *key, int64_t value) {
35 if(!value)
36 return;
37
38 char buf[UINT64_MAX_LENGTH];
39 print_int64(buf, value);
40 nd_sentry_set_tag(key, buf);
41 }
42
43 static void nd_sentry_set_tag_uint64(const char *key, uint64_t value) {
44 if(!value)
45 return;
46
47 char buf[UINT64_MAX_LENGTH];
48 print_uint64(buf, value);
49 nd_sentry_set_tag(key, buf);
50 }
51
52 static inline void nd_sentry_set_tag_uuid(const char *key, const ND_UUID uuid) {
53 if(UUIDiszero(uuid))
54 return;
55
56 char uuid_str[UUID_STR_LEN];
57 uuid_unparse_lower(uuid.uuid, uuid_str);
58 nd_sentry_set_tag(key, uuid_str);
59 }
60
61 static void nd_sentry_set_tag_uuid_compact(const char *key, const ND_UUID uuid) {
62 if(UUIDiszero(uuid))
63 return;
64
65 char uuid_str[UUID_COMPACT_STR_LEN];
66 uuid_unparse_lower_compact(uuid.uuid, uuid_str);
67 nd_sentry_set_tag(key, uuid_str);
68 }
69
70 static void nd_sentry_set_tag_uptime(void) {
71 nd_sentry_set_tag_int64("uptime", now_realtime_sec() - netdata_start_time);
72 }
73
74 static void nd_sentry_set_tag_status(void) {
75 nd_sentry_set_tag("status", DAEMON_STATUS_2str(daemon_status_file_get_status()));
76 }
77
78 // --------------------------------------------------------------------------------------------------------------------
79 // sentry hooks
80
81 static sentry_value_t nd_sentry_on_hook(sentry_value_t event) {
82 // IMPORTANT: this function is called from a signal handler
83
84 // sentry enables a custom allocator for their use,
85 // that is async signal safe, so the sentry API is available here.
86
87 if (!nd_sentry_crash_report_enabled) {
88 sentry_value_decref(event);
89 return sentry_value_new_null();
90 }
91
92 nd_sentry_add_deadly_signal_as_breadcrumb();
93
94 return event;
95 }
96
97 static sentry_value_t nd_sentry_on_crash(
98 const sentry_ucontext_t *uctx __maybe_unused, // provides the user-space context of the crash
99 sentry_value_t event, // used the same way as in `before_send`
100 void *closure __maybe_unused) { // user-data that you can provide at configuration time
101 return nd_sentry_on_hook(event);
102 }
103
104 static sentry_value_t nd_sentry_before_send(
105 sentry_value_t event,
106 void *hint __maybe_unused,
107 void *closure __maybe_unused) {
108 return nd_sentry_on_hook(event);
109 }
110
111 // --------------------------------------------------------------------------------------------------------------------
112 // sentry initialization
113
114 void nd_sentry_init(void) {
115 if (!analytics_check_enabled() || sentry_initialized)
116 return;
117
118 // path where sentry should save stuff
119 snprintfz(sentry_path, FILENAME_MAX - 1, "%s/.sentry-native", netdata_configured_cache_dir);
120
121 // ----------------------------------------------------------------------------------------------------------------
122 // sentry options
123
124 sentry_options_t *options = sentry_options_new();
125 sentry_options_set_dsn(options, NETDATA_SENTRY_DSN);
126 sentry_options_set_database_path(options, sentry_path);
127 sentry_options_set_environment(options, NETDATA_SENTRY_ENVIRONMENT);
128
129 if(NETDATA_VERSION[0] == 'v')
130 sentry_options_set_release(options, &NETDATA_VERSION[1]);
131 else
132 sentry_options_set_release(options, NETDATA_VERSION);
133
134 sentry_options_set_dist(options, NETDATA_SENTRY_DIST);
135 #ifdef NETDATA_INTERNAL_CHECKS
136 sentry_options_set_debug(options, 1);
137 #endif
138
139 sentry_options_set_on_crash(options, nd_sentry_on_crash, NULL);
140 sentry_options_set_before_send(options, nd_sentry_before_send, NULL);
141
142 // ----------------------------------------------------------------------------------------------------------------
143 // initialization
144
145 nd_cleanup_deadly_signals(); // remove our signal handlers, so that sentry will not hook back to us
146 sentry_init(options);
147 nd_initialize_signals(true); // add our signal handlers, we will hook back to sentry
148
149 // ----------------------------------------------------------------------------------------------------------------
150 // tags
151
152 nd_sentry_set_tag("install_type", daemon_status_file_get_install_type());
153 nd_sentry_set_tag("architecture", daemon_status_file_get_architecture());
154 nd_sentry_set_tag("virtualization", daemon_status_file_get_virtualization());
155 nd_sentry_set_tag("container", daemon_status_file_get_container());
156 nd_sentry_set_tag("os_name", daemon_status_file_get_os_name());
157 nd_sentry_set_tag("os_version", daemon_status_file_get_os_version());
158 nd_sentry_set_tag("os_id", daemon_status_file_get_os_id());
159 nd_sentry_set_tag("os_id_like", daemon_status_file_get_os_id_like());
160 nd_sentry_set_tag("cloud_provider", daemon_status_file_get_cloud_provider_type());
161 nd_sentry_set_tag("cloud_type", daemon_status_file_get_cloud_instance_type());
162 nd_sentry_set_tag("cloud_region", daemon_status_file_get_cloud_instance_region());
163 nd_sentry_set_tag("timezone", daemon_status_file_get_timezone());
164
165 nd_sentry_set_tag("hw_sys_vendor", daemon_status_file_get_sys_vendor());
166 nd_sentry_set_tag("hw_product_name", daemon_status_file_get_product_name());
167 nd_sentry_set_tag("hw_product_type", daemon_status_file_get_product_type());
168
169 // profile
170 CLEAN_BUFFER *profile = buffer_create(0, NULL);
171 ND_PROFILE_2buffer(profile, nd_profile_detect_and_configure(false), " ");
172 nd_sentry_set_tag("profile", buffer_tostring(profile));
173
174 // db_mode
175 nd_sentry_set_tag("db_mode", rrd_memory_mode_name(default_rrd_memory_mode));
176
177 // db_tiers
178 nd_sentry_set_tag_int64("db_tiers", (int64_t)nd_profile.storage_tiers);
179
180 // ephemeral_id
181 nd_sentry_set_tag_uuid_compact("ephemeral_id", nd_log_get_invocation_id());
182
183 // agent_events_version
184 nd_sentry_set_tag("agent_events_version", TOSTRING(STATUS_FILE_VERSION));
185
186 nd_sentry_set_tag_uint64("restarts", daemon_status_file_get_restarts());
187 nd_sentry_set_tag_int64("reliability", daemon_status_file_get_reliability());
188 nd_sentry_set_tag("stack_traces", daemon_status_file_get_stack_trace_backend());
189
190 sentry_initialized = true;
191 }
192
193 void nd_sentry_fini(void) {
194 if(!sentry_initialized)
195 return;
196
197 sentry_close();
198 }
199
200 void nd_sentry_set_user(const char *guid) {
201 sentry_value_t user = sentry_value_new_object();
202 sentry_value_set_by_key(user, "id", sentry_value_new_string(guid));
203 sentry_set_user(user);
204 }
205
206 // --------------------------------------------------------------------------------------------------------------------
207 // sentry breadcrumbs
208
209 static void nd_sentry_add_key_value_charp(sentry_value_t data, const char *key, const char *value) {
210 if (!value || !*value)
211 return;
212
213 sentry_value_set_by_key(data, key, sentry_value_new_string(value));
214 }
215
216 static void nd_sentry_add_key_value_int64(sentry_value_t data, const char *key, int64_t value) {
217 if (!value)
218 return;
219
220 char buf[UINT64_MAX_LENGTH];
221 print_int64(buf, value);
222
223 sentry_value_set_by_key(data, key, sentry_value_new_string(buf));
224 }
225
226 static void nd_sentry_add_key_value_uint64(sentry_value_t data, const char *key, uint64_t value) {
227 if (!value)
228 return;
229
230 char buf[UINT64_MAX_LENGTH];
231 print_uint64(buf, value);
232
233 sentry_value_set_by_key(data, key, sentry_value_new_string(buf));
234 }
235
236 void nd_sentry_add_breadcrumb(const char *category, const char *message) {
237 if(!sentry_initialized || breadcrumb_added)
238 return;
239
240 nd_sentry_set_tag_status();
241 nd_sentry_set_tag_uptime();
242
243 nd_sentry_set_tag("thread", daemon_status_file_get_fatal_thread());
244 nd_sentry_set_tag_uint64("thread_id", daemon_status_file_get_fatal_thread_id());
245 nd_sentry_set_tag_uint64("worker_job_id", daemon_status_file_get_fatal_worker_job_id());
246
247 const char *function = daemon_status_file_get_fatal_function();
248 if(!function || !*function)
249 function = category;
250
251 // Set the transaction name to the function where the error occurred
252 // this should be low cardinality
253 sentry_set_transaction(function);
254
255 // Set the fingerprint to the function where the error occurred
256 sentry_set_fingerprint("{{ default }}", function, NULL);
257
258 sentry_value_t crumb = sentry_value_new_breadcrumb("fatal", message);
259
260 sentry_value_t data = sentry_value_new_object();
261 nd_sentry_add_key_value_charp(data, "message", daemon_status_file_get_fatal_message());
262 nd_sentry_add_key_value_charp(data, "function", daemon_status_file_get_fatal_function());
263 nd_sentry_add_key_value_charp(data, "filename", daemon_status_file_get_fatal_filename());
264 nd_sentry_add_key_value_charp(data, "thread", daemon_status_file_get_fatal_thread());
265 nd_sentry_add_key_value_uint64(data, "thread_id", daemon_status_file_get_fatal_thread_id());
266 nd_sentry_add_key_value_int64(data, "line", daemon_status_file_get_fatal_line());
267 nd_sentry_add_key_value_charp(data, "errno", daemon_status_file_get_fatal_errno());
268 nd_sentry_add_key_value_charp(data, "stack_trace", daemon_status_file_get_fatal_stack_trace());
269 nd_sentry_add_key_value_charp(data, "status", DAEMON_STATUS_2str(daemon_status_file_get_status()));
270 nd_sentry_add_key_value_uint64(data, "worker_job_id", daemon_status_file_get_fatal_worker_job_id());
271
272 sentry_value_set_by_key(crumb, "data", data);
273 sentry_add_breadcrumb(crumb);
274 }
275
276 void nd_sentry_add_fatal_message_as_breadcrumb(void) {
277 nd_sentry_add_breadcrumb("fatal", "fatal message event details");
278 }
279
280 static void nd_sentry_add_deadly_signal_as_breadcrumb(void) {
281 nd_sentry_add_breadcrumb("deadly_signal", "deadly signal event details");
282 }
283
284 void nd_sentry_add_shutdown_timeout_as_breadcrumb(void) {
285 nd_sentry_add_breadcrumb("shutdown_timeout", "shutdown timeout event details");
286 }