diff --git a/apps/emqx/rebar.config b/apps/emqx/rebar.config index 4ade86dc6..458326b81 100644 --- a/apps/emqx/rebar.config +++ b/apps/emqx/rebar.config @@ -29,7 +29,7 @@ {esockd, {git, "https://github.com/emqx/esockd", {tag, "5.9.1"}}}, {ekka, {git, "https://github.com/emqx/ekka", {tag, "0.12.4"}}}, {gen_rpc, {git, "https://github.com/emqx/gen_rpc", {tag, "2.8.1"}}}, - {hocon, {git, "https://github.com/emqx/hocon.git", {tag, "0.27.3"}}}, + {hocon, {git, "https://github.com/emqx/hocon.git", {tag, "0.27.4"}}}, {pbkdf2, {git, "https://github.com/emqx/erlang-pbkdf2.git", {tag, "2.0.4"}}}, {recon, {git, "https://github.com/ferd/recon", {tag, "2.5.1"}}}, {snabbkaffe, {git, "https://github.com/kafka4beam/snabbkaffe.git", {tag, "1.0.0"}}} diff --git a/apps/emqx_conf/i18n/emqx_conf_schema.conf b/apps/emqx_conf/i18n/emqx_conf_schema.conf index 82913d061..03ed344fc 100644 --- a/apps/emqx_conf/i18n/emqx_conf_schema.conf +++ b/apps/emqx_conf/i18n/emqx_conf_schema.conf @@ -941,26 +941,15 @@ until the RPC connection is considered lost.""" log_file_handlers { desc { en: """Key-value list of file-based log handlers.""" - zh: """基于文件的日志处理进程的键值列表。""" + zh: """需要持久化到文件的日志处理进程列表。默认只有 default 一个处理进程。""" } label { en: "Log Handlers Key Val List" - zh: "日志处理进程键值列表" + zh: "日志 Handler 列表" } } - log_error_logger { - desc { - en: """Deprecated.""" - zh: """该配置已弃用。""" - } - label { - en: "Deprecate" - zh: "配置已弃用" - } - } - - console_handler_enable { + common_handler_enable { desc { en: """Enable this log handler.""" zh: """启用此日志处理进程。""" @@ -971,21 +960,23 @@ until the RPC connection is considered lost.""" } } - console_handler_level { + common_handler_level { desc { en: """Global log level. This includes the primary log level and all log handlers.""" - zh: """全局日志级别。 这包括主日志级别和所有日志处理进程。""" + zh: """设置日志级别。 默认为warning。""" } label { en: "Global Log Level" - zh: "全局日志级别" + zh: "日志级别" } } - console_handler_time_offset { + common_handler_time_offset { desc { en: """The time offset to be used when formatting the timestamp.""" - zh: """格式化时间戳时,使用的时间偏移量。""" + zh: """日志格式中的时间戳,使用的时间偏移量。默认使用系统时区system,当为utc为无时间偏移量 +为具体的N(1-24)数字时,则代表时间偏移量+N。 + """ } label { en: "Time Offset" @@ -993,10 +984,10 @@ until the RPC connection is considered lost.""" } } - console_handler_chars_limit { + common_handler_chars_limit { desc { en: """Set the maximum length of a single log message. If this length is exceeded, the log message will be truncated.""" - zh: """设置单个日志消息的最大长度。 如果超过此长度,则日志消息将被截断。""" + zh: """设置单个日志消息的最大长度。 如果超过此长度,则日志消息将被截断。最小可设置的长度为100。""" } label { en: "Single Log Max Length" @@ -1004,10 +995,10 @@ until the RPC connection is considered lost.""" } } - console_handler_formatter { + common_handler_formatter { desc { en: """Choose log format. text for free text, and json for structured logging.""" - zh: """选择日志格式。 text 用于自由文本,json 用于结构化日志记录。""" + zh: """选择日志格式。 text 用于纯文本,json 用于结构化日志记录。""" } label { en: "Log Format" @@ -1015,10 +1006,10 @@ until the RPC connection is considered lost.""" } } - console_handler_single_line { + common_handler_single_line { desc { en: """Print logs in a single line if set to true. Otherwise, log messages may span multiple lines.""" - zh: """如果设置为 true,则在一行中打印日志。 否则,日志消息可能跨越多行。""" + zh: """如果设置为 true,则单行打印日志。 否则,日志消息可能跨越多行。""" } label { en: "Single Line Mode" @@ -1026,10 +1017,24 @@ until the RPC connection is considered lost.""" } } - console_handler_sync_mode_qlen { + common_handler_sync_mode_qlen { desc { - en: """As long as the number of buffered log events is lower than this value, all log events are handled asynchronously.""" - zh: """只要缓冲的日志事件的数量低于这个值,所有的日志事件都会被异步处理。""" + en: """As long as the number of buffered log events is lower than this value, +all log events are handled asynchronously. This means that the client process sending the log event, +by calling a log function in the Logger API, does not wait for a response from the handler +but continues executing immediately after the event is sent. +It is not affected by the time it takes the handler to print the event to the log device. +If the message queue grows larger than this value, +the handler starts handling log events synchronously instead, +meaning that the client process sending the event must wait for a response. +When the handler reduces the message queue to a level below the sync_mode_qlen threshold, +asynchronous operation is resumed. +""" + zh: """只要缓冲的日志事件的数量低于这个值,所有的日志事件都会被异步处理。 +这意味着,日志落地速度不会影响正常的业务进程,因为它们不需要等待日志处理进程的响应。 +如果消息队列的增长超过了这个值,处理程序开始同步处理日志事件。也就是说,发送事件的客户进程必须等待响应。 +当处理程序将消息队列减少到低于sync_mode_qlen阈值的水平时,异步操作就会恢复。 +默认为100条信息,当等待的日志事件大于100条时,就开始同步处理日志。""" } label { en: "Sync Mode Max Log Events" @@ -1037,10 +1042,17 @@ until the RPC connection is considered lost.""" } } - console_handler_drop_mode_qlen { + common_handler_drop_mode_qlen { desc { - en: """When the number of buffered log events is larger than this value, the new log events are dropped.
When drop mode is activated or deactivated, a message is printed in the logs.""" - zh: """当缓冲的日志事件数大于此值时,新的日志事件将被丢弃。
启用或停用丢弃模式时,会在日志中打印一条消息。""" + en: """When the number of buffered log events is larger than this value, the new log events are dropped. +When drop mode is activated or deactivated, a message is printed in the logs.""" + zh: """当缓冲的日志事件数大于此值时,新的日志事件将被丢弃。起到过载保护的功能。 +为了使过载保护算法正常工作必须要: sync_mode_qlen =< drop_mode_qlen =< flush_qlen <\code> 且 drop_mode_qlen > 1 +要禁用某些模式,请执行以下操作。 +- 如果sync_mode_qlen被设置为0,所有的日志事件都被同步处理。也就是说,异步日志被禁用。 +- 如果sync_mode_qlen被设置为与drop_mode_qlen相同的值,同步模式被禁用。也就是说,处理程序总是以异步模式运行,除非调用drop或flushing。 +- 如果drop_mode_qlen被设置为与flush_qlen相同的值,则drop模式被禁用,永远不会发生。 +""" } label { en: "Drop Mode Max Log Events" @@ -1048,10 +1060,11 @@ until the RPC connection is considered lost.""" } } - console_handler_flush_qlen { + common_handler_flush_qlen { desc { en: """If the number of buffered log events grows larger than this threshold, a flush (delete) operation takes place. To flush events, the handler discards the buffered log messages without logging.""" - zh: """如果缓冲日志事件的数量增长大于此阈值,则会发生刷新(删除)操作。 为了完成刷新事件,处理进程丢弃缓冲的日志消息。""" + zh: """如果缓冲日志事件的数量增长大于此阈值,则会发生刷新(删除)操作。 日志处理进程会丢弃缓冲的日志消息。 +来缓解自身不会由于内存瀑涨而影响其它业务进程。日志内容会提醒有多少事件被删除。""" } label { en: "Flush Threshold" @@ -1059,14 +1072,14 @@ until the RPC connection is considered lost.""" } } - console_handler_supervisor_reports { + common_handler_supervisor_reports { desc { en: """Type of supervisor reports that are logged. - `error`: only log errors in the Erlang processes. - `progress`: log process startup.""" - zh: """ supervisor 报告的类型。 + zh: """ supervisor 报告的类型。默认为 error 类型。 - `error`:仅记录 Erlang 进程中的错误。 - - `progress`:记录进程启动。""" + - `progress`:除了 error 信息外,还需要记录进程启动的详细信息。""" } label { en: "Report Type" @@ -1074,7 +1087,7 @@ until the RPC connection is considered lost.""" } } - console_handler_max_depth { + common_handler_max_depth { desc { en: """Maximum depth for Erlang term log formatting and Erlang process message queue inspection.""" zh: """Erlang 内部格式日志格式化和 Erlang 进程消息队列检查的最大深度。""" @@ -1088,7 +1101,7 @@ until the RPC connection is considered lost.""" log_file_handler_file { desc { en: """Name the log file.""" - zh: """日志文件名字。""" + zh: """日志文件路径及名字。""" } label { en: "Log File Name" @@ -1099,7 +1112,9 @@ until the RPC connection is considered lost.""" log_file_handler_max_size { desc { en: """This parameter controls log file rotation. The value `infinity` means the log file will grow indefinitely, otherwise the log file will be rotated once it reaches `max_size` in bytes.""" - zh: """此参数控制日志文件轮换。 `infinity` 意味着日志文件将无限增长,否则日志文件将在达到 `max_size`(以字节为单位)时进行轮换。""" + zh: """此参数控制日志文件轮换。 `infinity` 意味着日志文件将无限增长,否则日志文件将在达到 `max_size`(以字节为单位)时进行轮换。 +与 rotation count配合使用。如果 counter 为 10,则是10个文件轮换。 +""" } label { en: "Rotation Size" @@ -1107,128 +1122,14 @@ until the RPC connection is considered lost.""" } } - log_file_handler_enable { + log_error_logger { desc { - en: """Enable this log handler.""" - zh: """启用此日志处理进程。""" + en: """Keep error_logger silent.""" + zh: """让 error_logger 日志处理进程关闭,防止一条异常信息被记录多次。""" } label { - en: "Enable Log Handler" - zh: "启用此日志处理进程" - } - } - - log_file_handler_level { - desc { - en: """Global log level. This includes the primary log level and all log handlers.""" - zh: """全局日志级别。 这包括主日志级别和所有日志处理进程。""" - } - label { - en: "Global Level" - zh: "全局日志级别" - } - } - - log_file_handler_time_offset { - desc { - en: """The time offset to be used when formatting the timestamp.""" - zh: """格式化时间戳时要使用的时间偏移量。""" - } - label { - en: "Time Offset" - zh: "时间偏移" - } - } - - log_file_handler_chars_limit { - desc { - en: """Set the maximum length of a single log message. If this length is exceeded, the log message will be truncated.""" - zh: """设置单个日志消息的最大长度。 如果超过此长度,则日志消息将被截断。""" - } - label { - en: "Single Log Max Length" - zh: "单个日志消息最大长度" - } - } - - log_file_handler_formatter { - desc { - en: """Choose log format. text for free text, and json for structured logging.""" - zh: """选择日志格式。 text 用于自由文本,json 用于结构化日志记录。""" - } - label { - en: "Log Format" - zh: "日志格式" - } - } - - log_file_handler_single_line { - desc { - en: """Print logs in a single line if set to true. Otherwise, log messages may span multiple lines.""" - zh: """如果设置为 true,则在一行中打印日志。 否则,日志消息可能跨越多行。""" - } - label { - en: "Single Line Mode" - zh: "单行模式" - } - } - - log_file_handler_sync_mode_qlen { - desc { - en: """As long as the number of buffered log events is lower than this value, all log events are handled asynchronously.""" - zh: """只要缓冲的日志事件的数量低于这个值,所有的日志事件都会被异步处理。""" - } - label { - en: "Sync Mode Max Log Events" - zh: "异步模式最大事件数" - } - } - - log_file_handler_drop_mode_qlen { - desc { - en: """When the number of buffered log events is larger than this value, the new log events are dropped.
When drop mode is activated or deactivated, a message is printed in the logs.""" - zh: """当缓冲的日志事件数大于此值时,新的日志事件将被丢弃。
启用或停用丢弃模式时,会在日志中打印一条消息。""" - } - label { - en: "Drop Mode Max Log Events" - zh: "缓存最大日志事件数" - } - } - - log_file_handler_flush_qlen { - desc { - en: """If the number of buffered log events grows larger than this threshold, a flush (delete) operation takes place. To flush events, the handler discards the buffered log messages without logging.""" - zh: """如果缓冲日志事件的数量增长大于此阈值,则会发生刷新(删除)操作。 为了完成刷新事件,处理进程丢弃缓冲的日志消息。""" - } - label { - en: "Flush Threshold" - zh: "刷新阈值" - } - } - - log_file_handler_supervisor_reports { - desc { - en: """Type of supervisor reports that are logged. - - `error`: only log errors in the Erlang processes. - - `progress`: log process startup.""" - zh: """ supervisor 报告的类型。 - - `error`:仅记录 Erlang 进程中的错误。 - - `progress`:记录进程启动。""" - } - label { - en: "Report Type" - zh: "报告类型" - } - } - - log_file_handler_max_depth { - desc { - en: """Maximum depth for Erlang term log formatting and Erlang process message queue inspection.""" - zh: """Erlang 内部格式日志格式化和 Erlang 进程消息队列检查的最大深度。""" - } - label { - en: "Max Depth" - zh: "最大深度" + en: "error_logger" + zh: "error_logger" } } @@ -1257,11 +1158,11 @@ until the RPC connection is considered lost.""" log_overload_kill_enable { desc { en: """Enable log handler overload kill feature.""" - zh: """启用日志处理进程过载终止功能。""" + zh: """日志处理进程过载时为保护自己节点其它的业务能正常,强制杀死日志处理进程。""" } label { en: "Log Handler Overload Kill" - zh: "日志处理进程过载终止" + zh: "日志处理进程过载保护" } } @@ -1290,22 +1191,22 @@ until the RPC connection is considered lost.""" log_overload_kill_restart_after { desc { en: """If the handler is terminated, it restarts automatically after a delay specified in milliseconds. The value `infinity` prevents restarts.""" - zh: """如果处理进程终止,它会在以毫秒为单位指定的延迟后自动重新启动。 `infinity` 防止重新启动。""" + zh: """如果处理进程终止,它会在以指定的时间后后自动重新启动。 `infinity` 不自动重启。""" } label { - en: "Handler Restart Delay" - zh: "处理进程重启延迟" + en: "Handler Restart Timer" + zh: "处理进程重启机制" } } log_burst_limit_enable { desc { en: """Enable log burst control feature.""" - zh: """启用日志突发控制功能。""" + zh: """启用日志限流保护机制。""" } label { en: "Enable Burst" - zh: "启用日志突发控制" + zh: "日志限流保护" } } @@ -1509,10 +1410,10 @@ By default, the logs are stored in `./log` directory (for installation from zip This section of the configuration controls the number of files kept for each log handler. """ zh: -""" -默认情况下,日志存储在 `./log` 目录(用于从 zip 文件安装)或 `/var/log/emqx`(用于二进制安装)。
-这部分配置,控制每个日志处理进程保留的文件数量。 -""" + """ + 默认情况下,日志存储在 `./log` 目录(用于从 zip 文件安装)或 `/var/log/emqx`(用于二进制安装)。
+ 这部分配置,控制每个日志处理进程保留的文件数量。 + """ } label { en: "Log Rotation" @@ -1568,5 +1469,4 @@ Log burst limit feature can temporarily disable logging to avoid these issues."" zh: "授权" } } - } diff --git a/apps/emqx_conf/src/emqx_conf_schema.erl b/apps/emqx_conf/src/emqx_conf_schema.erl index f882855e2..7ab815d9a 100644 --- a/apps/emqx_conf/src/emqx_conf_schema.erl +++ b/apps/emqx_conf/src/emqx_conf_schema.erl @@ -800,6 +800,7 @@ fields("log") -> #{ mapping => "kernel.error_logger", default => silent, + 'readOnly' => true, desc => ?DESC("log_error_logger") } )} @@ -811,7 +812,10 @@ fields("log_file_handler") -> {"file", sc( file(), - #{desc => ?DESC("log_file_handler_file")} + #{ + desc => ?DESC("log_file_handler_file"), + validator => fun file_location/1 + } )}, {"rotation", sc( @@ -822,7 +826,7 @@ fields("log_file_handler") -> sc( hoconsc:union([infinity, emqx_schema:bytesize()]), #{ - default => "10MB", + default => "50MB", desc => ?DESC("log_file_handler_max_size") } )} @@ -866,7 +870,7 @@ fields("log_overload_kill") -> )}, {"qlen", sc( - integer(), + pos_integer(), #{ default => 20000, desc => ?DESC("log_overload_kill_qlen") @@ -874,7 +878,7 @@ fields("log_overload_kill") -> )}, {"restart_after", sc( - hoconsc:union([emqx_schema:duration(), infinity]), + hoconsc:union([emqx_schema:duration_ms(), infinity]), #{ default => "5s", desc => ?DESC("log_overload_kill_restart_after") @@ -893,7 +897,7 @@ fields("log_burst_limit") -> )}, {"max_count", sc( - integer(), + pos_integer(), #{ default => 10000, desc => ?DESC("log_burst_limit_max_count") @@ -1073,7 +1077,7 @@ log_handler_common_confs() -> boolean(), #{ default => false, - desc => ?DESC("log_file_handler_enable") + desc => ?DESC("common_handler_enable") } )}, {"level", @@ -1081,7 +1085,7 @@ log_handler_common_confs() -> log_level(), #{ default => warning, - desc => ?DESC("log_file_handler_level") + desc => ?DESC("common_handler_level") } )}, {"time_offset", @@ -1089,15 +1093,15 @@ log_handler_common_confs() -> string(), #{ default => "system", - desc => ?DESC("log_file_handler_time_offset") + desc => ?DESC("common_handler_time_offset") } )}, {"chars_limit", sc( - hoconsc:union([unlimited, range(1, inf)]), + hoconsc:union([unlimited, range(100, inf)]), #{ default => unlimited, - desc => ?DESC("log_file_handler_chars_limit") + desc => ?DESC("common_handler_chars_limit") } )}, {"formatter", @@ -1105,7 +1109,7 @@ log_handler_common_confs() -> hoconsc:enum([text, json]), #{ default => text, - desc => ?DESC("log_file_handler_formatter") + desc => ?DESC("common_handler_formatter") } )}, {"single_line", @@ -1113,31 +1117,31 @@ log_handler_common_confs() -> boolean(), #{ default => true, - desc => ?DESC("log_file_handler_single_line") + desc => ?DESC("common_handler_single_line") } )}, {"sync_mode_qlen", sc( - integer(), + non_neg_integer(), #{ default => 100, - desc => ?DESC("log_file_handler_sync_mode_qlen") + desc => ?DESC("common_handler_sync_mode_qlen") } )}, {"drop_mode_qlen", sc( - integer(), + pos_integer(), #{ default => 3000, - desc => ?DESC("log_file_handler_drop_mode_qlen") + desc => ?DESC("common_handler_drop_mode_qlen") } )}, {"flush_qlen", sc( - integer(), + pos_integer(), #{ default => 8000, - desc => ?DESC("log_file_handler_flush_qlen") + desc => ?DESC("common_handler_flush_qlen") } )}, {"overload_kill", sc(ref("log_overload_kill"), #{})}, @@ -1147,7 +1151,7 @@ log_handler_common_confs() -> hoconsc:enum([error, progress]), #{ default => error, - desc => ?DESC("log_file_handler_supervisor_reports") + desc => ?DESC("common_handler_supervisor_reports") } )}, {"max_depth", @@ -1155,7 +1159,7 @@ log_handler_common_confs() -> hoconsc:union([unlimited, non_neg_integer()]), #{ default => 100, - desc => ?DESC("log_file_handler_max_depth") + desc => ?DESC("common_handler_max_depth") } )} ]. @@ -1328,3 +1332,15 @@ emqx_schema_high_prio_roots() -> #{desc => ?DESC(authorization)} )}, lists:keyreplace("authorization", 1, Roots, Authz). + +-define(VALID_FILE, "^[/\_a-zA-Z0-9\.\-]*$"). +file_location(File) -> + Error = {error, "Invalid file name: " ++ ?VALID_FILE}, + try + case re:run(File, ?VALID_FILE) of + nomatch -> Error; + _ -> ok + end + catch + _:_ -> Error + end. diff --git a/apps/emqx_management/src/emqx_mgmt_api_cluster.erl b/apps/emqx_management/src/emqx_mgmt_api_cluster.erl index 25ec5c9a2..e082fa745 100644 --- a/apps/emqx_management/src/emqx_mgmt_api_cluster.erl +++ b/apps/emqx_management/src/emqx_mgmt_api_cluster.erl @@ -43,7 +43,8 @@ schema("/cluster") -> responses => #{ 200 => [ {name, ?HOCON(string(), #{desc => "Cluster name"})}, - {nodes, ?HOCON(?ARRAY(string()), #{desc => "Node name"})} + {nodes, ?HOCON(?ARRAY(string()), #{desc => "Node name"})}, + {self, ?HOCON(string(), #{desc => "Self node name"})} ] } } @@ -97,7 +98,8 @@ cluster_info(get, _) -> ClusterName = application:get_env(ekka, cluster_name, emqxcl), Info = #{ name => ClusterName, - nodes => mria_mnesia:running_nodes() + nodes => mria_mnesia:running_nodes(), + self => node() }, {200, Info}. diff --git a/apps/emqx_prometheus/rebar.config b/apps/emqx_prometheus/rebar.config index a51d56b99..974192a41 100644 --- a/apps/emqx_prometheus/rebar.config +++ b/apps/emqx_prometheus/rebar.config @@ -4,7 +4,7 @@ [ {emqx, {path, "../emqx"}}, %% FIXME: tag this as v3.1.3 {prometheus, {git, "https://github.com/deadtrickster/prometheus.erl", {tag, "v4.8.1"}}}, - {hocon, {git, "https://github.com/emqx/hocon.git", {tag, "0.27.3"}}} + {hocon, {git, "https://github.com/emqx/hocon.git", {tag, "0.27.4"}}} ]}. {edoc_opts, [{preprocess, true}]}. diff --git a/mix.exs b/mix.exs index 4684c15c0..b999480ab 100644 --- a/mix.exs +++ b/mix.exs @@ -57,7 +57,7 @@ defmodule EMQXUmbrella.MixProject do {:mria, github: "emqx/mria", tag: "0.2.4", override: true}, {:ekka, github: "emqx/ekka", tag: "0.12.4", override: true}, {:gen_rpc, github: "emqx/gen_rpc", tag: "2.8.1", override: true}, - {:minirest, github: "emqx/minirest", tag: "1.2.12", override: true}, + {:minirest, github: "emqx/minirest", tag: "1.2.13", override: true}, {:ecpool, github: "emqx/ecpool", tag: "0.5.2"}, {:replayq, "0.3.4", override: true}, {:pbkdf2, github: "emqx/erlang-pbkdf2", tag: "2.0.4", override: true}, @@ -68,7 +68,7 @@ defmodule EMQXUmbrella.MixProject do # in conflict by emqtt and hocon {:getopt, "1.0.2", override: true}, {:snabbkaffe, github: "kafka4beam/snabbkaffe", tag: "1.0.0", override: true}, - {:hocon, github: "emqx/hocon", tag: "0.27.3", override: true}, + {:hocon, github: "emqx/hocon", tag: "0.27.4", override: true}, {:emqx_http_lib, github: "emqx/emqx_http_lib", tag: "0.5.1", override: true}, {:esasl, github: "emqx/esasl", tag: "0.2.0"}, {:jose, github: "potatosalad/erlang-jose", tag: "1.11.2"}, diff --git a/rebar.config b/rebar.config index 5b08853dd..95e3f705c 100644 --- a/rebar.config +++ b/rebar.config @@ -56,7 +56,7 @@ , {mria, {git, "https://github.com/emqx/mria", {tag, "0.2.4"}}} , {ekka, {git, "https://github.com/emqx/ekka", {tag, "0.12.4"}}} , {gen_rpc, {git, "https://github.com/emqx/gen_rpc", {tag, "2.8.1"}}} - , {minirest, {git, "https://github.com/emqx/minirest", {tag, "1.2.12"}}} + , {minirest, {git, "https://github.com/emqx/minirest", {tag, "1.2.13"}}} , {ecpool, {git, "https://github.com/emqx/ecpool", {tag, "0.5.2"}}} , {replayq, "0.3.4"} , {pbkdf2, {git, "https://github.com/emqx/erlang-pbkdf2.git", {tag, "2.0.4"}}} @@ -66,7 +66,7 @@ , {system_monitor, {git, "https://github.com/ieQu1/system_monitor", {tag, "3.0.3"}}} , {getopt, "1.0.2"} , {snabbkaffe, {git, "https://github.com/kafka4beam/snabbkaffe.git", {tag, "1.0.0"}}} - , {hocon, {git, "https://github.com/emqx/hocon.git", {tag, "0.27.3"}}} + , {hocon, {git, "https://github.com/emqx/hocon.git", {tag, "0.27.4"}}} , {emqx_http_lib, {git, "https://github.com/emqx/emqx_http_lib.git", {tag, "0.5.1"}}} , {esasl, {git, "https://github.com/emqx/esasl", {tag, "0.2.0"}}} , {jose, {git, "https://github.com/potatosalad/erlang-jose", {tag, "1.11.2"}}}