Google Cloud Platform

Reading and Writing Application Logs

Python |Java |PHP |Go

  1. Overview
  2. Request logs vs application logs
  3. Writing application logs
  4. Reading logs via API
  5. Log URL formats in App Engine and developer consoles
  6. Reading logs in the console
  7. Understanding request log fields
  8. Quotas and limits
  9. The development server and Logs API


When a request is sent to your app, a request log is automatically written by App Engine. During the handling of the request, your app can also write application logs. In this page, we'll show you how to write application logs from your application, how to read both application and request logs programmatically using the Logs API, how to view logging in the Google Cloud Platform Console, and how to understand the request log data that App Engine writes during the request.

Important: Be aware that logs by default do not persist when you use the development app server. For information on how to make logs persist, see The development server and Logs API.

Request logs vs application logs

There are two categories of log data: request logs and application logs. A request log is automatically written by App Engine for each request handled by your app, and contains information such as the app ID, HTTP version, and so forth. For a complete list of available properties for request logs, see RequestLog. See also the request log table for descriptions of the request log fields.

Each request log contains a list of application logs (AppLog) associated with that request, returned in the RequestLog.app_logs property. Each app log contains the time the log was written, the log message, and the log level.

Writing application logs

You may find it useful to read the documentation for the standard Python logging module at

The Python logging module allows a developer to log 5 levels of severity:

  • Debug
  • Info
  • Warning
  • Error
  • Critical

The following example shows how to use some of these levels to log events in a simple Guestbook application:

import logging
import os
import datetime
import wsgiref.handlers

from google.appengine.api import users
from google.appengine.ext import db
from google.appengine.ext import webapp
from google.appengine.ext.webapp import template

class Post(db.Model):
    author = db.StringProperty()
    content = db.StringProperty()
    date = db.DateTimeProperty()

class MainPage(webapp.RequestHandler):

  def get(self):
      # Retrieve existing posts from the datastore when getting the Main Page
          post_query = db.GqlQuery('SELECT * FROM Post ORDER BY date DESC')
          posts = [post for post in post_query]
          logging.error('There was an error retrieving posts from the datastore')

      template_values = {
          'posts': posts,
      path = os.path.join(os.path.dirname(__file__), 'index.html')
      self.response.out.write(template.render(path, template_values))

class Guestbook(webapp.RequestHandler):
    # The user has posted a comment to the guest book
    def post(self):
        logging.debug('Start guestbook signing request')

        post = Post()

        if users.get_current_user():
  'Signed by user %s', users.get_current_user().nickname())
   = users.get_current_user().user_id()
  'Signed by anonymous user')

        post.content = self.request.get('content') =

                logging.error('There was an error saving comment %s', self.request.get('content'))

        logging.debug('Finish guestbook signing')

application = webapp.WSGIApplication([('/', MainPage),
                                      ('/sign', Guestbook)],

def main():
    # Set the logging level in the main function
    # See the section on <a href="/appengine/docs/python/#Python_App_caching">Requests and App Caching</a> for information on how
    # App Engine reuses your request handlers when you specify a main function

if __name__ == '__main__':

In our example, we call debug() to provide information about the requests coming into our handler. We call info() to provide information about the users of our application, and we call error() to tell us where our application is experiencing errors.

Later, we can use this information to filter out only the actions we are interested in. This will help us troubleshoot our code, improve the user experience, or add additional features to our application.

Reading logs via API

The general process of getting logs using the Logs API is as follows:

  1. Use fetch() to return an iterator for the request logs.
  2. In each iteration, process each RequestLog as desired.
  3. Optionally, use RequestLog.app_logs to get the list of related AppLogs.
  4. If you retrieved the app logs list, for each AppLogLine, process the AppLog property data as desired.

Sample code

The following sample displays 5 request logs at at time, along with their application logs. It lets you cycle through each set of 10 logs using a Next link.

import base64
import datetime
import logging
import time
import urllib
import webapp2
from google.appengine.api.logservice import logservice

# This sample gets the app request logs up to the current time, displays 5 logs
# at a time, including all AppLogs, with a Next link to let the user "page"
# through the results, using the RequestLog offset property.

class MainPage(webapp2.RequestHandler):

    def get(self):'Starting Main handler')
        # Get the incoming offset param from the Next link to advance through
        # the logs. (The first time the page is loaded, there won't be any offset.)
            offset = self.request.get('offset') or None
            if offset:
                offset = base64.urlsafe_b64decode(str(offset))
        except TypeError:
            offset = None

        # Set up end time for our query.
        end_time = time.time()

        # Count specifies the max number of RequestLogs shown at one time.
        # Use a boolean to initially turn off visiblity of the "Next" link.
        count = 5
        show_next = False
        last_offset = None

        # Iterate through all the RequestLog objects, displaying some fields and
        # iterate through all AppLogs belonging to each RequestLog count times.
        # In each iteration, save the offset to last_offset; the last one when
        # count is reached will be used for the link.
        i = 0
        for req_log in logservice.fetch(end_time=end_time, offset=offset,
            self.response.out.write('<br /> REQUEST LOG <br />')
                'IP: %s <br /> Method: %s <br /> Resource: %s <br />' %
                (req_log.ip, req_log.method, req_log.resource))
                'Date: %s<br />' %
                datetime.datetime.fromtimestamp(req_log.end_time).strftime('%D %T UTC'))

            last_offset= req_log.offset
            i += 1

            for app_log in req_log.app_logs:
                self.response.out.write('<br />APP LOG<br />')
                    'Date: %s<br />' %
                    datetime.datetime.fromtimestamp(app_log.time).strftime('%D %T UTC'))
                self.response.out.write('<br />Message: %s<br />' % app_log.message)

            if i >= count:
                show_next = True

        # Prepare the offset URL parameters, if any.
        if show_next:
            query = self.request.GET
            query['offset'] = base64.urlsafe_b64encode(last_offset)
            next_link = urllib.urlencode(query)
            self.response.out.write('<a href="/?%s">Next</a>' % next_link)

app = webapp2.WSGIApplication([('/', MainPage)], debug=True)

In the sample, notice that the GET handler expects to be re-invoked by the user clicking on the Next link, and so it extracts the offset param, if present. That offset is used in the subsequent re-invocation of logservice.fetch() to "page through" each group of 5 request logs. There is nothing special about the number 5; it can be anything you want.

Log URL format in the Google Cloud Platform Console

See the following sample URL for an example of the log URL format in the Cloud Platform Console:

Reading logs in the console

To view logs using the Log Viewer:

  1. In the Cloud Platform Console, go to the logs for your project.

  2. Use the desired filter to retrieve the logs you want to see. You can filter by various combinations of time, log level, module, and log filter label or regular expression.

    Notice that labels are regular expressions for filtering the logs by logging fields. Valid labels include the following:

    • day
    • month
    • year
    • hour
    • minute
    • second
    • tzone
    • remotehost
    • identd_user
    • user
    • status
    • bytes
    • referrer
    • useragent
    • method
    • path
    • querystring
    • protocol
    • request_id

    For example, path:/foo.* useragent:.*Chrome.* gets logs for all requests to a path starting with /foo that were issued from a Chrome browser.

A typical App Engine log contains data in the Apache combined log format, along with some special App Engine fields, as shown in the following sample log: - test [27/Jun/2014:09:11:47 -0700] "GET / HTTP/1.1" 200 414
Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/35.0.1916.153 Safari/537.36"
"" ms=195 cpu_ms=42 cpm_usd=0.000046
loading_request=1 instance=00c61b117cfeb66f973d7df1b7f4ae1f064d app_engine_release=1.9.32

Understanding request log fields

The following table lists the fields in order of occurrence along with a description:

Field Order Field Name Always Present? Description
1 Client address Yes Client IP address. Example:
2 RFC1413 identity No RFC1413 identity of the client. This is nearly always the character -
3 User No Present only if the app uses the Users API and the user is logged in. This value is the "nickname" portion of the Google Account, for example, if the Google Account is, the nickname that is logged in this field is test.
4 Timestamp Yes Request timestamp. Example: [27/Jun/2014:09:11:47 -0700]
5 Request querystring Yes First line of the request, containing method, path, and HTTP version. Example: GET / HTTP/1.1
6 HTTP Status Code Yes Returned HTTP status code. Example: 200
7 Response size Yes Response size in bytes. Example: 414
8 Referrer path No If there is no referrer, the log contains no path, but only -. Example referrer path: "".
9 User-agent Yes Identifies the browser and operating system to the web server. Example: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/35.0.1916.153 Safari/537.36
10 Hostname Yes The hostname used by the client to connect to the App Engine application. Example : (
11 Wallclock time Yes Total clock time in milliseconds spent by App Engine on the request. This time duration does not include time spent between the client and the server running the instance of your application. Example: ms=195.
12 CPU milliseconds Yes CPU milliseconds required to fulfill the request. This is the number of milliseconds spent by the CPU actually executing your application code, expressed in terms of a baseline 1.2 GHz Intel x86 CPU. If the CPU actually used is faster than the baseline, the CPU milliseconds can be larger than the actual clock time defined above. Example: cpu_ms=42
13 Exit code No Only present if the instance shut down after getting the request. In the format exit_code=XXX where XXX is a 3 digit number corresponding to the reason the instance shut down. The exit codes are not documented since they are primarily intended to help Google spot and fix issues.
14 Estimated cost Yes DEPRECATED. Estimated cost of 1000 requests just like this one, in USD. Example: cpm_usd=0.000046
15 Queue name No The name of the task queue used. Only present if request used a task queue. Example: queue_name=default
16 Task name No The name of the task executed in the task queue for this request. Only present if the request resulted in the queuing of a task. Example: task_name=7287390692361099748
17 Pending queue No Only present if a request spent some time in a pending queue. If there are many of these in your logs and/or the values are high, it might be an indication that you need more instances to serve your traffic. Example: pending_ms=195
18 Loading request No Only present if the request is a loading request. This means an instance had to be started up. Ideally, your instances should be up and healthy for as long as possible, serving large numbers of requests before being recycled and needing to be started again. Which means you shouldn't see too many of these in your logs. Example: loading_request=1.
19 Instance Yes Unique identifier for the instance that handles the request. Example: instance=00c61b117cfeb66f973d7df1b7f4ae1f064d
20 Version Yes The current App Engine release version used in production App Engine: 1.9.32

Quotas and limits

Your application is affected by the following logs-related quotas:

  • Logs data retrieved via the Logs API.
  • Log storage, also called logs retention.

Quota for data retrieved

The first 100 megabytes of logs data retrieved per day via the Logs API calls are free. After this amount is exceeded, no further Logs API calls will succeed unless billing is enabled for your app. If billing is enabled for your app, data in excess of 100 megabytes results in charges of $0.12/GB.

Logs storage

You can control how much log data your application stores by means of its log retention settings in the Google Cloud Platform Console. By default, logs are stored for an application free of charge with the following per-application limits: a maximum of 1 gigabyte for a maximum of up to 90 days. If either limit is exceeded, more recent logs will be shown and older logs will be deleted to stay within the size limit. Logs older than the maximum retention time are also deleted.

If your app has billing enabled, you can pay for higher log size limits by specifying the desired maximum log size in gigabytes in the Cloud Platform Console. You can also set the retention time by specifying the desired number of days to keep logs, up to a maximum of 365 days. The cost of this extra log storage is $0.026 per gigabyte utilized per month.

Limit Amount Cost past free threshold
Maximum days storage per log 90 days free, 365 days if paid $0.026 per gigabyte utilized per month
Maximum total logs storage 1 gigabyte free, unlimited if paid $0.026 per gigabyte utilized per month

The development server and Logs API

By default, logs are stored in memory only in the development server and are accessible if you wish to test the Logs API feature. If you wish to persist logs from the development server to disk at the default location /tmp/dev_appserver.logs, supply the --persist_logs command line option as follows: --persist_logs your-app-directory

If you wish to persist the logs from the development server to disk at a location of your own choosing, supply the desired path and filename to the --logs_path command line option as follows: --logs_path=your-path/your-logfile-name your-app-directory