Fork me on GitHub
>

Chrome Logger

Technical Specification

Overview

Chrome Logger uses an HTTP header to send log data from your application server to the web browser. The Google Chrome extension decodes the data and outputs it to the Chrome Javascript Console.

This page is a guide to explain how to implement your own server side library in whatever language you choose.

Maximum Data Size

Chrome has a limit of 250kb across all headers for a single request so that is the maximum amount of encoded data you can send.

Data Format

Initial Data

The log data starts out on the server as a dictionary that looks like this

            {
    "version": "0.2",
    "columns": ["log", "backtrace", "type"],
    "rows": []
}
        

The version parameter should be the version of your server side library. Eventually there will be a way to pass in an identifier for your library so the extension can notify users when there is a new version of your library available.

Adding Logs

Every time something is logged you should add a new entry to the rows array. Each entry in the rows array contains 3 parts: log data, backtrace data, and the log type.

Log Data

Let's say for example we have a User object that looks like this:

            class User(object):
    def __init__(self, name, occupation):
        self.name = name
        self.occupation = occupation
        

If we are to log that user object

import chromelogger as console

console.log(User('Craig', 'NFL Player'))

We need to convert the data into a key/value dictionary representing the object being logged. Essentially it needs to be in a format that can be converted to JSON.

object_data = {
    '___class_name': 'User',
    'name': 'Craig',
    'occupation': 'NFL Player'
}

# log data is an array of all arguments passed into console.log
log_data = [object_data]

If properties are other objects then they also need to be converted into this format.

Note the special property ___class_name. This should be added to all objects that respresent a specific class. This allows the extension to log the class name separately when it logs your data to the console. Also for nested objects it makes it easy to see what type they are since it will float to the top of the properties when they are listed alphabetically.

Any log call can take an infinite number of arguments so the log data should be an array of all arguments converted to the proper data structure. For example this

console.log('Some Label', 123)

Would become

log_data = ['Some Label', 123]
Backtrace Data

Backtrace data is information about the filename and line number where this call originated from. It is the second piece of information needed to create a row.

This should be in the format

backtrace = '/path/to/file.py : 25'

Note that if you are logging in a loop then multiple log entries will end up coming from the same line. In this case you only want to send over the backtrace data for the first log that occured on the given line. This means you should keep a list of all backtraces and only add a new one if it is not in that list for the given request. Otherwise you should send back null.

Log Type

The last piece of information you need for logging a piece of data is what type of log it is. The current supported types are log, warn, error, info, group, groupEnd, groupCollapsed, and table.

In order to cut down on duplicate data it is recommended to pass in an empty string '' instead of log. The extension will default to log if the given type is empty string or not recognized.

Adding a new row

Now that you have all the data you need you should add a new row to your log data

row = [log_data, backtrace, '']
rows.append(row)

Encoding The Data

At this point you have your data ready to send. It looks like this:

data = {
    "version": "0.2",
    "columns": ["log", "backtrace", "type"],
    "rows": [
        [
            [{
                '___class_name': 'User',
                'name': 'Craig',
                'occupation': 'NFL Player'
            }],
            '/path/to/file.py : 25',
            ''
        ]
    ]
}

Encoding the data is simple.

  1. First you should JSON encode it:

    json_data = json.dumps(data)
  2. Then you should utf8 encode it:

    utf8_data = json_data.encode('utf-8')
  3. Finally you should base64 encode it:

    final_data = utf8_data.encode('base64').replace('\n', '')

    Make sure there are no new lines in the base64 encoded data since it needs to be sent in a single HTTP header.

Sending a header

The final step at the end is sending the data you have encoded in a header.

The header is called X-ChromeLogger-Data.

self.set_header('X-ChromeLogger-Data', final_data)

Different libraries/frameworks will have to implement this part differently. PHP is the only language I know of where you can set any HTTP header from any part of your code base.

For other languages you will have to store the header information and then have a method to retrieve it and flush out the log data from that request.