Python Flask + Gunicorn + Docker 的日志输出设置

这篇具有很好参考价值的文章主要介绍了Python Flask + Gunicorn + Docker 的日志输出设置。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

我们一个项目使用了 Python Flask 框架来实现 Web 服务,之前的日志输出一直有问题。而从项目需求、运行维护出发,正确的日志输出对使用者来说都是非常重要的。

这里完整的整理了从 开发 Flask 时的日志设置,到生产环境使用 Gunicorn 运行 Flask 的日志设置 以及 使用 Docker 容器化部署的日志输出 的全部细节。

普通 Flask 日志设置

Flask 本身使用 Python 的 logging 模块来实现日志记录、输出,以一个最简单的 Flask 应用代码为例:

import logging
from flask import Flask, jsonify

app = Flask(__name__)

@app.route('/')
def default_route():
    """Default route"""
    app.logger.debug('this is a DEBUG message')
    app.logger.info('this is an INFO message')
    app.logger.warning('this is a WARNING message')
    app.logger.error('this is an ERROR message')
    app.logger.critical('this is a CRITICAL message')
    return jsonify('hello world')

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=8000, debug=True)

运行程序:

$ pip install flask

$ python app.py

 * Serving Flask app "app" (lazy loading)
 * Environment: production
   WARNING: This is a development server. Do not use it in a production deployment.
   Use a production WSGI server instead.
 * Debug mode: on
 * Running on http://0.0.0.0:8000/ (Press CTRL+C to quit)
 * Restarting with stat
 * Debugger is active!
 * Debugger PIN: 265-991-043

访问 http://localhost:8000/ ,程序会在命令行窗口输出:

[2022-12-21 15:46:29,113] DEBUG in app: this is a DEBUG message
[2022-12-21 15:46:29,113] INFO in app: this is an INFO message
[2022-12-21 15:46:29,113] WARNING in app: this is a WARNING message
[2022-12-21 15:46:29,113] ERROR in app: this is an ERROR message
[2022-12-21 15:46:29,113] CRITICAL in app: this is a CRITICAL message
127.0.0.1 - - [21/Dec/2022 15:46:29] "GET / HTTP/1.1" 200 -

输出日志文件

我们可以使用 logging 模块的不同输出处理器(Handler)来实现标准输出、文件输出或邮件提醒。例如添加日志文件:

import logging
from flask import Flask, jsonify

app = Flask(__name__)

@app.route('/')
def default_route():
    """Default route"""
    app.logger.debug('this is a DEBUG message')
    app.logger.info('this is an INFO message')
    app.logger.warning('this is a WARNING message')
    app.logger.error('this is an ERROR message')
    app.logger.critical('this is a CRITICAL message')
    return jsonify('hello world')

if __name__ == '__main__':
    # Define log level and log file
    app.debug = True
    handler = logging.FileHandler('flask.log')
    app.logger.addHandler(handler)

    app.run(host='0.0.0.0', port=8000, debug=True)

访问 http://localhost:8000 ,我们可以看到在当前目录下生成了 flask.log 日志文件:

$ ls
app.py  flask.log
$ cat flask.log
this is a DEBUG message
this is an INFO message
this is a WARNING message
this is an ERROR message
this is a CRITICAL message

按天分割、调整格式

我们常见的日志文件一般按天分割,这样方便查找。另外日志里还需要记录一些必须的时间等值:

import logging
from flask import Flask, jsonify
from logging.handlers import TimedRotatingFileHandler

app = Flask(__name__)

@app.route('/')
def default_route():
    """Default route"""
    app.logger.debug('this is a DEBUG message')
    app.logger.info('this is an INFO message')
    app.logger.warning('this is a WARNING message')
    app.logger.error('this is an ERROR message')
    app.logger.critical('this is a CRITICAL message')
    return jsonify('hello world')

if __name__ == '__main__':
    app.debug = True
    formatter = logging.Formatter(
        "[%(asctime)s][%(filename)s:%(lineno)d][%(levelname)s][%(thread)d] - %(message)s")
    handler = TimedRotatingFileHandler(
        "flask.log", when="D", interval=1, backupCount=30,
        encoding="UTF-8", delay=False, utc=False)

    handler.setFormatter(formatter)
    app.logger.addHandler(handler)

    app.run(host='0.0.0.0', port=8000, debug=True)

TimedRotatingFileHandler 的参数设置:

  • when=D: 按天进行切分,还可以按秒(S)、分钟(M)、小时(H)、星期时间(‘W0’-‘W6’,W0=周一,此时 interval 不起作用)、半夜时间(midnight)
  • interval=1: 每1天切分,这个值根据上面的 when 来确定单位
  • backupCount=30:保留30天的日志
  • encoding=UTF-8: 使用 UTF-8 的编码
  • delay=False:如果为 True,文件打开会被推迟到第一次调用 emit()
  • utc=False:使用当前系统时间而非 UTC 时间

Formatter 的格式可以参考 https://docs.python.org/2/library/logging.html#logrecord-attributes

再次访问 http://localhost:8000 ,日志文件输出变成了:

[2022-12-21 16:17:05,825][app.py:10][DEBUG][139836122048256] - this is a DEBUG message
[2022-12-21 16:17:05,826][app.py:11][INFO][139836122048256] - this is an INFO message
[2022-12-21 16:17:05,826][app.py:12][WARNING][139836122048256] - this is a WARNING message
[2022-12-21 16:17:05,827][app.py:13][ERROR][139836122048256] - this is an ERROR message
[2022-12-21 16:17:05,827][app.py:14][CRITICAL][139836122048256] - this is a CRITICAL message

引入蓝图 BluePrint

稍微复杂一点的 Flask 程序,可能会使用 BluePrint 来对不同功能模块进行拆分。而使用 BluePrint 后的 Flask 程序,其日志的调用方式有少许变化。

.
├── api
│   └── test.py
├── app.py
└── flask.log

我们在当前项目目录下新建一个 api 目录,然后在其下创建 test.py ,这里我们创建了一个名为 testBlueprint 对象,并且添加了路由 /test 到其下的 show() 方法。这里要获得 Flask 的应用对象,就需要用它的 current_app 来实现对 app 的引用了。

from flask import Blueprint, jsonify, current_app

test = Blueprint('test', __name__)

@test.route('/test', methods=('GET', 'POST'))
def show():
    current_app.logger.debug('Test DEBUG message')
    current_app.logger.info('Test INFO message')
    current_app.logger.warning('Test WARNING message')
    current_app.logger.error('Test ERROR message')
    current_app.logger.critical('Test CRITICAL message')
    return jsonify('Show test')

然后对 app.py 进行修改,把上面的 test 蓝图注册到 app 对象里,并且添加路由前缀 /api

import logging

from flask import Flask, jsonify
from logging.handlers import TimedRotatingFileHandler

app = Flask(__name__)

from api.test import test
app.register_blueprint(test, url_prefix='/api')

@app.route('/')
def default_route():
    """Default route"""
    app.logger.debug('this is a DEBUG message')
    app.logger.info('this is an INFO message')
    app.logger.warning('this is a WARNING message')
    app.logger.error('this is an ERROR message')
    app.logger.critical('this is a CRITICAL message')
    return jsonify('hello world')

if __name__ == '__main__':
    app.debug = True
    formatter = logging.Formatter(
        "[%(asctime)s][%(filename)s:%(lineno)d][%(levelname)s][%(thread)d] - %(message)s")
    handler = TimedRotatingFileHandler(
        "flask.log", when="D", interval=1, backupCount=30,
        encoding="UTF-8", delay=False, utc=False)

    handler.setFormatter(formatter)
    app.logger.addHandler(handler)

    app.run(host='0.0.0.0', port=8000, debug=True)

我们分别访问 http://localhost:8000 和 http://localhost:8000/api/test ,可以看到返回正确的结果,日志文件变成了:

[2022-12-21 17:30:34,627][app.py:14][DEBUG][140282256979712] - this is a DEBUG message
[2022-12-21 17:30:34,628][app.py:15][INFO][140282256979712] - this is an INFO message
[2022-12-21 17:30:34,628][app.py:16][WARNING][140282256979712] - this is a WARNING message
[2022-12-21 17:30:34,629][app.py:17][ERROR][140282256979712] - this is an ERROR message
[2022-12-21 17:30:34,629][app.py:18][CRITICAL][140282256979712] - this is a CRITICAL message
[2022-12-21 17:30:50,946][test.py:7][DEBUG][140282256979712] - Test DEBUG message
[2022-12-21 17:30:50,947][test.py:8][INFO][140282256979712] - Test INFO message
[2022-12-21 17:30:50,947][test.py:9][WARNING][140282256979712] - Test WARNING message
[2022-12-21 17:30:50,947][test.py:10][ERROR][140282256979712] - Test ERROR message
[2022-12-21 17:30:50,949][test.py:11][CRITICAL][140282256979712] - Test CRITICAL message

至此,Flask 的日志一切都运转良好。然后我们在生产服务器上部署的时候,现在常常会使用 Gunicorn 来运行,这时候的日志输出就有问题了,日志文件没有内容写入。

使用 Gunicorn 运行的 Flask 日志设置

Gunicorn 有自己的日志记录器,它通过本身的机制控制日志级别。我们只能通过配置 Gunicorn 的日志设定,来实现我们的需求。同时,需要对上面例子应用的日志处理器设置进行调整。

修改 app.py,注意我们添加的 if __name__ != '__main__' 这部分,就是在 Gunicorn 运行时,让 Flask 使用它的日志处理器。

import logging

from flask import Flask, jsonify
from logging.handlers import TimedRotatingFileHandler

app = Flask(__name__)

from api.test import test
app.register_blueprint(test, url_prefix='/api')

@app.route('/')
def default_route():
    """Default route"""
    app.logger.debug('this is a DEBUG message')
    app.logger.info('this is an INFO message')
    app.logger.warning('this is a WARNING message')
    app.logger.error('this is an ERROR message')
    app.logger.critical('this is a CRITICAL message')
    return jsonify('hello world')

# make app to use gunicorn logger handler
if __name__ != '__main__':
    gunicorn_logger = logging.getLogger('gunicorn.error')
    app.logger.handlers = gunicorn_logger.handlers
    app.logger.setLevel(gunicorn_logger.level)

if __name__ == '__main__':
    app.debug = True
    formatter = logging.Formatter(
        "[%(asctime)s][%(filename)s:%(lineno)d][%(levelname)s][%(thread)d] - %(message)s")
    handler = TimedRotatingFileHandler(
        "flask.log", when="D", interval=1, backupCount=30,
        encoding="UTF-8", delay=False, utc=False)

    handler.setFormatter(formatter)
    app.logger.addHandler(handler)

    app.run(host='0.0.0.0', port=8000, debug=True)

参考 Gunicorn 日志设置,我们在启动 Gunicorn 时可以使用下面的命令行:

# 创建日志目录
$ mkdir -p /var/log/gunicorn/

# 赋予日志目录当前用户权限
$ chown tattoo:tattoo /var/log/gunicorn/

# 运行 gunicorn
$ gunicorn -w 2 \
        -b 0.0.0.0:5000 \
        --log-level debug \
        --log-file /var/log/gunicorn/gunicorn.log \
        app:app

运行后,访问 http://localhost:5000 和 http://localhost:5000/api/test ,我们得到 gunicorn.log 日志:

[2022-12-21 17:59:05 +0800] [32174] [DEBUG] Current configuration:
  config: ./gunicorn.conf.py
  wsgi_app: None
  bind: ['0.0.0.0:5000']
  backlog: 2048
  workers: 2
  worker_class: sync
  threads: 1
  worker_connections: 1000
  max_requests: 0
  max_requests_jitter: 0
  timeout: 30
  graceful_timeout: 30
  keepalive: 2
  limit_request_line: 4094
  limit_request_fields: 100
  limit_request_field_size: 8190
  reload: False
  reload_engine: auto
  reload_extra_files: []
  spew: False
  check_config: False
  print_config: False
  preload_app: False
  sendfile: None
  reuse_port: False
  chdir: /home/tattoo/workspace/flask
  daemon: False
  raw_env: []
  pidfile: None
  worker_tmp_dir: None
  user: 1000
  group: 1000
  umask: 0
  initgroups: False
  tmp_upload_dir: None
  secure_scheme_headers: {'X-FORWARDED-PROTOCOL': 'ssl', 'X-FORWARDED-PROTO': 'https', 'X-FORWARDED-SSL': 'on'}
  forwarded_allow_ips: ['127.0.0.1']
  accesslog: None
  disable_redirect_access_to_syslog: False
  access_log_format: %(h)s %(l)s %(u)s %(t)s "%(r)s" %(s)s %(b)s "%(f)s" "%(a)s"
  errorlog: /var/log/gunicorn/gunicorn.log
  loglevel: debug
  capture_output: False
  logger_class: gunicorn.glogging.Logger
  logconfig: None
  logconfig_dict: {}
  syslog_addr: udp://localhost:514
  syslog: False
  syslog_prefix: None
  syslog_facility: user
  enable_stdio_inheritance: False
  statsd_host: None
  dogstatsd_tags:
  statsd_prefix:
  proc_name: None
  default_proc_name: app:app
  pythonpath: None
  paste: None
  on_starting: <function OnStarting.on_starting at 0x7f2ae3bbd4c0>
  on_reload: <function OnReload.on_reload at 0x7f2ae3bbd5e0>
  when_ready: <function WhenReady.when_ready at 0x7f2ae3bbd700>
  pre_fork: <function Prefork.pre_fork at 0x7f2ae3bbd820>
  post_fork: <function Postfork.post_fork at 0x7f2ae3bbd940>
  post_worker_init: <function PostWorkerInit.post_worker_init at 0x7f2ae3bbda60>
  worker_int: <function WorkerInt.worker_int at 0x7f2ae3bbdb80>
  worker_abort: <function WorkerAbort.worker_abort at 0x7f2ae3bbdca0>
  pre_exec: <function PreExec.pre_exec at 0x7f2ae3bbddc0>
  pre_request: <function PreRequest.pre_request at 0x7f2ae3bbdee0>
  post_request: <function PostRequest.post_request at 0x7f2ae3bbdf70>
  child_exit: <function ChildExit.child_exit at 0x7f2ae3bd10d0>
  worker_exit: <function WorkerExit.worker_exit at 0x7f2ae3bd11f0>
  nworkers_changed: <function NumWorkersChanged.nworkers_changed at 0x7f2ae3bd1310>
  on_exit: <function OnExit.on_exit at 0x7f2ae3bd1430>
  proxy_protocol: False
  proxy_allow_ips: ['127.0.0.1']
  keyfile: None
  certfile: None
  ssl_version: 2
  cert_reqs: 0
  ca_certs: None
  suppress_ragged_eofs: True
  do_handshake_on_connect: False
  ciphers: None
  raw_paste_global_conf: []
  strip_header_spaces: False
[2022-12-21 17:59:05 +0800] [32174] [INFO] Starting gunicorn 20.1.0
[2022-12-21 17:59:05 +0800] [32174] [DEBUG] Arbiter booted
[2022-12-21 17:59:05 +0800] [32174] [INFO] Listening at: http://0.0.0.0:5000 (32174)
[2022-12-21 17:59:05 +0800] [32174] [INFO] Using worker: sync
[2022-12-21 17:59:05 +0800] [32176] [INFO] Booting worker with pid: 32176
[2022-12-21 17:59:05 +0800] [32177] [INFO] Booting worker with pid: 32177
[2022-12-21 17:59:05 +0800] [32174] [DEBUG] 2 workers
[2022-12-21 17:59:11 +0800] [32177] [DEBUG] GET /
[2022-12-21 17:59:11 +0800] [32177] [DEBUG] this is a DEBUG message
[2022-12-21 17:59:11 +0800] [32177] [INFO] this is an INFO message
[2022-12-21 17:59:11 +0800] [32177] [WARNING] this is a WARNING message
[2022-12-21 17:59:11 +0800] [32177] [ERROR] this is an ERROR message
[2022-12-21 17:59:11 +0800] [32177] [CRITICAL] this is a CRITICAL message
[2022-12-21 17:59:17 +0800] [32177] [DEBUG] GET /api/test
[2022-12-21 17:59:17 +0800] [32177] [DEBUG] Test DEBUG message
[2022-12-21 17:59:17 +0800] [32177] [INFO] Test INFO message
[2022-12-21 17:59:17 +0800] [32177] [WARNING] Test WARNING message
[2022-12-21 17:59:17 +0800] [32177] [ERROR] Test ERROR message
[2022-12-21 17:59:17 +0800] [32177] [CRITICAL] Test CRITICAL message

你也可以参考 https://docs.python.org/3/library/logging.config.html ,使用 Gunicorn 的 --log-config FILE 选项,用一个日志配置文件来做更精细的日志调整。

对日志文件进行分割

Gunicorn 自身不对日志进行分割,所以我们可以使用 logrotate 工具来实现日志分割的需求。

创建 /etc/logrotate.d/gunicorn 文件:

/var/log/gunicorn/gunicorn.log {
    daily
    dateext
    dateformat -%Y-%m-%d
    dateyesterday
    rotate 90
    missingok
    notifempty
}
  • daily 每天执行,也可以是 weekly 或 monthly

  • dateext 使用当期日期作为命名格式

  • dateformat 日志文件命名格式

  • rotate 分割次数,如果设置的是daily,就代表保留多少天的记录

如果要立即执行: logrotate -f gunicorn

使用 Docker 部署 Gunicorn + Flask 应用并输出日志

我们在服务器上配置 Python 环境,常常会碰到各种各样的麻烦,而使用现成的 Docker 镜像就很方便,它在各种环境下的行为都能保持一致。

添加 Dockerfilerequirements.txtconf/supervisor_flask.conf 文件:

.
├── api
│   └── test.py
├── app.py
├── conf
│   └── supervisor_flask.conf
├── Dockerfile
└── requirements.txt
Dockerfile

我们使用 alpine 基础镜像以减小镜像体积。使用 supervisord 来后台启动 Gunicorn + Flask。日志输出到 /var/log/flask-app 目录。

FROM python:3-alpine

WORKDIR /home/app

USER root

RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories \
    # upgrade pip
    && pip config set global.index-url https://mirrors.aliyun.com/pypi/simple \
    && pip install --upgrade pip \
    # install supervisor gunicorn
    && pip install supervisor gunicorn \
    && mkdir -p /etc/supervisor \
    && /usr/local/bin/echo_supervisord_conf > /etc/supervisor/supervisord.conf \
    && echo "[include]" >> /etc/supervisor/supervisord.conf \
    && echo "files = /etc/supervisor/*.conf" >> /etc/supervisor/supervisord.conf \
    # get curl for healthchecks
    && apk add curl \
    # add log folder
    && mkdir -p /var/log/flask-app

COPY conf/supervisor_flask.conf /etc/supervisor

# copy all the files to the container
COPY . .

# python setup
RUN pip install -r requirements.txt

# define the port number the container should expose
EXPOSE 5000

CMD ["supervisord","-n","-c","/etc/supervisor/supervisord.conf"]
requirements.txt

注意 markupsafe 2.1.0 移除了 soft_unicode,不指定较低版本的话会导致 Flask 报错。

markupsafe==2.0.1
Flask~=1.1.4
conf/supervisor_flask.conf
[supervisord]
nodaemon=true

[program:flask]
command=gunicorn -w 2 -b 0.0.0.0:5000 --log-level debug --log-file /var/log/flask-app/gunicorn.log app:app
directory=/home/app
autostart=true
autorestart=true
redirect_stderr=true
stdout_logfile=/dev/stdout


构建镜像、运行容器

使用下面的命令构建镜像 flasktest

docker build -t flasktest .

运行,这里我们把日志输出目录映射到了宿主机的 /home/logs 目录:

docker run --name flask \
    -it -d \
    -p 5000:5000 \
    -v /home/logs:/var/log/flask-app \
    flasktest

运行起来后,docker logs -f flask 查看一下容器输出有没有问题,正常的应该是:

$ docker logs -f flasktest
2022-12-22 03:33:32,368 CRIT Supervisor is running as root.  Privileges were not dropped because no user is specified in the config file.  If you intend to run as root, you can set user=root in the config file to avoid this message.
2022-12-22 03:33:32,368 INFO Included extra file "/etc/supervisor/supervisor_flask.conf" during parsing
2022-12-22 03:33:32,368 INFO Included extra file "/etc/supervisor/supervisord.conf" during parsing
2022-12-22 03:33:32,371 INFO RPC interface 'supervisor' initialized
2022-12-22 03:33:32,371 CRIT Server 'unix_http_server' running without any HTTP authentication checking
2022-12-22 03:33:32,372 INFO supervisord started with pid 1
2022-12-22 03:33:33,374 INFO spawned: 'flask' with pid 6
2022-12-22 03:33:34,376 INFO success: flask entered RUNNING state, process has stayed up for > than 1 seconds (startsecs)

访问 http://localhost:5000 和 http://localhost:5000/api/test ,我们可以在 /home/logs 目录下找到 gunicorn.log 日志,跟前面的没有什么区别文章来源地址https://www.toymoban.com/news/detail-449463.html

到了这里,关于Python Flask + Gunicorn + Docker 的日志输出设置的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 如何控制docker服务的日志输出?

    引言 通过docker部署的线上服务,由于宿主机存储空间有限,加上长时间累积的客观条件下,会出现docker服务的控制台日志过大。再三思量后决定不输出控制台日志,从而从根源解决此问题。 在Docker Compose中,您可以通过添加或删除适当的配置选项来开启或关闭日志功能。 D

    2024年02月02日
    浏览(38)
  • docker_查询日志并输出到文件

    想查询一下docker容器内服务的log,但是因为log太多,想自定义筛选一下。 1、导出全部的log到文件 2、按照时间导出log信息 但是全部的log太多,我想按照时间进行查询,这就用到了--since参数 --since参数的含义是,显示某个时间点后的log信息,也可以是相对于现在多长时间的l

    2024年02月01日
    浏览(39)
  • was下log4j设置日志不输出问题

    WAS 也是用的 commons-logging 日志框架 commons-logging 确定 LogFactory 实现的顺序是 从应用的 META-INF/services/org.apache.commons.logging.LogFactory 中获得 LogFactory 实现 从系统环境中获得 org.apache.commons.logging.LogFactory 获得 LogFactory 实现 从 classpath 下的 commons-logging.properties 文件中获得 LogFactory 实

    2024年02月08日
    浏览(53)
  • Python Flask 封装ChatGPT流式输出接口

    首先, 恭喜你搜到了这篇文章, 因为, 我解决这个问题时, 也搜了很久的博客, 结果都无法正确实现 截至今日, 快有半年博客没有更新了, 今天忙里偷闲, 将最近在忙的事情做一个总结 之前搞了个域名, 做了个chatgpt的工具站, 欢迎大家体验, 可以评论区留下宝贵建议: www.ktatom.com 封

    2024年02月11日
    浏览(35)
  • nohup 输出到指定文件 Linux nohup 实现命令后台运行并输出或记录到指定日志文件 设置日志结果文件名称 重定向到某个文件 标准误 标准错误输出定向 输入报错信息保留

    # yourcommand:启动对象命令。可以跟该命令需要的各种参数。 # 是指在后台运行,但当用户推出(挂起)的时候,命令自动也跟着退出. nohup与结合起来,可以实现不挂断的后台运行。 实现屏幕输出记录到日志文件 # 0 – stdin (standard input),1 – stdout (standard output),2 – stderr (standa

    2024年02月05日
    浏览(58)
  • python日志输出详细讲解版

    一个成熟的软件产品,日志是必不可少的一部分,在python中我们怎么实现产品级别的日志输出呢? python有一个内置模块logging,用来输出日志信息,可以进行各种配置,能满足我们大部分场景对日志的需求。 导入logging模块,直接输出5个级别的日志信息,我们看一下会输出什

    2023年04月08日
    浏览(34)
  • 设置、清理docker容器日志

    1、清理docker容器日志 a. 通过df -h查询存储使用率 b. 通过du -sh /var/lib/docker/* | sort -nr查询文件大小 c. 可使用脚本来批量清理容器日志,代码如下: #!/bin/sh echo “======== start clean docker containers logs \\\" logs=$(find /var/lib/docker/containers/ -name *-json.log) for log in $logs do echo “clean logs : $log”

    2024年02月16日
    浏览(42)
  • Python单元测试pytest捕获日志输出

    使用pytest进行单元测试时,遇到了需要测试日志输出的情况,查看了文档 https://docs.pytest.org/en/latest/how-to/capture-stdout-stderr.html https://docs.pytest.org/en/latest/how-to/logging.html 然后试了一下,捕捉logger.info可以用caplog,获取print输出可用capsys,Demo如下: - a.py - test_a.py - 验证:

    2024年04月10日
    浏览(41)
  • python 常用内置模块之 logging(日志输出)

    Python logging 模块定义了为应用程序和库实现灵活的事件日志记录的函数和类,可以方便第三方模块或者是应用使用。这个模块提供不同的日志级别,并可以采用不同的方式记录日志,比如文件,HTTP GET/POST,SMTP,Socket 等,甚至可以自定义实现具体的日志记录方式。 Logging 优点

    2024年02月05日
    浏览(52)
  • flask python 设置定时任务 flask 周期性执行任务方案

    flask 通常使用 flask_apscheduler 框架设计定时任务,flask_apscheduler 功能很全面,能按设定的时间规则执行任务,可以持久化到各类数据库(mysql,redis,mongodb),实现对定时任务增、删、改、查等操作。 方法三:通过调用 flask_apscheduler 的 api (推荐) 实例对象 scheduler 拥有增、删

    2024年01月21日
    浏览(42)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包