Send error mail to admin in Django - django

I am trying to send a mail to ADMIN when any exception is occurred in django. So i tried but not able to find the solution. I don't have a clear idea about how to send a error mail to admin
Any help would be appreciated.
Views.py
def emit(self, record):
try:
request = record.request
subject = '%s (%s IP): %s' % (
record.levelname,
('internal' if request.META.get('REMOTE_ADDR') in settings.INTERNAL_IPS
else 'EXTERNAL'),
record.getMessage()
)
filter = get_exception_reporter_filter(request)
request_repr = '\n{}'.format(force_text(filter.get_request_repr(request)))
except Exception:
subject = '%s: %s' % (
record.levelname,
record.getMessage()
)
request = None
request_repr = "unavailable"
subject = self.format_subject(subject)
if record.exc_info:
exc_info = record.exc_info
else:
exc_info = (None, record.getMessage(), None)
message = "%s\n\nRequest repr(): %s" % (self.format(record), request_repr)
reporter = ExceptionReporter(request, is_email=True, *exc_info)
html_message = reporter.get_traceback_html() if self.include_html else None
self.send_mail(subject, message, fail_silently=True, html_message=html_message)
def send_mail(self, subject, message, *args, **kwargs):
mail.mail_admins(subject, message, *args, connection=self.connection(), **kwargs)
def connection(self):
return get_connection(backend=self.email_backend, fail_silently=True)
def format_subject(self, subject):
"""
Escape CR and LF characters, and limit length.
RFC 2822's hard limit is 998 characters per line. So, minus "Subject: "
the actual subject must be no longer than 989 characters.
"""
formatted_subject = subject.replace('\n', '\\n').replace('\r', '\\r')
return formatted_subject[:989]
def test_view(request):
try:
raise Exception
except Exception as e:
send_mail(self, subject, message, *args, **kwargs)
logger_setting.py
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'filters': {
'require_debug_false': {
'()': 'django.utils.log.RequireDebugFalse',
},
'require_debug_true': {
'()': 'django.utils.log.RequireDebugTrue',
},
},
'handlers': {
'console': {
'level': 'INFO',
'filters': ['require_debug_true'],
'class': 'logging.StreamHandler',
},
'null': {
'class': 'logging.NullHandler',
},
'mail_admins': {
'level': 'ERROR',
'filters': ['require_debug_false'],
'class': 'django.utils.log.AdminEmailHandler'
}
},
'loggers': {
'django': {
'handlers': ['console'],
},
'django.request': {
'handlers': ['mail_admins'],
'level': 'ERROR',
'propagate': False,
},
'django.security': {
'handlers': ['mail_admins'],
'level': 'ERROR',
'propagate': False,
},
'py.warnings': {
'handlers': ['console'],
},
}
}
I also tried by changing my view.py code as following but didn't any error mail
import sys
import traceback
from django.core import mail
from django.views.debug import ExceptionReporter
def send_manually_exception_email(request, e):
exc_info = sys.exc_info()
reporter = ExceptionReporter(request, is_email=True, *exc_info)
subject = e.message.replace('\n', '\\n').replace('\r', '\\r')[:989]
message = "%s\n\n%s" % (
'\n'.join(traceback.format_exception(*exc_info)),
reporter.filter.get_request_repr(request)
)
mail.mail_admins(
subject, message, fail_silently=True,
html_message=reporter.get_traceback_html()
)
Generating a test exception-
def test_view(request):
try:
raise Exception
except Exception as e:
send_manually_exception_email(request, e)

First follow these instructions: https://docs.djangoproject.com/en/dev/howto/error-reporting/#email-reports
and set the settings for EMAIL_HOST, EMAIL_HOST_USER, EMAIL_HOST_PASSWORD, ADMINS, and SERVER_EMAIL.
You'll also need to set an email server. For starters, any of these will do:
an email program (like sendmail)
an email server (like Sendgrid)
the python debugging email server python -m smtpd -n -c DebuggingServer localhost:1025
The values for your settings must match the email server that you choose.
Don't worry about the using email with logging until you get the simple case configured from settings first.

Related

Sending Logs to GCP from Django DRF application

I am trying to access the logs from my django app in GCP logging. I have thus far been unsuccessful.
Here is my logging config:
client = gcp_logging.Client.from_service_account_json(
json_credentials_path='logging_service_account.json')
client.setup_logging()
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'formatters': {
'standard': {'format': '%(levelname)s : %(message)s - [in %(pathname)s:%(lineno)d]'},
'short': {'format': '%(message)s'}
},
'handlers': {
'stackdriver': {
'formatter': 'standard',
'class': 'google.cloud.logging.handlers.CloudLoggingHandler',
'client': client
},
'requestlogs_to_stdout': {
'class': 'logging.StreamHandler',
'filters': ['request_id_context'],
}
},
'filters': {
'request_id_context': {
'()': 'requestlogs.logging.RequestIdContext'
}
},
'loggers': {
'StackDriverHandler': {
'handlers': ['stackdriver'],
'level': "DEBUG"
},
'django.request': {
'handlers': ['stackdriver']
},
'requestlogs': {
'handlers': ['requestlogs_to_stdout'],
'level': 'INFO',
'propagate': False,
},
},
}
I invoke the logs along the lines of:
import logging
logger = logging.getLogger('StackDriverHandler')
class OrganisationDetail(generics.RetrieveUpdateDestroyAPIView):
///
def patch(self, request, pk, format=None):
try:
///
if serializer.is_valid():
serializer.save()
logger.info(f"PATCH SUCCESSFUL: {serializer.data}")
return Response(serializer.data)
logger.warning(f"PATCH Failed: {serializer.errors}")
return JsonResponse(serializer.errors, status=400)
except Exception as e:
logger.error(f"PATCH Failed with exception: {e}")
return JsonResponse({'error': str(e)}, status=500)
In GCP, I set up a service account, enabled logging api and gave the SA write logs and monitor metrics permissions.
I then made a secret to contain my service_account key, and in my cloud-build.yaml file I run a step like this:
- name: gcr.io/cloud-builders/gcloud
entrypoint: 'bash'
args: [ '-c', "gcloud secrets versions access latest --secret=<secret_name> --format='get(payload.data)' | tr '_-' '/+' | base64 -d > logging_service_account.json" ]
The above step should:
Fetch the secret
Write it to a json file in the app instance container that can be accessed by my settings.py file with gcp_logging.Client.from_service_account_json( json_credentials_path='logging_service_account.json')
Perhaps there is a more straight forward way to achieve this, but it feels like it should work. Any help would be much appreciated. Thanks
After all the steps above, when I visit the logging service on my gcp console, I only see the one log under my logging service account that says it is created, none of the logs from my actual django app.

django: How can I create a custom Logging Filter for SuspiciousOperation exception?

After migrate to 1.11 ( from 1.8 ) I'm receiving some SuspiciousOperation errors from logging.
It seems it comes from JS request who keeps session alive if user move their mouse. But this is not important.
How can I filter just this exception?
What I tried:
I just created a filter somewhere:
import logging
from django.core.exceptions import SuspiciousOperation
class StopSuspiciousOperation(logging.Filter):
def filter(self, record):
if record.exc_info:
exc_value = record.exc_info[1]
return isinstance(exc_value, SuspiciousOperation)
return True
Then I added this filter to my configuration:
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'filters': {
'require_debug_false': {
'()': 'django.utils.log.RequireDebugFalse',
},
'stop_suspicious_operation': {
'()': 'aula.utils.loggingFilters.StopSuspiciousOperation',
}
},
'handlers': {
'mail_admins': {
'level': 'ERROR',
'filters': ['require_debug_false',
'stop_suspicious_operation',], #<-- here
'class': 'django.utils.log.AdminEmailHandler'
}
},
'loggers': {
'django.request': {
'handlers': ['mail_admins'],
'level': 'ERROR',
'propagate': True,
},
}
}
But I'm still receiving the error:
Internal Server Error: /keepalive
SuspiciousOperation at /keepalive
The request's session was deleted before the request completed. The user may have logged out in a concurrent request, for example.
Request Method: GET
Request URL: https://XXXXXX/YYYYYY
Django Version: 1.11.9
Python Executable: /usr/bin/python
Python Version: 2.7.3
I am not sure about the correct answer, but I think django is catching the SuspiciousOperation at WSGI level and is logging an ERROR. See:
https://docs.djangoproject.com/en/dev/ref/exceptions/#suspiciousoperation
If a SuspiciousOperation exception reaches the WSGI handler level it
is logged at the Error level and results in a HttpResponseBadRequest.
You maybe just want to filter out the bad requests like this:
from logging import LogRecord
def filter_400(record: LogRecord) -> bool:
'''Filter out HTTP Error Code 400 Bad Request'''
return record.status_code != 400

django logger can't output the response content

I'm use django 1.4.6, I want to use the logger module integrated with django to output the response content, however, I cannot see it in the log file.
Source sample shown here:
import logging
logger = logging.getLogger('__file__')
...
response = redirect(url)
logger.debug(response.content)
return response
Once you have configured your loggers, handlers, filters and formatters,
You need to call it as follows:
import logging
# Standard instance of a logger with __name__
stdlogger = logging.getLogger(__name__)
logger.debug(response.content)
response = redirect(url)
return response
The call to logging.getLogger() obtains (creating, if necessary) an instance of a logger. The logger instance is identified by a name. This name is used to identify the logger for configuration purposes.
By convention, the logger name is usually name .
The Python name syntax used for getLogger automatically assigns the package name as the logger name.
Please show peple configuration of logging in static file
I have little change for your code
logger = logging.getLogger(__name__)
Log configuation on settings.py
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'formatters': {
'verbose': {
'format': "[%(asctime)s] %(levelname)s %(message)s",
'datefmt': "%d/%b/%Y %H:%M:%S"
}
},
'handlers': {
'file': {
'level': 'DEBUG',
'class': 'logging.FileHandler',
'filename': '/var/log/django_practices.log',
'formatter': 'verbose'
},
'console': {
'level': 'DEBUG',
'class': 'logging.StreamHandler',
'stream': sys.stdout,
'formatter': 'verbose'
},
},
'loggers': {
'name_your_app_django': {
'handlers': ['file', 'console'],
'level': ode'DEBUG',
}
}
}
In my configuration log will be print in console and file name.
Note : name_your_app_django change to fix with your code.

Why are assertions being logged in sentry when DEBUG = True?

I'm in the middle of deploying sentry to handle our django error messages. I've configured django's LOGGING settings to only log when DEBUG = False via the use of 'filters': ['require_debug_false'].
If I manually log an error in a django view as in the following example, it is successfully filtered and therefore not sent to sentry:
import logging
logger = logging.getLogger(__name__)
def view_name(request):
logger.error('An error message')
...
However, if I use an assert statement as in the following example, it is not filtered and does get sent to sentry:
import logging
logger = logging.getLogger(__name__)
def view_name(request):
assert False, 'An error message'
...
It is also worth noting that the assert statement does not get sent to the mail_admins handler, which also uses the same filter.
Can someone please help me prevent assert errors from begin sent to sentry whilst DEBUG = True?
Here are the package versions I am using:
Django==1.6.7
raven==5.1.1
And here are the relevant parts of my settings.py:
DEBUG = True
LOGGING = {
'version': 1,
'disable_existing_loggers': True,
'filters': {
'require_debug_false': {
'()': 'django.utils.log.RequireDebugFalse'
}
},
'formatters': {
'simple': {
'format': '%(levelname)s %(asctime)s %(message)s'
},
},
'handlers': {
'console': {
'level': 'WARNING',
'filters': ['require_debug_false'],
'class': 'logging.StreamHandler',
'formatter': 'simple'
},
'mail_admins': {
'level': 'ERROR',
'filters': ['require_debug_false'],
'class': 'django.utils.log.AdminEmailHandler',
'include_html': True,
},
'sentry': {
'level': 'WARNING',
'filters': ['require_debug_false'],
'class': 'raven.contrib.django.raven_compat.handlers.SentryHandler',
},
},
'loggers': {
'': {
'handlers': ['console', 'mail_admins', 'sentry'],
'level': 'WARNING',
'propagate': False,
},
},
}
In raven-python 3.0.0 DEBUG setting in Django no longer disables Raven.
From documentation:
Raven to install a hook in Django that will automatically report
uncaught exceptions
In your case, assert generate uncaught exceptions AssertionError, which logged in Sentry.
To disable this functionality set dsn = None or remove dsn (source):
RAVEN_CONFIG = {
'dsn': None
}

Manually fire Django 1.3's traceback/exception log

I'm using djutils's async decorator, which has the nasty side effect of not sending traceback emails when an exception is raised, since it runs on a separate thread.
It does, however, have the following place to put a logger.
def worker_thread():
while 1:
func, args, kwargs = queue.get()
try:
func(*args, **kwargs)
except:
pass # <-- log error here
finally:
queue.task_done()
I've confirmed this will work, but even with the try/except removed, it won't trip Django's traceback logger.
While it'd be pretty easy to tell it to write to a db/file on exception, I'd really like it to send a regular traceback as defined in settings. How can I do that?
Edit: answer seems to involve django.utils.log.AdminEmailHandler - but I'm having a hard time finding an example.
Edit 2: Here's my current (99% likely to be wrong) attempt.
from django.utils.log import AdminEmailHandler
def worker_thread():
while 1:
func, args, kwargs = queue.get()
try:
func(*args, **kwargs)
except:
import logging
from django.conf import settings
print settings.EMAIL_HOST
logger = logging.getLogger("async.logger")
logger.exception("Async exploded")
AdminEmailHandler
pass # <-- log error here
finally:
queue.task_done()
first, configure your logging settings i settings.py:
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'handlers': {
'mail_admins': {
'level': 'ERROR',
'class': 'django.utils.log.AdminEmailHandler'
}
},
'loggers': {
'myproject': {
'handlers': ['mail_admins'],
'level': 'INFO',
'propagate': True,
},
'django.request': {
'handlers': ['mail_admins'],
'level': 'ERROR',
'propagate': True,
},
}
}
from now, all loggers which starts with 'myproject' should use AdminEmailHandler
your code should look like this:
import logging
logger = logging.getLogger('myproject.optional.path')
# example
# logger = logging.getLogger('myprojects.myapp.views')
def worker_thread():
while 1:
func, args, kwargs = queue.get()
try:
func(*args, **kwargs)
except:
logger.exception("Async exploded")
finally:
queue.task_done()