# Buggy code
def make_sentry_teller(env):
    if env.sentry_dsn:
        try:
            release = get_version()
            if "-" in release:
                release = None
        except Exception:
            release = None
        sentry = raven.Client(
            env.sentry_dsn,
            environment=env.instance_type,
            release=release,
        )
    else:
        sentry = None
        print("Won't log to Sentry (SENTRY_DSN is empty).")

    def tell_sentry(exception, state, allow_reraise=True):
        if isinstance(exception, pando.Response) and exception.code < 500:
            # Only log server errors
            return

        if isinstance(exception, NeedDatabase):
            # Don't flood Sentry when DB is down
            return

        if isinstance(exception, psycopg2.Error):
            from liberapay.website import website

            if getattr(website, "db", None):
                try:
                    website.db.one("SELECT 1 AS x")
                except psycopg2.Error as e:
                    # If it can't answer this simple query, then it's either
                    # down or unreachable. Show the proper 503 error page.
                    website.db.okay = False
                    state["exception"] = NeedDatabase()
                    if sentry:
                        # Record the exception raised above instead of the
                        # original one, to avoid duplicate issues.
                        return tell_sentry(e, state, allow_reraise=True)

                if "read-only" in str(exception):
                    # DB is in read only mode
                    state["db_is_readonly"] = True
                    # Show the proper 503 error page
                    state["exception"] = NeedDatabase()
                    # Don't reraise this in tests
                    allow_reraise = False

        if isinstance(exception, ValueError):
            if "cannot contain NUL (0x00) characters" in str(exception):
                # https://github.com/liberapay/liberapay.com/issues/675
                response = state.get("response") or pando.Response()
                response.code = 400
                response.body = str(exception)
                return {"exception": None}

        if not sentry:
            # No Sentry, log to stderr instead
            traceback.print_exc()
            # Reraise if allowed
            if env.sentry_reraise and allow_reraise:
                raise
            return {"sentry_ident": None}

        # Prepare context data
        sentry_data = {}
        if state:
            try:
                sentry_data["tags"] = {
                    "lang": getattr(state.get("locale"), "language", None),
                }
                request = state.get("request")
                user_data = sentry_data["user"] = {}
                if request is not None:
                    user_data["ip_address"] = str(request.source)
                    decode = lambda b: b.decode("ascii", "backslashreplace")
                    sentry_data["request"] = {
                        "method": request.method,
                        "url": request.line.uri.decoded,
                        "headers": {
                            decode(k): decode(b", ".join(v))
                            for k, v in request.headers.items()
                            if k != b"Cookie"
                        },
                    }
                user = state.get("user")
                if isinstance(user, Participant):
                    user_data["id"] = getattr(user, "id", None)
                    user_data["username"] = getattr(user, "username", None)
            except Exception as e:
                tell_sentry(e, {})

        # Tell Sentry
        result = sentry.captureException(data=sentry_data)

        # Put the Sentry id in the state for logging, etc
        return {"sentry_ident": sentry.get_ident(result)}

    CustomUndefined._tell_sentry = staticmethod(tell_sentry)

    return {"tell_sentry": tell_sentry}


# Traceback (psycopg2_pool.PoolError)
Traceback (most recent call last):
...
File "env/lib/python3.6/site-packages/postgres/__init__.py", line 451, in get_cursor
return CursorContextManager(self.pool, **kw)
File "env/lib/python3.6/site-packages/postgres/context_managers.py", line 35, in __init__
conn = self.pool.getconn()
File "env/lib/python3.6/site-packages/psycopg2_pool/__init__.py", line 236, in getconn
return super(ThreadSafeConnectionPool, self).getconn()
File "env/lib/python3.6/site-packages/psycopg2_pool/__init__.py", line 120, in getconn
raise PoolError("connection pool exhausted")
psycopg2_pool.PoolError: connection pool exhausted

# Fixed code
def make_sentry_teller(env):
    if env.sentry_dsn:
        try:
            release = get_version()
            if "-" in release:
                release = None
        except Exception:
            release = None
        sentry = raven.Client(
            env.sentry_dsn,
            environment=env.instance_type,
            release=release,
        )
    else:
        sentry = None
        print("Won't log to Sentry (SENTRY_DSN is empty).")

    def tell_sentry(exception, state, allow_reraise=True):
        if isinstance(exception, pando.Response) and exception.code < 500:
            # Only log server errors
            return

        if isinstance(exception, NeedDatabase):
            # Don't flood Sentry when DB is down
            return

        if isinstance(exception, PoolError):
            # If this happens, then the `DATABASE_MAXCONN` value is too low.
            state["exception"] = NeedDatabase()

        if isinstance(exception, psycopg2.Error):
            from liberapay.website import website

            if getattr(website, "db", None):
                try:
                    website.db.one("SELECT 1 AS x")
                except psycopg2.Error as e:
                    # If it can't answer this simple query, then it's either
                    # down or unreachable. Show the proper 503 error page.
                    website.db.okay = False
                    state["exception"] = NeedDatabase()
                    if sentry:
                        # Record the exception raised above instead of the
                        # original one, to avoid duplicate issues.
                        return tell_sentry(e, state, allow_reraise=True)

                if "read-only" in str(exception):
                    # DB is in read only mode
                    state["db_is_readonly"] = True
                    # Show the proper 503 error page
                    state["exception"] = NeedDatabase()
                    # Don't reraise this in tests
                    allow_reraise = False

        if isinstance(exception, ValueError):
            if "cannot contain NUL (0x00) characters" in str(exception):
                # https://github.com/liberapay/liberapay.com/issues/675
                response = state.get("response") or pando.Response()
                response.code = 400
                response.body = str(exception)
                return {"exception": None}

        if not sentry:
            # No Sentry, log to stderr instead
            traceback.print_exc()
            # Reraise if allowed
            if env.sentry_reraise and allow_reraise:
                raise
            return {"sentry_ident": None}

        # Prepare context data
        sentry_data = {}
        if state:
            try:
                sentry_data["tags"] = {
                    "lang": getattr(state.get("locale"), "language", None),
                }
                request = state.get("request")
                user_data = sentry_data["user"] = {}
                if request is not None:
                    user_data["ip_address"] = str(request.source)
                    decode = lambda b: b.decode("ascii", "backslashreplace")
                    sentry_data["request"] = {
                        "method": request.method,
                        "url": request.line.uri.decoded,
                        "headers": {
                            decode(k): decode(b", ".join(v))
                            for k, v in request.headers.items()
                            if k != b"Cookie"
                        },
                    }
                user = state.get("user")
                if isinstance(user, Participant):
                    user_data["id"] = getattr(user, "id", None)
                    user_data["username"] = getattr(user, "username", None)
            except Exception as e:
                tell_sentry(e, {})

        # Tell Sentry
        result = sentry.captureException(data=sentry_data)

        # Put the Sentry id in the state for logging, etc
        return {"sentry_ident": sentry.get_ident(result)}

    CustomUndefined._tell_sentry = staticmethod(tell_sentry)

    return {"tell_sentry": tell_sentry}