diff --git a/saas-modules/pom.xml b/saas-modules/pom.xml
index 7a626e7663..7e8adebdd9 100644
--- a/saas-modules/pom.xml
+++ b/saas-modules/pom.xml
@@ -20,6 +20,7 @@
stripe
twilio
twitter4j
+ sentry-servlet
diff --git a/saas-modules/sentry-servlet/pom.xml b/saas-modules/sentry-servlet/pom.xml
new file mode 100644
index 0000000000..c86fcbce03
--- /dev/null
+++ b/saas-modules/sentry-servlet/pom.xml
@@ -0,0 +1,49 @@
+
+
+ 4.0.0
+
+ com.baeldung
+ saas-modules
+ 1.0.0-SNAPSHOT
+
+ sentry-servlet
+ sentry-servlet
+ war
+
+
+ 6.11.0
+ 1.10.4
+
+
+
+
+ io.sentry
+ sentry-servlet
+ ${sentry.version}
+
+
+
+ javax.servlet
+ javax.servlet-api
+ provided
+
+
+
+
+
+
+ org.codehaus.cargo
+ cargo-maven3-plugin
+ ${cargo.version}
+
+
+ tomcat9x
+ embedded
+
+
+
+
+
+
\ No newline at end of file
diff --git a/saas-modules/sentry-servlet/src/main/java/com/baeldung/sentry/servlet/FaultyServlet.java b/saas-modules/sentry-servlet/src/main/java/com/baeldung/sentry/servlet/FaultyServlet.java
new file mode 100644
index 0000000000..7a32094221
--- /dev/null
+++ b/saas-modules/sentry-servlet/src/main/java/com/baeldung/sentry/servlet/FaultyServlet.java
@@ -0,0 +1,32 @@
+package com.baeldung.sentry.servlet;
+
+import java.io.IOException;
+
+import javax.servlet.ServletException;
+import javax.servlet.annotation.WebServlet;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+@WebServlet(urlPatterns = "/fault", loadOnStartup = 1)
+public class FaultyServlet extends HttpServlet {
+
+ private static final long serialVersionUID = 1L;
+
+ @Override
+ protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
+
+ String op = req.getParameter("op");
+ if( "fault".equals(op) ) {
+ resp.sendError(500, "Something bad happened !");
+ }
+ else if ( "exception".equals(op) ) {
+ throw new IllegalArgumentException("Internal error");
+ }
+ else {
+ resp.setStatus(200);
+ resp.setContentType("text/plain");
+ resp.getWriter().println("OK");
+ }
+ }
+}
diff --git a/saas-modules/sentry-servlet/src/main/java/com/baeldung/sentry/support/SentryContextListener.java b/saas-modules/sentry-servlet/src/main/java/com/baeldung/sentry/support/SentryContextListener.java
new file mode 100644
index 0000000000..6f25dd36aa
--- /dev/null
+++ b/saas-modules/sentry-servlet/src/main/java/com/baeldung/sentry/support/SentryContextListener.java
@@ -0,0 +1,39 @@
+package com.baeldung.sentry.support;
+
+import javax.servlet.ServletContext;
+import javax.servlet.ServletContextEvent;
+import javax.servlet.ServletContextListener;
+import javax.servlet.annotation.WebListener;
+
+import io.sentry.Sentry;
+import io.sentry.SentryLevel;
+
+@WebListener
+public class SentryContextListener implements ServletContextListener {
+ @Override
+ public void contextInitialized(ServletContextEvent sce) {
+
+ // Besides standard supported locations, let's also allow the DSN to be
+ // passed using servlet container managed parameters. This can be useful if your app
+ // is hosted in a shared application server.
+ ServletContext context = sce.getServletContext();
+ String sentryDsn = context.getInitParameter("sentry.dsn");
+
+ if ( sentryDsn != null ) {
+ context.log("[I21] sentry.dsn init parameter found. Configuring Sentry SDK...");
+ Sentry.init(sentryDsn);
+ }
+ else {
+ context.log("[I25] sentry.dsn init parameter not found. Configuring Sentry SDK with defaults");
+ Sentry.init();
+ }
+
+ }
+
+ @Override
+ public void contextDestroyed(ServletContextEvent sce) {
+ sce.getServletContext().log("[I34] shutting down context");
+ Sentry.captureMessage("[I35] contextDestroyed", SentryLevel.INFO);
+ Sentry.close();
+ }
+}
diff --git a/saas-modules/sentry-servlet/src/main/java/com/baeldung/sentry/support/SentryFilter.java b/saas-modules/sentry-servlet/src/main/java/com/baeldung/sentry/support/SentryFilter.java
new file mode 100644
index 0000000000..e853368ad9
--- /dev/null
+++ b/saas-modules/sentry-servlet/src/main/java/com/baeldung/sentry/support/SentryFilter.java
@@ -0,0 +1,32 @@
+package com.baeldung.sentry.support;
+
+import java.io.IOException;
+
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.annotation.WebFilter;
+import javax.servlet.http.HttpServletResponse;
+
+import io.sentry.Sentry;
+import io.sentry.SentryLevel;
+
+@WebFilter(urlPatterns = "/*")
+public class SentryFilter implements Filter {
+
+ @Override
+ public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
+ try {
+ chain.doFilter(request, response);
+ int rc = ((HttpServletResponse) response).getStatus();
+ if (rc/100 == 5) {
+ Sentry.captureMessage("Application error: code=" + rc, SentryLevel.ERROR);
+ }
+ } catch (Throwable t) {
+ Sentry.captureException(t);
+ throw t;
+ }
+ }
+}
diff --git a/saas-modules/sentry-servlet/src/main/resources/sentry.properties b/saas-modules/sentry-servlet/src/main/resources/sentry.properties
new file mode 100644
index 0000000000..c937874420
--- /dev/null
+++ b/saas-modules/sentry-servlet/src/main/resources/sentry.properties
@@ -0,0 +1,3 @@
+# Sentry configuration file
+# put your DSN here
+dsn=https://xxxxxxxxxxxxxxxx@zzzzzzz.ingest.sentry.io/wwww
\ No newline at end of file
diff --git a/saas-modules/sentry-servlet/src/main/webapp/WEB-INF/web.xml b/saas-modules/sentry-servlet/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 0000000000..e89de5f352
--- /dev/null
+++ b/saas-modules/sentry-servlet/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/saas-modules/sentry-servlet/src/test/java/com/baeldung/sentry/servlet/FaultyServletLiveTest.java b/saas-modules/sentry-servlet/src/test/java/com/baeldung/sentry/servlet/FaultyServletLiveTest.java
new file mode 100644
index 0000000000..3db0d1c66e
--- /dev/null
+++ b/saas-modules/sentry-servlet/src/test/java/com/baeldung/sentry/servlet/FaultyServletLiveTest.java
@@ -0,0 +1,46 @@
+package com.baeldung.sentry.servlet;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import java.net.HttpURLConnection;
+import java.net.URL;
+
+import org.junit.jupiter.api.Test;
+
+class FaultyServletLiveTest {
+
+
+ @Test
+ void testGivenFaultyRequestWithNoQueryString_thenSuccess() throws Exception {
+
+ //int port = getServerPort();
+ URL u = new URL("http://localhost:8080/sentry-servlet/fault");
+ HttpURLConnection conn = (HttpURLConnection)u.openConnection();
+ int rc = conn.getResponseCode();
+ assertThat(rc)
+ .isEqualTo(200);
+ }
+
+ @Test
+ void testGivenFaultyRequestWithFaultString_thenFail() throws Exception {
+
+ //int port = getServerPort();
+ URL u = new URL("http://localhost:8080/sentry-servlet/fault?fault=true");
+ HttpURLConnection conn = (HttpURLConnection)u.openConnection();
+ int rc = conn.getResponseCode();
+ assertThat(rc)
+ .isEqualTo(500);
+ }
+
+ @Test
+ void testGivenFaultyRequestWithExceptionString_thenFail() throws Exception {
+
+ //int port = getServerPort();
+ URL u = new URL("http://localhost:8080/sentry-servlet/fault?exception=true");
+ HttpURLConnection conn = (HttpURLConnection)u.openConnection();
+ int rc = conn.getResponseCode();
+ assertThat(rc)
+ .isEqualTo(500);
+ }
+
+}