Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,7 @@ rake app=ruby/rails7-delayed-job mode=collector app:up
rake app=ruby/rails7-goodjob mode=collector app:up
rake app=ruby/rails7-postgres mode=collector app:up
rake app=ruby/rails7-sequel mode=collector app:up
rake app=ruby/rails7-sidekiq mode=collector app:up
rake app=ruby/rails7-solid-cache mode=collector app:up
rake app=ruby/rails7-solid-queue mode=collector app:up
rake app=ruby/rails8-delayed-job mode=collector app:up
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True

ALLOWED_HOSTS = ['app', 'localhost']
ALLOWED_HOSTS = ['app', 'downstream', 'localhost']


# Application definition
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import os
import requests
import json
import time
Expand Down Expand Up @@ -84,7 +85,7 @@ def error_queue_inline(request):


def make_request(request):
requests.get('https://www.appsignal.com/')
requests.get(os.environ.get("DOWNSTREAM_URL", "https://www.appsignal.com/"))
return HttpResponse("I did a request to appsignal.com!")


Expand Down
14 changes: 14 additions & 0 deletions python/django4-asgi/app/processmon.downstream.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
[[paths_to_watch]]
path = "/app"

# Downstream web server on :4002 (no celery here; that is the worker split).
[processes.django]
command = "python3"
args = [
"-m",
"uvicorn",
"appsignal_python_opentelemetry.asgi:application",
"--host",
"0.0.0.0"
]
working_dir = "/app"
10 changes: 0 additions & 10 deletions python/django4-asgi/app/processmon.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,3 @@ args = [
"0.0.0.0"
]
working_dir = "/app"

[processes.celery]
command = "celery"
args = [
"--app=tasks",
"worker",
"--loglevel=INFO",
"--queues=high-priority,low-priority"
]
working_dir = "/app"
15 changes: 15 additions & 0 deletions python/django4-asgi/app/processmon.worker.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
[[paths_to_watch]]
path = "/app"

# The worker service runs only the background worker. Work enqueued by the web
# `app` service is performed here, so it reports under the worker's own app name
# (agent mode) or `worker` service name (collector mode).
[processes.celery]
command = "celery"
args = [
"--app=tasks",
"worker",
"--loglevel=INFO",
"--queues=high-priority,low-priority"
]
working_dir = "/app"
24 changes: 24 additions & 0 deletions python/django4-asgi/commands/run-downstream
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#!/bin/bash

set -eu

cd /app

# The `app` service builds the appsignal dist into the shared /integration
# volume and runs migrations, then serves HTTP. Wait for it to be reachable:
# that means the dist is built (so we install from it without racing the app's
# `hatch build`) and the schema is migrated (so we never migrate from here).
echo "Waiting for the app service to be ready..."
until python3 -c "import socket; socket.create_connection(('app', 4001), 2).close()" >/dev/null 2>&1; do
echo "App not ready yet, retrying..."
sleep 2
done

echo "Installing dependencies"
pip3 install -r requirements.txt
pip3 install /integration/dist/* --force-reinstall

export UVICORN_PORT=4002

echo "Starting downstream processes"
/commands/processmon processmon.downstream.toml
23 changes: 23 additions & 0 deletions python/django4-asgi/commands/run-worker
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#!/bin/bash

set -eu

cd /app

# The `app` service builds the appsignal dist into the shared /integration
# volume and (for the Django apps) runs migrations, then serves HTTP. Wait for
# it to be reachable before installing here: that means the dist is built (so we
# install from it without racing the app's `hatch build`) and the schema is
# migrated (so this container never migrates).
echo "Waiting for the app service to be ready..."
until python3 -c "import socket; socket.create_connection(('app', 4001), 2).close()" >/dev/null 2>&1; do
echo "App not ready yet, retrying..."
sleep 2
done

echo "Installing dependencies"
pip3 install -r requirements.txt
pip3 install /integration/dist/* --force-reinstall

echo "Starting worker processes"
/commands/processmon processmon.worker.toml
6 changes: 6 additions & 0 deletions python/django4-asgi/docker-compose.agent.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,9 @@ services:
app:
environment:
- APPSIGNAL_APP_NAME=python/django4-asgi
downstream:
environment:
- APPSIGNAL_APP_NAME=python/django4-asgi-downstream
worker:
environment:
- APPSIGNAL_APP_NAME=python/django4-asgi-worker
11 changes: 11 additions & 0 deletions python/django4-asgi/docker-compose.collector.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,15 @@ services:
app:
environment:
- APPSIGNAL_APP_NAME=python/django4-asgi-collector
- APPSIGNAL_SERVICE_NAME=app
- APPSIGNAL_COLLECTOR_ENDPOINT=http://appsignal-collector:8099
downstream:
environment:
- APPSIGNAL_APP_NAME=python/django4-asgi-collector
- APPSIGNAL_SERVICE_NAME=downstream
- APPSIGNAL_COLLECTOR_ENDPOINT=http://appsignal-collector:8099
worker:
environment:
- APPSIGNAL_APP_NAME=python/django4-asgi-collector
- APPSIGNAL_SERVICE_NAME=worker
- APPSIGNAL_COLLECTOR_ENDPOINT=http://appsignal-collector:8099
30 changes: 30 additions & 0 deletions python/django4-asgi/docker-compose.shared.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,36 @@ services:
- ../../appsignal_key.env
environment:
- PORT=4001
- DOWNSTREAM_URL=http://downstream:4002/
volumes:
- ./app:/app
- ../integration:/integration
# Second instance of the same image (image-only; only `app` builds). Receives
# the HTTP request the `app` service makes, so the trace continues into it.
downstream:
image: python/django4-asgi
command: /commands/run-downstream
depends_on:
- postgres
- redis
env_file:
- postgres.env
- ../../appsignal.env
- ../../appsignal_key.env
volumes:
- ./app:/app
- ../integration:/integration
# Runs only the background worker (image-only; only `app` builds).
worker:
image: python/django4-asgi
command: /commands/run-worker
depends_on:
- postgres
- redis
env_file:
- postgres.env
- ../../appsignal.env
- ../../appsignal_key.env
volumes:
- ./app:/app
- ../integration:/integration
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True

ALLOWED_HOSTS = ['app', 'localhost']
ALLOWED_HOSTS = ['app', 'downstream', 'localhost']


# Application definition
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import os
import requests
import json
import time
Expand Down Expand Up @@ -91,9 +92,13 @@ def error_queue_inline(request):
return HttpResponse("I ran an error_task inline!")


# Calls a second, separately-instrumented app (the `downstream` service) so the
# injected traceparent is extracted there and the trace spans both apps. Falls
# back to the external, uninstrumented URL when DOWNSTREAM_URL is unset, so the
# setup still works as a single service.
def make_request(request):
requests.get('https://www.appsignal.com/')
return HttpResponse("I did a request to appsignal.com!")
requests.get(os.environ.get("DOWNSTREAM_URL", "https://www.appsignal.com/"))
return HttpResponse("I did a request downstream!")


def custom_instrumentation(request):
Expand Down
15 changes: 15 additions & 0 deletions python/django4-celery/app/processmon.downstream.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
[[paths_to_watch]]
path = "/app"

# The downstream service is a second instance of the same Django app on a
# different port. It receives the HTTP request the `app` service makes, so the
# trace continues into it. In collector mode it reports under the `downstream`
# service name; in agent mode under a separate `-downstream` app.
[processes.django]
command = "python3"
args = [
"manage.py",
"runserver",
"0.0.0.0:4002"
]
working_dir = "/app"
10 changes: 0 additions & 10 deletions python/django4-celery/app/processmon.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,3 @@ args = [
"0.0.0.0:4001"
]
working_dir = "/app"

[processes.celery]
command = "celery"
args = [
"--app=tasks",
"worker",
"--loglevel=INFO",
"--queues=high-priority,low-priority"
]
working_dir = "/app"
15 changes: 15 additions & 0 deletions python/django4-celery/app/processmon.worker.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
[[paths_to_watch]]
path = "/app"

# The worker service runs only the background worker. Work enqueued by the web
# `app` service is performed here, so it reports under the worker's own app name
# (agent mode) or `worker` service name (collector mode).
[processes.celery]
command = "celery"
args = [
"--app=tasks",
"worker",
"--loglevel=INFO",
"--queues=high-priority,low-priority"
]
working_dir = "/app"
23 changes: 23 additions & 0 deletions python/django4-celery/commands/run-downstream
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#!/bin/bash

set -eu

cd /app

# The `app` service owns building the appsignal dist into the shared
# /integration volume and running migrations. Wait for it to start serving
# before doing anything here. That single signal means the dist is built, so we
# can install from it without racing the app's `hatch build`, and it means the
# schema is migrated, so this container never runs migrations itself.
echo "Waiting for the app service to be ready..."
until python3 -c "import socket; socket.create_connection(('app', 4001), 2).close()" >/dev/null 2>&1; do
echo "App not ready yet, retrying..."
sleep 2
done

echo "Installing dependencies"
pip3 install -r requirements.txt
pip3 install /integration/dist/* --force-reinstall

echo "Starting downstream processes"
/commands/processmon processmon.downstream.toml
23 changes: 23 additions & 0 deletions python/django4-celery/commands/run-worker
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#!/bin/bash

set -eu

cd /app

# The `app` service builds the appsignal dist into the shared /integration
# volume and (for the Django apps) runs migrations, then serves HTTP. Wait for
# it to be reachable before installing here: that means the dist is built (so we
# install from it without racing the app's `hatch build`) and the schema is
# migrated (so this container never migrates).
echo "Waiting for the app service to be ready..."
until python3 -c "import socket; socket.create_connection(('app', 4001), 2).close()" >/dev/null 2>&1; do
echo "App not ready yet, retrying..."
sleep 2
done

echo "Installing dependencies"
pip3 install -r requirements.txt
pip3 install /integration/dist/* --force-reinstall

echo "Starting worker processes"
/commands/processmon processmon.worker.toml
6 changes: 6 additions & 0 deletions python/django4-celery/docker-compose.agent.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,9 @@ services:
app:
environment:
- APPSIGNAL_APP_NAME=python/django4-celery
downstream:
environment:
- APPSIGNAL_APP_NAME=python/django4-celery-downstream
worker:
environment:
- APPSIGNAL_APP_NAME=python/django4-celery-worker
11 changes: 11 additions & 0 deletions python/django4-celery/docker-compose.collector.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,15 @@ services:
app:
environment:
- APPSIGNAL_APP_NAME=python/django4-celery-collector
- APPSIGNAL_SERVICE_NAME=app
- APPSIGNAL_COLLECTOR_ENDPOINT=http://appsignal-collector:8099
downstream:
environment:
- APPSIGNAL_APP_NAME=python/django4-celery-collector
- APPSIGNAL_SERVICE_NAME=downstream
- APPSIGNAL_COLLECTOR_ENDPOINT=http://appsignal-collector:8099
worker:
environment:
- APPSIGNAL_APP_NAME=python/django4-celery-collector
- APPSIGNAL_SERVICE_NAME=worker
- APPSIGNAL_COLLECTOR_ENDPOINT=http://appsignal-collector:8099
35 changes: 35 additions & 0 deletions python/django4-celery/docker-compose.shared.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,41 @@ services:
- ../../appsignal_key.env
environment:
- PORT=4001
# The make_request view calls this second, separately-instrumented app so
# the trace propagates across services. Falls back to an external URL when
# unset (single-service runs).
- DOWNSTREAM_URL=http://downstream:4002/
volumes:
- ./app:/app
- ../integration:/integration
# A second instance of the same image, running as a distinct AppSignal app
# (agent mode) or service (collector mode). It receives the HTTP request the
# `app` service makes, so the trace continues into it. Image-only: only `app`
# builds, so `docker compose build` does not collide on the shared image tag.
downstream:
image: python/django4-celery
command: /commands/run-downstream
depends_on:
- postgres
- redis
env_file:
- postgres.env
- ../../appsignal.env
- ../../appsignal_key.env
volumes:
- ./app:/app
- ../integration:/integration
# Runs only the background worker (image-only; only `app` builds).
worker:
image: python/django4-celery
command: /commands/run-worker
depends_on:
- postgres
- redis
env_file:
- postgres.env
- ../../appsignal.env
- ../../appsignal_key.env
volumes:
- ./app:/app
- ../integration:/integration
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True

ALLOWED_HOSTS = ['app', 'localhost']
ALLOWED_HOSTS = ['app', 'downstream', 'localhost']


# Application definition
Expand Down
Loading
Loading