# Flask

> Instrument your Flask application with OpenTelemetry to send traces, metrics, and logs to Last9

Source: https://last9.io/docs/integrations/frameworks/python/flask/

Use OpenTelemetry to instrument your Flask application and send telemetry data to Last9. This integration provides automatic instrumentation for HTTP requests, database queries, templating, and other operations in your Flask application.

You can either run OpenTelemetry Collector as a separate service or send telemetry directly from your application to Last9.

## Prerequisites

Before setting up Flask monitoring, ensure you have:

- Python 3.7 or higher installed
- Flask application running
- Last9 account with integration credentials
- `pip` package manager available

1.  **Install OpenTelemetry Packages**

    Install the required OpenTelemetry packages for Flask instrumentation:

    ```bash
    pip install opentelemetry-api opentelemetry-sdk opentelemetry-exporter-otlp opentelemetry-distro
    ```

    You can also add these packages to your `requirements.txt` file and install them using:

    ```bash
    pip install -r requirements.txt
    ```

2.  **Bootstrap Automatic Instrumentation**

    Run the following command to detect and install the required instrumentation packages for your Flask application:

    ```bash
    opentelemetry-bootstrap -a requirements
    ```

    This command analyzes your application dependencies and suggests relevant OpenTelemetry instrumentation packages. Common packages for Flask applications include:

    ```
    opentelemetry-instrumentation-asyncio
    opentelemetry-instrumentation-dbapi
    opentelemetry-instrumentation-logging
    opentelemetry-instrumentation-sqlite3
    opentelemetry-instrumentation-threading
    opentelemetry-instrumentation-urllib
    opentelemetry-instrumentation-wsgi
    opentelemetry-instrumentation-flask
    opentelemetry-instrumentation-grpc
    opentelemetry-instrumentation-jinja2
    opentelemetry-instrumentation-requests
    opentelemetry-instrumentation-urllib3
    ```

3.  **Install Instrumentation Packages**

    Install all the recommended instrumentation packages automatically:

    ```bash
    opentelemetry-bootstrap -a install
    ```

    This command installs all the necessary instrumentation packages based on your application's dependencies.

4.  **Set Environment Variables**

    Configure OpenTelemetry environment variables for your Flask application. Replace the placeholder values with your actual Last9 credentials:

    ```bash
    export OTEL_SERVICE_NAME=<service_name>
    export OTEL_EXPORTER_OTLP_ENDPOINT={{ .Logs.WriteURL }}
    export OTEL_EXPORTER_OTLP_HEADERS="Authorization={{ .Logs.AuthValue }}"
    export OTEL_TRACES_EXPORTER=otlp
    export OTEL_TRACES_SAMPLER="always_on"
    export OTEL_RESOURCE_ATTRIBUTES="deployment.environment=local"
    export OTEL_LOG_LEVEL=error
    ```

    **Environment Variables Explained:**

    - `OTEL_SERVICE_NAME`: Name of your Flask service
    - `OTEL_EXPORTER_OTLP_ENDPOINT`: Last9 endpoint URL for telemetry data
    - `OTEL_EXPORTER_OTLP_HEADERS`: Authentication header for Last9
    - `OTEL_RESOURCE_ATTRIBUTES`: Service metadata including environment

5.  **Run Your Flask Application**

    Choose the deployment method that matches your setup:

**Development Server**

    For development or simple deployments, start your Flask application with OpenTelemetry instrumentation:

    ```bash
    opentelemetry-instrument python app.py
    ```

    This starts your Flask application with automatic OpenTelemetry instrumentation enabled.

**Gunicorn**

    For production deployments using Gunicorn, you need to use a pre-fork hook to initialize OpenTelemetry instrumentation correctly.

    Create a new file named `last9_gunicorn.py`:

    ```python
    from opentelemetry import metrics, trace
    from opentelemetry.exporter.otlp.proto.grpc.metric_exporter import (
        OTLPMetricExporter,
    )
    from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import (
        OTLPSpanExporter,
    )
    from opentelemetry.sdk.metrics import MeterProvider
    from opentelemetry.sdk.metrics.export import PeriodicExportingMetricReader
    from opentelemetry.sdk.resources import Resource
    from opentelemetry.sdk.trace import TracerProvider
    from opentelemetry.sdk.trace.export import BatchSpanProcessor
    from flask import Flask, g, current_app

    app = Flask(__name__)

    # Configuration
    app.config.setdefault('OTEL_METRIC_EXPORT_INTERVAL', 60000)

    # Initialize OpenTelemetry on application startup
    @app.before_first_request
    def setup_telemetry():
        # Initialize tracing
        tracer_provider = TracerProvider()
        trace.set_tracer_provider(tracer_provider)
        trace.get_tracer_provider().add_span_processor(
            BatchSpanProcessor(OTLPSpanExporter())
        )

        # Initialize metrics
        metric_reader = PeriodicExportingMetricReader(
            OTLPMetricExporter(),
            export_interval_millis=app.config['OTEL_METRIC_EXPORT_INTERVAL']
        )
        meter_provider = MeterProvider(metric_readers=[metric_reader])
        metrics.set_meter_provider(meter_provider)

        current_app.logger.info("OpenTelemetry initialized")
    ```

    Start the application with Gunicorn:

    ```bash
    opentelemetry-instrument gunicorn -c last9_gunicorn.py application:application
    ```

**Docker**

    If you're using Docker, update your Dockerfile's CMD instruction:

    ```dockerfile
    CMD ["opentelemetry-instrument", "gunicorn", "-c", "last9_gunicorn.py", "application:application"]
    ```

    For development server in Docker:

    ```dockerfile
    CMD ["opentelemetry-instrument", "python", "app.py"]
    ```

## What Gets Instrumented

When you use automatic instrumentation with Flask, OpenTelemetry automatically captures:

### HTTP Requests

- Request method, URL, and headers
- Response status codes and timing
- Route parameters and query strings
- Request and response body sizes

### Template Rendering

- Jinja2 template compilation and rendering
- Template names and render duration
- Template context and variables

### Database Operations

- SQL queries and execution times
- Database connection details
- Query parameters and result counts

### External API Calls

- HTTP client requests (using `requests`, `urllib`, etc.)
- Request/response details and timing
- Error handling and retry attempts

### WSGI Middleware

- WSGI application lifecycle
- Middleware processing times
- Request routing and dispatching

## Advanced Configuration

### Custom Service Metadata

Enhance your service metadata by updating the `OTEL_RESOURCE_ATTRIBUTES` environment variable:

```bash
export OTEL_RESOURCE_ATTRIBUTES="service.name=flask-api,service.version=1.5.0,deployment.environment=production,service.instance.id=web-server-1,team=backend"
```

### Sampling Configuration

Control trace sampling to manage data volume:

```bash
# Always sample (development)
export OTEL_TRACES_SAMPLER="always_on"

# Sample 10% of traces (production)
export OTEL_TRACES_SAMPLER="traceidratio"
export OTEL_TRACES_SAMPLER_ARG="0.1"
```

### Custom Span Attributes

Add custom attributes to your spans programmatically:

```python
from opentelemetry import trace
from flask import Flask, request

app = Flask(__name__)
tracer = trace.get_tracer(__name__)

@app.route('/users/<user_id>')
def get_user(user_id):
    with tracer.start_as_current_span("get_user") as span:
        span.set_attribute("user.id", user_id)
        span.set_attribute("operation.type", "user_lookup")
        span.set_attribute("request.remote_addr", request.remote_addr)

        # Your Flask logic here
        return {"user_id": user_id}
```

### Error Handling and Logging

Integrate OpenTelemetry with Flask's error handling:

```python
from opentelemetry import trace
from flask import Flask
import logging

app = Flask(__name__)
tracer = trace.get_tracer(__name__)

# Configure logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

@app.errorhandler(500)
def handle_internal_error(error):
    span = trace.get_current_span()
    if span:
        span.set_status(trace.Status(trace.StatusCode.ERROR))
        span.set_attribute("error.type", type(error).__name__)
        span.set_attribute("error.message", str(error))

    logger.error(f"Internal server error: {error}", exc_info=True)
    return "Internal Server Error", 500
```

## Verification

1. **Check Application Startup**

   When you start your Flask application, you should see OpenTelemetry initialization messages in the console output.

2. **Generate Test Traffic**

   Make some requests to your Flask application to generate telemetry data:

   ```bash
   curl http://localhost:5000/
   curl http://localhost:5000/health
   curl http://localhost:5000/api/users/123
   ```

3. **Verify Data in Last9**

   Log into your Last9 account and check that traces and metrics are being received in [Grafana](https://app.last9.io/grafana).

   Look for:

   - HTTP request traces with timing information
   - Template rendering spans
   - Database query spans
   - Error traces for failed requests

## Troubleshooting

### Common Issues

**Missing Flask Instrumentation**
If Flask routes aren't being traced, ensure the Flask instrumentation package is installed:

```bash
pip install opentelemetry-instrumentation-flask
```

**Gunicorn Worker Issues**
If using Gunicorn with multiple workers, ensure each worker initializes OpenTelemetry correctly. The pre-fork hook approach ensures consistent initialization.

**Environment Variables Not Set**
Verify your environment variables are properly configured:

```bash
env | grep OTEL_
```

**Template Rendering Not Traced**
Install Jinja2 instrumentation for template tracing:

```bash
pip install opentelemetry-instrumentation-jinja2
```

### Performance Considerations

- Use sampling in production to control trace volume
- Monitor the instrumentation overhead on your application performance
- Consider using async patterns for high-throughput applications
- Use the OpenTelemetry Collector as a buffer for high-traffic applications

### Best Practices

- Set meaningful service names that reflect your Flask application's purpose
- Use consistent environment naming across services
- Include version information in resource attributes
- Add custom spans for critical business operations
- Implement proper error handling with span status updates
- Use Flask's application context for span correlation

## Supported Libraries

OpenTelemetry provides automatic instrumentation for many popular Python libraries commonly used with Flask:

- **HTTP Clients**: `requests`, `urllib`, `urllib3`
- **Databases**: `sqlalchemy`, `psycopg2`, `pymysql`, `sqlite3`
- **Template Engines**: `jinja2`
- **Message Queues**: `celery`, `pika` (RabbitMQ)
- **Cloud Services**: `boto3` (AWS), `google-cloud`
- **WSGI Servers**: `gunicorn`, `uwsgi`, `waitress`

For a complete list of supported libraries, visit the [OpenTelemetry Python Contrib documentation](https://opentelemetry-python-contrib.readthedocs.io/en/latest/index.html).

## Need Help?

If you encounter any issues or have questions:

- Join our [Discord community](https://discord.com/channels/652153247672729619/652153247672729621) for real-time support
- Contact our support team at [support@last9.io](mailto:support@last9.io)
