From 2f04c7593a906eb8c4ce3675e2eafc95582c7009 Mon Sep 17 00:00:00 2001 From: Ben Zhang Date: Sun, 21 Jan 2024 01:01:22 +0000 Subject: [PATCH] Add environment and release to Sentry, and ignore healthcheck traces --- .github/workflows/push-image.yml | 8 ++-- Dockerfile | 8 +++- README.md | 2 +- main.py | 72 ++++++++++++++++++++++++-------- 4 files changed, 67 insertions(+), 23 deletions(-) diff --git a/.github/workflows/push-image.yml b/.github/workflows/push-image.yml index 5bacab8..72c9a48 100644 --- a/.github/workflows/push-image.yml +++ b/.github/workflows/push-image.yml @@ -6,8 +6,8 @@ on: - main env: - IMAGE_NAME: discord-provisioner-bot - IMAGE_REGISTRY: ghcr.io/${{ github.repository_owner }} + IMAGE_NAME: ${{ github.repository }} + IMAGE_REGISTRY: ghcr.io REGISTRY_USER: ${{ github.actor }} REGISTRY_PASSWORD: ${{ github.token }} @@ -40,5 +40,7 @@ jobs: with: context: . push: true + build-args: | + DOCKER_METADATA_OUTPUT_JSON tags: ${{ steps.meta.outputs.tags }} - labels: ${{ steps.meta.outputs.labels }} + labels: ${{ steps.meta.outputs.labels }} \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index c75c124..d1a7bbc 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,9 +1,15 @@ FROM python:3.11-alpine3.18 +RUN apk add curl + RUN mkdir /app WORKDIR /app -COPY . /app +COPY requirements.txt /app/ RUN pip install -r /app/requirements.txt +COPY . /app/ + +HEALTHCHECK --interval=10s --timeout=5s --retries=3 CMD curl --fail http://localhost:8000/health || exit 1 + CMD ["python3", "/app/main.py"] \ No newline at end of file diff --git a/README.md b/README.md index 391c01a..ba96f82 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ This is a simple Discord bot used to trigger WATO's infrastructure provisioning ```bash docker build . -t discord-provisioner-bot -docker run -e DISCORD_TOKEN= -e GITHUB_TOKEN= -e SENTRY_DSN= discord-provisioner-bot +docker run --rm -it -e DISCORD_TOKEN= -e GITHUB_TOKEN= -e SENTRY_DSN= discord-provisioner-bot ``` ## Deployment diff --git a/main.py b/main.py index 4e90ead..635c1e2 100644 --- a/main.py +++ b/main.py @@ -1,5 +1,6 @@ import aiohttp import discord +import json import logging import os import sentry_sdk @@ -10,21 +11,59 @@ from time import perf_counter, sleep -sentry_logging = LoggingIntegration( - level=logging.INFO, # Capture info and above as breadcrumbs - event_level=logging.ERROR # Send errors as events -) -sentry_sdk.init( - dsn=os.environ["SENTRY_DSN"], - integrations=[ - sentry_logging, - ], - - # Set traces_sample_rate to 1.0 to capture 100% - # of transactions for performance monitoring. - # We recommend adjusting this value in production, - traces_sample_rate=1.0, -) +# BUILD_INFO is generated by the build pipeline (e.g. docker/metadata-action). +# It looks like: +# {"tags":["ghcr.io/watonomous/repo-ingestion:main"],"labels":{"org.opencontainers.image.title":"repo-ingestion","org.opencontainers.image.description":"Simple server to receive file changes and open GitHub pull requests","org.opencontainers.image.url":"https://github.com/WATonomous/repo-ingestion","org.opencontainers.image.source":"https://github.com/WATonomous/repo-ingestion","org.opencontainers.image.version":"main","org.opencontainers.image.created":"2024-01-20T16:10:39.421Z","org.opencontainers.image.revision":"1d55b62b15c78251e0560af9e97927591e260a98","org.opencontainers.image.licenses":""}} +BUILD_INFO=json.loads(os.getenv("DOCKER_METADATA_OUTPUT_JSON", "{}")) + +# Set up Sentry +if os.getenv("SENTRY_DSN"): + build_labels = BUILD_INFO.get("labels", {}) + image_title = build_labels.get("org.opencontainers.image.title", "unknown_image") + image_version = build_labels.get("org.opencontainers.image.version", "unknown_version") + image_rev = build_labels.get("org.opencontainers.image.revision", "unknown_rev") + + sentry_config = { + "dsn": os.getenv("SENTRY_DSN"), + "environment": os.getenv("SENTRY_ENVIRONMENT", "unknown"), + "release": os.getenv("SENTRY_RELEASE", f'{image_title}:{image_version}@{image_rev}'), + } + + print(f"Sentry DSN found. Setting up Sentry with config: {sentry_config}") + + sentry_logging = LoggingIntegration( + level=logging.INFO, # Capture info and above as breadcrumbs + event_level=logging.ERROR # Send errors as events + ) + + def sentry_traces_sampler(sampling_context): + # Inherit parent sampling decision + if sampling_context["parent_sampled"] is not None: + return sampling_context["parent_sampled"] + + # Don't need to sample health checks + aiohttp_request = sampling_context.get("aiohttp_request") + if aiohttp_request is not None and aiohttp_request.path == "/health": + return 0 + + # Sample everything else + return 1 + + sentry_sdk.init( + **sentry_config, + integrations=[sentry_logging], + + # Set traces_sample_rate to 1.0 to capture 100% + # of transactions for performance monitoring. + # We recommend adjusting this value in production, + # traces_sample_rate=1.0, + traces_sampler=sentry_traces_sampler, + + enable_tracing=True, + ) +else: + print("No Sentry DSN found. Skipping Sentry setup.") + logger = logging.getLogger('discord.wato-provisioner') intents = discord.Intents.none() @@ -32,9 +71,6 @@ client = discord.Client(intents=intents) -# TODO: sentry integration for logging: -# https://docs.sentry.io/platforms/python/guides/logging/ - @client.event async def on_member_join(member): logger.info(f'{member} has joined the server.')