-
-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathpy_logger.erl
More file actions
115 lines (104 loc) · 2.99 KB
/
py_logger.erl
File metadata and controls
115 lines (104 loc) · 2.99 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
%% Copyright 2026 Benoit Chesneau
%%
%% 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.
%%% @doc Python logging integration with Erlang logger.
%%%
%%% This gen_server receives log messages from Python's logging module
%%% and forwards them to Erlang's logger.
%%%
%%% == Architecture ==
%%%
%%% ```
%%% Python logging.info("msg")
%%% |
%%% v
%%% ErlangHandler.emit()
%%% |
%%% v
%%% erlang._log(level, name, msg, meta)
%%% |
%%% NIF: enif_send()
%%% |
%%% v
%%% py_logger (this gen_server)
%%% |
%%% v
%%% logger:log(Level, Msg, Meta)
%%% '''
%%%
%%% == Usage ==
%%%
%%% The py_logger is automatically started by the erlang_python application.
%%% To configure Python logging from Erlang:
%%%
%%% ```
%%% ok = py:configure_logging().
%%% %% or with options:
%%% ok = py:configure_logging(#{level => info}).
%%% '''
%%%
%%% Then from Python:
%%% ```python
%%% import logging
%%% logging.info("This will appear in Erlang logs")
%%% '''
%%%
%%% @private
-module(py_logger).
-behaviour(gen_server).
-export([start_link/0, start_link/1]).
-export([init/1, handle_info/2, handle_call/3, handle_cast/2, terminate/2]).
%% @doc Start the logger with default options.
-spec start_link() -> {ok, pid()} | {error, term()}.
start_link() ->
start_link(#{}).
%% @doc Start the logger with options.
%% Options:
%% level => debug | info | warning | error | critical
-spec start_link(map()) -> {ok, pid()} | {error, term()}.
start_link(Opts) ->
gen_server:start_link({local, ?MODULE}, ?MODULE, Opts, []).
%% @private
init(Opts) ->
Level = maps:get(level, Opts, debug),
LevelInt = level_to_int(Level),
ok = py_nif:set_log_receiver(self(), LevelInt),
{ok, #{level => Level}}.
%% @private Handle log messages from Python
handle_info({py_log, Level, Logger, Message, Meta, _Ts}, State) ->
%% Forward to Erlang logger with Python metadata
logger:log(Level, Message, #{
domain => [python],
py_logger => Logger,
py_meta => Meta
}),
{noreply, State};
handle_info(_Msg, State) ->
{noreply, State}.
%% @private
handle_call(_Request, _From, State) ->
{reply, ok, State}.
%% @private
handle_cast(_Msg, State) ->
{noreply, State}.
%% @private
terminate(_Reason, _State) ->
py_nif:clear_log_receiver(),
ok.
%% @private Convert Erlang log level to Python levelno
level_to_int(debug) -> 0;
level_to_int(info) -> 10;
level_to_int(warning) -> 20;
level_to_int(error) -> 30;
level_to_int(critical) -> 40;
level_to_int(_) -> 0.