Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to add sentry support? #1855

Closed
gforcada opened this issue Aug 14, 2018 · 11 comments
Closed

How to add sentry support? #1855

gforcada opened this issue Aug 14, 2018 · 11 comments

Comments

@gforcada
Copy link

I use gunicorn to serve a django app where I already have configured sentry to report any error.

By playing with django-silk I made a mistake and introduced an error during the start up of django. If the error happens during a regular HTTP call, I get a traceback on gunicorn but that is not reported by gunicorn.

How should one configure gunicorn to report these errors? 🤔

@gforcada gforcada changed the title How to add sentry? How to add sentry support? Aug 14, 2018
@benoitc
Copy link
Owner

benoitc commented Aug 29, 2018

whay do you mean by " I get a traceback on gunicorn but that is not reported by gunicorn." ?

@tilgovi
Copy link
Collaborator

tilgovi commented Aug 29, 2018

You may want to install Sentry as a logging handler to catch exceptions outside Django.

Here's an example file that could be loaded with the --log-config option of Gunicorn:

# Logging configuration
# =====================

[loggers]
keys = root, gunicorn.access, gunicorn.error, app

[handlers]
keys = access, error, sentry

[formatters]
keys = generic

# Root logger
# -----------
# The root logger sends messages to the console and to Sentry.

[logger_root]
handlers = error, sentry

# Gunicorn loggers
# ----------------
# Gunicorn logging is configured with two loggers: 'gunicorn.access' and 'gunicorn.error'.
#
# The access log is sent to stdout and the error log is sent to stderr, both without propagation.
#
# Only the error logger has a handler to send messages to Sentry.

[logger_gunicorn.access]
handlers = access
qualname = gunicorn.access
level = INFO
propagate = 0

[logger_gunicorn.error]
handlers = error, sentry
qualname = gunicorn.error
level = WARNING
propagate = 0

# Application logger
# ---------------
# This logger is used by application code.

[logger_app]
handlers =
qualname = app
level = INFO

# Handlers
# --------

[handler_access]
class = StreamHandler
formatter = generic
args = (sys.stdout,)

[handler_error]
class = StreamHandler
formatter = generic
args = (sys.stderr,)

[handler_sentry]
class = raven.handlers.logging.SentryHandler
level = WARNING
args = ()

# Formatters
# ----------

[formatter_generic]
format = [%(levelname)s] [%(name)s]: %(message)s

This example doesn't pass a Sentry DSN explicitly from the configuration file, so you'd have to set SENTRY_DSN environment variable. You can also probably pass it directly with a dsn key under the [handler_sentry] section.

See the Sentry documentation for more information on integrating Sentry at the logging level.

@tilgovi
Copy link
Collaborator

tilgovi commented Aug 29, 2018

Keep in mind that I'm not a very experience Django user, so you may need to include other log configuration for Django in there. I do not know how Django configures logging. What I wrote, above, is what I use for a Flask application.

@tilgovi
Copy link
Collaborator

tilgovi commented Aug 29, 2018

Also, you will want this in addition to your Django Sentry integration.

The Django integration will catch application exceptions and then your application or Django can catch those exceptions and render an error page.

Exceptions outside Django, such as during application startup or inside Gunicorn, will hopefully be caught by the logger integration.

If you get duplicate exceptions, you might be logging exceptions from Django. You can always move the Sentry handler from the root to the gunicorn error logger so that it doesn't catch your application errors, but Sentry might also de-duplicate the events anyway.

@gforcada
Copy link
Author

@benoitc sorry that was a typo, it should read:

I get a traceback on gunicorn but that is not reported by sentry

@tilgovi thanks for the tips and links, I will have a look at it later, I missed this docs about sentry integration at logging level

@gforcada
Copy link
Author

gforcada commented Sep 3, 2018

@tilgovi thanks again for the pointers!

At the end though, the way I configured the sentry handler was as follows:

[handler_sentry]
class = raven.handlers.logging.SentryHandler
formatter = generic
level = ERROR
args = ('https://[email protected]/yyy',)

As SentryHandler expects a string as a positional argument when called:

https://github.com/getsentry/raven-python/blob/master/raven/handlers/logging.py#L63-L72

Should this be documented somewhere in gunicorn itself, or rather sent as a documentation enhancement to sentry? 🤔 I would be fine with either way.

@tilgovi
Copy link
Collaborator

tilgovi commented Sep 3, 2018

You're welcome!

It should be possible to just say args = () if you have a SENTRY_DSN environment variable set, if I remember correctly.

Sentry does have the logging documentation that I linked you to. Nothing about this integration is Gunicorn-specific. If you think there's a good place in the docs for it, feel free to open a PR. However, I think people searching for it might come across it here just as easily, so don't feel any need to do so.

I'll go ahead and close this issue, but feel free to reply if you run into any more issues with Gunicorn and Sentry.

@tilgovi tilgovi closed this as completed Sep 3, 2018
@json2d
Copy link

json2d commented Dec 11, 2018

So I'm using the new Unified Python SDK which is to replace the old deprecated client raven.

It exposes the sentry_sdk.integrations.logging.SentryHandler class which is similar but different than raven.handlers.logging.SentryHandler, namely it no longer takes the DSN as an arg.

ref: https://getsentry.github.io/sentry-python/integrations/logging.m.html#sentry_sdk.integrations.logging.EventHandler

Instead it seems to use the DSN arg passed to sentry_sdk.init() wherever its called (in my case within my flask app code), effectively employing some kind of singleton pattern.

# somewhere in my flask app code
from sentry_sdk.integrations.flask import FlaskIntegration

sentry_sdk.init(
    dsn='https://[email protected]/yyy', # will also be used by gunicorn!
    integrations=[FlaskIntegration()]
)

In my log config file for gunicorn, working off @tilgovi example, I have the log handler:

[handler_sentry]
class = sentry_sdk.integrations.logging.SentryHandler
formatter = generic
level = WARNING
args = ()

This works, but I was hoping to use a different DSN just for gunicorn logs, and now I can't figure out how that would be done with this new SentryHandler. 😕

@gforcada
Copy link
Author

@bitstrider I stumbled upon the same problem some days ago 😅

We are using gunicorn to proxy to django, so we added the sentry.init on the django wsgi.py file itself and remove all the sentry specific bits on the log configuration, as sentry is supposed to autoconfigue itself.

@json2d
Copy link

json2d commented Dec 12, 2018

@gforcada ha glad I’m not the only one!

It feels like bad practice to init sentry for gunicorn in django/flask code, but its the path of least resistance at this point.

What might be better but more work would be to run gunicorn from a python script and init sentry there.

ref: http://docs.gunicorn.org/en/stable/custom.html

@berkerpeksag
Copy link
Collaborator

You could probably call sentry.init() inside on_starting() or when_ready() hook: http://docs.gunicorn.org/en/latest/settings.html#server-hooks

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants