199 lines
6.3 KiB
Erlang
199 lines
6.3 KiB
Erlang
%%--------------------------------------------------------------------
|
|
%% Copyright (c) 2020-2024 EMQ Technologies Co., Ltd. All Rights Reserved.
|
|
%%
|
|
%% Licensed under the Apache License, Version 2.0 (the "License");
|
|
%% you may not use this file except in compliance with the License.
|
|
%% You may obtain a copy of the License at
|
|
%%
|
|
%% http://www.apache.org/licenses/LICENSE-2.0
|
|
%%
|
|
%% Unless required by applicable law or agreed to in writing, software
|
|
%% distributed under the License is distributed on an "AS IS" BASIS,
|
|
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
%% See the License for the specific language governing permissions and
|
|
%% limitations under the License.
|
|
%%--------------------------------------------------------------------
|
|
-module(emqx_otel_config).
|
|
|
|
-behaviour(emqx_config_handler).
|
|
|
|
-include_lib("emqx/include/logger.hrl").
|
|
|
|
-define(OPTL, [opentelemetry]).
|
|
-define(CERTS_PATH, filename:join(["opentelemetry", "exporter"])).
|
|
|
|
-define(OTEL_EXPORTER, opentelemetry_exporter).
|
|
-define(OTEL_LOG_HANDLER, otel_log_handler).
|
|
-define(OTEL_LOG_HANDLER_ID, opentelemetry_handler).
|
|
|
|
-export([add_handler/0, remove_handler/0]).
|
|
-export([pre_config_update/3, post_config_update/5]).
|
|
-export([update/1]).
|
|
-export([add_otel_log_handler/0, remove_otel_log_handler/0]).
|
|
-export([otel_exporter/1]).
|
|
|
|
update(Config) ->
|
|
case
|
|
emqx_conf:update(
|
|
?OPTL,
|
|
Config,
|
|
#{rawconf_with_defaults => true, override_to => cluster}
|
|
)
|
|
of
|
|
{ok, #{raw_config := NewConfigRows}} ->
|
|
{ok, NewConfigRows};
|
|
{error, Reason} ->
|
|
{error, Reason}
|
|
end.
|
|
|
|
add_handler() ->
|
|
ok = emqx_config_handler:add_handler(?OPTL, ?MODULE),
|
|
ok.
|
|
|
|
remove_handler() ->
|
|
ok = emqx_config_handler:remove_handler(?OPTL),
|
|
ok.
|
|
|
|
pre_config_update(?OPTL, RawConf, RawConf) ->
|
|
{ok, RawConf};
|
|
pre_config_update(?OPTL, NewRawConf, _RawConf) ->
|
|
{ok, convert_certs(NewRawConf)}.
|
|
|
|
post_config_update(?OPTL, _Req, Old, Old, _AppEnvs) ->
|
|
ok;
|
|
post_config_update(?OPTL, _Req, New, Old, AppEnvs) ->
|
|
application:set_env(AppEnvs),
|
|
MetricsRes = ensure_otel_metrics(New, Old),
|
|
LogsRes = ensure_otel_logs(New, Old),
|
|
TracesRes = ensure_otel_traces(New, Old),
|
|
case {MetricsRes, LogsRes, TracesRes} of
|
|
{ok, ok, ok} -> ok;
|
|
Other -> {error, Other}
|
|
end;
|
|
post_config_update(_ConfPath, _Req, _NewConf, _OldConf, _AppEnvs) ->
|
|
ok.
|
|
|
|
add_otel_log_handler() ->
|
|
ensure_otel_logs(emqx:get_config(?OPTL), #{}).
|
|
|
|
remove_otel_log_handler() ->
|
|
remove_handler_if_present(?OTEL_LOG_HANDLER_ID).
|
|
|
|
otel_exporter(ExporterConf) ->
|
|
#{
|
|
endpoint := Endpoint,
|
|
protocol := Proto,
|
|
ssl_options := SSLOpts
|
|
} = ExporterConf,
|
|
{?OTEL_EXPORTER, #{
|
|
endpoint => Endpoint,
|
|
protocol => Proto,
|
|
ssl_options => ssl_opts(Endpoint, SSLOpts)
|
|
}}.
|
|
|
|
%% Internal functions
|
|
|
|
convert_certs(#{<<"exporter">> := ExporterConf} = NewRawConf) ->
|
|
NewRawConf#{<<"exporter">> => convert_exporter_certs(ExporterConf)};
|
|
convert_certs(#{exporter := ExporterConf} = NewRawConf) ->
|
|
NewRawConf#{exporter => convert_exporter_certs(ExporterConf)};
|
|
convert_certs(NewRawConf) ->
|
|
NewRawConf.
|
|
|
|
convert_exporter_certs(#{<<"ssl_options">> := SSLOpts} = ExporterConf) ->
|
|
ExporterConf#{<<"ssl_options">> => do_convert_certs(SSLOpts)};
|
|
convert_exporter_certs(#{ssl_options := SSLOpts} = ExporterConf) ->
|
|
ExporterConf#{ssl_options => do_convert_certs(SSLOpts)};
|
|
convert_exporter_certs(ExporterConf) ->
|
|
ExporterConf.
|
|
|
|
do_convert_certs(SSLOpts) ->
|
|
case emqx_tls_lib:ensure_ssl_files(?CERTS_PATH, SSLOpts) of
|
|
{ok, undefined} ->
|
|
SSLOpts;
|
|
{ok, SSLOpts1} ->
|
|
SSLOpts1;
|
|
{error, Reason} ->
|
|
?SLOG(error, Reason#{msg => "bad_ssl_config", name => "opentelemetry_exporter"}),
|
|
throw({bad_ssl_config, Reason})
|
|
end.
|
|
|
|
ensure_otel_metrics(
|
|
#{metrics := MetricsConf, exporter := Exporter},
|
|
#{metrics := MetricsConf, exporter := Exporter}
|
|
) ->
|
|
ok;
|
|
ensure_otel_metrics(#{metrics := #{enable := true}} = Conf, _Old) ->
|
|
ok = emqx_otel_cpu_sup:stop_otel_cpu_sup(),
|
|
_ = emqx_otel_cpu_sup:start_otel_cpu_sup(Conf),
|
|
_ = emqx_otel_metrics:stop_otel(),
|
|
emqx_otel_metrics:start_otel(Conf);
|
|
ensure_otel_metrics(#{metrics := #{enable := false}}, _Old) ->
|
|
ok = emqx_otel_cpu_sup:stop_otel_cpu_sup(),
|
|
emqx_otel_metrics:stop_otel();
|
|
ensure_otel_metrics(_, _) ->
|
|
ok.
|
|
|
|
ensure_otel_logs(
|
|
#{logs := LogsConf, exporter := Exporter},
|
|
#{logs := LogsConf, exporter := Exporter}
|
|
) ->
|
|
ok;
|
|
ensure_otel_logs(#{logs := #{enable := true}} = Conf, _OldConf) ->
|
|
ok = remove_handler_if_present(?OTEL_LOG_HANDLER_ID),
|
|
HandlerConf = tr_handler_conf(Conf),
|
|
%% NOTE: should primary logger level be updated if it's higher than otel log level?
|
|
logger:add_handler(?OTEL_LOG_HANDLER_ID, ?OTEL_LOG_HANDLER, HandlerConf);
|
|
ensure_otel_logs(#{logs := #{enable := false}}, _OldConf) ->
|
|
remove_handler_if_present(?OTEL_LOG_HANDLER_ID).
|
|
|
|
ensure_otel_traces(
|
|
#{traces := TracesConf, exporter := Exporter},
|
|
#{traces := TracesConf, exporter := Exporter}
|
|
) ->
|
|
ok;
|
|
ensure_otel_traces(#{traces := #{enable := true}} = Conf, _OldConf) ->
|
|
_ = emqx_otel_trace:stop(),
|
|
emqx_otel_trace:start(Conf);
|
|
ensure_otel_traces(#{traces := #{enable := false}}, _OldConf) ->
|
|
emqx_otel_trace:stop().
|
|
|
|
remove_handler_if_present(HandlerId) ->
|
|
case logger:get_handler_config(HandlerId) of
|
|
{ok, _} ->
|
|
ok = logger:remove_handler(HandlerId);
|
|
_ ->
|
|
ok
|
|
end.
|
|
|
|
tr_handler_conf(#{logs := LogsConf, exporter := ExporterConf}) ->
|
|
#{
|
|
level := Level,
|
|
max_queue_size := MaxQueueSize,
|
|
exporting_timeout := ExportingTimeout,
|
|
scheduled_delay := ScheduledDelay
|
|
} = LogsConf,
|
|
#{
|
|
level => Level,
|
|
config => #{
|
|
max_queue_size => MaxQueueSize,
|
|
exporting_timeout_ms => ExportingTimeout,
|
|
scheduled_delay_ms => ScheduledDelay,
|
|
exporter => otel_exporter(ExporterConf)
|
|
}
|
|
}.
|
|
|
|
ssl_opts(Endpoint, SSLOpts) ->
|
|
case is_ssl(Endpoint) of
|
|
true ->
|
|
%% force enable ssl
|
|
emqx_tls_lib:to_client_opts(SSLOpts#{enable => true});
|
|
false ->
|
|
[]
|
|
end.
|
|
|
|
is_ssl(<<"https://", _/binary>>) ->
|
|
true;
|
|
is_ssl(<<"http://", _/binary>>) ->
|
|
false.
|