记录事件可以大大改善调试体验。Frappe 的开发和生产环境都自带日志记录功能。
根据访问方式的不同,日志主要分为两大类。桌面日志(Desk Logs)存储在站点数据库中,可以作为文档进行访问和查询;服务器日志(Server Logs)则存储在由日志轮转系统管理的文件中。
桌面日志
可以通过桌面界面(Desk UI)访问的日志(通常是从 Awesomebar 中搜索查找)。这些日志一般用于追踪操作事件;但你也可以利用它们的 API 来追踪 Frappe 应用中的任何内容。
Core 模块中的部分日志包括:
- 访问日志
- 活动日志
- 错误日志
- 计划任务日志
你可以通过嵌入的链接了解更多信息。了解每种日志的最佳方式是直接在你的站点上查看它们。
服务器日志
与桌面日志相比,服务器日志通常包含更底层的、事务性的数据。从版本 13 开始,日志也可以在站点级别获取。这些站点日志由 Frappe 应用程序创建,而许多 bench 级别的日志文件则由支持 Frappe 环境的进程生成。在你的 bench 文件夹中,你可以在以下位置找到日志:
./logs./sites/{site}/logs
在撰写本文时,只有 frappe.web.log 和 scheduler.log 会在站点和 bench 级别记录日志。在 bench 级别,一些最有用的文件可能是:
bench.logscheduler.logworker.log
在这些日志中,你可以追踪已执行的 bench 命令、调度器或后台任务运行的状态。
> 要在你的站点上启用 Frappe Web 日志记录,请使用 > enable_frappe_logger 更新站点配置:true
错误快照
除此之外,Frappe 还会为你的站点维护 HTTP 响应码 500 及以上的错误快照。这样做是为了在发生服务器错误时提供更多的故障上下文。例如,某个桌面操作因 HTTP 状态码 500 而失败,这会在 ./sites/{site}/frappe.log 文件中添加一条相应的日志,如下所示:
2021-05-06 14:08:12,177 ERROR frappe New Exception collected with snapshot id: 2021-05-06 14:08:11.940809-127.0.0.1-bde
Site: frappeframework.com
Form Dict: {'docs': '{"name":"assets.frappeframework.com","owner":"Administrator","creation":"2020-04-04 14:58:18.771247","modified":"2021-02-08 09:40:56.820300","modified_by":"gavin@frappe.io","idx":0,"docstatus":0,"status":"Active","hostname":"assets","domain":"frappeframework.com","cluster":"Default","provider":"Generic","is_server_setup":1,"ip":"192.168.29.100","private_ip":"10.120.120.120","agent_password":"*****","is_primary":1,"doctype":"Server","__last_sync_on":"2021-05-06T08:38:08.931Z"}', 'method': 'ping_agent', 'cmd': 'run_doc_method'}
日志的第一行提到了该错误的快照 ID,在本例中是 2021-05-06 14:08:11.940809-127.0.0.1-bde。在 ./sites/frappeframework.com/error-snapshots 下应该有一个以该快照 ID 标识的对应文件,文件名为 2021-05-06 14:08:11.940809-127.0.0.1-bde.json。文件内容可能是:
{
"etype": "UnboundLocalError",
"evalue": "UnboundLocalError("local variable 'headers' referenced before assignment")",
"exception": {
"args": [...],
"with_traceback": [...]
},
"frames": [
{
"call": "(request=<request 'https:="" frappeframework.com="" api="" method="" run_doc_method'="" [post]="">)",
"dump": {
"frappe. api": "<module 'frappe.api'="" from="" '="" home="" frappe="" frappe-bench="" apps="" api.py'="">",
"frappe.api. handle": "<function handle="">",
"global frappe": "<module 'frappe'="" from="" '="" home="" frappe="" frappe-bench="" apps="" __init__.py'="">",
"response": "None"
},
"file": "/home/frappe/frappe-bench/apps/frappe/frappe/app.py",
"func": "application",
"lines": {
"66": "",
"67": "elif request.path.startswith("/api/"):",
"68": "response = frappe.api.handle()",
"69": "",
"70": "elif request.path.startswith('/backups'):"
},
"lnum": 68
},
...
],
"locals": {
"data": "None",
"files": "None",
"method": "GET",
"path": "ping",
"self": "<orchestrator.agent.agent object="">",
"url": "https://assets.frappeframework.com:443/agent/ping"
},
"pyver": "Python 3.8.2: /home/frappe/frappe-bench/env/bin/python (prefix: /home/frappe/frappe-bench/env)",
"timestamp": "2021-05-06 14:08:11.940971",
"traceback": "Traceback (most recent call last):
File "/home/frappe/frappe-bench/env/lib/python3.8/site-packages/cryptography/fernet.py", line 119, in _verify_signature
h.verify(data[-32:])
File "/home/frappe/frappe-bench/env/lib/python3.8/site-packages/cryptography/hazmat/primitives/hmac.py", line 74, in verify
ctx.verify(signature)
File "/home/frappe/frappe-bench/env/lib/python3.8/site-packages/cryptography/hazmat/backends/openssl/hmac.py", line 75, in verify
raise InvalidSignature("Signature did not match digest.")
cryptography.exceptions.InvalidSignature: Signature did not match digest.
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/home/frappe/frappe-bench/apps/frappe/frappe/app.py", line 68, in application
response = frappe.api.handle()
File "/home/frappe/frappe-bench/apps/frappe/frappe/api.py", line 54, in handle
return frappe.handler.handle()
File "/home/frappe/frappe-bench/apps/orchestrator/orchestrator/agent.py", line 474, in request
headers=headers,
UnboundLocalError: local variable 'headers' referenced before assignment
"
}
这些快照会定期与站点数据库同步,并可以通过桌面端访问,记录会添加到 DocType Error Snapshot 下。它们从创建之日起保留一个月,之后每条记录会被删除。
> 错误快照默认启用追踪。要为你站点禁用此功能,请使用 > disable_error_snapshot 更新站点配置:true
进程
服务器日志可能由 bench CLI、你的进程管理器或 Frappe 应用程序直接生成。你的进程管理器负责启动和运行 Frappe 环境的所有组件,并将输出流和错误流定向到日志文件。
除了“始终运行”的默认日志记录外,你还可以使用 Monitor 来记录更多关于站点请求的信息。
维护
如果你不注意的话,进程管理器生成的日志文件可能会随着时间的推移变得非常大。要了解你需要追踪哪些文件,请查看你 bench 上的 supervisor.conf 或 Procfile,具体取决于你运行的是生产实例还是开发实例。
旧日志的自动清理
Frappe 中的大多数日志 DocType 都支持可配置的保留期限,并带有合理的默认值。
“Log Settings” DocType 让你可以为默认的日志 DocType 配置保留期限。
你也可以通过向 DocType 控制器添加一个静态方法来将自定义日志 DocType 注册到日志设置中,如下所示。实现了此方法的自定义日志 DocType 将自动显示在 Log Settings 中。
class CustomLoggingDoctype(Document):
@staticmethod
def clear_old_logs(days=180):
from frappe.query_builder import Interval
from frappe.query_builder.functions import Now
table = frappe.qb.DocType("Custom Logging Doctype")
frappe.db.delete(table, filters=(table.modified < (Now() - Interval(days=days))))
更多
在生产环境中,你可能希望为 Frappe 应用程序或构成 Frappe 环境的进程记录更多信息。以下是一些你可以查阅的资源,以了解更多。
Frappe 应用程序
- Frappe 日志 API
服务器监控
- MySQL
- PostgreSQL
- NGINX