Airflow 删除任务日志

Airflow 删除任务日志,airflow,Airflow,我正在运行5个DAG,它们在base\u log\u文件夹中在一个月内总共生成了大约6GB的日志数据。我刚刚添加了一个remote\u base\u log\u文件夹,但它似乎不排除登录到base\u log\u文件夹 是否存在自动删除旧日志文件、旋转它们或强制不登录磁盘(基本日志文件夹)的方法仅在远程存储中?我不认为存在旋转机制,但您可以将它们存储在S3或google云存储中,如下所述:Airflow维护人员不认为截断日志是Airflow核心逻辑的一部分,请看,在本期中,维护人员建议更改日志级

我正在运行5个DAG,它们在
base\u log\u文件夹中在一个月内总共生成了大约6GB的日志数据。我刚刚添加了一个
remote\u base\u log\u文件夹
,但它似乎不排除登录到
base\u log\u文件夹


是否存在自动删除旧日志文件、旋转它们或强制不登录磁盘(基本日志文件夹)的方法仅在远程存储中?

我不认为存在旋转机制,但您可以将它们存储在S3或google云存储中,如下所述:

Airflow维护人员不认为截断日志是Airflow核心逻辑的一部分,请看,在本期中,维护人员建议更改日志级别以避免太多日志数据

在PR中,我们可以学习如何在
afflow.cfg
中更改日志级别

祝你好运。

请参考

这个插件有DAG,可以杀死暂停的任务和日志清理。
您可以掌握这些概念,并提出一个新的DAG,它可以根据您的要求进行清理。

我们通过实现自己的
FileTaskHandler
来删除任务日志,然后在
aiffort.cfg
中指向它。因此,我们覆盖默认的LogHandler,只保留N个任务日志,而不安排额外的DAG

我们使用的是
气流==1.10.1

[core]
logging_config_class = log_config.LOGGING_CONFIG
log\u config.LOGGING\u config

BASE_LOG_FOLDER = conf.get('core', 'BASE_LOG_FOLDER')
FOLDER_TASK_TEMPLATE = '{{ ti.dag_id }}/{{ ti.task_id }}'
FILENAME_TEMPLATE = '{{ ti.dag_id }}/{{ ti.task_id }}/{{ ts }}/{{ try_number }}.log'

LOGGING_CONFIG = {
    'formatters': {},
    'handlers': {
        '...': {},
        'task': {
            'class': 'file_task_handler.FileTaskRotationHandler',
            'formatter': 'airflow.job',
            'base_log_folder': os.path.expanduser(BASE_LOG_FOLDER),
            'filename_template': FILENAME_TEMPLATE,
            'folder_task_template': FOLDER_TASK_TEMPLATE,
            'retention': 20
        },
        '...': {}
    },
    'loggers': {
        'airflow.task': {
            'handlers': ['task'],
            'level': JOB_LOG_LEVEL,
            'propagate': False,
        },
        'airflow.task_runner': {
            'handlers': ['task'],
            'level': LOG_LEVEL,
            'propagate': True,
        },
        '...': {}
    }
}
文件任务处理程序。文件任务旋转处理程序

import os
import shutil

from airflow.utils.helpers import parse_template_string
from airflow.utils.log.file_task_handler import FileTaskHandler


class FileTaskRotationHandler(FileTaskHandler):

    def __init__(self, base_log_folder, filename_template, folder_task_template, retention):
        """
        :param base_log_folder: Base log folder to place logs.
        :param filename_template: template filename string.
        :param folder_task_template: template folder task path.
        :param retention: Number of folder logs to keep
        """
        super(FileTaskRotationHandler, self).__init__(base_log_folder, filename_template)
        self.retention = retention
        self.folder_task_template, self.folder_task_template_jinja_template = \
            parse_template_string(folder_task_template)

    @staticmethod
    def _get_directories(path='.'):
        return next(os.walk(path))[1]

    def _render_folder_task_path(self, ti):
        if self.folder_task_template_jinja_template:
            jinja_context = ti.get_template_context()
            return self.folder_task_template_jinja_template.render(**jinja_context)

        return self.folder_task_template.format(dag_id=ti.dag_id, task_id=ti.task_id)

    def _init_file(self, ti):
        relative_path = self._render_folder_task_path(ti)
        folder_task_path = os.path.join(self.local_base, relative_path)
        subfolders = self._get_directories(folder_task_path)
        to_remove = set(subfolders) - set(subfolders[-self.retention:])

        for dir_to_remove in to_remove:
            full_dir_to_remove = os.path.join(folder_task_path, dir_to_remove)
            print('Removing', full_dir_to_remove)
            shutil.rmtree(full_dir_to_remove)

        return FileTaskHandler._init_file(self, ti)

我知道这听起来很野蛮,但是你试过把
base\u log\u文件夹
指向
/dev/null
?我使用气流作为容器的一部分,因此我也不关心文件,只要记录器也将管道连接到
STDOUT


不过,我不确定这对S3有多好。

对于您的具体问题,我有一些建议。 对于这些,您将始终需要一个专门的日志记录配置,如以下回答中所述:

  • 自动删除旧日志文件并对其进行旋转

我还没有使用Python标准库中的
TimedRotatingFileHandler
的任何实际经验,但您可以尝试一下:

它不仅提供根据时间间隔旋转文件,而且如果指定
backupCount
参数,它甚至会删除旧的日志文件:

如果
backupCount
为非零,则最多将保留
backupCount
文件,如果发生滚动时将创建更多文件,则删除最旧的文件。删除逻辑使用间隔来确定要删除的文件,因此更改间隔可能会留下旧文件

这听起来很像是解决第一个问题的最佳方案


  • 强制不在磁盘(基本日志文件夹)上登录,而仅在远程存储中登录

在这种情况下,您应该指定日志配置,这样您就不会有任何写入文件的日志处理程序,即删除所有
FileHandlers

相反,尝试查找直接将输出发送到远程地址的日志处理程序。 例如,哪些直接登录到ElasticSearch,但需要在日志调用中添加一些额外字段。 或者,编写自己的处理程序类,并让它继承Python标准库的



最后一个建议是将
TimedRotatingFileHandler
和setup ElasticSearch与FileBeat结合使用,这样您就可以将日志存储在ElasticSearch中(即远程),但是您不会在Airflow磁盘上存储大量日志,因为它们将被
TimedRotatingFileHandler的
备份保留策略删除
通常apache Airflow会因为以下三个原因占用磁盘空间

  • 1。气流调度器记录文件 2.mysql二进制日志[主要] 3.xcom表格记录。
为了定期清理,我设置了一个dag,每天运行,清理二进制日志,截断xcom表以释放磁盘空间 您可能还需要安装[pip install mysql connector python]。 为了清理调度程序日志文件,我每周手动删除它们两次,以避免由于某些原因需要删除日志的风险

我通过[sudo rm-rd/logs/]命令清理日志文件

下面是我的python代码供参考

'
"""Example DAG demonstrating the usage of the PythonOperator."""

from airflow import DAG
from airflow.operators.python import PythonOperator
from datetime import datetime, timedelta
from airflow.utils.dates import days_ago
from airflow.operators.bash import BashOperator
from airflow.providers.postgres.operators.postgres import PostgresOperator


args = {
    'owner': 'airflow',
    'email_on_failure':True,
    'retries': 1,
    'email':['Your Email Id'],
    'retry_delay': timedelta(minutes=5)
}


dag = DAG(
    dag_id='airflow_logs_cleanup',
    default_args=args,
    schedule_interval='@daily',
    start_date=days_ago(0),
    catchup=False,
    max_active_runs=1,
    tags=['airflow_maintenance'],
)

def truncate_table():
    import mysql.connector

    connection = mysql.connector.connect(host='localhost',
                                         database='db_name',
                                         user='username',
                                         password='your password',
                                         auth_plugin='mysql_native_password')
    cursor = connection.cursor()
    sql_select_query = """TRUNCATE TABLE xcom"""
    cursor.execute(sql_select_query)
    connection.commit()
    connection.close()
    print("XCOM Table truncated successfully")


def delete_binary_logs():
    import mysql.connector
    from datetime import datetime
    date = datetime.today().strftime('%Y-%m-%d')
    connection = mysql.connector.connect(host='localhost',
                                         database='db_name',
                                         user='username',
                                         password='your_password',
                                         auth_plugin='mysql_native_password')
    cursor = connection.cursor()
    query = 'PURGE BINARY LOGS BEFORE ' + "'" + str(date) + "'"

    sql_select_query = query
    cursor.execute(sql_select_query)
    connection.commit()
    connection.close()
    print("Binary logs deleted  successfully")

t1 = PythonOperator(

    task_id='truncate_table',
    python_callable=truncate_table, dag=dag

)

t2 = PythonOperator(

    task_id='delete_binary_logs',
    python_callable=delete_binary_logs, dag=dag
)
t2 << t1
'
“”“演示PythonOperator用法的示例DAG。”“”
从气流导入DAG
从afflow.operators.python导入PythonOperator
从datetime导入datetime,timedelta
从airflow.utils.dates导入天\u
从afflow.operators.bash导入bash操作符
从afflow.providers.postgres.operators.postgres导入PostgresOperator
args={
“所有者”:“气流”,
“失败时发送电子邮件”:True,
“重试”:1,
“电子邮件”:[“您的电子邮件Id”],
“重试延迟”:时间增量(分钟=5)
}
dag=dag(
dag_id='airflow_logs_cleanup',
默认值_args=args,
计划时间间隔=“@daily”,
开始日期=日前(0),
catchup=False,
最大有效运行次数=1,
标签=['airflow_maintenance'],
)
def truncate_表():
导入mysql.connector
connection=mysql.connector.connect(host='localhost',
数据库='db\u name',
user='username',
password='your password',
auth\u plugin='mysql\u native\u password')
cursor=connection.cursor()
sql\u选择\u查询=“截断表xcom”“”
cursor.execute(sql\u select\u查询)
commit()连接
连接。关闭()
打印(“XCOM表已成功截断”)
def delete_binary_logs():
导入mysql.connector
从日期时间导入日期时间
date=datetime.today().strftime(“%Y-%m-%d”)
connection=mysql.connector.connect(host='localhost',
数据库='db\u name',