mirror of
https://github.com/ClickHouse/ClickHouse.git
synced 2024-11-24 16:42:05 +00:00
Merge pull request #71560 from xogoodnow/json-utc-formatted-log
Added UTC date format (RFC 3339) for json formatted logging
This commit is contained in:
commit
3e05ffe675
@ -1643,6 +1643,7 @@ You can specify the log format that will be outputted in the console log. Curren
|
|||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
|
"date_time_utc": "2024-11-06T09:06:09Z",
|
||||||
"date_time": "1650918987.180175",
|
"date_time": "1650918987.180175",
|
||||||
"thread_name": "#1",
|
"thread_name": "#1",
|
||||||
"thread_id": "254545",
|
"thread_id": "254545",
|
||||||
|
@ -70,12 +70,15 @@
|
|||||||
You can specify log format(for now, JSON only). In that case, the console log will be printed
|
You can specify log format(for now, JSON only). In that case, the console log will be printed
|
||||||
in specified format like JSON.
|
in specified format like JSON.
|
||||||
For example, as below:
|
For example, as below:
|
||||||
|
|
||||||
{"date_time":"1650918987.180175","thread_name":"#1","thread_id":"254545","level":"Trace","query_id":"","logger_name":"BaseDaemon","message":"Received signal 2","source_file":"../base/daemon/BaseDaemon.cpp; virtual void SignalListener::run()","source_line":"192"}
|
{"date_time":"1650918987.180175","thread_name":"#1","thread_id":"254545","level":"Trace","query_id":"","logger_name":"BaseDaemon","message":"Received signal 2","source_file":"../base/daemon/BaseDaemon.cpp; virtual void SignalListener::run()","source_line":"192"}
|
||||||
|
{"date_time_utc":"2024-11-06T09:06:09Z","thread_name":"#1","thread_id":"254545","level":"Trace","query_id":"","logger_name":"BaseDaemon","message":"Received signal 2","source_file":"../base/daemon/BaseDaemon.cpp; virtual void SignalListener::run()","source_line":"192"}
|
||||||
To enable JSON logging support, please uncomment the entire <formatting> tag below.
|
To enable JSON logging support, please uncomment the entire <formatting> tag below.
|
||||||
|
|
||||||
a) You can modify key names by changing values under tag values inside <names> tag.
|
a) You can modify key names by changing values under tag values inside <names> tag.
|
||||||
For example, to change DATE_TIME to MY_DATE_TIME, you can do like:
|
For example, to change DATE_TIME to MY_DATE_TIME, you can do like:
|
||||||
<date_time>MY_DATE_TIME</date_time>
|
<date_time>MY_DATE_TIME</date_time>
|
||||||
|
<date_time_utc>MY_UTC_DATE_TIME</date_time_utc>
|
||||||
b) You can stop unwanted log properties to appear in logs. To do so, you can simply comment out (recommended)
|
b) You can stop unwanted log properties to appear in logs. To do so, you can simply comment out (recommended)
|
||||||
that property from this file.
|
that property from this file.
|
||||||
For example, if you do not want your log to print query_id, you can comment out only <query_id> tag.
|
For example, if you do not want your log to print query_id, you can comment out only <query_id> tag.
|
||||||
@ -86,6 +89,7 @@
|
|||||||
<type>json</type>
|
<type>json</type>
|
||||||
<names>
|
<names>
|
||||||
<date_time>date_time</date_time>
|
<date_time>date_time</date_time>
|
||||||
|
<date_time_utc>date_time_utc</date_time_utc>
|
||||||
<thread_name>thread_name</thread_name>
|
<thread_name>thread_name</thread_name>
|
||||||
<thread_id>thread_id</thread_id>
|
<thread_id>thread_id</thread_id>
|
||||||
<level>level</level>
|
<level>level</level>
|
||||||
|
@ -7,12 +7,18 @@
|
|||||||
#include <base/terminalColors.h>
|
#include <base/terminalColors.h>
|
||||||
#include <Common/CurrentThread.h>
|
#include <Common/CurrentThread.h>
|
||||||
#include <Common/HashTable/Hash.h>
|
#include <Common/HashTable/Hash.h>
|
||||||
|
#include <Common/DateLUT.h>
|
||||||
|
#include <Common/DateLUTImpl.h>
|
||||||
|
|
||||||
|
|
||||||
OwnJSONPatternFormatter::OwnJSONPatternFormatter(Poco::Util::AbstractConfiguration & config)
|
OwnJSONPatternFormatter::OwnJSONPatternFormatter(Poco::Util::AbstractConfiguration & config)
|
||||||
{
|
{
|
||||||
if (config.has("logger.formatting.names.date_time"))
|
if (config.has("logger.formatting.names.date_time"))
|
||||||
date_time = config.getString("logger.formatting.names.date_time", "");
|
date_time = config.getString("logger.formatting.names.date_time", "");
|
||||||
|
|
||||||
|
if (config.has("logger.formatting.names.date_time_utc"))
|
||||||
|
date_time_utc= config.getString("logger.formatting.names.date_time_utc", "");
|
||||||
|
|
||||||
if (config.has("logger.formatting.names.thread_name"))
|
if (config.has("logger.formatting.names.thread_name"))
|
||||||
thread_name = config.getString("logger.formatting.names.thread_name", "");
|
thread_name = config.getString("logger.formatting.names.thread_name", "");
|
||||||
|
|
||||||
@ -41,6 +47,7 @@ OwnJSONPatternFormatter::OwnJSONPatternFormatter(Poco::Util::AbstractConfigurati
|
|||||||
&& logger_name.empty() && message.empty() && source_file.empty() && source_line.empty())
|
&& logger_name.empty() && message.empty() && source_file.empty() && source_line.empty())
|
||||||
{
|
{
|
||||||
date_time = "date_time";
|
date_time = "date_time";
|
||||||
|
date_time_utc = "date_time_utc";
|
||||||
thread_name = "thread_name";
|
thread_name = "thread_name";
|
||||||
thread_id = "thread_id";
|
thread_id = "thread_id";
|
||||||
level = "level";
|
level = "level";
|
||||||
@ -62,8 +69,22 @@ void OwnJSONPatternFormatter::formatExtended(const DB::ExtendedLogMessage & msg_
|
|||||||
const Poco::Message & msg = msg_ext.base;
|
const Poco::Message & msg = msg_ext.base;
|
||||||
DB::writeChar('{', wb);
|
DB::writeChar('{', wb);
|
||||||
|
|
||||||
|
if (!date_time_utc.empty())
|
||||||
|
{
|
||||||
|
writeJSONString(date_time_utc, wb, settings);
|
||||||
|
DB::writeChar(':', wb);
|
||||||
|
|
||||||
|
DB::writeChar('\"', wb);
|
||||||
|
static const DateLUTImpl & utc_time_zone = DateLUT::instance("UTC");
|
||||||
|
writeDateTimeTextISO(msg_ext.time_seconds, 0, wb, utc_time_zone);
|
||||||
|
|
||||||
|
DB::writeChar('\"', wb);
|
||||||
|
print_comma = true;
|
||||||
|
}
|
||||||
|
|
||||||
if (!date_time.empty())
|
if (!date_time.empty())
|
||||||
{
|
{
|
||||||
|
if (print_comma) DB::writeChar(',', wb);
|
||||||
writeJSONString(date_time, wb, settings);
|
writeJSONString(date_time, wb, settings);
|
||||||
DB::writeChar(':', wb);
|
DB::writeChar(':', wb);
|
||||||
|
|
||||||
@ -81,6 +102,7 @@ void OwnJSONPatternFormatter::formatExtended(const DB::ExtendedLogMessage & msg_
|
|||||||
print_comma = true;
|
print_comma = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (!thread_name.empty())
|
if (!thread_name.empty())
|
||||||
{
|
{
|
||||||
if (print_comma)
|
if (print_comma)
|
||||||
|
@ -33,6 +33,7 @@ public:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
std::string date_time;
|
std::string date_time;
|
||||||
|
std::string date_time_utc;
|
||||||
std::string thread_name;
|
std::string thread_name;
|
||||||
std::string thread_id;
|
std::string thread_id;
|
||||||
std::string level;
|
std::string level;
|
||||||
|
@ -6,12 +6,14 @@
|
|||||||
in specified format like JSON.
|
in specified format like JSON.
|
||||||
For example, as below:
|
For example, as below:
|
||||||
{"date_time":"1650918987.180175","thread_name":"#1","thread_id":"254545","level":"Trace","query_id":"","logger_name":"BaseDaemon","message":"Received signal 2","source_file":"../base/daemon/BaseDaemon.cpp; virtual void SignalListener::run()","source_line":"192"}
|
{"date_time":"1650918987.180175","thread_name":"#1","thread_id":"254545","level":"Trace","query_id":"","logger_name":"BaseDaemon","message":"Received signal 2","source_file":"../base/daemon/BaseDaemon.cpp; virtual void SignalListener::run()","source_line":"192"}
|
||||||
|
{"date_time_utc":"2024-11-06T09:06:09Z","thread_name":"#1","thread_id":"254545","level":"Trace","query_id":"","logger_name":"BaseDaemon","message":"Received signal 2","source_file":"../base/daemon/BaseDaemon.cpp; virtual void SignalListener::run()","source_line":"192"}
|
||||||
To enable JSON logging support, just uncomment <formatting> tag below.
|
To enable JSON logging support, just uncomment <formatting> tag below.
|
||||||
-->
|
-->
|
||||||
<formatting>
|
<formatting>
|
||||||
<type>json</type>
|
<type>json</type>
|
||||||
<names>
|
<names>
|
||||||
<date_time>DATE_TIME</date_time>
|
<date_time>DATE_TIME</date_time>
|
||||||
|
<date_time_utc>DATE_TIME_UTC</date_time_utc>
|
||||||
<thread_name>THREAD_NAME</thread_name>
|
<thread_name>THREAD_NAME</thread_name>
|
||||||
<thread_id>THREAD_ID</thread_id>
|
<thread_id>THREAD_ID</thread_id>
|
||||||
<level>LEVEL</level>
|
<level>LEVEL</level>
|
||||||
|
@ -5,12 +5,14 @@
|
|||||||
in specified format like JSON.
|
in specified format like JSON.
|
||||||
For example, as below:
|
For example, as below:
|
||||||
{"date_time":"1650918987.180175","thread_name":"#1","thread_id":"254545","level":"Trace","query_id":"","logger_name":"BaseDaemon","message":"Received signal 2","source_file":"../base/daemon/BaseDaemon.cpp; virtual void SignalListener::run()","source_line":"192"}
|
{"date_time":"1650918987.180175","thread_name":"#1","thread_id":"254545","level":"Trace","query_id":"","logger_name":"BaseDaemon","message":"Received signal 2","source_file":"../base/daemon/BaseDaemon.cpp; virtual void SignalListener::run()","source_line":"192"}
|
||||||
|
{"date_time_utc":"2024-11-06T09:06:09Z","thread_name":"#1","thread_id":"254545","level":"Trace","query_id":"","logger_name":"BaseDaemon","message":"Received signal 2","source_file":"../base/daemon/BaseDaemon.cpp; virtual void SignalListener::run()","source_line":"192"}
|
||||||
To enable JSON logging support, just uncomment <formatting> tag below.
|
To enable JSON logging support, just uncomment <formatting> tag below.
|
||||||
-->
|
-->
|
||||||
<formatting>
|
<formatting>
|
||||||
<type>json</type>
|
<type>json</type>
|
||||||
<names>
|
<names>
|
||||||
<date_time>DATE_TIME</date_time>
|
<date_time>DATE_TIME</date_time>
|
||||||
|
<date_time_utc>DATE_TIME_UTC</date_time_utc>
|
||||||
<thread_name>THREAD_NAME</thread_name>
|
<thread_name>THREAD_NAME</thread_name>
|
||||||
<thread_id>THREAD_ID</thread_id>
|
<thread_id>THREAD_ID</thread_id>
|
||||||
<level>LEVEL</level>
|
<level>LEVEL</level>
|
||||||
|
@ -6,12 +6,14 @@
|
|||||||
in specified format like JSON.
|
in specified format like JSON.
|
||||||
For example, as below:
|
For example, as below:
|
||||||
{"date_time":"1650918987.180175","thread_name":"#1","thread_id":"254545","level":"Trace","query_id":"","logger_name":"BaseDaemon","message":"Received signal 2","source_file":"../base/daemon/BaseDaemon.cpp; virtual void SignalListener::run()","source_line":"192"}
|
{"date_time":"1650918987.180175","thread_name":"#1","thread_id":"254545","level":"Trace","query_id":"","logger_name":"BaseDaemon","message":"Received signal 2","source_file":"../base/daemon/BaseDaemon.cpp; virtual void SignalListener::run()","source_line":"192"}
|
||||||
|
{"date_time_utc":"2024-11-06T09:06:09Z","thread_name":"#1","thread_id":"254545","level":"Trace","query_id":"","logger_name":"BaseDaemon","message":"Received signal 2","source_file":"../base/daemon/BaseDaemon.cpp; virtual void SignalListener::run()","source_line":"192"}
|
||||||
To enable JSON logging support, just uncomment <formatting> tag below.
|
To enable JSON logging support, just uncomment <formatting> tag below.
|
||||||
-->
|
-->
|
||||||
<formatting>
|
<formatting>
|
||||||
<type>json</type>
|
<type>json</type>
|
||||||
<!--<names>
|
<!--<names>
|
||||||
<date_time>DATE_TIME</date_time>
|
<date_time>DATE_TIME</date_time>
|
||||||
|
<date_time_utc>DATE_TIME_UTC</date_time_utc>
|
||||||
<thread_name>THREAD_NAME</thread_name>
|
<thread_name>THREAD_NAME</thread_name>
|
||||||
<thread_id>THREAD_ID</thread_id>
|
<thread_id>THREAD_ID</thread_id>
|
||||||
<level>LEVEL</level>
|
<level>LEVEL</level>
|
||||||
|
@ -6,12 +6,14 @@
|
|||||||
in specified format like JSON.
|
in specified format like JSON.
|
||||||
For example, as below:
|
For example, as below:
|
||||||
{"date_time":"1650918987.180175","thread_name":"#1","thread_id":"254545","level":"Trace","query_id":"","logger_name":"BaseDaemon","message":"Received signal 2","source_file":"../base/daemon/BaseDaemon.cpp; virtual void SignalListener::run()","source_line":"192"}
|
{"date_time":"1650918987.180175","thread_name":"#1","thread_id":"254545","level":"Trace","query_id":"","logger_name":"BaseDaemon","message":"Received signal 2","source_file":"../base/daemon/BaseDaemon.cpp; virtual void SignalListener::run()","source_line":"192"}
|
||||||
|
{"date_time_utc":"2024-11-06T09:06:09Z","thread_name":"#1","thread_id":"254545","level":"Trace","query_id":"","logger_name":"BaseDaemon","message":"Received signal 2","source_file":"../base/daemon/BaseDaemon.cpp; virtual void SignalListener::run()","source_line":"192"}
|
||||||
To enable JSON logging support, just uncomment <formatting> tag below.
|
To enable JSON logging support, just uncomment <formatting> tag below.
|
||||||
-->
|
-->
|
||||||
<formatting>
|
<formatting>
|
||||||
<type>json</type>
|
<type>json</type>
|
||||||
<names>
|
<names>
|
||||||
<date_time>DATE_TIME</date_time>
|
<date_time>DATE_TIME</date_time>
|
||||||
|
<date_time_utc>DATE_TIME_UTC</date_time_utc>
|
||||||
<thread_name>THREAD_NAME</thread_name>
|
<thread_name>THREAD_NAME</thread_name>
|
||||||
<thread_id>THREAD_ID</thread_id>
|
<thread_id>THREAD_ID</thread_id>
|
||||||
<level>LEVEL</level>
|
<level>LEVEL</level>
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import json
|
import json
|
||||||
|
from datetime import datetime
|
||||||
from xml.etree import ElementTree as ET
|
from xml.etree import ElementTree as ET
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
@ -58,12 +59,25 @@ def validate_log_level(config, logs):
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def is_valid_utc_datetime(datetime_str):
|
||||||
|
try:
|
||||||
|
datetime.strptime(datetime_str, "%Y-%m-%dT%H:%M:%S.%fZ")
|
||||||
|
return True
|
||||||
|
except ValueError:
|
||||||
|
try:
|
||||||
|
datetime.strptime(datetime_str, "%Y-%m-%dT%H:%M:%SZ")
|
||||||
|
return True
|
||||||
|
except ValueError:
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
def validate_log_config_relation(config, logs, config_type):
|
def validate_log_config_relation(config, logs, config_type):
|
||||||
root = ET.fromstring(config)
|
root = ET.fromstring(config)
|
||||||
keys_in_config = set()
|
keys_in_config = set()
|
||||||
|
|
||||||
if config_type == "config_no_keys":
|
if config_type == "config_no_keys":
|
||||||
keys_in_config.add("date_time")
|
keys_in_config.add("date_time")
|
||||||
|
keys_in_config.add("date_time_utc")
|
||||||
keys_in_config.add("thread_name")
|
keys_in_config.add("thread_name")
|
||||||
keys_in_config.add("thread_id")
|
keys_in_config.add("thread_id")
|
||||||
keys_in_config.add("level")
|
keys_in_config.add("level")
|
||||||
@ -80,14 +94,17 @@ def validate_log_config_relation(config, logs, config_type):
|
|||||||
length = min(10, len(logs))
|
length = min(10, len(logs))
|
||||||
for i in range(0, length):
|
for i in range(0, length):
|
||||||
json_log = json.loads(logs[i])
|
json_log = json.loads(logs[i])
|
||||||
keys_in_log = set()
|
keys_in_log = set(json_log.keys())
|
||||||
for log_key in json_log.keys():
|
|
||||||
keys_in_log.add(log_key)
|
if not keys_in_config.issubset(keys_in_log):
|
||||||
if log_key not in keys_in_config:
|
|
||||||
return False
|
return False
|
||||||
for config_key in keys_in_config:
|
|
||||||
if config_key not in keys_in_log:
|
# Validate the UTC datetime format in "date_time_utc" if it exists
|
||||||
|
if "date_time_utc" in json_log and not is_valid_utc_datetime(
|
||||||
|
json_log["date_time_utc"]
|
||||||
|
):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
except ValueError as e:
|
except ValueError as e:
|
||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
@ -101,7 +118,7 @@ def validate_logs(logs):
|
|||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
def valiade_everything(config, node, config_type):
|
def validate_everything(config, node, config_type):
|
||||||
node.query("SELECT 1")
|
node.query("SELECT 1")
|
||||||
logs = node.grep_in_log("").split("\n")
|
logs = node.grep_in_log("").split("\n")
|
||||||
return (
|
return (
|
||||||
@ -122,8 +139,11 @@ def test_structured_logging_json_format(start_cluster):
|
|||||||
["cat", "/etc/clickhouse-server/config.d/config_no_keys_json.xml"]
|
["cat", "/etc/clickhouse-server/config.d/config_no_keys_json.xml"]
|
||||||
)
|
)
|
||||||
|
|
||||||
assert valiade_everything(config_all_keys, node_all_keys, "config_all_keys") == True
|
|
||||||
assert (
|
assert (
|
||||||
valiade_everything(config_some_keys, node_some_keys, "config_some_keys") == True
|
validate_everything(config_all_keys, node_all_keys, "config_all_keys") == True
|
||||||
)
|
)
|
||||||
assert valiade_everything(config_no_keys, node_no_keys, "config_no_keys") == True
|
assert (
|
||||||
|
validate_everything(config_some_keys, node_some_keys, "config_some_keys")
|
||||||
|
== True
|
||||||
|
)
|
||||||
|
assert validate_everything(config_no_keys, node_no_keys, "config_no_keys") == True
|
||||||
|
Loading…
Reference in New Issue
Block a user