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 <[email protected]>
+%% Author: Vadim Yanitskiy <[email protected]>
+%%
+%% 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 <[email protected]>
+%% Author: Vadim Yanitskiy <[email protected]>
+%%
+%% 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 <[email protected]>

Reply via email to