Skip to content

Commit 043acc6

Browse files
solonkoolesyaOlesia Solonko
andauthored
Add application insights integration to pygeoapi (#38)
* Add Application Insight integration * Add Application Insight integration --------- Co-authored-by: Olesia Solonko <olesia.solonko@ngi.no>
1 parent f103ad4 commit 043acc6

5 files changed

Lines changed: 137 additions & 0 deletions

File tree

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ When deploying in different environments (local, Kubernetes, etc.), the followin
2929
- `PYGEOAPI_SERVER_URL`: The external URL where pygeoapi will be accessible. This is used to generate correct links in the API responses.
3030
- For local development: `http://localhost:5000`
3131
- For Kubernetes/production: Your domain, e.g., `https://your-domain.com/api`
32+
- `APPLICATIONINSIGHTS_CONNECTION_STRING`: Connection string to Azure Application Insights. If not specifies, telemetry disabled, no user statistic gathered.
3233

3334
#### Frontend:
3435

docker-compose.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ services:
4141
- CONTAINER_HOST=0.0.0.0
4242
- CONTAINER_PORT=5000
4343
- PYGEOAPI_SERVER_URL=http://localhost:5000
44+
- APPLICATIONINSIGHTS_CONNECTION_STRING=""
4445
profiles:
4546
- local
4647

pygeoapi/docker/Dockerfile.pygeoapi

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,9 @@ RUN apt-get update \
3030
&& apt-get clean \
3131
&& rm -rf /var/lib/apt/lists/*
3232

33+
# Install Application Insights for Python
34+
RUN pip install opencensus-ext-azure opencensus-ext-flask opencensus-ext-requests
35+
3336
# Build version string
3437
ARG REPO_VERSION
3538
RUN PY_VER=$(python3 -c 'import pkg_resources;print(pkg_resources.get_distribution("pygeoapi").version)') \
@@ -38,6 +41,9 @@ RUN PY_VER=$(python3 -c 'import pkg_resources;print(pkg_resources.get_distributi
3841
# Create readiness indicator directory
3942
RUN mkdir -p /tmp/health
4043

44+
# Add Application Insights instrumentation script
45+
COPY pygeoapi/docker/appinsights_init.py /appinsights_init.py
46+
4147
# Add init script
4248
COPY pygeoapi/docker/init.sh /init.sh
4349
RUN chmod +x /init.sh
@@ -46,6 +52,9 @@ RUN chmod +x /init.sh
4652
COPY pygeoapi/docker/healthcheck.sh /healthcheck.sh
4753
RUN chmod +x /healthcheck.sh
4854

55+
# Environment variable for Application Insights
56+
ENV APPLICATIONINSIGHTS_CONNECTION_STRING=""
57+
4958
HEALTHCHECK --interval=15s --timeout=10s --start-period=180s --retries=10 CMD ["/healthcheck.sh"]
5059

5160
ENTRYPOINT ["/init.sh"]
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
# appinsights_init.py - Lightweight Application Insights setup for pygeoapi
2+
import os
3+
import logging
4+
from opencensus.ext.azure.log_exporter import AzureLogHandler
5+
from opencensus.ext.azure.trace_exporter import AzureExporter
6+
from opencensus.ext.flask.flask_middleware import FlaskMiddleware
7+
from opencensus.trace.samplers import ProbabilitySampler
8+
9+
10+
def setup_app_insights(app):
11+
"""
12+
Setup lightweight Application Insights for pygeoapi
13+
Only tracks user interactions and basic telemetry
14+
"""
15+
connection_string = os.environ.get('APPLICATIONINSIGHTS_CONNECTION_STRING')
16+
17+
if not connection_string:
18+
print("Application Insights not configured - APPLICATIONINSIGHTS_CONNECTION_STRING not set")
19+
return
20+
21+
try:
22+
# Setup request tracking with low sampling rate for lightweight monitoring
23+
middleware = FlaskMiddleware(
24+
app,
25+
exporter=AzureExporter(connection_string=connection_string),
26+
sampler=ProbabilitySampler(rate=0.1) # Sample 10% of requests for lightweight monitoring
27+
)
28+
29+
# Setup basic logging for user interactions
30+
logger = logging.getLogger(__name__)
31+
logger.addHandler(AzureLogHandler(connection_string=connection_string))
32+
logger.setLevel(logging.INFO)
33+
34+
# Add custom properties for better analytics
35+
@app.before_request
36+
def before_request():
37+
from flask import request
38+
from opencensus.trace import execution_context
39+
40+
tracer = execution_context.get_opencensus_tracer()
41+
if tracer:
42+
span = tracer.current_span()
43+
if span:
44+
# Add custom properties for analytics
45+
span.add_attribute('user_agent', request.headers.get('User-Agent', ''))
46+
span.add_attribute('referrer', request.headers.get('Referer', ''))
47+
span.add_attribute('api_endpoint', request.endpoint or 'unknown')
48+
49+
print("Application Insights configured successfully")
50+
51+
except Exception as e:
52+
print(f"Failed to setup Application Insights: {e}")
53+
54+
55+
# Custom logging function for user interФactions
56+
def log_user_interaction(action, details=None):
57+
"""Log user interactions for analytics"""
58+
connection_string = os.environ.get('APPLICATIONINSIGHTS_CONNECTION_STRING')
59+
if connection_string:
60+
logger = logging.getLogger('user_analytics')
61+
logger.addHandler(AzureLogHandler(connection_string=connection_string))
62+
63+
log_data = {'action': action}
64+
if details:
65+
log_data.update(details)
66+
67+
logger.info(f"User interaction: {action}", extra={'custom_dimensions': log_data})

pygeoapi/docker/init.sh

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,14 @@ fi
2020
PYGEOAPI_VERSION=$(pygeoapi --version 2>&1)
2121
echo "$(get_timestamp) [init] PyGeoAPI version: $PYGEOAPI_VERSION"
2222

23+
# Check Application Insights configuration
24+
if [ -n "$APPLICATIONINSIGHTS_CONNECTION_STRING" ]; then
25+
echo "$(get_timestamp) [init] Application Insights connection string found, enabling telemetry"
26+
export PYTHONPATH="/appinsights_init.py:$PYTHONPATH"
27+
else
28+
echo "$(get_timestamp) [init] No Application Insights connection string found, running without telemetry"
29+
fi
30+
2331
# Remove any existing readiness indicator
2432
rm -f /tmp/health/init_complete
2533
echo "$(get_timestamp) [init] Removed any existing readiness indicators"
@@ -44,6 +52,57 @@ mkdir -p /tmp/health
4452
touch /tmp/health/init_complete
4553
echo "$(get_timestamp) [init] Initialization complete"
4654

55+
# Start pygeoapi with Application Insights if configured
56+
if [ -n "$APPLICATIONINSIGHTS_CONNECTION_STRING" ]; then
57+
echo "$(get_timestamp) [init] Starting pygeoapi service with Application Insights"
58+
59+
# First, generate the OpenAPI document that pygeoapi needs
60+
echo "$(get_timestamp) [init] Generating OpenAPI document"
61+
pygeoapi openapi generate /pygeoapi/local.config.yml --output-file /tmp/openapi.yml
62+
63+
# Create a startup script that integrates Application Insights
64+
cat > /tmp/start_with_insights.py << 'EOF'
65+
import sys
66+
import os
67+
68+
# Add the root directory to Python path to find appinsights_init
69+
sys.path.insert(0, '/')
70+
71+
try:
72+
# Import the Application Insights setup
73+
import appinsights_init
74+
print("[init] Application Insights module loaded successfully")
75+
76+
# Import pygeoapi app
77+
from pygeoapi.flask_app import APP as app
78+
79+
# Setup Application Insights
80+
appinsights_init.setup_app_insights(app)
81+
print("[init] Application Insights integrated successfully")
82+
83+
except ImportError as e:
84+
print(f"[init] Application Insights setup skipped: {e}")
85+
# Continue without Application Insights
86+
pass
87+
except Exception as e:
88+
print(f"[init] Application Insights setup failed: {e}")
89+
# Continue without Application Insights
90+
pass
91+
92+
print("[init] Application Insights setup completed")
93+
EOF
94+
95+
# Execute the Application Insights setup (but don't start the app yet)
96+
python3 /tmp/start_with_insights.py
97+
98+
else
99+
echo "$(get_timestamp) [init] Starting pygeoapi service without Application Insights"
100+
# Still need to generate OpenAPI document
101+
echo "$(get_timestamp) [init] Generating OpenAPI document"
102+
pygeoapi openapi generate /pygeoapi/local.config.yml --output-file /tmp/openapi.yml
103+
fi
104+
105+
47106
# Start the pygeoapi service
48107
echo "$(get_timestamp) [init] Starting pygeoapi service"
49108
exec /entrypoint.sh "$@"

0 commit comments

Comments
 (0)