fixeria has uploaded this change for review. (
https://gerrit.osmocom.org/c/erlang/osmo-s1gw/+/39415?usp=email )
Change subject: WIP: add GSMTAP logging handler
......................................................................
WIP: add GSMTAP logging handler
Change-Id: Iea884e2ca146b852c64bd9f135d8c71f4c925f09
---
M config/sys.config
A src/logger_gsmtap.erl
A src/logger_gsmtap_h.erl
3 files changed, 258 insertions(+), 1 deletion(-)
git pull ssh://gerrit.osmocom.org:29418/erlang/osmo-s1gw refs/changes/15/39415/1
diff --git a/config/sys.config b/config/sys.config
index d405db8..3bac4e4 100644
--- a/config/sys.config
+++ b/config/sys.config
@@ -26,7 +26,9 @@
{kernel,
[{logger_level, info},
{logger,
- [{handler, default, logger_std_h,
+ [{handler, gsmtap, logger_gsmtap_h,
+ #{level => debug}},
+ {handler, default, logger_std_h,
#{formatter =>
{logger_color_formatter, #{legacy_header => false,
single_line => false,
diff --git a/src/logger_gsmtap.erl b/src/logger_gsmtap.erl
new file mode 100644
index 0000000..18998e6
--- /dev/null
+++ b/src/logger_gsmtap.erl
@@ -0,0 +1,178 @@
+%% Copyright (C) 2025 by sysmocom - s.f.m.c. GmbH <info(a)sysmocom.de>
+%% Author: Vadim Yanitskiy <vyanitskiy(a)sysmocom.de>
+%%
+%% All Rights Reserved
+%%
+%% SPDX-License-Identifier: AGPL-3.0-or-later
+%%
+%% This program is free software; you can redistribute it and/or modify
+%% it under the terms of the GNU Affero General Public License as
+%% published by the Free Software Foundation; either version 3 of the
+%% License, or (at your option) any later version.
+%%
+%% This program is distributed in the hope that it will be useful,
+%% but WITHOUT ANY WARRANTY; without even the implied warranty of
+%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+%% GNU General Public License for more details.
+%%
+%% You should have received a copy of the GNU Affero General Public License
+%% along with this program. If not, see <https://www.gnu.org/licenses/>.
+%%
+%% Additional Permission under GNU AGPL version 3 section 7:
+%%
+%% If you modify this Program, or any covered work, by linking or
+%% combining it with runtime libraries of Erlang/OTP as released by
+%% Ericsson on
https://www.erlang.org (or a modified version of these
+%% libraries), containing parts covered by the terms of the Erlang Public
+%% License (
https://www.erlang.org/EPLICENSE), the licensors of this
+%% Program grant you additional permission to convey the resulting work
+%% without the need to license the runtime libraries of Erlang/OTP under
+%% the GNU Affero General Public License. Corresponding Source for a
+%% non-source form of such a combination shall include the source code
+%% for the parts of the runtime libraries of Erlang/OTP used as well as
+%% that of the covered work.
+
+-module(logger_gsmtap).
+
+-behaviour(gen_server).
+
+%% public API
+-export([start/1,
+ stop/1,
+ log/2]).
+%% gen_server callbacks
+-export([init/1,
+ handle_call/3,
+ handle_cast/2,
+ terminate/2]).
+
+-define(GSMTAP_PORT, 4729).
+-define(GSMTAP_VERSION, 16#02).
+-define(GSMTAP_HDR_LEN, 16#04). %% in number of 32bit words
+-define(GSMTAP_TYPE_OSMOCORE_LOG, 16#10).
+
+
+%% ------------------------------------------------------------------
+%% public API
+%% ------------------------------------------------------------------
+
+-spec start(map()) -> gen_server:start_ret().
+start(Config) ->
+ gen_server:start(?MODULE, [Config], []).
+
+
+-spec log(pid(), logger:log_event()) -> ok.
+log(Pid, LogEvent) ->
+ gen_server:cast(Pid, {?FUNCTION_NAME, LogEvent}).
+
+
+-spec stop(pid()) -> ok.
+stop(Pid) ->
+ gen_server:stop(Pid).
+
+
+%% ------------------------------------------------------------------
+%% gen_server API
+%% ------------------------------------------------------------------
+
+init([RAddr]) ->
+ %% TODO: case gen_udp:open() of ... end, return an error
+ %% TODO: [{ip, Address}] -- bind IP
+ {ok, Sock} = gen_udp:open(0, [binary,
+ {reuseaddr, true}]),
+ %% ok = gen_udp:connect(Sock, RemAddr),
+ {ok, #{gsmtap_sock => Sock,
+ gsmtap_raddr => RAddr}}.
+
+
+handle_call(_Request, _From, S) ->
+ {reply, {error, not_implemented}, S}.
+
+
+handle_cast({log, LogEvent}, S) ->
+ PDU = gsmtap_pdu(LogEvent),
+ send_data(PDU, S),
+ {noreply, S};
+
+handle_cast(_Request, S) ->
+ {noreply, S}.
+
+
+terminate(_Reason, #{sock := Sock}) ->
+ gen_udp:close(Sock),
+ ok.
+
+
+%% ------------------------------------------------------------------
+%% private API
+%% ------------------------------------------------------------------
+
+-spec send_data(binary(), map()) -> ok | {error, term()}.
+send_data(Data, #{gsmtap_sock := Sock,
+ gsmtap_raddr := RemAddr}) ->
+ gen_udp:send(Sock, RemAddr, ?GSMTAP_PORT, Data).
+
+
+-spec gsmtap_pdu(logger:log_event()) -> binary().
+gsmtap_pdu(#{msg := Msg,
+ level := Level,
+ meta := #{pid := Pid,
+ time := Time} = M}) ->
+ MsgStr = msg2str(Msg),
+ FileName = filename(M),
+ LineNr = maps:get(line, M, 0),
+ << ?GSMTAP_VERSION,
+ ?GSMTAP_HDR_LEN,
+ ?GSMTAP_TYPE_OSMOCORE_LOG,
+ 16#00:(128 - 3 * 8), %% padding
+ (Time div 1_000_000):32, %% seconds
+ (Time rem 1_000_000):32, %% microseconds
+ (charbuf("OsmoS1GW", 16))/bytes, %% XXX: hard-coded
+ 16#00:32, %% dummy, Pid goes to subsys
+ (log_level(Level)),
+ 16#00:24, %% padding
+ (charbuf(pid_to_list(Pid), 16))/bytes,
+ (charbuf(FileName, 32))/bytes,
+ LineNr:32, %% line number
+ (list_to_binary(MsgStr))/bytes
+ >>.
+
+
+msg2str({string, Str}) ->
+ Str;
+
+msg2str({report, Report}) ->
+ io_lib:format("~p", [Report]);
+
+msg2str({FmtStr, Args}) ->
+ io_lib:format(FmtStr, Args).
+
+
+filename(#{file := FileName}) ->
+ filename:basename(FileName);
+
+filename(#{}) -> "(none)".
+
+
+-spec charbuf(Str0, Size) -> binary()
+ when Str0 :: string(),
+ Size :: non_neg_integer().
+charbuf(Str0, Size) ->
+ Str1 = string:slice(Str0, 0, Size - 1), %% truncate, if needed
+ Str2 = string:pad(Str1, Size, trailing, 16#00), %% pad, if needed
+ list_to_binary(Str2).
+
+
+-spec log_level(atom()) -> 0..255.
+log_level(debug) -> 1;
+log_level(info) -> 3;
+log_level(notice) -> 5;
+log_level(warning) -> 6; %% XXX: non-standard
+log_level(error) -> 7;
+log_level(critical) -> 8;
+log_level(alert) -> 9; %% XXX: non-standard
+log_level(emergency) -> 11; %% XXX: non-standard
+log_level(_) -> 255. %% XXX: non-standard
+
+
+%% vim:set ts=4 sw=4 et:
diff --git a/src/logger_gsmtap_h.erl b/src/logger_gsmtap_h.erl
new file mode 100644
index 0000000..1ce40ba
--- /dev/null
+++ b/src/logger_gsmtap_h.erl
@@ -0,0 +1,77 @@
+%% Copyright (C) 2025 by sysmocom - s.f.m.c. GmbH <info(a)sysmocom.de>
+%% Author: Vadim Yanitskiy <vyanitskiy(a)sysmocom.de>
+%%
+%% All Rights Reserved
+%%
+%% SPDX-License-Identifier: AGPL-3.0-or-later
+%%
+%% This program is free software; you can redistribute it and/or modify
+%% it under the terms of the GNU Affero General Public License as
+%% published by the Free Software Foundation; either version 3 of the
+%% License, or (at your option) any later version.
+%%
+%% This program is distributed in the hope that it will be useful,
+%% but WITHOUT ANY WARRANTY; without even the implied warranty of
+%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+%% GNU General Public License for more details.
+%%
+%% You should have received a copy of the GNU Affero General Public License
+%% along with this program. If not, see <https://www.gnu.org/licenses/>.
+%%
+%% Additional Permission under GNU AGPL version 3 section 7:
+%%
+%% If you modify this Program, or any covered work, by linking or
+%% combining it with runtime libraries of Erlang/OTP as released by
+%% Ericsson on
https://www.erlang.org (or a modified version of these
+%% libraries), containing parts covered by the terms of the Erlang Public
+%% License (
https://www.erlang.org/EPLICENSE), the licensors of this
+%% Program grant you additional permission to convey the resulting work
+%% without the need to license the runtime libraries of Erlang/OTP under
+%% the GNU Affero General Public License. Corresponding Source for a
+%% non-source form of such a combination shall include the source code
+%% for the parts of the runtime libraries of Erlang/OTP used as well as
+%% that of the covered work.
+
+-module(logger_gsmtap_h).
+
+-moduledoc """
+GSMTAP logging handler.
+""".
+
+-behaviour(logger_handler).
+
+%% logger_handler callbacks
+-export([adding_handler/1,
+ removing_handler/1,
+ filter_config/1,
+ log/2]).
+
+
+%% ------------------------------------------------------------------
+%% logger_handler API
+%% ------------------------------------------------------------------
+
+-spec adding_handler(logger_handler:config()) -> {ok, logger_handler:config()} |
+ {error, term()}.
+adding_handler(Config) ->
+ {ok, Pid} = logger_gsmtap:start({127,0,0,1}), %% XXX: hard-coded
+ {ok, Config#{pid => Pid}}.
+
+
+-spec removing_handler(logger_handler:config()) -> ok.
+removing_handler(#{pid := Pid}) ->
+ logger_gsmtap:stop(Pid),
+ ok.
+
+
+-spec filter_config(logger_handler:config()) -> logger_handler:config().
+filter_config(Config) ->
+ maps:without([pid], Config).
+
+
+-spec log(logger:log_event(), logger_handler:config()) -> term().
+log(LogEvent, #{pid := Pid}) ->
+ logger_gsmtap:log(Pid, LogEvent).
+
+
+%% vim:set ts=4 sw=4 et:
--
To view, visit
https://gerrit.osmocom.org/c/erlang/osmo-s1gw/+/39415?usp=email
To unsubscribe, or for help writing mail filters, visit
https://gerrit.osmocom.org/settings?usp=email
Gerrit-MessageType: newchange
Gerrit-Project: erlang/osmo-s1gw
Gerrit-Branch: master
Gerrit-Change-Id: Iea884e2ca146b852c64bd9f135d8c71f4c925f09
Gerrit-Change-Number: 39415
Gerrit-PatchSet: 1
Gerrit-Owner: fixeria <vyanitskiy(a)sysmocom.de>