I'm having troubles getting the Google Stackdriver Error Reporting to work using an App Engine Flexible Environment w/ Python 2.7
The documentation says a flexible environment requires manual configuration: https://cloud.google.com/error-reporting/docs/setting-up-on-app-engine
By default with the python runtime, it looks like google-fluentd is installed because ps ax | grep fluentd returns what the docs suggest. However, doing a sudo service google-fluentd restart fails.
I have fluent-logger==0.4.1 in my requirements.txt file.
I switched to a custom runtime to be able to place the forward.conf file in /etc/google-fluentd/config.d where the docs suggest.
My Docker file looks like this:
FROM gcr.io/google_appengine/python
RUN virtualenv /env -p python2.7
# stackdriver logging (for error reporting)
RUN mkdir -p /etc/google-fluentd/config.d
ADD forward.conf /etc/google-fluentd/config.d/
# Set virtualenv environment variables. This is equivalent to running
# source /env/bin/activate
ENV VIRTUAL_ENV /env
ENV PATH /env/bin:$PATH
ADD requirements.txt /app/
RUN pip install --upgrade pip
RUN pip install -r requirements.txt
ADD . /app/
RUN python manage.py collectstatic --noinput
RUN python manage.py migrate --noinput
CMD gunicorn -b :$PORT project_name.wsgi
and forward.conf looks like:
<source>
type forward
port 24224
</source>
In my app I have a view that should report the error, but nothing comes through:
from django.http import HttpResponse
from django.views.generic import View
from fluent import sender
from fluent import event
import traceback
sender.setup('myapp', host='localhost', port=24224)
def report(ex):
data = {}
data['message'] = '{0}'.format(ex)
data['serviceContext'] = {'service' : 'myapp'}
# ... add more metadata
event.Event('errors', data)
class ErrorView(View):
def get(self, request, *args, **kwargs):
# report exception data using:
try:
Exception("Woops.. an Error Occurred")
except Exception as e:
report(traceback.format_exc())
raise e
Is there something I'm missing with the setup? The web server access logs come through just fine, but not exceptions or writing out to stderr or stdout
Thanks to @Steren's answer... it is much simpler than I made it out to be with Django in the Python Flexible Environment. There is no need for a custom runtime and installing fluentd. Below is a working sample that outputs a file log to /var/log/app_engine/custom_logs/error.log and a log formatter to put the log in the appropriate format.
LOGGING Settings:
'formatters': {
'gcp_json': {
'()': 'helloworld.gcp_logger.GCPJsonFormatter',
},
},
'handlers': {
'file': {
'level': 'DEBUG',
'class': 'logging.FileHandler',
'filename': '/var/log/app_engine/custom_logs/errors.json',
'formatter': 'gcp_json',
},
},
'loggers': {
'django.request': {
'filters': ['require_debug_false'],
'handlers': ['file'],
'level': 'ERROR',
'propagate': True,
},
},
and the formatter:
import logging
import json
import os
class GCPJsonFormatter(logging.Formatter):
def format(self, record):
# https://cloud.google.com/error-reporting/docs/formatting-error-messages
return json.dumps({
'eventTime': record.created,
'message': self.formatException(record.exc_info),
'level': record.levelname,
'serviceContext': {
'service': os.environ.get('GAE_MODULE_NAME', ''),
'version': os.environ.get('GAE_MODULE_VERSION', ''),
},
"context": {
"httpRequest": self._get_request_info(record),
'user': str(record.request.user) if record.request else "",
"reportLocation": {
"filePath": record.pathname,
"lineNumber": record.lineno,
"functionName": record.funcName,
},
}
})
def _get_request_info(self, record):
try:
request = record.request
return {
"method": request.method,
"url": request.get_full_path(),
"userAgent": request.META.get("HTTP_USER_AGENT", ""),
"referrer": request.META.get("HTTP_REFERER", ""),
"responseStatusCode": record.status_code,
"remoteIp": request.META.get("REMOTE_ADDR", "")
}
except Exception:
return {}
There is a way to configure the App Engine flexible runtimes without requiring a custom runtime, it will soon be documented on the official documentation, and we are also working to make these steps simpler:
You should use similar code as the one detailed in the Google Compute Engine samples to send exception data as structured logs into a file named /var/log/app_engine/custom_logs/errors.json (do not send them via a TCP port)
It is important that:
/var/log/app_engine/custom_logs/.jsonerr in the name.You can then confirm in Stackdriver Logging that you see the errors appearing as structPayload in a custom log stream:
Pick App Engine in the first dropdown, and then custom.var.lop.app_engine.app.custom_logs.errors.json.
If this is the case, then Error Reporting should automatically starts processing these errors.
Let me know if it is not the case.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With