-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathfunction_app.py
More file actions
132 lines (102 loc) · 4.26 KB
/
Copy pathfunction_app.py
File metadata and controls
132 lines (102 loc) · 4.26 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
"""
Copyright (c) AtLongLast Analytics LLC
Licensed under the Apache License, Version 2.0
Project: https://github.com/AtLongLastAnalytics/vigil
Author: Robert Long
Date: 2026-03
Version: 0.1.0
File: function_app.py
Description: Azure Function App entry point; defines the timer-triggered function that
orchestrates monitoring and email reporting.
"""
# import standard library
import logging
import os
from datetime import datetime, timezone
# import third-party libraries
import azure.functions as func
# import project modules
from archive_service import archive_runs
from config import load_config
from constants import PipelineStatus
from email_service import generate_email_body, send_email
from monitor import get_credential, get_recent_pipeline_runs
# configure logging
logging.basicConfig(level=logging.INFO)
# initialize logger
logger = logging.getLogger(__name__)
app = func.FunctionApp()
# schedule defaults to 06:00 and 08:00 UTC daily — override with MONITOR_SCHEDULE app setting
_MONITOR_SCHEDULE = os.environ.get("MONITOR_SCHEDULE", "0 0 6,8 * * *")
@app.timer_trigger(schedule=_MONITOR_SCHEDULE, arg_name="myTimer", run_on_startup=False)
def vigil_monitor(myTimer: func.TimerRequest) -> None:
"""
Monitor Synapse pipelines and send daily report.
Schedule is controlled by the MONITOR_SCHEDULE environment variable
(default: "0 0 6,8 * * *" — 06:00 and 08:00 UTC daily).
Fetches pipeline runs from the past N hours and sends an email report.
Args:
myTimer (func.TimerRequest): timer trigger request object provided by the Azure Functions host
"""
logger.info("=== Vigil Starting ===")
try:
# load and validate configuration
logger.info("Loading configuration...")
config = load_config()
# get Azure credential
logger.debug("Obtaining Azure credential...")
credential = get_credential(config)
# fetch recent pipeline runs
logger.info(f"Fetching pipeline runs for the past {config.hours_back} hours...")
runs = get_recent_pipeline_runs(credential, config)
if not runs:
logger.info("No pipeline runs found in the specified time range")
return
logger.info(f"Retrieved {len(runs)} pipeline runs")
now_utc = datetime.now(timezone.utc)
# structured summary — custom_dimensions are forwarded to Application Insights
failed_count = sum(1 for r in runs if r['status'] == PipelineStatus.FAILED)
succeeded_count = sum(1 for r in runs if r['status'] == PipelineStatus.SUCCEEDED)
in_progress_count = sum(1 for r in runs if r['status'] == PipelineStatus.IN_PROGRESS)
success_rate = (succeeded_count / len(runs) * 100) if runs else 0.0
logger.info(
f"Pipeline run summary: total={len(runs)}, succeeded={succeeded_count}, "
f"failed={failed_count}, in_progress={in_progress_count}",
extra={"custom_dimensions": {
"total_runs": len(runs),
"succeeded": succeeded_count,
"failed": failed_count,
"in_progress": in_progress_count,
"success_rate": f"{success_rate:.1f}",
"hours_back": config.hours_back,
}},
)
logger.debug("Generating email body...")
html_body = generate_email_body(runs, now_utc)
# create subject line
date_str = now_utc.strftime('%Y-%m-%d')
subject = (
f"Vigil - Pipeline Alert: {failed_count} Failed - {date_str}"
if failed_count > 0
else f"Vigil - All Clear - {date_str}"
)
# send email
logger.info(f"Sending email: {subject}")
send_email(
subject,
html_body,
config.acs_endpoint,
credential,
config.sender_address,
config.recipient_list
)
# archive pipeline run data
logger.debug("Archiving pipeline run data...")
archive_runs(runs, config, credential, now_utc)
logger.info("=== Vigil Completed Successfully ===")
except ValueError as e:
logger.error(f"Configuration error: {e}")
raise
except Exception as e:
logger.error(f"Unexpected error in pipeline monitor: {e}", exc_info=True)
raise