From 0cd3d6572028046cc2361424f09bdc129b6b6022 Mon Sep 17 00:00:00 2001 From: Alan West <3676547+alanwest@users.noreply.github.com> Date: Mon, 3 Jun 2024 13:26:32 -0700 Subject: [PATCH] Update python getting started guid --- getting-started-guides/compose.yaml | 11 +++ getting-started-guides/python/Dockerfile | 46 ++++++++++ .../python/Instrumented/README.md | 46 ---------- .../python/Instrumented/app.py | 91 ------------------- .../python/Instrumented/call-app.ps1 | 14 --- .../python/Instrumented/call-app.sh | 21 ----- .../python/Instrumented/load-generator.ps1 | 7 -- .../python/Instrumented/load-generator.sh | 10 -- .../python/Instrumented/requirements.txt | 31 ------- getting-started-guides/python/README.md | 45 +++++++++ .../python/Uninstrumented/app.py | 21 ----- .../python/Uninstrumented/call-app.ps1 | 14 --- .../python/Uninstrumented/call-app.sh | 21 ----- .../python/Uninstrumented/load-generator.ps1 | 7 -- .../python/Uninstrumented/load-generator.sh | 10 -- .../python/Uninstrumented/readme.md | 80 ---------------- getting-started-guides/python/app.py | 39 ++++++++ .../python/requirements.txt | 28 ++++++ .../supporting-files/envoy.yaml | 14 +++ .../supporting-files/loadgenerator.py | 2 +- 20 files changed, 184 insertions(+), 374 deletions(-) create mode 100644 getting-started-guides/python/Dockerfile delete mode 100644 getting-started-guides/python/Instrumented/README.md delete mode 100644 getting-started-guides/python/Instrumented/app.py delete mode 100644 getting-started-guides/python/Instrumented/call-app.ps1 delete mode 100755 getting-started-guides/python/Instrumented/call-app.sh delete mode 100644 getting-started-guides/python/Instrumented/load-generator.ps1 delete mode 100755 getting-started-guides/python/Instrumented/load-generator.sh delete mode 100644 getting-started-guides/python/Instrumented/requirements.txt create mode 100644 getting-started-guides/python/README.md delete mode 100644 getting-started-guides/python/Uninstrumented/app.py delete mode 100644 getting-started-guides/python/Uninstrumented/call-app.ps1 delete mode 100755 getting-started-guides/python/Uninstrumented/call-app.sh delete mode 100644 getting-started-guides/python/Uninstrumented/load-generator.ps1 delete mode 100755 getting-started-guides/python/Uninstrumented/load-generator.sh delete mode 100644 getting-started-guides/python/Uninstrumented/readme.md create mode 100644 getting-started-guides/python/app.py create mode 100644 getting-started-guides/python/requirements.txt diff --git a/getting-started-guides/compose.yaml b/getting-started-guides/compose.yaml index a95705f1..7e09c711 100644 --- a/getting-started-guides/compose.yaml +++ b/getting-started-guides/compose.yaml @@ -29,6 +29,16 @@ services: - OTEL_RESOURCE_ATTRIBUTES=service.instance.id=123 ports: - 8080 + python: + build: python + environment: + - OTEL_SERVICE_NAME=getting-started-python + - OTEL_EXPORTER_OTLP_ENDPOINT + - OTEL_EXPORTER_OTLP_HEADERS + - OTEL_ATTRIBUTE_VALUE_LENGTH_LIMIT=4095 + - OTEL_RESOURCE_ATTRIBUTES=service.instance.id=123 + ports: + - 8080 ruby: build: ruby environment: @@ -54,4 +64,5 @@ services: - dotnet - go - javascript + - python - ruby diff --git a/getting-started-guides/python/Dockerfile b/getting-started-guides/python/Dockerfile new file mode 100644 index 00000000..ac8548da --- /dev/null +++ b/getting-started-guides/python/Dockerfile @@ -0,0 +1,46 @@ +# syntax=docker/dockerfile:1 + +ARG PYTHON_VERSION=3.12.2 +FROM python:${PYTHON_VERSION}-slim as base + +# Prevents Python from writing pyc files. +# ENV PYTHONDONTWRITEBYTECODE=1 + +# Keeps Python from buffering stdout and stderr to avoid situations where +# the application crashes without emitting any logs due to buffering. +# ENV PYTHONUNBUFFERED=1 + +WORKDIR /app + +# Create a non-privileged user that the app will run under. +# See https://docs.docker.com/go/dockerfile-user-best-practices/ +ARG UID=10001 +RUN adduser \ + --disabled-password \ + --gecos "" \ + --home "/nonexistent" \ + --shell "/sbin/nologin" \ + --no-create-home \ + --uid "${UID}" \ + appuser + +# Download dependencies as a separate step to take advantage of Docker's caching. +# Leverage a cache mount to /root/.cache/pip to speed up subsequent builds. +# Leverage a bind mount to requirements.txt to avoid having to copy them into +# into this layer. +RUN --mount=type=cache,target=/root/.cache/pip \ + --mount=type=bind,source=requirements.txt,target=requirements.txt \ + python -m pip install -r requirements.txt + +ENV OTEL_PYTHON_LOGGING_AUTO_INSTRUMENTATION_ENABLED=true + +RUN opentelemetry-bootstrap -a install + +COPY . . + +# Switch to the non-privileged user to run the application. +USER appuser + +EXPOSE 8080 + +CMD opentelemetry-instrument --logs_exporter otlp python3 app.py diff --git a/getting-started-guides/python/Instrumented/README.md b/getting-started-guides/python/Instrumented/README.md deleted file mode 100644 index 32786c83..00000000 --- a/getting-started-guides/python/Instrumented/README.md +++ /dev/null @@ -1,46 +0,0 @@ -# Get Started Guide - Python - -This is the solution (completely instrumented with OpenTelemetry) for the Python demo application used in the Get Started Guide - Python doc. - -Requires: -* Python -* [A New Relic account](https://one.newrelic.com/) - -To run this demo app via the CLI, follow the steps below. **For steps 2-10, refer to the [table](#commands-table) below to find the commands to use for your environment.** - -1. Switch to the working directory: -```shell -cd ./getting-started-guides/python/instrumented/ -``` -2. Install `virtualenv`, if not already done -3. Create a virtual environment for the application -4. Activate virtual environment -5. Upgrade `pip` in virtual environment -6. Install dependencies listed in `requirements.txt` -7. To send telemetry data to New Relic, set the following environment variables -```shell -OTEL_EXPORTER_OTLP_ENDPOINT="https://otlp.nr-data.net" -OTEL_EXPORTER_OTLP_HEADERS="api-key=[YOUR_INGEST_KEY]" -``` -* Make sure to use your [ingest license key](https://docs.newrelic.com/docs/apis/intro-apis/new-relic-api-keys/#license-key) -* If your account is based in the EU, set the endpoint to: https://otlp.eu01.nr-data.net -8. Set this environment variable to name the demo app: -```shell -OTEL_SERVICE_NAME=getting-started-python -``` -9. Run the app -10. To generate traffic, in a new terminal tab run the load generator -11. To shut down the program, run the following in both shells or terminal tabs: `ctrl + c`. - -## Commands table - -| Step | Windows (PowerShell) | Linux / MacOS (bash) | -|------|------------------------------------------|-------------------------------------------| -| 2 | `pip install virtualenv` | `pip3 install virtualenv` | -| 3 | `python -m venv venv` | `python3 -m venv venv` | -| 4 | `.\venv\Scripts\Activate.ps1` | `source venv/bin/activate` | -| 5 | `python -m pip install --upgrade pip` | `python3 -m pip install --upgrade pip` | -| 6 | `pip install -r requirements.txt` | `pip install -r requirements.txt` | -| 7-8 | Set OTEL environment variables `$Env:` | Set OTEL environment variables `export` | -| 9 | `python app.py` | `python3 app.py` | -| 10 | `.\load-generator.ps1` | `./load-generator.sh` | diff --git a/getting-started-guides/python/Instrumented/app.py b/getting-started-guides/python/Instrumented/app.py deleted file mode 100644 index b8aebd20..00000000 --- a/getting-started-guides/python/Instrumented/app.py +++ /dev/null @@ -1,91 +0,0 @@ -########################## -# OpenTelemetry Settings # -########################## -from opentelemetry.sdk.resources import Resource -import uuid - -OTEL_RESOURCE_ATTRIBUTES = { - "service.instance.id": str(uuid.uuid1()), - "environment": "local" -} - -########## -# Traces # -########## -from opentelemetry import trace -from opentelemetry.sdk.trace import TracerProvider -from opentelemetry.sdk.trace.export import BatchSpanProcessor -from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter -from opentelemetry.trace.status import Status, StatusCode - -# Initialize tracing and an exporter that can send data to an OTLP endpoint -# SELECT * FROM Span WHERE instrumentation.provider='opentelemetry' -trace.set_tracer_provider(TracerProvider(resource=Resource.create(OTEL_RESOURCE_ATTRIBUTES))) -trace.get_tracer_provider().add_span_processor(BatchSpanProcessor(OTLPSpanExporter())) - -########### -# Metrics # -########### -from opentelemetry import metrics -from opentelemetry.sdk.metrics import MeterProvider -from opentelemetry.sdk.metrics.export import PeriodicExportingMetricReader -from opentelemetry.exporter.otlp.proto.http.metric_exporter import OTLPMetricExporter - -# Initialize metering and an exporter that can send data to an OTLP endpoint -# SELECT count(`http.server.active_requests`) FROM Metric FACET `service.name` TIMESERIES -metrics.set_meter_provider(MeterProvider(resource=Resource.create(OTEL_RESOURCE_ATTRIBUTES), metric_readers=[PeriodicExportingMetricReader(OTLPMetricExporter())])) -metrics.get_meter_provider() -fib_counter = metrics.get_meter("opentelemetry.instrumentation.custom").create_counter("fibonacci.invocations", unit="1", description="Measures the number of times the fibonacci method is invoked.") - -######## -# Logs # - OpenTelemetry Logs are still in the "experimental" state, so function names may change in the future -######## -import logging -logging.basicConfig(level=logging.DEBUG) - -from opentelemetry import _logs -from opentelemetry.sdk._logs import LoggerProvider, LoggingHandler -from opentelemetry.sdk._logs.export import BatchLogRecordProcessor -from opentelemetry.exporter.otlp.proto.http._log_exporter import OTLPLogExporter - -# Initialize logging and an exporter that can send data to an OTLP endpoint by attaching OTLP handler to root logger -# SELECT * FROM Log WHERE instrumentation.provider='opentelemetry' -_logs.set_logger_provider(LoggerProvider(resource=Resource.create(OTEL_RESOURCE_ATTRIBUTES))) -logging.getLogger().addHandler(LoggingHandler(logger_provider=_logs.get_logger_provider().add_log_record_processor(BatchLogRecordProcessor(OTLPLogExporter())))) - -##################### -# Flask Application # -##################### -from flask import Flask, jsonify, request -from opentelemetry.instrumentation.flask import FlaskInstrumentor - -app = Flask(__name__) -FlaskInstrumentor().instrument_app(app) - -@app.route("/fibonacci") -@trace.get_tracer("opentelemetry.instrumentation.custom").start_as_current_span("/fibonacci") -def fibonacci(): - args = request.args - x = int(args.get("n")) - error_message = "n must be 1 <= n <= 90." - trace.get_current_span().set_attribute("fibonacci.n", x) - - try: - assert 1 <= x <= 90 - array = [0, 1] - for n in range(2, x + 1): - array.append(array[n - 1] + array[n - 2]) - - trace.get_current_span().set_attribute("fibonacci.result", array[x]) - fib_counter.add(1, {"fibonacci.valid.n": "true"}) - logging.info("Compute fibonacci(" + str(x) + ") = " + str(array[x])) - return jsonify(n=x, result=array[x]) - - except AssertionError: - trace.get_current_span().record_exception(exception=Exception, attributes={"exception.type": "AssertionError", "exception.message": error_message}) - trace.get_current_span().set_status(Status(StatusCode.ERROR, error_message)) - fib_counter.add(1, {"fibonacci.valid.n": "false"}) - logging.error("Failed to compute fibonacci(" + str(x) + ")") - return jsonify({"message": error_message}) - -app.run(host='0.0.0.0', port=8080) diff --git a/getting-started-guides/python/Instrumented/call-app.ps1 b/getting-started-guides/python/Instrumented/call-app.ps1 deleted file mode 100644 index 04e5d92d..00000000 --- a/getting-started-guides/python/Instrumented/call-app.ps1 +++ /dev/null @@ -1,14 +0,0 @@ -Write-Output "GET $ENDPOINT/fibonacci?n=5" -Invoke-WebRequest -Uri "$ENDPOINT/fibonacci?n=5" | Select-Object -Expand Content - -Write-Output "GET $ENDPOINT/fibonacci?n=283" -Invoke-WebRequest -Uri "$ENDPOINT/fibonacci?n=283" | Select-Object -Expand Content - -Write-Output "GET $ENDPOINT/fibonacci?n=10" -Invoke-WebRequest -Uri "$ENDPOINT/fibonacci?n=10" | Select-Object -Expand Content - -Write-Output "GET $ENDPOINT/fibonacci?n=90" -Invoke-WebRequest -Uri "$ENDPOINT/fibonacci?n=90" | Select-Object -Expand Content - -Write-Output "GET $ENDPOINT/fibonacci?n=0" -Invoke-WebRequest -Uri "$ENDPOINT/fibonacci?n=0" | Select-Object -Expand Content diff --git a/getting-started-guides/python/Instrumented/call-app.sh b/getting-started-guides/python/Instrumented/call-app.sh deleted file mode 100755 index d63a747a..00000000 --- a/getting-started-guides/python/Instrumented/call-app.sh +++ /dev/null @@ -1,21 +0,0 @@ -#!/bin/bash - -echo "GET ${1}/fibonacci?n=5" -curl "${1}/fibonacci?n=5" || true -echo - -echo "GET ${1}/fibonacci?n=283" -curl "${1}/fibonacci?n=283" || true -echo - -echo "GET ${1}/fibonacci?n=10" -curl "${1}/fibonacci?n=10" || true -echo - -echo "GET ${1}/fibonacci?n=90" -curl "${1}/fibonacci?n=90" || true -echo - -echo "GET ${1}/fibonacci?n=0" -curl "${1}/fibonacci?n=0" || true -echo diff --git a/getting-started-guides/python/Instrumented/load-generator.ps1 b/getting-started-guides/python/Instrumented/load-generator.ps1 deleted file mode 100644 index fafb2ba5..00000000 --- a/getting-started-guides/python/Instrumented/load-generator.ps1 +++ /dev/null @@ -1,7 +0,0 @@ -$ENDPOINT="http://localhost:8080" - -while ($true) { - Write-Output "Calling getting-started-python" - & ".\call-app.ps1" - Start-Sleep -s 2 -} diff --git a/getting-started-guides/python/Instrumented/load-generator.sh b/getting-started-guides/python/Instrumented/load-generator.sh deleted file mode 100755 index ed2f519d..00000000 --- a/getting-started-guides/python/Instrumented/load-generator.sh +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/bash - -while : -do - echo "Calling getting-started-python" - ./call-app.sh http://localhost:8080 || true - echo - - sleep 2 -done \ No newline at end of file diff --git a/getting-started-guides/python/Instrumented/requirements.txt b/getting-started-guides/python/Instrumented/requirements.txt deleted file mode 100644 index e2dec1ca..00000000 --- a/getting-started-guides/python/Instrumented/requirements.txt +++ /dev/null @@ -1,31 +0,0 @@ -backoff==2.2.1 -certifi==2023.7.22 -charset-normalizer==3.1.0 -click==8.1.3 -Deprecated==1.2.13 -Flask==2.2.5 -googleapis-common-protos==1.58.0 -grpcio==1.53.2 -idna==3.7 -itsdangerous==2.1.2 -Jinja2==3.1.4 -MarkupSafe==2.1.2 -opentelemetry-api==1.24.0 -opentelemetry-distro==0.45b0 -opentelemetry-exporter-otlp==1.24.0 -opentelemetry-exporter-otlp-proto-grpc==1.24.0 -opentelemetry-exporter-otlp-proto-http==1.24.0 -opentelemetry-instrumentation==0.45b0 -opentelemetry-instrumentation-flask==0.45b0 -opentelemetry-instrumentation-logging==0.45b0 -opentelemetry-instrumentation-wsgi==0.45b0 -opentelemetry-proto==1.24.0 -opentelemetry-sdk==1.24.0 -opentelemetry-semantic-conventions==0.45b0 -opentelemetry-util-http==0.45b0 -protobuf==4.22.1 -requests==2.31.0 -typing_extensions==4.5.0 -urllib3==1.26.18 -Werkzeug==3.0.3 -wrapt==1.15.0 diff --git a/getting-started-guides/python/README.md b/getting-started-guides/python/README.md new file mode 100644 index 00000000..9e27511b --- /dev/null +++ b/getting-started-guides/python/README.md @@ -0,0 +1,45 @@ +# Getting Started Guide - Python + +This is a simple application instrumented with [OpenTelemetry Python](https://github.com/open-telemetry/opentelemetry-python). +It demonstrates how to configure OpenTelemetry Python to send data to New Relic. + +## Requirements + +* [Python](https://www.python.org/downloads/) +* [A New Relic account](https://one.newrelic.com/) +* [A New Relic license key](https://docs.newrelic.com/docs/apis/intro-apis/new-relic-api-keys/#license-key) + +## Running the application + +1. Set the following environment variables to configure OpenTelemetry to send + data to New Relic: + + ```shell + export OTEL_PYTHON_LOGGING_AUTO_INSTRUMENTATION_ENABLED=true + export OTEL_EXPORTER_OTLP_ENDPOINT=https://otlp.nr-data.net + export OTEL_EXPORTER_OTLP_HEADERS=api-key= + export OTEL_ATTRIBUTE_VALUE_LENGTH_LIMIT=4095 + export OTEL_SERVICE_NAME=getting-started-python + export OTEL_RESOURCE_ATTRIBUTES=service.instance.id=123 + ``` + + * If your account is based in the EU, set the endpoint to: [https://otlp.eu01.nr-data.net](https://otlp.eu01.nr-data.net) + +2. Run the following command to install dependencies: + + ```shell + python -m pip install -r requirements.txt + opentelemetry-bootstrap -a install + ``` + +3. Run the application with the following command and open + [http://localhost:8080/fibonacci?n=1](http://localhost:8080/fibonacci?n=1) + in your web browser to ensure it is working. + + ```shell + opentelemetry-instrument --logs_exporter otlp python3 app.py + ``` + +4. Experiment with providing different values for `n` in the query string. + Valid values are between 1 and 90. Values outside this range cause an error + which will show up in New Relic. diff --git a/getting-started-guides/python/Uninstrumented/app.py b/getting-started-guides/python/Uninstrumented/app.py deleted file mode 100644 index a84ced02..00000000 --- a/getting-started-guides/python/Uninstrumented/app.py +++ /dev/null @@ -1,21 +0,0 @@ -from flask import Flask, jsonify, request - -app = Flask(__name__) - -@app.route("/fibonacci") -def fibonacci(x): - args = request.args - x = int(args.get("n")) - - try: - assert 1 <= x <= 90 - array = [0, 1] - for n in range(2, x + 1): - array.append(array[n - 1] + array[n - 2]) - - return jsonify(n=x, result=array[x]) - - except (ValueError, AssertionError): - return jsonify({"message": "n must be 1 <= n <= 90."}) - -app.run(host='0.0.0.0', port=8080) \ No newline at end of file diff --git a/getting-started-guides/python/Uninstrumented/call-app.ps1 b/getting-started-guides/python/Uninstrumented/call-app.ps1 deleted file mode 100644 index 04e5d92d..00000000 --- a/getting-started-guides/python/Uninstrumented/call-app.ps1 +++ /dev/null @@ -1,14 +0,0 @@ -Write-Output "GET $ENDPOINT/fibonacci?n=5" -Invoke-WebRequest -Uri "$ENDPOINT/fibonacci?n=5" | Select-Object -Expand Content - -Write-Output "GET $ENDPOINT/fibonacci?n=283" -Invoke-WebRequest -Uri "$ENDPOINT/fibonacci?n=283" | Select-Object -Expand Content - -Write-Output "GET $ENDPOINT/fibonacci?n=10" -Invoke-WebRequest -Uri "$ENDPOINT/fibonacci?n=10" | Select-Object -Expand Content - -Write-Output "GET $ENDPOINT/fibonacci?n=90" -Invoke-WebRequest -Uri "$ENDPOINT/fibonacci?n=90" | Select-Object -Expand Content - -Write-Output "GET $ENDPOINT/fibonacci?n=0" -Invoke-WebRequest -Uri "$ENDPOINT/fibonacci?n=0" | Select-Object -Expand Content diff --git a/getting-started-guides/python/Uninstrumented/call-app.sh b/getting-started-guides/python/Uninstrumented/call-app.sh deleted file mode 100755 index d63a747a..00000000 --- a/getting-started-guides/python/Uninstrumented/call-app.sh +++ /dev/null @@ -1,21 +0,0 @@ -#!/bin/bash - -echo "GET ${1}/fibonacci?n=5" -curl "${1}/fibonacci?n=5" || true -echo - -echo "GET ${1}/fibonacci?n=283" -curl "${1}/fibonacci?n=283" || true -echo - -echo "GET ${1}/fibonacci?n=10" -curl "${1}/fibonacci?n=10" || true -echo - -echo "GET ${1}/fibonacci?n=90" -curl "${1}/fibonacci?n=90" || true -echo - -echo "GET ${1}/fibonacci?n=0" -curl "${1}/fibonacci?n=0" || true -echo diff --git a/getting-started-guides/python/Uninstrumented/load-generator.ps1 b/getting-started-guides/python/Uninstrumented/load-generator.ps1 deleted file mode 100644 index fafb2ba5..00000000 --- a/getting-started-guides/python/Uninstrumented/load-generator.ps1 +++ /dev/null @@ -1,7 +0,0 @@ -$ENDPOINT="http://localhost:8080" - -while ($true) { - Write-Output "Calling getting-started-python" - & ".\call-app.ps1" - Start-Sleep -s 2 -} diff --git a/getting-started-guides/python/Uninstrumented/load-generator.sh b/getting-started-guides/python/Uninstrumented/load-generator.sh deleted file mode 100755 index ed2f519d..00000000 --- a/getting-started-guides/python/Uninstrumented/load-generator.sh +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/bash - -while : -do - echo "Calling getting-started-python" - ./call-app.sh http://localhost:8080 || true - echo - - sleep 2 -done \ No newline at end of file diff --git a/getting-started-guides/python/Uninstrumented/readme.md b/getting-started-guides/python/Uninstrumented/readme.md deleted file mode 100644 index e9b47fff..00000000 --- a/getting-started-guides/python/Uninstrumented/readme.md +++ /dev/null @@ -1,80 +0,0 @@ -# Getting Started Guide - Python (Uninstrumented) -This is the solution for the Python demo application used in the Getting Started Guide - Python. - -Requires: -- Python 3.8+ -- [A New Relic account](https://one.newrelic.com/) - -To run this demo app via the CLI, start by switching to the `python/Uninstrumented` directory. - -``` -cd ./getting-started-guides/python/Uninstrumented/ -``` - -## Summary -1. Install `virtualenv`, if not already done -2. Create a virtual environment for the application, if needed -3. Activate virtual environment -4. Upgrade `pip` in virtual environment -5. Install dependencies listed in `requirements.txt` -6. Set the required environment variables -7. Run the application -8. Generate traffic - -| Step | Windows (PowerShell) | Linux / MacOS (bash) | -|------|------------------------------------------|-------------------------------------------| -| 1 | `pip install virtualenv` | `pip3 install virtualenv` | -| 2 | `python -m venv venv` | `python3 -m venv venv` | -| 3 | `.\venv\Scripts\Activate.ps1` | `source venv/bin/activate` | -| 4 | `python -m pip install --upgrade pip` | `python3 -m pip install --upgrade pip` | -| 5 | `pip install flask`
`pip install opentelemetry-instrumentation-flask`
`pip install opentelemetry-exporter-otlp`
`pip install opentelemetry-distro` | `pip install flask`
`pip install opentelemetry-instrumentation-flask`
`pip install opentelemetry-exporter-otlp`
`pip install opentelemetry-distro` | -| 6 | Set environment variables with `$Env:` | Set environment variables with `export` | -| 7 | `opentelemetry-instrument python app.py` | `opentelemetry-instrument python3 app.py` | -| 8 | `.\load-generator.ps1` | `./load-generator.sh` | - -Set the Application Name and New Relic [Ingest - License Key](https://docs.newrelic.com/docs/apis/intro-apis/new-relic-api-keys/#license-key). - -## Details -1. To send telemetry data to New Relic, set the following environment variables - - Windows (PowerShell) - ``` - $Env:OTEL_EXPORTER_OTLP_ENDPOINT="https://otlp.nr-data.net:4317" - $Env:OTEL_EXPORTER_OTLP_HEADERS="api-key=XXXX...NRAL" - $Env:OTEL_SERVICE_NAME="getting-started-python" - $Env:OTEL_RESOURCE_ATTRIBUTES="service.instance.id=localhost" - ``` - - Linux / macOS - ``` - export OTEL_EXPORTER_OTLP_ENDPOINT="https://otlp.nr-data.net:4317" - export OTEL_EXPORTER_OTLP_HEADERS="api-key=XXXX...NRAL" - export OTEL_SERVICE_NAME="getting-started-python" - export OTEL_RESOURCE_ATTRIBUTES="service.instance.id=localhost" - ``` - -2. Install the following packages to your virtual environment. - ``` - pip install flask - pip install opentelemetry-instrumentation-flask - pip install opentelemetry-exporter-otlp - pip install opentelemetry-distro - ``` - -3. No changes to the code is needed, just run the app as usual but with `opentelemetry-instrument` before the command to start the application. For example: - - ``` - opentelemetry-instrument python app.py - ``` - -4. To generate traffic, in a new terminal tab, run the following command: - PowerShell: - ```powershell - .\load-generator.ps1 - ``` - Bash: - ```bash - ./load-generator.sh - ``` - -5. To shut down the program, run the following in both shells or terminal tabs: `CTRL + C`. \ No newline at end of file diff --git a/getting-started-guides/python/app.py b/getting-started-guides/python/app.py new file mode 100644 index 00000000..726a04e3 --- /dev/null +++ b/getting-started-guides/python/app.py @@ -0,0 +1,39 @@ +from flask import Flask, jsonify, request +import logging +from opentelemetry import metrics +from opentelemetry import trace +from opentelemetry.trace.status import Status, StatusCode + +fib_counter = metrics.get_meter("opentelemetry.instrumentation.custom").create_counter("fibonacci.invocations", unit="1", description="Measures the number of times the fibonacci method is invoked.") + +logging.basicConfig(level=logging.DEBUG) + +app = Flask(__name__) + +@app.route("/fibonacci") +@trace.get_tracer("opentelemetry.instrumentation.custom").start_as_current_span("/fibonacci") +def fibonacci(): + args = request.args + x = int(args.get("n")) + error_message = "n must be 1 <= n <= 90." + trace.get_current_span().set_attribute("fibonacci.n", x) + + try: + assert 1 <= x <= 90 + array = [0, 1] + for n in range(2, x + 1): + array.append(array[n - 1] + array[n - 2]) + + trace.get_current_span().set_attribute("fibonacci.result", array[x]) + fib_counter.add(1, {"fibonacci.valid.n": "true"}) + logging.info("Compute fibonacci(" + str(x) + ") = " + str(array[x])) + return jsonify(n=x, result=array[x]) + + except AssertionError: + trace.get_current_span().record_exception(exception=Exception, attributes={"exception.type": "AssertionError", "exception.message": error_message}) + trace.get_current_span().set_status(Status(StatusCode.ERROR, error_message)) + fib_counter.add(1, {"fibonacci.valid.n": "false"}) + logging.error("Failed to compute fibonacci(" + str(x) + ")") + return jsonify({"message": error_message}) + +app.run(host='0.0.0.0', port=8080) diff --git a/getting-started-guides/python/requirements.txt b/getting-started-guides/python/requirements.txt new file mode 100644 index 00000000..34465d57 --- /dev/null +++ b/getting-started-guides/python/requirements.txt @@ -0,0 +1,28 @@ +blinker==1.8.2 +certifi==2024.2.2 +charset-normalizer==3.3.2 +click==8.1.7 +Deprecated==1.2.14 +Flask==3.0.3 +googleapis-common-protos==1.63.0 +idna==3.7 +importlib-metadata==7.0.0 +itsdangerous==2.2.0 +Jinja2==3.1.4 +MarkupSafe==2.1.5 +opentelemetry-api==1.24.0 +opentelemetry-distro==0.45b0 +opentelemetry-exporter-otlp-proto-common==1.24.0 +opentelemetry-exporter-otlp-proto-http==1.24.0 +opentelemetry-instrumentation==0.45b0 +opentelemetry-proto==1.24.0 +opentelemetry-sdk==1.24.0 +opentelemetry-semantic-conventions==0.45b0 +protobuf==4.25.3 +requests==2.32.3 +setuptools==70.0.0 +typing_extensions==4.12.0 +urllib3==2.2.1 +Werkzeug==3.0.3 +wrapt==1.16.0 +zipp==3.19.0 diff --git a/getting-started-guides/supporting-files/envoy.yaml b/getting-started-guides/supporting-files/envoy.yaml index 56725b60..99f9b940 100644 --- a/getting-started-guides/supporting-files/envoy.yaml +++ b/getting-started-guides/supporting-files/envoy.yaml @@ -24,6 +24,8 @@ static_resources: route: { cluster: go, prefix_rewrite: "/fibonacci" } - match: { prefix: "/javascript/fibonacci" } route: { cluster: javascript, prefix_rewrite: "/fibonacci" } + - match: { prefix: "/python/fibonacci" } + route: { cluster: python, prefix_rewrite: "/fibonacci" } - match: { prefix: "/ruby/fibonacci" } route: { cluster: ruby, prefix_rewrite: "/fibonacci" } http_filters: @@ -68,6 +70,18 @@ static_resources: socket_address: address: javascript port_value: 8080 + - name: python + type: STRICT_DNS + lb_policy: ROUND_ROBIN + load_assignment: + cluster_name: python + endpoints: + - lb_endpoints: + - endpoint: + address: + socket_address: + address: python + port_value: 8080 - name: ruby type: STRICT_DNS lb_policy: ROUND_ROBIN diff --git a/getting-started-guides/supporting-files/loadgenerator.py b/getting-started-guides/supporting-files/loadgenerator.py index 0748dcea..ffcc49ab 100644 --- a/getting-started-guides/supporting-files/loadgenerator.py +++ b/getting-started-guides/supporting-files/loadgenerator.py @@ -6,7 +6,7 @@ def signal_handler(signal, frame): signal.signal(signal.SIGINT, signal_handler) -languages = ["dotnet", "go", "javascript", "ruby"] +languages = ["dotnet", "go", "javascript", "python", "ruby"] while True: n = random.randint(1, 100)