Python 模板在气流';s打开\u失败\u回调
问题:在调用失败时使用的Python 模板在气流';s打开\u失败\u回调,python,airflow,Python,Airflow,问题:在调用失败时使用的运算符中的模板不呈现 def报告失败(上下文): 发送电子邮件=电子邮件操作员( task\u id=“email\u失败”, 收件人=电子邮件收件人, subject=“{execution_date}}”, html_content=get_email_body()#返回“body:{{execution_date}” ) #我尝试的一个解决方案失败了:AttributeError:“EmailOperator”对象没有属性“render\u template\u f
运算符中的模板不呈现
def报告失败(上下文):
发送电子邮件=电子邮件操作员(
task\u id=“email\u失败”,
收件人=电子邮件收件人,
subject=“{execution_date}}”,
html_content=get_email_body()#返回“body:{{execution_date}”
)
#我尝试的一个解决方案失败了:AttributeError:“EmailOperator”对象没有属性“render\u template\u fields”
#这很奇怪,因为这种方法出现在EmailOperator的基础上:BaseOperator
#发送\电子邮件。呈现\模板\字段(上下文)
发送电子邮件。执行(上下文)
默认参数={
“on\u failure\u callback”:报告\u失败
}
发送的电子邮件包含字面上的{{execution\u date}}
,而不是呈现值
在这个简单的例子中,我可以使用.format(**context)
对这些模板化字符串进行适当的格式化,但是在context
不可用且模板化工作正常的其他地方,我可以重复使用此电子邮件发送
更大的目标是在DAG(或其子DAG)的任何步骤失败时启动此“错误处理程序”。您这样做可能会奏效,但您肯定需要双花括号来创建jinja模板:
{execution\u date}
->{{execution\u date}
您还应该能够使用上下文参数获取执行日期:
def报告失败(上下文):
发送电子邮件=电子邮件操作员(
task\u id=“email\u失败”,
收件人=电子邮件收件人,
主题=上下文[“执行日期”],
html_content=“Body:{execution_date}”。格式(execution_date=context['execution_date'])
)
您可能还会发现这一点很有用:
模板在那里不起作用的原因是,模板不是由
execute()
来呈现的,而是事先呈现的
你需要做的就是
def报告_故障(上下文):
发送电子邮件=电子邮件操作员(
task\u id=“email\u失败”,
收件人=电子邮件收件人,
subject=“{execution_date}}”,
html_content=get_email_body()#返回“body:{{execution_date}”
)
send_email.dag=上下文['dag']
send_email.start_date=send_email.dag.start_date
发送电子邮件。呈现模板字段(上下文,jinja\u env=context['dag'].get\u template\u env())
发送电子邮件。执行(上下文)
使用不同的方法多次尝试后,我发现以下解决方案有效:
解决方案A:不要使用on\u failure\u回调,而是使用trigger\u rule='all\u failed'/'one\u failed'创建任务;
这是雅各布提出的气流的松弛,它似乎工作良好。这是概念上最简单的IMHO
status_failed = SimpleHttpOperator(
trigger_rule='all_failed', # See https://airflow.apache.org/docs/stable/concepts.html
task_id='updateStatus',
...
)
email_failed = EmailOperator(
trigger_rule='all_failed',
)
start_task >> do_the_thing >> status_success >> email_success
# Handling errors in case any job fails
email_success >> email_failed
email_success >> status_failed
示例:成功运行,将整个DAG的状态设置为“成功”,并将错误处理程序设置为跳过。
解决方案B:使用on_failure_回调并手动渲染模板
正如Ash所说的模板不起作用的原因是,模板不是由execute()呈现的,而是事先呈现的。
我发现以下解决方案起作用:
def report_failure(context):
send_email = EmailOperator(
task_id="email_failed",
start_date=datetime(2015, 12, 1), # Any date in the past, if you won't set it you will get an error
to=emailreceipients,
subject="{{execution_date}}",
html_content=get_email_body() # Which returns "Body: {{execution_date}}"
)
# Set DAG, otherwise we will get errors
send_email.dag = context['dag']
# Manually render templates
# send_email.render_template_fields(context) # Working in Airflow 1.10.6
# send_email.html_content = send_email.render_template('', send_email.html_content, context) # Working in Airflow 1.10.4
# Looking at codebase seems to be working in both versions
send_email.html_content = send_email.get_template_env().from_string(send_email.html_content).render(**context)
send_email.execute(context)
它的问题是,这些函数似乎经常更改,因此可能很难对其进行更新
解决方案C:使用上下文
变量格式化消息
正如雅各布所建议的
当生成模板的代码与不使用上下文的操作符共享时,它不能解决我的特殊情况。尽管如此,它可能对更简单的情况有所帮助,因此我将其发布在这里。需要帮助在on_failure_回调中呈现jinja模板电子邮件ID 使用Variable.get('email\u edw\u alert')可以很好地工作,但我不想使用Variable方法来避免命中DB 下面是Dag文件
import datetime
import os
from functools import partial
from datetime import timedelta
from airflow.models import DAG,Variable
from airflow.contrib.operators.snowflake_operator import SnowflakeOperator
from alerts.email_operator import dag_failure_email
def get_db_dag(
*,
dag_id,
start_date,
schedule_interval,
max_taskrun,
max_dagrun,
proc_nm,
load_sql
):
default_args = {
'owner': 'airflow',
'start_date': start_date,
'provide_context': True,
'execution_timeout': timedelta(minutes=max_taskrun),
'retries': 0,
'retry_delay': timedelta(minutes=3),
'retry_exponential_backoff': True,
'email_on_retry': False,
}
dag = DAG(
dag_id=dag_id,
schedule_interval=schedule_interval,
dagrun_timeout=timedelta(hours=max_dagrun),
template_searchpath=tmpl_search_path,
default_args=default_args,
max_active_runs=1,
catchup='{{var.value.dag_catchup}}',
on_failure_callback=partial(dag_failure_email, config={'email_address': '{{var.value.email_edw_alert}}'}),
)
load_table = SnowflakeOperator(
task_id='load_table',
sql=load_sql,
snowflake_conn_id=CONN_ID,
autocommit=True,
dag=dag,
)
load_table
return dag
# ======== DAG DEFINITIONS #
edw_table_A = get_db_dag(
dag_id='edw_table_A',
start_date=datetime.datetime(2020, 5, 21),
schedule_interval='0 5 * * *',
max_taskrun=3, # Minutes
max_dagrun=1, # Hours
load_sql='recon/extract.sql',
)
下面是python代码
import logging
from airflow.utils.email import send_email
from airflow.models import Variable
logger = logging.getLogger(__name__)
TIME_FORMAT = "%Y-%m-%d %H:%M:%S"
def dag_failure_email(context, config=None):
config = {} if config is None else config
task_id = context.get('task_instance').task_id
dag_id = context.get("dag").dag_id
execution_time = context.get("execution_date").strftime(TIME_FORMAT)
reason = context.get("reason")
alerting_email_address = config.get('email_address')
dag_failure_html_body = f"""<html>
<header><title>The following DAG has failed!</title></header>
<body>
<b>DAG Name</b>: {dag_id}<br/>
<b>Task Id</b>: {task_id}<br/>
<b>Execution Time (UTC)</b>: {execution_time}<br/>
<b>Reason for Failure</b>: {reason}<br/>
</body>
</html>
"""
try:
if reason != 'dagrun_timeout':
send_email(
to=alerting_email_address,
subject=f"Airflow alert: <DagInstance: {dag_id} - {execution_time} [failed]",
html_content=dag_failure_html_body,
)
except Exception as e:
logger.error(
f'Error in sending email to address {alerting_email_address}: {e}',
exc_info=True,
)
导入日志
从afflow.utils.email导入发送电子邮件
从airflow.models导入变量
logger=logging.getLogger(_名称__)
时间\u格式=“%Y-%m-%d%H:%m:%S”
def dag_故障_电子邮件(上下文,配置=无):
config={}如果config是None-else-config
task\u id=context.get('task\u instance')。task\u id
dag_id=context.get(“dag”).dag_id
执行时间=context.get(“执行日期”).strftime(时间格式)
reason=context.get(“reason”)
警报\u email\u address=config.get('email\u address'))
dag\u失败\u html\u正文=f”“”
以下DAG已失败!
DAG名称:{DAG_id}
任务Id:{Task_Id}
执行时间(UTC):{Execution_Time}
失败原因:{Reason}
"""
尝试:
如果有理由“运行超时”:
发送电子邮件(
收件人=通知电子邮件地址,
主题=f"气流警报:我在给出这个示例时犯了错误。我使用了双大括号,但它不起作用。我不能/不想直接使用上下文
参数,因为正如我提到的,电子邮件正文来自实用函数。我更新了示例以更清楚地说明问题。我不完全确定on\u failure\u回调
是用于触发的更多运算符,这可能是模板设置不起作用的原因。我建议使用以下方法解决此问题:格式
和上下文
,或者将此EmailOperator添加到dag中,使用触发器规则一个失败
发送电子邮件。呈现模板字段(上下文)
不起作用,因为我浏览了1.10.6版,而Docker中有1.10.4版。我收到错误操作员尚未分配给DAG
。添加了send_email.DAG=context['DAG']
,然后得到DAG没有设置开始日期(或类似).我正在尝试不同的方法..我还需要向EmailOperator添加start\u date=datetime(2015,12,1)
。
try:
if reason != 'dagrun_timeout':
send_email = EmailOperator(
to=alerting_email_address,
task_id='email_task',
subject=f"Airflow alert: <DagInstance: {dag_id} - {execution_time} [failed]",
params={'content1': 'random'},
html_content=dag_failure_html_body,
)
send_email.dag = context['dag']
#send_email.to = send_email.get_template_env().from_string(send_email.to).render(**context)
send_email.to = send_email.render_template(alerting_email_address, send_email.to, context)
send_email.execute(context)
except Exception as e:
logger.error(
f'Error in sending email to address {alerting_email_address}: {e}',
exc_info=True,
)