AppMap Agent for Python

About

appmap is a Python package for recording AppMaps of your code.

The AppMap data format includes code structure (packages, modules, classes, and methods), trace events (function calls, web services, RPC calls, SQL, parameters, return values, exceptions, etc), and code metadata (repo URL, commit SHA, etc). It’s more granular than a performance profile, but it’s less granular than a full debug trace. It’s designed to be optimal for understanding the design intent and structure of code and key data flows.

Supported versions

Support for new versions is added frequently, please check back regularly for updates.

Installation

If your project uses pip for dependency management, add the appmap package to the requirements file or install it directly with

pip install appmap

For projects that use poetry , add the appmap package to pyproject.toml.

poetry add --dev appmap

Configuration

Add your modules as path entries in appmap.yml, and external packages (distributions) as dist:

  name: my_python_app
  packages:
  - path: app.mod1
    shallow: true
  - path: app.mod2
    exclude:
    - MyClass
    - MyOtherClass.my_instance_method
    - MyOtherClass.my_class_method
  # You can record dependency packages, such as Django.
  # We don't recommend recording Django by default though, your AppMaps will be quite large
  # and mostly about Django itself, not your own code.
  #- dist: Django
  #  exclude:
  #  - django.db

Note that excludes are resolved relative to the associated path. So, for example, this configuration excludes app.mod2.MyClass.

For external distribution packages use the dist specifier; the names are looked up in the database of installed Python distributions. This is generally the same package name as you’d give to pip install or put in pyproject.toml. You can additionally use path and exclude on dist entries to limit the capture to specific patterns.

Note by default shallow capture is enabled on dist packages, supressing tracking of most internal execution flow, which allows you to capture the interaction without getting bogged down with detail. If this isn’t what you want, use shallow: false. You can also use shallow: true on path entries.

Environment Variables

  • APPMAP if true, code will be instrumented and AppMaps will be generated. Not case-sensitive, defaults to ‘false’.

  • APPMAP_CONFIG specifies the configuration file to use. Defaults to appmap.yml in the current directory

  • APPMAP_LOG_LEVEL specifies log level to use, from the set CRITICAL, ERROR, WARNING, INFO, DEBUG. Not case-sensitive, defaults to WARNING.

  • APPMAP_OUTPUT_DIR specifies the root directory for writing AppMaps. Defaults to tmp/appmap.

  • APPMAP_DISPLAY_PARAMS enables rendering of parameters as strings. If true (the default, not case-sensitive), parameters are rendered using repr. If false, a generic string is used instead.

Labels

The AppMap data format provides for class and function labels, which can be used to enhance the AppMap visualizations, and to programatically analyze the data.

You can apply function labels using the appmap.labels decorator in your Python code. To apply a labels to a function, decorate the function with @appmap.labels.

For example

import appmap

class ApiKey
  @appmap.labels('provider.authentication', 'security')
  def authenticate(self, key):
      # logic to verify the key here...

Then the AppMap metadata section for this function will include:

  {
    "name": "authenticate",
    "type": "function",
    "labels": [ "provider.authentication", "security" ]
  }

Tests recording

appmap supports recording pytest and unittest test cases.

pytest

appmap is a pytest plugin. When it’s installed in a project that uses pytest, it will be available to generate AppMaps.

root@e9987eaa93c8:/src/appmap/test/data/pytest# pip show appmap
Name: appmap
Version: 0.0.0
Summary: Create AppMap files by recording a Python application.
Home-page: None
Author: Alan Potter
Author-email: alan@app.land
License: None
Location: /usr/local/lib/python3.9/site-packages
Requires: orjson, PyYAML, inflection
Required-by:
root@e9987eaa93c8:/src/appmap/test/data/pytest# APPMAP=true APPMAP_LOG_LEVEL=info pytest -svv
[2021-02-10 11:37:59,345] INFO root: appmap enabled: True
[2021-02-10 11:37:59,350] INFO appmap._implementation.configuration: ConfigFilter, includes {'simple'}
[2021-02-10 11:37:59,350] INFO appmap._implementation.configuration: ConfigFilter, excludes set()
===================================================================== test session starts =====================================================================
platform linux -- Python 3.9.1, pytest-6.2.2, py-1.10.0, pluggy-0.13.1 -- /usr/local/bin/python
cachedir: .pytest_cache
rootdir: /src, configfile: pytest.ini
plugins: appmap-0.0.0
collected 1 item

test_simple.py::test_hello_world [2021-02-10 11:37:59,482] INFO appmap.pytest: starting recording /tmp/pytest/test_hello_world.appmap.json
[2021-02-10 11:37:59,484] INFO appmap._implementation.configuration: included class simple.Simple
[2021-02-10 11:37:59,484] INFO appmap._implementation.configuration: included function simple.Simple.hello
[2021-02-10 11:37:59,489] INFO appmap._implementation.configuration: included function simple.Simple.hello_world
[2021-02-10 11:37:59,490] INFO appmap._implementation.configuration: included function simple.Simple.world
[2021-02-10 11:37:59,828] INFO appmap.pytest: wrote recording /tmp/pytest/test_hello_world.appmap.json
PASSED

====================================================================== 1 passed in 0.45s ======================================================================

unittest

import appmap.unittest. Instruments subclasses of unittest.TestCase and records each test_* function in the subclasses. You can also use python -m appmap.unittest exactly like python -m unittest and leave your code unmodified (just remember to set the APPMAP=true environment variable or set APPMAP=true on Windows).

Run your tests

Once you’ve configured your tests to generate AppMaps, run the tests with the APPMAP=true in the environment (set APPMAP=true on Windows). For example, to run a pytest test suite:

$ APPMAP=true pytest

Remote recording

The AppMap agent supports remote recording of Django and Flask web applications. Import the appropriate remote recording support into your web app.

Django

Add appmap.django.Middleware to your MIDDLEWARE.

Flask

For projects that use a Flask application factory, adding the appmap dependency automatically configures the project for remote recording. No further modifications are required. When the application initializes, appmap adds middleware that handles the /_appmap/record routes.

For projects that don’t provide an application factory, appmap can be used as a Flask extension.

For example:

from flask import Flask

from appmap.flask import AppmapFlask

app = Flask(__name__)

appmap_flask = AppmapFlask(app)

This will add the /_appmap/record routes your app.

Run your web app

Once you’ve configured your web app to add the remote-recording routes, you can use the routes to manage recordings. The AppMap browser extension, CLI, or just plain cURL will all work for this.

As when running tests, start the web server with APPMAP=true in the environment (set APPMAP=true on Windows). For example, to start a Flask app:

$ APPMAP=true flask run

An app with remote recording enabled supports these routes:

  • POST /_appmap/record Starts a new recording

    200 if the recording was started successfully 409 if there’s already a recording in progress

  • GET /_appmap/record Returns JSON describing current recording state 200 with body

    {
      "enabled": true
    }
    

    enabled indicates whether recording has been enabled

  • DELETE /_appmap/record Returns AppMap as JSON 200 with AppMap as body 404 if there’s no recording in progress

Context manager

You can use appmap.record as a context manager to record your code.

With a file called record_sample.py like this

import os
import sys

import appmap

r = appmap.Recording()
with r:
    import sample
    print(sample.C().hello_world(), file=sys.stderr)

with os.fdopen(sys.stdout.fileno(), "w", closefd=False) as stdout:
    stdout.write(appmap.generation.dump(r))
    stdout.flush()

and a source file called sample.py like this

class C:
    def make_str(self, s):
        return s;

    def hello_world(self):
        return f'{self.make_str("Hello")} {self.make_str("world!")}'

as well as an appmap.yml

name: sample
packages:
- path: sample

you can generate a recording of the code

% APPMAP=true python record_sample.py > record_sample.appmap.json
% jq '.events | length' record_sample.appmap.json
6
% jq < record_sample.appmap.json | head -10
{
  "version": "1.4",
  "metadata": {
    "language": {
      "name": "python",
      "engine": "CPython",
      "version": "3.9.1"
    },
    "client": {
      "name": "appmap",

GitHub repository

https://github.com/getappmap/appmap-python

Uploading AppMaps

https://app.land can be used to store, analyze, and share AppMaps.

For instructions on uploading, see the documentation of the AppMap CLI.


Was this page helpful? thumb_up Yes thumb_down No
Thank you for your feedback!