Skip to content
This repository
Browse code

Support pretty printing records found in a module at compile time

  • Loading branch information...
commit 9bd0a411a3329b575728ff936dcd79b4aff11c74 1 parent 8d353aa
Andrew Thompson authored
32  src/lager.erl
@@ -28,7 +28,7 @@
28 28
         clear_all_traces/0, stop_trace/1, status/0,
29 29
         get_loglevel/1, set_loglevel/2, set_loglevel/3, get_loglevels/0,
30 30
         minimum_loglevel/1, posix_error/1,
31  
-        safe_format/3, safe_format_chop/3,dispatch_log/5]).
  31
+        safe_format/3, safe_format_chop/3,dispatch_log/5, pr/2]).
32 32
 
33 33
 -type log_level() :: debug | info | notice | warning | error | critical | alert | emergency.
34 34
 -type log_level_number() :: 0..7.
@@ -265,3 +265,33 @@ safe_format(Fmt, Args, Limit, Options) ->
265 265
 %% @private
266 266
 safe_format_chop(Fmt, Args, Limit) ->
267 267
     safe_format(Fmt, Args, Limit, [{chomp, true}]).
  268
+
  269
+%% @doc Print a record lager found during parse transform
  270
+pr(Record, Module) when is_tuple(Record), is_atom(element(1, Record)) ->
  271
+    try Module:module_info(attributes) of
  272
+        Attrs ->
  273
+            case lists:keyfind(lager_records, 1, Attrs) of
  274
+                false ->
  275
+                    Record;
  276
+                {lager_records, Records} ->
  277
+                    RecordName = element(1, Record),
  278
+                    RecordSize = tuple_size(Record) - 1,
  279
+                    case lists:filter(fun({Name, Fields}) when Name == RecordName,
  280
+                                length(Fields) == RecordSize ->
  281
+                                    true;
  282
+                                (_) ->
  283
+                                    false
  284
+                            end, Records) of
  285
+                        false ->
  286
+                            Record;
  287
+                        [{RecordName, RecordFields}|_] ->
  288
+                            {'$lager_record', RecordName,
  289
+                                lists:zip(RecordFields, tl(tuple_to_list(Record)))}
  290
+                    end
  291
+            end
  292
+    catch
  293
+        error:undef ->
  294
+            Record
  295
+    end;
  296
+pr(Record, _) ->
  297
+    Record.
27  src/lager_transform.erl
@@ -31,10 +31,11 @@
31 31
 parse_transform(AST, Options) ->
32 32
     TruncSize = proplists:get_value(lager_truncation_size, Options, ?DEFAULT_TRUNCATION),
33 33
     put(truncation_size, TruncSize),
  34
+    erlang:put(records, []),
34 35
     walk_ast([], AST).
35 36
 
36 37
 walk_ast(Acc, []) ->
37  
-    lists:reverse(Acc);
  38
+    insert_record_attribute(Acc);
38 39
 walk_ast(Acc, [{attribute, _, module, {Module, _PmodArgs}}=H|T]) ->
39 40
     %% A wild parameterized module appears!
40 41
     put(module, Module),
@@ -46,6 +47,14 @@ walk_ast(Acc, [{function, Line, Name, Arity, Clauses}|T]) ->
46 47
     put(function, Name),
47 48
     walk_ast([{function, Line, Name, Arity,
48 49
                 walk_clauses([], Clauses)}|Acc], T);
  50
+walk_ast(Acc, [{attribute, _, record, {Name, Fields}}=H|T]) ->
  51
+    FieldNames = lists:map(fun({record_field, _, {atom, _, FieldName}}) ->
  52
+                FieldName;
  53
+            ({record_field, _, {atom, _, FieldName}, _Default}) ->
  54
+                FieldName
  55
+        end, Fields),
  56
+    stash_record({Name, FieldNames}),
  57
+    walk_ast([H|Acc], T);
49 58
 walk_ast(Acc, [H|T]) ->
50 59
     walk_ast([H|Acc], T).
51 60
 
@@ -129,3 +138,19 @@ concat_lists({nil, _Line}, B) ->
129 138
     B;
130 139
 concat_lists({cons, Line, Element, Tail}, B) ->
131 140
     {cons, Line, Element, concat_lists(Tail, B)}.
  141
+
  142
+stash_record(Record) ->
  143
+    Records = case erlang:get(records) of
  144
+        undefined ->
  145
+            [];
  146
+        R ->
  147
+            R
  148
+    end,
  149
+    erlang:put(records, [Record|Records]).
  150
+
  151
+insert_record_attribute(AST) ->
  152
+    lists:foldl(fun({attribute, Line, module, _}=E, Acc) ->
  153
+                [E, {attribute, Line, lager_records, erlang:get(records)}|Acc];
  154
+            (E, Acc) ->
  155
+                [E|Acc]
  156
+        end, [], AST).
22  src/lager_trunc_io.erl
@@ -262,6 +262,11 @@ print(Port, _Max, _Options) when is_port(Port) ->
262 262
     L = erlang:port_to_list(Port),
263 263
     {L, length(L)};
264 264
 
  265
+print({'$lager_record', Name, Fields}, Max, Options) ->
  266
+    Leader = "#" ++ atom_to_list(Name) ++ "{",
  267
+    {RC, Len} = record_fields(Fields, Max - length(Leader) + 1, dec_depth(Options)),
  268
+    {[Leader, RC, "}"], Len + length(Leader) + 1};
  269
+
265 270
 print(Tuple, Max, Options) when is_tuple(Tuple) -> 
266 271
     {TC, Len} = tuple_contents(Tuple, Max-2, Options),
267 272
     {[${, TC, $}], Len + 2};
@@ -436,6 +441,23 @@ escape($\f) -> "\\f";
436 441
 escape($\b) -> "\\b";
437 442
 escape($\v) -> "\\v".
438 443
 
  444
+record_fields([], _, _) ->
  445
+    {"", 0};
  446
+record_fields(_, Max, #print_options{depth=D}) when Max < 4; D == 0 ->
  447
+    {"...", 3};
  448
+record_fields([{Field, Value}|T], Max, Options) ->
  449
+    {ExtraChars, Terminator} = case T of
  450
+        [] ->
  451
+            {1, []};
  452
+        _ ->
  453
+            {2, ","}
  454
+    end,
  455
+    {FieldStr, FieldLen} = print(Field, Max - ExtraChars, Options),
  456
+    {ValueStr, ValueLen} = print(Value, Max - (FieldLen + ExtraChars), Options),
  457
+    {Final, FLen} = record_fields(T, Max - (FieldLen + ValueLen + ExtraChars), dec_depth(Options)),
  458
+    {[FieldStr++"="++ValueStr++Terminator|Final], FLen + FieldLen + ValueLen + ExtraChars}.
  459
+
  460
+
439 461
 -ifdef(TEST).
440 462
 %%--------------------
441 463
 %% The start of a test suite. So far, it only checks for not crashing.
18  test/lager_test_backend.erl
@@ -28,7 +28,7 @@
28 28
 
29 29
 -ifdef(TEST).
30 30
 -include_lib("eunit/include/eunit.hrl").
31  
--export([pop/0, count/0, count_ignored/0, flush/0]).
  31
+-export([pop/0, count/0, count_ignored/0, flush/0, print_state/0]).
32 32
 -endif.
33 33
 
34 34
 init(Level) ->
@@ -51,6 +51,10 @@ handle_call(get_loglevel, #state{level=Level} = State) ->
51 51
     {ok, Level, State};
52 52
 handle_call({set_loglevel, Level}, State) ->
53 53
     {ok, ok, State#state{level=lager_util:level_to_num(Level)}};
  54
+handle_call(print_state, State) ->
  55
+    spawn(fun() -> lager:info("State ~p", [lager:pr(State, ?MODULE)]) end),
  56
+    timer:sleep(100),
  57
+    {ok, ok, State};
54 58
 handle_call(_Request, State) ->
55 59
     {ok, ok, State}.
56 60
 
@@ -89,6 +93,9 @@ count_ignored() ->
89 93
 flush() ->
90 94
     gen_event:call(lager_event, ?MODULE, flush).
91 95
 
  96
+print_state() ->
  97
+    gen_event:call(lager_event, ?MODULE, print_state).
  98
+
92 99
 has_line_numbers() ->
93 100
     %% are we R15 or greater
94 101
     Rel = erlang:system_info(otp_release),
@@ -220,6 +227,15 @@ lager_test_() ->
220 227
                         ?assertEqual(1, count()),
221 228
                         ok
222 229
                 end
  230
+            },
  231
+            {"record printing works",
  232
+                fun() ->
  233
+                        print_state(),
  234
+                        {Level, _Time, Message, _Metadata}  = pop(),
  235
+                        ?assertMatch(Level, lager_util:level_to_num(info)),
  236
+                        ?assertEqual("State #state{level=6,buffer=[],ignored=[]}", lists:flatten(Message)),
  237
+                        ok
  238
+                end
223 239
             }
224 240
         ]
225 241
     }.

0 notes on commit 9bd0a41

Please sign in to comment.
Something went wrong with that request. Please try again.