Python 异常处理与日志记录

👽发现宝藏

前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。【点击进入巨牛的人工智能学习网站】。

异常处理是任何编程语言中的重要组成部分,Python 也不例外。Python 提供了丰富的异常处理机制,让开发者可以更好地管理程序中出现的错误。除了捕获和处理异常外,记录异常信息也是至关重要的,以便日后排查问题和改进程序。本文将介绍如何在 Python 中捕获异常,并将异常信息记录到日志文件中。

异常处理

在 Python 中,使用 try-except 语句来捕获异常。try 代码块中放置可能引发异常的代码,except 代码块中处理异常情况。

try:
    # 可能引发异常的代码
    result = 10 / 0
except ZeroDivisionError as e:
    # 处理异常情况
    print("除零错误发生:", e)

在上面的例子中,当除以零时会触发 ZeroDivisionError 异常,except 代码块会捕获这个异常并进行处理。

异常日志记录

除了简单地在控制台打印异常信息,我们还可以将异常信息记录到日志文件中,以便后续分析。Python 提供了内置的 logging 模块,可以轻松实现这一功能。

import logging

# 配置日志记录器
logging.basicConfig(filename='error.log', level=logging.ERROR)

try:
    result = 10 / 0
except ZeroDivisionError as e:
    # 记录异常信息到日志文件
    logging.error("除零错误发生: %s", e)

在上述代码中,我们首先通过 basicConfig 方法配置了日志记录器,指定了日志文件名为 error.log,并设置记录级别为 ERROR,这意味着只有 ERROR 级别及以上的日志才会被记录。然后,在 except 代码块中,我们使用 logging.error 方法将异常信息记录到日志文件中。

日志文件分析

通过记录异常信息到日志文件,我们可以随时查看程序运行中出现的异常情况,以便及时定位和解决问题。可以使用文本编辑器或日志分析工具来查看日志文件内容,分析异常发生的原因和频率。

error.log:

ERROR:root:除零错误发生: division by zero

上述日志文件记录了一条除零错误的异常信息,其中包含了异常类型和具体错误信息。通过分析日志文件,我们可以清晰地了解到程序中存在的问题,并采取相应的措施进行修复。

高级日志记录配置

除了基本的日志记录配置外,logging 模块还提供了更多高级的配置选项,以满足不同场景下的需求。

1. 添加时间戳

可以通过配置 format 参数来添加时间戳到日志记录中,以便更好地跟踪异常发生的时间。

import logging

logging.basicConfig(filename='error.log', level=logging.ERROR, format='%(asctime)s - %(levelname)s - %(message)s')
2. 指定日志格式

可以通过配置 format 参数来指定日志的格式,以便更清晰地展示异常信息。

import logging

logging.basicConfig(filename='error.log', level=logging.ERROR, format='%(asctime)s - %(levelname)s - %(message)s - %(filename)s:%(lineno)d')
3. 添加异常堆栈信息

可以通过设置 exc_info 参数为 True 来添加异常的堆栈信息到日志记录中。

import logging

try:
    result = 10 / 0
except ZeroDivisionError as e:
    logging.error("除零错误发生: %s", e, exc_info=True)
4. 日志分级

除了 ERROR 级别外,logging 模块还支持其他几种日志级别,包括 DEBUG、INFO、WARNING、CRITICAL 等。可以根据实际需求选择合适的日志级别进行记录。

import logging

logging.basicConfig(filename='app.log', level=logging.DEBUG)

日志轮换与归档

对于大型项目或长期运行的程序,日志文件可能会变得非常庞大。为了解决这个问题,logging 模块支持日志轮换和归档功能,可以按照一定的策略将日志文件分割、压缩或移动到其他位置。

import logging
from logging.handlers import RotatingFileHandler

handler = RotatingFileHandler('app.log', maxBytes=10000, backupCount=5)
handler.setLevel(logging.DEBUG)
formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
handler.setFormatter(formatter)

logger = logging.getLogger(__name__)
logger.addHandler(handler)

logger.debug('This is a debug message')

在上述代码中,我们使用 RotatingFileHandler 类来实现日志轮换功能,设置了日志文件的最大字节数为 10000 字节,备份文件的数量为 5。当日志文件大小超过设定的最大字节数时,会自动创建新的日志文件,并将旧的日志文件备份。

日志配置文件

在较大的项目中,可能需要更加灵活和复杂的日志配置。为了避免在代码中硬编码日志配置,可以将日志配置信息单独存放在一个配置文件中,例如 logging.conf

[loggers]
keys=root

[handlers]
keys=fileHandler

[formatters]
keys=myFormatter

[logger_root]
level=DEBUG
handlers=fileHandler

[handler_fileHandler]
class=FileHandler
level=DEBUG
formatter=myFormatter
args=('app.log',)

[formatter_myFormatter]
format=%(asctime)s - %(levelname)s - %(message)s

然后在 Python 代码中使用 logging.config 模块加载配置文件。

import logging
import logging.config

logging.config.fileConfig('logging.conf')

logger = logging.getLogger(__name__)
logger.debug('This is a debug message')

异常链追踪

有时候,程序中的异常不仅仅是由当前位置的代码引发的,可能还涉及到调用栈上的其他函数或模块。为了更好地追踪异常的来源,可以使用 logging.exception 方法来记录异常链。

import logging

try:
    result = 10 / 0
except ZeroDivisionError as e:
    logging.exception("除零错误发生")

上述代码会将当前异常以及调用栈上的所有异常信息记录到日志中,方便后续排查问题。

异常处理装饰器

为了简化异常处理的代码,可以定义一个异常处理的装饰器,统一处理函数中的异常情况。

import logging
from functools import wraps

def handle_exceptions(logger):
    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            try:
                return func(*args, **kwargs)
            except Exception as e:
                logger.exception("异常发生: %s", e)
        return wrapper
    return decorator

logger = logging.getLogger(__name__)

@handle_exceptions(logger)
def divide(x, y):
    return x / y

result = divide(10, 0)

上述代码定义了一个名为 handle_exceptions 的装饰器,可以将函数中的异常信息记录到日志中。然后,我们将 divide 函数应用了该装饰器,即使在函数内部发生异常,也会被捕获并记录到日志中。

日志记录的性能考虑

在实际项目中,日志记录的性能也是需要考虑的重要因素。过于频繁或过度详细的日志记录可能会对程序的性能产生负面影响,因此需要在记录足够信息的同时,尽量减少对性能的影响。

1. 日志级别控制

在配置日志记录器时,可以根据不同的场景和需求设置不同的日志级别。在开发和测试阶段,可以设置为 DEBUG 级别以记录更详细的信息,而在生产环境中,可以设置为 WARNING 或更高级别以减少日志记录的数量,从而降低对性能的影响。

import logging

logging.basicConfig(level=logging.DEBUG)  # 开发/测试环境
# logging.basicConfig(level=logging.WARNING)  # 生产环境
2. 异步日志记录

为了减少日志记录对主程序的阻塞,可以使用异步日志记录器,将日志记录操作放到独立的线程或进程中执行,从而提高程序的响应速度。

import logging
import logging.handlers

logger = logging.getLogger(__name__)
handler = logging.handlers.QueueHandler()
logger.addHandler(handler)

queue = logging.handlers.QueueListener(handler, *logger.handlers)
queue.start()
3. 批量写入

在高并发环境中,频繁地向日志文件写入可能会导致文件 I/O 压力过大,影响系统性能。为了解决这个问题,可以考虑将日志记录操作改为批量写入,例如使用队列缓存日志消息,然后定期将队列中的消息批量写入日志文件。

import logging
import queue
import threading

log_queue = queue.Queue()

def log_worker():
    while True:
        record = log_queue.get()
        logger = logging.getLogger(record.name)
        logger.handle(record)
        log_queue.task_done()

thread = threading.Thread(target=log_worker, daemon=True)
thread.start()

logging.basicConfig(level=logging.INFO)
logging.handlers.QueueHandler(log_queue)

日志记录的安全性考虑

除了性能和功能需求外,日志记录的安全性也是需要考虑的重要因素。在实际项目中,可能存在一些敏感信息需要记录,如用户密码、API 密钥等,如果不加以保护,可能会造成信息泄露和安全漏洞。

1. 敏感信息过滤

在记录日志时,应当避免将敏感信息直接记录到日志中,尤其是在生产环境中。可以通过过滤器来检查日志消息,将其中的敏感信息进行替换或删除。

import logging

class SensitiveFilter(logging.Filter):
    def filter(self, record):
        record.msg = record.msg.replace('password', '***')
        return True

logger = logging.getLogger(__name__)
logger.addFilter(SensitiveFilter())
2. 安全存储

日志文件本身可能包含敏感信息,因此需要采取相应的安全措施来保护日志文件的安全性。可以将日志文件存储在受限制的访问路径下,设置合适的文件权限,以及定期清理和归档日志文件。

import logging

logging.basicConfig(filename='/var/log/app.log', level=logging.INFO)
3. 加密传输

如果日志信息需要通过网络传输,应当采取加密的方式来保护数据的安全性,防止被中间人窃取或篡改。可以使用 TLS/SSL 协议来加密日志传输通道。

import logging
import logging.handlers

handler = logging.handlers.SysLogHandler(address=('localhost', 514))
handler.socktype = socket.SOCK_STREAM
handler.ssl_context = ssl.create_default_context()

logger = logging.getLogger(__name__)
logger.addHandler(handler)

日志记录审计

除了安全性考虑外,日志记录还可以用于审计目的,帮助监控系统的运行情况、用户行为和操作记录,以及追踪安全事件和异常情况。通过分析日志记录,可以及时发现潜在的安全威胁和漏洞,并采取相应的措施进行预防和应对。

import logging

logging.basicConfig(filename='audit.log', level=logging.INFO)

def audit_log(func):
    def wrapper(*args, **kwargs):
        result = func(*args, **kwargs)
        logging.info(f'{func.__name__} called with arguments: {args}, {kwargs}. Result: {result}')
        return result
    return wrapper

@audit_log
def add(x, y):
    return x + y

add(3, 4)

在上述代码中,我们定义了一个名为 audit_log 的装饰器,用于记录函数调用的参数和结果到审计日志文件中。通过对关键操作和敏感函数应用该装饰器,可以实现对系统行为的全面监控和审计。

日志监控和警报

除了记录日志和审计功能外,日志监控和警报也是关键的安全实践。通过监控日志文件的变化、异常和异常频率,可以及时发现系统中的异常行为和安全威胁,从而采取相应的措施进行响应和修复。

1. 实时监控

使用监控工具或脚本定期扫描日志文件,监控日志文件的变化和异常情况,及时发现和处理异常事件。

import os
import time

logfile = '/var/log/app.log'

def monitor_logfile(logfile):
    while True:
        with open(logfile, 'r') as f:
            f.seek(0, os.SEEK_END)
            new_data = f.read()
            if new_data:
                print("发现新日志:", new_data)
        time.sleep(10)

monitor_logfile(logfile)
2. 异常警报

当发现异常日志时,可以通过发送警报通知管理员或安全团队,以便及时响应和处理。

import smtplib
from email.mime.text import MIMEText

def send_alert(subject, message):
    sender_email = "your_email@example.com"
    receiver_email = "admin@example.com"
    password = "your_email_password"
    
    msg = MIMEText(message)
    msg['Subject'] = subject
    msg['From'] = sender_email
    msg['To'] = receiver_email
    
    server = smtplib.SMTP('smtp.example.com', 587)
    server.starttls()
    server.login(sender_email, password)
    server.sendmail(sender_email, receiver_email, msg.as_string())
    server.quit()

# 在发现异常时调用 send_alert 函数发送邮件警报
3. 日志分析

使用日志分析工具或脚本对日志文件进行分析,识别异常行为、异常模式和安全事件,以及监控系统运行状态和性能表现。

import re

def analyze_logfile(logfile):
    pattern = r'ERROR|WARNING'
    with open(logfile, 'r') as f:
        for line in f:
            if re.search(pattern, line):
                print("发现异常日志:", line)

analyze_logfile(logfile)

自动化响应

为了进一步提高安全性和响应速度,可以将日志监控和警报的流程自动化,通过脚本或自动化工具来自动检测、警报和响应异常事件。

import subprocess

def automate_response(logfile):
    pattern = r'ERROR|WARNING'
    result = subprocess.run(['grep', '-E', pattern, logfile], capture_output=True)
    if result.stdout:
        send_alert("发现异常日志", result.stdout.decode())

automate_response(logfile)

日志备份和恢复

除了监控和警报外,日志备份和恢复也是关键的安全实践。定期备份日志文件可以防止日志丢失或被篡改,同时在需要时可以快速恢复到历史状态,帮助排查问题和恢复系统功能。

1. 定期备份

使用定时任务或脚本定期备份日志文件,将当前日志文件复制到备份目录或存储介质中,以确保日志数据的安全性和可靠性。

import shutil
import time

def backup_logfile(logfile, backup_dir):
    timestamp = time.strftime('%Y%m%d%H%M%S')
    backup_file = f'{backup_dir}/app_{timestamp}.log'
    shutil.copy(logfile, backup_file)
2. 日志归档

对于长期运行的系统,日志文件可能会变得非常庞大,为了节省存储空间和提高检索效率,可以定期对日志文件进行归档,将旧的日志文件压缩或移动到归档目录中。

import os
import shutil
import zipfile

def archive_logfile(logfile, archive_dir):
    if os.path.exists(logfile):
        shutil.move(logfile, archive_dir)
        with zipfile.ZipFile(f'{archive_dir}/app_logs.zip', 'w') as zipf:
            zipf.write(f'{archive_dir}/app.log', arcname='app.log')
3. 日志恢复

在需要恢复日志文件时,可以从备份或归档中找到对应的日志文件,并将其复制到原始位置进行恢复。

def restore_logfile(backup_file, original_file):
    shutil.copy(backup_file, original_file)

日志清理和管理

为了避免日志文件过大或占用过多存储空间,可以定期清理和管理日志文件,删除过期和不必要的日志数据,以保持系统的性能和稳定性。

import os
import glob

def cleanup_logs(log_dir, max_age):
    current_time = time.time()
    for logfile in glob.glob(f'{log_dir}/*.log'):
        if os.path.getmtime(logfile) < current_time - max_age:
            os.remove(logfile)

日志文件加密和权限控制

除了备份和管理外,保护日志文件的安全性也是至关重要的。通过加密日志文件和限制文件权限,可以防止未授权访问和篡改,保障日志数据的保密性和完整性。

1. 文件加密

使用加密算法对日志文件进行加密,以确保日志数据在存储和传输过程中的安全性。可以使用加密库如 cryptography 来实现对日志文件的加密和解密操作。

from cryptography.fernet import Fernet

def encrypt_logfile(logfile, key):
    with open(logfile, 'rb') as f:
        data = f.read()
    fernet = Fernet(key)
    encrypted_data = fernet.encrypt(data)
    with open(logfile + '.enc', 'wb') as f:
        f.write(encrypted_data)

def decrypt_logfile(encrypted_file, key):
    with open(encrypted_file, 'rb') as f:
        encrypted_data = f.read()
    fernet = Fernet(key)
    decrypted_data = fernet.decrypt(encrypted_data)
    with open(encrypted_file[:-4], 'wb') as f:
        f.write(decrypted_data)
2. 文件权限控制

通过设置文件权限来限制对日志文件的访问权限,只允许授权用户或组访问和修改日志文件,防止未授权用户篡改或查看日志内容。

import os

def set_file_permissions(logfile, mode):
    os.chmod(logfile, mode)

日志访问审计

对于敏感信息和重要日志数据,应当实施严格的访问控制和审计机制,记录和监控对日志文件的访问和操作记录,及时发现和处理未授权的访问和异常行为。

import subprocess

def audit_log_access(logfile):
    result = subprocess.run(['ls', '-l', logfile], capture_output=True)
    print("日志文件权限信息:", result.stdout.decode())

结论

通过加密日志文件、限制文件权限和实施访问审计,可以有效保护日志数据的安全性和完整性,防止未授权访问和篡改,保障系统的安全运行和数据保护。

在实际项目中,建议结合加密库、操作系统权限控制和审计工具,建立完善的日志安全管理机制,以确保日志系统的安全性和可靠性。

希望本文的介绍能够帮助您更好地理解和应用日志文件加密和权限控制在实际项目中的重要性和作用。

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/567128.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

解读无源 PoE 交换机:最佳选择比较指南

了解无源 PoE 交换机的复杂性可能是一项艰巨的任务。本文作为帮助您解码这些技术设备的综合指南。在这里&#xff0c;我们将详细比较各种无源 PoE 交换机、它们的独特特性以及它们的最佳使用案例。本指南重点关注客观事实&#xff0c;旨在为您提供必要的知识&#xff0c;以便在…

牛客周赛 Round 40(A,B,C,D,E,F)

比赛链接 官方讲解 这场简单&#xff0c;没考什么算法&#xff0c;感觉有点水。D是个分组01背包&#xff0c;01背包的一点小拓展&#xff0c;没写过的可以看看&#xff0c;这个分类以及这个题目本身都是很板的。E感觉就是排名放高了导致没人敢写&#xff0c;本质上是个找规律…

aardio - 【库】图片转字符画

库文件及例程下载&#xff1a;https://aardio.online/thread-261.htm

PyCharm 中的特殊标记

再使用 PyCharm 开发 Python 项目的时候&#xff0c;经常会有一些特殊的标记&#xff0c;有些是编辑器提示的代码规范&#xff0c;有些则为了方便查找而自定义的标记。 我在之前写过一些关于异常捕获的文章&#xff1a;Python3 PyCharm 捕获异常报 Too broad exception clause…

苹果手机怎么换行?分享3个换行小窍门

“晕&#xff01;第一次使用苹果手机&#xff0c;还有很多功能不懂&#xff0c;比如我在手机上打字怎么换行&#xff1f;我在键盘上找了很久&#xff0c;还是没有找到。” “为什么在发消息用苹果手机自带键盘没有换行键&#xff1f;我该怎么快速换行&#xff1f;求方法&#…

重学java 19.面向对象 继承 上

走不出的那段阴霾&#xff0c;很多时候只不过是&#xff0c;我们把它当成了唯一 —— 24.4.22 面向对象整体知识导向&#xff1a; 知识梳理&#xff1a; 1.知道继承的好处 2.会使用继承 3.继承之后成员变量和成员方法的访问特点 4.方法的重写&#xff0c;知道方法重写的使用场景…

sprinboot+人大金仓配置

1. .yml 配置 spring:datasource:type: com.alibaba.druid.pool.DruidDataSource#driverClassName: dm.jdbc.driver.DmDriver## todo 人大金仓driverClassName: com.kingbase8.Driverdruid:## todo 人大金仓master:url: jdbc:kingbase8://111.111.111.111:54321/dbname?cu…

helpdesk桌面运维常见问题解决

helpdesk是一套帮助IT团队管理IT工单生命周期、自动化日常工作、优化工作流程的软件或软件集合&#xff0c;它可以帮助IT团队提高生产力、降低成本、改善服务水平和客户体验。 在现代企业中&#xff0c;helpdesk桌面运维是一项至关重要的工作&#xff0c;helpdesk团队负责处理员…

虚拟信用卡是什么,可以用来开亚马逊店铺吗?

虚拟信用卡是什么&#xff1f; 虚拟信用卡就是一组由银行随机生成的数字的虚拟卡&#xff0c;使用起来方便快捷&#xff0c;对于个人而言保守自己的隐私&#xff0c;并且下卡快&#xff0c;即开即用 可以用来开亚马逊店铺吗&#xff1f; 可以&#xff0c;因为市场的需求很多…

面试官:在原生input上面使用v-model和组件上面使用有什么区别?

前言 还是上一篇面试官&#xff1a;来说说vue3是怎么处理内置的v-for、v-model等指令&#xff1f; 文章的那个粉丝&#xff0c;面试官接着问了他另外一个v-model的问题。 面试官&#xff1a;vue3的v-model都用过吧&#xff0c;来讲讲。 粉丝&#xff1a;v-model其实就是一个语…

储能展-CBTC-2024上海储能技术展会共话储能高质量发展

2024-CBTC上海国际储能技术展会 展会时间&#xff1a;7月24-26日 展会地址&#xff1a;上海&#xff08;虹桥&#xff09;国家会展中心 主办单位&#xff1a;湖南省电池产业协会/ 中国设备管理协会 /沪粤储能产业联盟/ 深圳国际投融资商会 国际氢能投融资与发展联…

Qt Debug模式下应用程序输出界面乱码【已解决】

Qt Debug模式下应用程序输出乱码 一、问题描述二、解决方法三、相关测试 一、问题描述 源码为utf-8编码. Qt Creator在Debug模式下运行程序&#xff0c;下方应用程序输出界面显示乱码. 但正常运行无乱码&#xff1a; 二、解决方法 尝试修改文件编码、执行编码无果… 可参考…

Python从0到100(十四):高级函数及函数使用进阶

前言&#xff1a; 零基础学Python&#xff1a;Python从0到100最新最全教程。 想做这件事情很久了&#xff0c;这次我更新了自己所写过的所有博客&#xff0c;汇集成了Python从0到100&#xff0c;共一百节课&#xff0c;帮助大家一个月时间里从零基础到学习Python基础语法、Pyth…

Day11-Java进阶-HashSet集合LinkedHashSet-Collection工具类Map集合

1. HashSet集合 HashSet-JDK8版本及以后-面试常问 2. LinkedHashSet-Collection工具类 2.1 LinkedHashSet 2.2 Collection工具类 3. Map集合 3.1 Map接口介绍 3.2 Map 集合的遍历方式 3.2.1 三种方式介绍 package com.itheima.map;import java.util.HashMap; import java.ut…

【C++类和对象】日期类的实现

&#x1f49e;&#x1f49e; 前言 hello hello~ &#xff0c;这里是大耳朵土土垚~&#x1f496;&#x1f496; &#xff0c;欢迎大家点赞&#x1f973;&#x1f973;关注&#x1f4a5;&#x1f4a5;收藏&#x1f339;&#x1f339;&#x1f339; &#x1f4a5;个人主页&#x…

【MATLAB源码-第64期】matlab基于DWA算法的机器人局部路径规划包含动态障碍物和静态障碍物。

操作环境&#xff1a; MATLAB 2022a 1、算法描述 动态窗口法&#xff08;Dynamic Window Approach&#xff0c;DWA&#xff09;是一种局部路径规划算法&#xff0c;常用于移动机器人的导航和避障。这种方法能够考虑机器人的动态约束&#xff0c;帮助机器人在复杂环境中安全、…

PCB上有哪些元素

过孔&#xff1a;是用来切换层的 丝印&#xff1a;就是标记&#xff08;白色的线或者符号&#xff09; 焊盘&#xff1a;焊接元器件&#xff0c;相当于线头&#xff0c;连接各个元件 通孔埋孔盲孔&#xff0c;都是用来换层&#xff0c;内部没有桐&#xff0c;是用来固定的 线路…

C++:基础语法

一、命名空间 在C/C中&#xff0c;变量、函数和后面要学到的类都是大量存在的&#xff0c;这些变量、函数和类的名称将都存在于全局作用域中&#xff0c;可能会导致很多冲突。使用命名空间的目的是对标识符的名称进行本地化&#xff0c; 以避免命名冲突或名字污染&#xff0c;n…

uniapp微信小程序(商城项目)

最近&#xff0c;闲来无事&#xff0c;打算学一下uniapp小程序 于是在跟着某站上学着做了一个小程序&#xff0c;主要是为了学uniapp和vue。某站黑马优购 完成的功能主要有&#xff1a;首页、搜索、分类和购物车。 有人问了为什么没有登录、和添加订单呢&#xff1f;问的很好…