1 % (c) 2009-2019 Lehrstuhl fuer Softwaretechnik und Programmiersprachen,
2 % Heinrich Heine Universitaet Duesseldorf
3 % This software is licenced under EPL 1.0 (http://www.eclipse.org/org/documents/epl-v10.html)
4
5 :- module(logger, [set_log_file/1, get_log_file/1, set_logging_mode/1, reset_logger/0,
6 writeln_log/1,
7 writeln_log_time/1,
8 write_xml_element_to_log/2,
9 write_prolog_term_as_xml_to_log/1,
10 write_bstate_to_log/1, write_bstate_to_log/2,
11 start_xml_group_in_log/1, start_xml_group_in_log/3,
12 stop_xml_group_in_log/1, stop_xml_group_in_log_no_statistics/1,
13 close_all_xml_groups_in_log/0,
14 logging_is_enabled/0]).
15
16 :- use_module(module_information).
17
18 :- module_info(group,infrastructure).
19 :- module_info(description,'This module is responsible for (xml and prolog) logging.').
20
21
22 :- use_module(self_check).
23 :- use_module(error_manager).
24 :- use_module(library(xml)).
25 :- use_module(library(lists)).
26
27 :- dynamic logfile/1.
28 set_log_file(F) :- retractall(logfile(_)), assert(logfile(F)).
29 get_log_file(F) :- logfile(F).
30
31 :- dynamic logging_mode/1.
32 logging_mode(prolog).
33 % valid modes: prolog and xml
34 set_logging_mode(Mode) :- retractall(logging_mode(_)), assert(logging_mode(Mode)),
35 (Mode=xml -> format_log_header(reset,'<?xml version="1.0" encoding="ASCII"?>~n',[]) ; true).
36
37 % TO DO: use:
38 %get_preference(xml_encoding,EncodingPref),
39
40 logging_is_enabled :- logfile(_),!.
41
42 prolog_log_file(F) :- logfile(F), logging_mode(prolog).
43
44 reset_logger :- retractall(open_xml_group(_,_)),
45 retractall(logfile(_)), set_logging_mode(prolog).
46
47 % TO DO: try and move most writeln_log calls to write_xml_element_to_log format
48 writeln_log(Term) :-
49 (prolog_log_file(F)
50 -> (open(F,append,S),
51 write_term(S,Term,[quoted(true)]), write(S,'.'),nl(S),
52 close(S))
53 ; true
54 ).
55
56 open_logfile(Stream) :- logfile(F), open(F,append,Stream).
57
58 format_log(FormatString,Args) :- %format(FormatString,Args),nl,
59 (logfile(F)
60 -> (open(F,append,S),
61 format(S,FormatString,Args),
62 close(S))
63 ; true
64 ).
65
66 writeln_log_time(Term) :-
67 (prolog_log_file(_) ->
68 statistics(runtime,[Time,_]),
69 statistics(walltime,[WTime,_]),
70 statistics(memory_used,M), MB is M / 1048576,
71 Term=..[H|Args],
72 append(Args,[Time,WTime,mb(MB)],NArgs),
73 NT =.. [H|NArgs],
74 writeln_log(NT)
75 ; true).
76
77 :- use_module(library(file_systems),[file_exists/1]).
78 format_log_header(reset,FormatString,Args) :- !, % reset means we want to start with a fresh log file
79 (logfile(F)
80 -> (open(F,write,S),
81 format(S,FormatString,Args),
82 close(S))
83 ; true
84 ).
85 format_log_header(_,FormatString,Args) :-
86 % like format_log, but only writes if the file does not exist yet
87 (logfile(F), \+ file_exists(F)
88 -> (open(F,append,S),
89 format(S,FormatString,Args),
90 close(S))
91 ; true
92 ).
93
94 :- assert_must_succeed((logger:xml_encode_text("b<>c",R),R=="b<>c")).
95 xml_encode_text(Codes,Res) :- XML = xml([],[pcdata(Codes)]), xml_parse(Encoded,XML),!,Res=Encoded.
96 xml_encode_text(Codes,Encoded) :- format(user_error,'Could not encode for XML: ~s~n',[Codes]),
97 Encoded=Codes.
98 % we could also use xml:pcdata_generation(Codes, Encoded, []).
99
100 :- assert_must_succeed((logger:xml_encode_element(check_goal,[true/1],R),R=="<check_goal true=\"1\" />")).
101 xml_encode_element(Tag,Attributes,Encoded2) :-
102 maplist(prepare_attribute,Attributes,XMLAttr),
103 XML = xml([],[element(Tag,XMLAttr,[])]),
104 xml_parse(Encoded,XML),
105 peel_off_leading_newline(Encoded,Encoded2).
106
107 peel_off_leading_newline([10|T],R) :- !, peel_off_leading_newline(T,R).
108 peel_off_leading_newline([13|T],R) :- !, peel_off_leading_newline(T,R).
109 peel_off_leading_newline(R,R).
110
111 % write a tag with attributes to the log file
112 write_xml_element_to_log(_,_) :- \+ logfile(_), !.
113 write_xml_element_to_log(Tag,Attributes) :- logging_mode(xml),!,
114 xml_encode_element(Tag,Attributes,Encoded),
115 indent_log(WS),
116 format_log("~s~s~n",[WS,Encoded]).
117 write_xml_element_to_log(Tag,Attributes) :-
118 Term =.. [Tag,Attributes],
119 format_log("~w.~n",[Term]).
120
121 % write a Prolog Term either as Prolog Term in Prolog mode or in nested XML form
122 %write_term_to_log(Term) :- logging_mode(xml),!,
123 % write_prolog_term_as_xml_to_log(Term).
124 %write_term_to_log(Term) :- writeln_log(Term).
125
126 write_prolog_term_as_xml_to_log(A) :- number(A),!,
127 indent_log(WS),
128 format_log("~s<number>~w</number>~n",[WS,A]).
129 write_prolog_term_as_xml_to_log(A) :- var(A),!,
130 indent_log(WS),
131 format_log("~s<variable>~w</variable>~n",[WS,A]).
132 write_prolog_term_as_xml_to_log(A) :- atomic(A),!, convert_to_codes(A,Codes),
133 xml_encode_text(Codes,Encoded),
134 indent_log(WS),
135 (is_a_file_path(Encoded) -> format_log("~s<path>~s</path>~n",[WS,Encoded])
136 ; format_log("~s<atom>~s</atom>~n",[WS,Encoded])).
137 write_prolog_term_as_xml_to_log(A/B) :- !,
138 start_xml_group_in_log(bind),
139 write_prolog_term_as_xml_to_log(A),
140 write_prolog_term_as_xml_to_log(B),
141 stop_xml_group_in_log_no_statistics(bind).
142 write_prolog_term_as_xml_to_log([H|T]) :- !, % Note: we assume we have a proper list !
143 start_xml_group_in_log(list),
144 maplist(write_prolog_term_as_xml_to_log,[H|T]),
145 stop_xml_group_in_log_no_statistics(list).
146 write_prolog_term_as_xml_to_log(T) :- T =.. [Functor|Args],
147 %TO DO, something like: escape / xml_encode_text(Functor,EFunc),
148 start_xml_group_in_log(Functor),
149 maplist(write_prolog_term_as_xml_to_log,Args),
150 stop_xml_group_in_log_no_statistics(Functor).
151
152 write_bstate_to_log(State) :- write_bstate_to_log(State,'').
153
154 % in response to logxml_write_vars
155 write_bstate_to_log(State,Prefix) :- logging_mode(xml),!,
156 start_xml_group_in_log(state),
157 atom_codes(Prefix,PrefixCodes),
158 (State=root -> start_xml_group_in_log(root), stop_xml_group_in_log_no_statistics(root)
159 ; maplist(write_b_binding_as_xml_to_log(PrefixCodes),State) -> true
160 ; add_internal_error('Could not write state to xml logfile: ',write_bstate_to_log(State))),
161 stop_xml_group_in_log_no_statistics(state).
162 write_bstate_to_log(_,_Prefix).
163
164 write_b_binding_as_xml_to_log(Prefix,bind(VarName,Value)) :- % TO DO: encode VarName
165 atom_codes(VarName,Codes),
166 append(Prefix,_,Codes), % check that variable name starts with prefix
167 !,
168 xml_encode_text(Codes,ECodes),
169 atom_codes(EVarName,ECodes),
170 start_xml_group_in_log(variable,name,EVarName),
171 xml_write_b_value_to_log(Value),
172 stop_xml_group_in_log_no_statistics(variable).
173 write_b_binding_as_xml_to_log(_,_).
174
175 xml_write_b_value_to_log(Value) :-
176 open_logfile(Stream),
177 indent_log(WS),format(Stream,'~s ',[WS]),
178 xml_write_b_value(Value,Stream),
179 format(Stream,'~n',[]),
180 close(Stream).
181
182 :- use_module(probsrc(custom_explicit_sets),[expand_custom_set_to_list/2]).
183 :- use_module(probsrc(translate),[translate_bvalue_to_codes/2]).
184 xml_write_b_value_map(Stream,O) :- xml_write_b_value(O,Stream).
185 xml_write_b_value(Var,Stream) :- var(Var),!,
186 add_internal_error('Illegal variable value:',xml_write_b_value(Var,Stream)),
187 format(Stream,'<value>~w</value>',[Var]).
188 xml_write_b_value((Fst,Snd),Stream) :- !,
189 write(Stream,'<pair><fst>'),
190 xml_write_b_value(Fst,Stream),
191 write(Stream,'</fst><snd>'),
192 xml_write_b_value(Snd,Stream),
193 write(Stream,'</snd></pair> ').
194 xml_write_b_value([],Stream) :- !,write(Stream,'<empty_set></empty_set> ').
195 xml_write_b_value(CS,Stream) :- custom_set_to_expand(CS),!,
196 expand_custom_set_to_list(CS,Elements),
197 write(Stream,'<set>'),
198 maplist(xml_write_b_value_map(Stream),Elements),
199 write(Stream,'</set> ').
200 xml_write_b_value([H|T],Stream) :- !,
201 write(Stream,'<set>'),
202 maplist(xml_write_b_value_map(Stream),[H|T]),
203 write(Stream,'</set> ').
204 xml_write_b_value(rec(Fields),Stream) :- !,
205 write(Stream,'<record>'),
206 maplist(xml_write_b_field_value(Stream),Fields),
207 write(Stream,'</record> ').
208 xml_write_b_value(string(S),Stream) :- !,
209 atom_codes(S,Codes),
210 xml_encode_text(Codes,Encoded),
211 format(Stream,'<string>~s</string>',[Encoded]).
212 xml_write_b_value(int(N),Stream) :- !,
213 format(Stream,'<integer>~w</integer>',[N]).
214 xml_write_b_value(pred_true,Stream) :- !,
215 format(Stream,'<bool>TRUE</bool>',[]).
216 xml_write_b_value(pred_false,Stream) :- !,
217 format(Stream,'<bool>FALSE</bool>',[]).
218 xml_write_b_value(fd(Nr,Type),Stream) :- !,
219 translate_bvalue_to_codes(fd(Nr,Type),SValue),
220 format(Stream,"<enum type=\"~w\" nr=\"~w\">~s</enum>",[Type,Nr,SValue]).
221 xml_write_b_value(Value,Stream) :-
222 is_custom_explicit_set(Value,xml_write),
223 is_interval_closure(Value,Low,Up),
224 !,
225 write(Stream,'<interval_set><from>'),
226 xml_write_b_value(int(Low),Stream),
227 write(Stream,'</from><to>'),
228 xml_write_b_value(int(Up),Stream),
229 write(Stream,'</to></interval_set>').
230 xml_write_b_value(Value,Stream) :- % other value, freetype, freeval, closure, ...
231 is_custom_explicit_set(Value,xml_write),
232 !,
233 translate_bvalue_to_codes(Value,SValue),
234 xml_encode_text(SValue,Encoded),
235 format(Stream,'<symbolic_set>~s</symbolic_set>',[Encoded]).
236 xml_write_b_value(Value,Stream) :- % other value freeval, ...
237 translate_bvalue_to_codes(Value,SValue),
238 xml_encode_text(SValue,Encoded),
239 format(Stream,'<value>~s</value>',[Encoded]).
240 % TO DO: check if there are uncovered values, e.g., freeval(ID,Case,Value)
241
242 :- use_module(custom_explicit_sets,[is_interval_closure/3,
243 is_custom_explicit_set/2, dont_expand_this_explicit_set/2]).
244 custom_set_to_expand(avl_set(_)).
245 custom_set_to_expand(CS) :- nonvar(CS),
246 is_custom_explicit_set(CS,xml_write),
247 \+ dont_expand_this_explicit_set(CS,1000).
248
249 xml_write_b_field_value(Stream,field(Name,Val)) :-
250 atom_codes(Name,Codes), xml_encode_text(Codes,Encoded),
251 format(Stream,'<field name=\"~s\">',[Encoded]),
252 xml_write_b_value(Val,Stream), write(Stream,'</field>').
253
254 % ---------------------------
255
256 :- use_module(tools,[host_platform/1]).
257 is_a_file_path(Codes) :- member(47,Codes).
258 is_a_file_path(Codes) :- host_platform(windows), member(92,Codes). % windows
259
260 prepare_attribute('='(Tag,Atom),'='(Tag,Codes)) :- convert_to_codes(Atom,Codes).
261 prepare_attribute('/'(Tag,Atom),'='(Tag,Codes)) :- convert_to_codes(Atom,Codes).
262
263 :- use_module(library(codesio),[write_to_codes/2]).
264 convert_to_codes(V,Codes) :- var(V),!,Codes="_".
265 convert_to_codes([H|T],Codes) :- number(H),!, Codes=[H|T].
266 convert_to_codes(N,Codes) :- number(N),!, number_codes(N,Codes).
267 convert_to_codes(A,Codes) :- atom(A),!,atom_codes(A,Codes).
268 convert_to_codes(A,Codes) :- write_to_codes(A,Codes).
269
270 :- dynamic open_xml_group/2, nesting_level/1.
271 nesting_level(0).
272 update_nesting_level(X) :- retract(nesting_level(Y)),
273 New is Y+X, assert(nesting_level(New)).
274
275 space(32).
276 indent_log(WS) :- nesting_level(Lvl), length(WS,Lvl),
277 maplist(space,WS).
278
279 check_and_generate_group_stats(Group,Stats) :- open_xml_group(A,_),!,
280 (A=Group
281 -> retract(open_xml_group(Group,WTimeStart)),
282 (Stats=no_statistics -> true
283 ; statistics(walltime,[WTimeEnd,_]),
284 Delta is WTimeEnd - WTimeStart,
285 statistics(memory_used,M),
286 write_xml_element_to_log(statistics,[walltime/Delta,walltime_since_start/WTimeEnd,memory_used/M])
287 ),
288 update_nesting_level(-1)
289 ; add_internal_error('XML closing tag mismatch: ', Group/A)).
290 check_and_generate_group_stats(Group,_) :-
291 add_internal_error('XML closing tag error, no tag open: ', Group).
292
293 start_xml_group_in_log(Group) :- logging_mode(xml),!,
294 statistics(walltime,[WTime,_]),
295 indent_log(WS),
296 asserta(open_xml_group(Group,WTime)),
297 update_nesting_level(1),
298 format_log("~s<~w>~n",[WS,Group]).
299 start_xml_group_in_log(_).
300
301 % we currently only support a single attribute and value
302 start_xml_group_in_log(Group,Attr,Value) :- logging_mode(xml),!,
303 statistics(walltime,[WTime,_]),
304 indent_log(WS),
305 asserta(open_xml_group(Group,WTime)),
306 update_nesting_level(1),
307 convert_to_codes(Value,ValueC),
308 xml_encode_text(ValueC,EValueC),
309 format_log("~s<~w ~w=\"~s\">~n",[WS,Group,Attr,EValueC]).
310 start_xml_group_in_log(_,_,_).
311
312 stop_xml_group_in_log(Group,Stats) :- logging_mode(xml),!,
313 check_and_generate_group_stats(Group,Stats),
314 indent_log(WS),
315 format_log("~s</~w>~n",[WS,Group]).
316 stop_xml_group_in_log(_,_).
317
318
319 stop_xml_group_in_log(Group) :- stop_xml_group_in_log(Group,statistics).
320 stop_xml_group_in_log_no_statistics(Group) :- stop_xml_group_in_log(Group,no_statistics).
321
322 % call if you need to prematurely exit probcli
323 close_all_xml_groups_in_log :- open_xml_group(Group,_), !,
324 stop_xml_group_in_log(Group),
325 close_all_xml_groups_in_log.
326 close_all_xml_groups_in_log.
327
328
329
330
331 /*
332 <?xml version="1.0" encoding="ASCII"?>
333
334 | ?- xml_parse("<PT ID=\"2\" stID=\"3\"/>",R).
335 R = xml([],[element('PT',['ID'=[50],stID=[51]],[])]) ?
336
337 <PointsTelegram elementID="W90" stationID="FR" interlockingID="FR" interlockingElementID="W90"/>
338
339
340 */
341