@@ -97,6 +97,16 @@ if Code.ensure_loaded?(Plug) do
9797 which Plug.RequestId (and therefore Phoenix) also default to.
9898
9999 use Sentry.Plug, request_id_header: "application-request-id"
100+
101+ ### Collect User Feedback after Error
102+
103+ Sentry allows collecting user feedback after they hit an error.
104+ Information about it is available [here](https://docs.sentry.io/enriching-error-data/user-feedback).
105+ If a Plug request experiences an error that Sentry is reporting, and the request
106+ accepts the content-type "text/html" or "*/*", the feedback form will be rendered.
107+ The configuration is limited to the defaults at the moment, but it can be enabled with:
108+
109+ use Sentry.Plug, collect_feedback: [enabled: true]
100110 """
101111
102112 @ default_plug_request_id_header "x-request-id"
@@ -108,6 +118,8 @@ if Code.ensure_loaded?(Plug) do
108118 cookie_scrubber = Keyword . get ( env , :cookie_scrubber , { __MODULE__ , :default_cookie_scrubber } )
109119
110120 request_id_header = Keyword . get ( env , :request_id_header )
121+ collect_feedback = Keyword . get ( env , :collect_feedback , [ ] )
122+ collect_feedback_enabled = Keyword . get ( collect_feedback , :enabled , false )
111123
112124 quote do
113125 # Ignore 404s for Plug routes
@@ -130,18 +142,63 @@ if Code.ensure_loaded?(Plug) do
130142 request_id_header: unquote ( request_id_header )
131143 ]
132144
145+ collect_feedback_enabled = unquote ( collect_feedback_enabled )
133146 request = Sentry.Plug . build_request_interface_data ( conn , opts )
134147 exception = Exception . normalize ( kind , reason , stack )
135148
136- Sentry . capture_exception (
137- exception ,
138- stacktrace: stack ,
139- request: request ,
140- event_source: :plug ,
141- error_type: kind
142- )
149+ accept_html =
150+ Plug.Conn . get_req_header ( conn , "accept" )
151+ |> Enum . any? ( fn header ->
152+ String . split ( header , "," )
153+ |> Enum . any? ( & ( & 1 == "text/html" || & 1 == "*/*" ) )
154+ end )
155+
156+ if accept_html && collect_feedback_enabled do
157+ result =
158+ Sentry . capture_exception (
159+ exception ,
160+ stacktrace: stack ,
161+ request: request ,
162+ event_source: :plug ,
163+ error_type: kind ,
164+ result: :sync
165+ )
166+
167+ render_sentry_feedback ( conn , result )
168+ else
169+ Sentry . capture_exception (
170+ exception ,
171+ stacktrace: stack ,
172+ request: request ,
173+ event_source: :plug ,
174+ error_type: kind
175+ )
176+ end
143177 end
144178
179+ defp render_sentry_feedback ( conn , { :ok , id } ) do
180+ html = """
181+ <!DOCTYPE HTML>
182+ <html lang="en">
183+ <head>
184+ <meta charset="utf-8">
185+ <script src="https://browser.sentry-cdn.com/5.9.1/bundle.min.js" integrity="sha384-/x1aHz0nKRd6zVUazsV6CbQvjJvr6zQL2CHbQZf3yoLkezyEtZUpqUNnOLW9Nt3v" crossorigin="anonymous"></script>
186+ <script>
187+ Sentry.init({ dsn: '#{ Sentry.Config . dsn ( ) } ' });
188+ Sentry.showReportDialog({ eventId: '#{ id } ' })
189+ </script>
190+ </head>
191+ <body>
192+ </body>
193+ </html>
194+ """
195+
196+ Plug.Conn . put_resp_header ( conn , "content-type" , "text/html" )
197+ |> Plug.Conn . send_resp ( conn . status , html )
198+ end
199+
200+ defp render_sentry_feedback ( _conn , _result ) , do: nil
201+
145202 defoverridable handle_errors: 2
146203 end
147204 end
0 commit comments