此问题的动机是 logstash 中的以下 grok 语句:
grok {
match => { "message" => "\[(?<gunicorn.time>%{YEAR}-%{MONTHNUM}-%{MONTHDAY}[T ]%{HOUR}:?%{MINUTE}(?::?%{SECOND})? %{ISO8601_TIMEZONE})\] \[%{NUMBER:[gunicorn][worker_id]}\] \[%{LOGLEVEL:[gunicorn][level]}\] %{GREEDYDATA:[gunicorn][message]}" }
remove_field => "message"
}
如果日期格式在时间和时区之间不包含空格,则以下语句将删除它:
grok {
match => { "message" => "\[%{TIMESTAMP_ISO8601:[gunicorn][time]}\] \[%{NUMBER:[gunicorn][worker_id]}\] \[%{LOGLEVEL:[gunicorn][level]}\] %{GREEDYDATA:[gunicorn][message]}" }
remove_field => "message"
}
我认为标准日期格式更适合默认配置。 我认为在那里增加一个额外的空间不会增加任何价值,反而会产生问题。 要么必须替换整个默认的 logconfig,这是多余的,要么必须在日志解析器中编写自定义表达式来满足它。
日期格式遵循通用日志格式: https ://en.wikipedia.org/wiki/Common_Log_Format
哦,对不起。 我的回答是关于访问日志格式。 该报告是关于错误日志格式的。
看起来你可以使用%{DATESTAMP} %{ISO8601_TIMEZONE}
对于 19.x,做任何更改都为时已晚。 它会破坏大量的日志记录使用。 这是我们想在下一个主要版本中改变的东西吗?
@tilgovi所以这是想要的东西吗?
许多部署的系统依赖于 Current 格式。 我担心这是一个太大的变化,不会带来太大的价值,因为总是可以在类似系统的日志存储中编写一条规则。 想法?
可能的妥协可能是切换和弃用。 您可以开始逐步淘汰不符合标准的奇数(尽管如此轻微)日期格式,并为用户提供到固定日志行格式的切换。 我知道我可以通过 python 覆盖记录器,但是由于 gunicorn 附带了它的竞争设置,所以它不是我最喜欢的选择:DI 不是强迫症患者,但编写 grok 查询是一种现代类型的折磨,尤其是当差异您要考虑的是一个空间:D 然后,如果有任何变化,则由开发人员来维持比必要的匹配时间更长的时间。 如果必须这样做,这不是世界末日,但即使人们依赖有缺陷的软件,我们也会继续修复错误......这就是我的想法......不确定问题是否指向我......
我们可以考虑更改 R20 的默认值。
这种格式实际上很常见。 RFC 3339对此有注释:
注意:ISO 8601 定义了以“T”分隔的日期和时间。 为便于阅读,使用此语法的应用程序可以选择指定由(例如)空格字符分隔的完整日期和完整时间。
错误日志也打印在命令行上,应该由人阅读,我想保持这种状态。 时区格式有什么问题吗?
好的,这确实意味着logstash中的grok预加载了严格的模式而不是宽松的模式。 我之前曾在 BBC/EBU 与标准人员合作过,我记得这种糟糕的措辞。 为了可读性“(比如说)一个空格字符”这是不可实现的......那个字符是什么? 当然,此时您可以允许任何字符,但这是不行的。 所以 Logstash 的人用以下方式实现了这个,要么是 T 要么是空间
TIMESTAMP_ISO8601 %{YEAR}-%{MONTHNUM}-%{MONTHDAY}[T ]%{HOUR}:?%{MINUTE}(?::?%{SECOND})?%{ISO8601_TIMEZONE}?
此外,这不是问题所在。 问题在于时区后缀有一个标准不允许的空间。
所以不,不幸的是这种格式并不常见。 这种格式对于 gunicorn 来说是非正统的。 我知道我们在争论一个空间这听起来多么愚蠢,但是当涉及到语言和汽车时,语法就是一切。
这很有帮助,@kozmaz87。 感谢您深入了解规范。
但既然我们在这里,我也想指出访问日志格式,这更糟。 我什至没有设法在代码中找到它来自的任何地方......它是由一些非常模糊的配置机制构成的。 从其他地方分配。 我没有签出要调查的代码,但它确实产生了以下输出:
127.0.0.1 - - [13/Aug/2018:15:03:26 +0000] "GET /debug/sms HTTP/1.1" 400 74 "-" "python-requests/2.18.4"
查看 gunicorn 文档,我们了解到该日志的第二部分是“-”,无论出于何种原因……第三部分应该是用户,它也在评估“-”,不确定为什么,然后就这么可爱了日期格式,其中小时通过“:”与年份结合,当然还有最喜欢的空格分隔时区后缀。 但是在四处挖掘之后,我发现这就是 nginx 记录它的方式,所以我假设这是通过尝试模仿 nginx 访问日志来产生这个空间分隔时区的东西的地方。 HAProxy 也使用它,只是它没有在上面加上时区后缀......
伐木太疯狂了……有人给我拿一桶冰水:)
访问日志格式绝对是“普通”: https ://en.wikipedia.org/wiki/Common_Log_Format
不过,我们在最后添加了引用者和用户代理。 请参阅--access-logformat
标志: http ://docs.gunicorn.org/en/latest/settings.html#access -log-format
我意识到这一点。 我只是觉得第二个项目是一个'-'很有趣:D感觉就像实施它的人已经拥有它并且只是放入一个'-'
我会保留当前的日志格式。 imo 通用格式很好,我不知道上游服务器的任何变化。 想法? 抄送@tilgovi
碰撞@tilgovi也@berkerpeksag
关闭问题不会解决。 正如@tilgovi所说,我们使用的是[通用日志格式](
https://en.wikipedia.org/wiki/Common_Log_Format。
我认为我们可以保持开放。 我们没有为错误日志使用通用日志格式。 常见的日志格式是访问日志格式,我们在那里使用它。
其实,没关系。 我刚才仔细检查了一下,我们对两者都使用了相同的时间格式字符串。 这对我来说似乎比任何其他选择都好。 我不希望访问日志和错误日志具有不同的日期格式。
我现在更好地理解了这个问题,并认为我们应该重新开放。
这是 Gunicorn 使用默认设置的示例输出:
[2019-01-25 11:44:34 -0800] [22794] [INFO] 启动 gunicorn 19.9.0
[2019-01-25 11:44:34 -0800] [22794] [INFO] 收听地址:http: //127.0.0.1 :8000 (22794)
[2019-01-25 11:44:34 -0800] [22794] [INFO] 使用工作者:同步
[2019-01-25 11:44:34 -0800] [22797] [INFO] 使用 pid 引导工作人员:22797
[2019-01-25 11:44:36 -0800] [22797] [INFO] 127.0.0.1 - - [25/Jan/2019:11:44:36 -0800]“GET / HTTP/1.1”200 14“ -”“卷曲/7.54.0”
问题不在于解析访问日志的通用日志格式,而在于解析_整个日志行_。
Gunicorn 在日志行的开头输出时间戳、pid 和级别。 访问日志行_也_有一条带有自己时间戳的通用日志格式的消息。
请注意时间戳的格式不同。 对这个问题的最初要求是让日志行开头的时间戳没有空格_就像常见的日志格式一样_。
看起来像这样:
[25/Jan/2019:11:44:34 -0800] [22794] [INFO] 启动 gunicorn 19.9.0
[25/Jan/2019:11:44:34 -0800] [22794] [INFO] 收听地址:http: //127.0.0.1 :8000 (22794)
[25/Jan/2019:11:44:34 -0800] [22794] [INFO] 使用工作人员:同步
[25/Jan/2019:11:44:34 -0800] [22797] [INFO] 使用 pid 引导工作人员:22797
[25/Jan/2019:11:44:36 -0800] [22797] [INFO] 127.0.0.1 - - [25/Jan/2019:11:44:36 -0800]“GET / HTTP/1.1”200 14 "-" "卷曲/7.54.0"
我认为答案可能不是,因为常见的日志格式不那么国际化(它的月份名称很短)。
但是,我们可以将每个日志行开头的时间戳更改为 ISO8601 时间戳。
https://en.wikipedia.org/wiki/ISO_8601#Combined_date_and_time_representations
日期和时间之间的空格实际上可以代替 T 字符,但时间和区域偏移之间的空格不允许。
以下是我们可以拥有的有效 ISO8601 格式:
[2019-01-25T11:44:34-0800]
[2019-01-25 11:44:34-0800]
与我们现在拥有的相比:
[2019-01-25 11:44:34 -0800]
^ there is a space here
不过,我非常担心破坏已部署的系统。
很好地解析/重新格式化日志可以很容易地处理像logstash这样的工具,所以我不确定这是一个问题。 我现在会保持这种状态。
如果人们想要不同格式的访问日志,也许我们可以为它添加一个特定的标识符? 这样我们就不会破坏格式。 然而,错误日志是一个问题,因为我们不提供格式化它们的方法。 在这种情况下,也许环境变量会这样做?
我不认为访问日志格式应该改变。 这是现在常见的日志格式,我们有--access-log-format
设置。
访问日志格式仅格式化传递给处理程序的访问日志的_message_。 然后处理程序有自己的格式化程序。
我们在 stdout 和 stderr 上的流处理程序的默认格式化程序在开头放置了一个时间戳。 这意味着在默认配置下,访问日志有_两个_时间戳:一个在开头,一个在消息中。
更改处理程序的格式化程序需要使用--logconfig
选项之一(文件或字典)。
我们应该考虑在默认格式化程序中使用 ISO8601 时间戳。
解析/重新格式化日志可以很容易地被logstash之类的工具处理
是的,但是当这些工具可以使用内置模式解析时间戳时非常方便,因此用户不必编写正则表达式。 最初的问题被打开是因为 grok 具有 ISO8601 时间戳的内置模式。
@tilgovi我不想破坏兼容性。 此外,NGINX 确实提供了使用 ISO8601 格式或通用日志格式设置时间的可能性:
$time_iso8601
local time in the ISO 8601 standard format
$time_local
local time in the Common Log Format
https://nginx.org/en/docs/http/ngx_http_log_module.html
我也会这样做,因为它不会破坏遗产。 顺便说一句,我们不应该只在输出中显示访问日志行吗? 看来我们不应该有第一个带有 PID 的标头。 想法?
关于使其成为默认格式化程序的一部分,我担心它会破坏一些工具。 有一个自定义环境变量TIME_ISO8601=true
来强制它怎么样?
我不想破坏兼容性。
我也没有。 我只想重新打开票,因为我认为我们出于错误的原因关闭了它。 我们都回答好像问题是从通用日志格式改变。 问题是默认格式化程序中的时间戳,而不是访问日志消息的格式。 我很高兴我们可以进行更多讨论,但答案可能仍然是什么也不做。
顺便说一句,我们不应该只在输出中显示访问日志行吗? 看来我们不应该有第一个带有 PID 的标头。 想法?
可能的。 我不确定。
有一个自定义环境变量怎么样
也许还好。 用户始终可以使用高级日志配置完全控制日志。 我们尝试为 CLI 简化一些设置,例如--log-level
,因此用户不必使用配置文件。 也许我们可以添加--log-date-format
? 它甚至可以识别像iso8601
这样的符号字符串。 此设置适用于不想使用--log-config
或log_config_dict
的用户。
@tilgovi同时,20.0 版本是更改格式的好时机,因为我们破坏了与 python 2 的兼容性。
我认为任何遗留问题让我更担心的是 ISO8601 很难用人眼解析,包括我在内的很多人都在使用控制台作为观察正在发生的事情的机会。
我想提出以下建议:
-iso8601
强制标准输出和标准错误日志使用这种格式(就像你建议的那样)虽然我们在这里,也许我们也可以有一个-utc
选项来使用 UTC 时间? 想法?
可以肯定的是,如果我们只是更改默认值,这将是建议的差异:
diff --git a/gunicorn/glogging.py b/gunicorn/glogging.py
index 56cc5bd..0735e58 100644
--- a/gunicorn/glogging.py
+++ b/gunicorn/glogging.py
@@ -80,7 +80,7 @@ CONFIG_DEFAULTS = dict(
formatters={
"generic": {
"format": "%(asctime)s [%(process)d] [%(levelname)s] %(message)s",
- "datefmt": "[%Y-%m-%d %H:%M:%S %z]",
+ "datefmt": "[%Y-%m-%d %H:%M:%S%z]",
"class": "logging.Formatter"
}
}
@@ -175,7 +175,7 @@ class Logger(object):
loglevel = logging.INFO
error_fmt = r"%(asctime)s [%(process)d] [%(levelname)s] %(message)s"
- datefmt = r"[%Y-%m-%d %H:%M:%S %z]"
+ datefmt = r"[%Y-%m-%d %H:%M:%S%z]"
access_fmt = "%(message)s"
syslog_fmt = "[%(process)d] %(message)s"
我认为在访问日志令牌中使用 ISO8601 日期时间的选项很有趣,但它与引发此问题的原因不同。
顺便说一句,我对此感觉并不强烈。 😄 我只是想准确地表达这个问题。
我们可能需要多考虑一下。 我将它推迟到 20.1 让我们多玩几次。
亲爱的开发者,
我正面临请求丢失问题(与 Gunicorn 无关)。 我需要能够获得包括微秒在内的精确时间戳,如本例中来自我们的一台 Apache 服务器: 2019-10-30 14:27:16.960421
。 这将是一个很酷的增强,谢谢考虑。
如果标志log-date-format iso8601
将在下一版本的 gunicorn 中可用,是否有希望?
最有用的评论
好的,这确实意味着logstash中的grok预加载了严格的模式而不是宽松的模式。 我之前曾在 BBC/EBU 与标准人员合作过,我记得这种糟糕的措辞。 为了可读性“(比如说)一个空格字符”这是不可实现的......那个字符是什么? 当然,此时您可以允许任何字符,但这是不行的。 所以 Logstash 的人用以下方式实现了这个,要么是 T 要么是空间
TIMESTAMP_ISO8601 %{YEAR}-%{MONTHNUM}-%{MONTHDAY}[T ]%{HOUR}:?%{MINUTE}(?::?%{SECOND})?%{ISO8601_TIMEZONE}?
此外,这不是问题所在。 问题在于时区后缀有一个标准不允许的空间。
所以不,不幸的是这种格式并不常见。 这种格式对于 gunicorn 来说是非正统的。 我知道我们在争论一个空间这听起来多么愚蠢,但是当涉及到语言和汽车时,语法就是一切。