/src/lib/log/conf/log_internal_conf.e

http://github.com/tybor/Liberty · Specman e · 654 lines · 578 code · 43 blank · 33 comment · 49 complexity · 41d04ad48a16b18e838e370c60a0831e MD5 · raw file

  1. -- This file is part of a Liberty Eiffel library.
  2. -- See the full copyright at the end.
  3. --
  4. class LOG_INTERNAL_CONF
  5. --
  6. -- The internal logging configuration manager
  7. --
  8. inherit
  9. EIFFEL_NON_TERMINAL_NODE_IMPL_VISITOR
  10. undefine
  11. is_equal
  12. end
  13. EIFFEL_TERMINAL_NODE_IMPL_VISITOR
  14. undefine
  15. is_equal
  16. end
  17. EIFFEL_LIST_NODE_IMPL_VISITOR
  18. undefine
  19. is_equal
  20. end
  21. insert
  22. SINGLETON
  23. create {LOG_CONFIGURATION}
  24. make
  25. feature {}
  26. root: LOGGER
  27. loggers: HASHED_DICTIONARY[LOGGER, FIXED_STRING]
  28. outputs: HASHED_DICTIONARY[LOG_OUTPUT, FIXED_STRING]
  29. feature {EIFFEL_NON_TERMINAL_NODE_IMPL}
  30. visit_eiffel_non_terminal_node_impl (node: EIFFEL_NON_TERMINAL_NODE_IMPL)
  31. do
  32. pass.call([node])
  33. end
  34. feature {}
  35. do_pass_1 (on_error: PROCEDURE[TUPLE[STRING]]; path_resolver: FUNCTION[TUPLE[STRING], STRING]; node: EIFFEL_NON_TERMINAL_NODE_IMPL)
  36. -- Create outputs and loggers
  37. require
  38. on_error /= Void
  39. local
  40. output_name: FIXED_STRING; output: LOG_OUTPUT
  41. logger_name: FIXED_STRING; logger: LOGGER
  42. term: EIFFEL_TERMINAL_NODE
  43. num: TYPED_EIFFEL_IMAGE[INTEGER_64]; rotation: INTEGER_64
  44. rotation_condition: PREDICATE[TUPLE[FILE_STREAM]]
  45. file_path: STRING
  46. file_options_index: INTEGER
  47. do
  48. inspect
  49. node.name
  50. when "Configuration" then
  51. node.node_at(3).accept(Current)
  52. node.node_at(4).accept(Current)
  53. node.node_at(2).accept(Current)
  54. root := loggers.fast_reference_at(last_class_name.intern)
  55. if root = Void then
  56. on_error.call(["Unknown root logger: " + last_class_name])
  57. end
  58. when "Root" then
  59. node.node_at(1).accept(Current)
  60. when "Outputs" then
  61. node.node_at(1).accept(Current)
  62. when "Output" then
  63. node.node_at(0).accept(Current)
  64. output_name := last_entity_name.intern
  65. output := outputs.fast_reference_at(output_name)
  66. if output = Void then
  67. last_format := Void
  68. inspect
  69. node.node_at(2).name
  70. when "KW file" then
  71. node.node_at(3).accept(Current)
  72. -- TODO: to check when the "url" construct is implemented
  73. file_path := path_resolver.item([last_string])
  74. create file_options.file(output_name, file_path)
  75. file_options_index := 4
  76. when "KW console" then
  77. create file_options.console(output_name)
  78. file_options_index := 3
  79. end
  80. if file_options.is_connected then
  81. node.node_at(file_options_index).accept(Current)
  82. create output.make(file_options.retriever, output_name)
  83. outputs.put(output, output_name)
  84. else
  85. on_error.call([output_name + ": could not connect to " + file_path])
  86. end
  87. if last_format /= Void then
  88. output.set_format(last_format)
  89. end
  90. else
  91. on_error.call(["Duplicate output name: " + output_name])
  92. end
  93. when "File_Options" then
  94. if not node.is_empty then
  95. node.node_at(0).accept(Current)
  96. end
  97. when "File_Option" then
  98. inspect
  99. node.name_at(0)
  100. when "KW rotated" then
  101. node.node_at(3).accept(Current)
  102. node.node_at(2).accept(Current)
  103. when "KW zipped" then
  104. node.node_at(2).accept(Current)
  105. file_options.zipped(last_string.intern, on_error)
  106. when "KW format" then
  107. node.node_at(1).accept(Current)
  108. last_format := last_string.intern
  109. else
  110. node.node_at(0).accept(Current)
  111. end
  112. when "Rotation" then
  113. if node.count = 1 then
  114. rotation := 1
  115. else
  116. term ::= node.node_at(1)
  117. if num ?:= term.image then
  118. num ::= term.image
  119. rotation := num.decoded
  120. if rotation < 1 then
  121. on_error.call(["Bad retention value: " + term.image.image])
  122. rotation := 1
  123. end
  124. else
  125. on_error.call(["Bad retention value: " + term.image.image])
  126. rotation := 1
  127. end
  128. end
  129. inspect
  130. node.name_at(node.upper)
  131. when "KW day", "KW days" then
  132. rotation_condition := agent each_days(?, rotation)
  133. when "KW week", "KW weeks" then
  134. rotation_condition := agent each_weeks(?, rotation, 0)
  135. when "KW year", "KW years" then
  136. rotation_condition := agent each_years(?, rotation)
  137. when "KW month", "KW months" then
  138. rotation_condition := agent each_months(?, rotation)
  139. when "KW hour", "KW hours" then
  140. rotation_condition := agent each_hours(?, rotation)
  141. when "KW minute", "KW minutes" then
  142. rotation_condition := agent each_minutes(?, rotation)
  143. when "KW byte", "KW bytes" then
  144. rotation_condition := agent each_bytes(?, rotation)
  145. when "KW kilobyte", "KW kilobytes" then
  146. rotation_condition := agent each_bytes(?, rotation * 1024)
  147. when "KW megabyte", "KW megabytes" then
  148. rotation_condition := agent each_bytes(?, rotation * 1024 * 1024)
  149. when "KW gigabyte", "KW gigabytes" then
  150. rotation_condition := agent each_bytes(?, rotation * 1024 * 1024 * 1024)
  151. end
  152. file_options.rotated(rotation_condition, last_retention, on_error)
  153. when "Retention" then
  154. if node.is_empty then
  155. last_retention := -1
  156. else
  157. term ::= node.node_at(1)
  158. if num ?:= term.image then
  159. num ::= term.image
  160. last_retention := num.decoded
  161. if last_retention < 0 then
  162. on_error.call(["Bad retention value: " + term.image.image])
  163. last_retention := -1
  164. end
  165. else
  166. on_error.call(["Bad retention value: " + term.image.image])
  167. last_retention := -1
  168. end
  169. end
  170. when "Loggers" then
  171. node.node_at(1).accept(Current)
  172. when "Logger" then
  173. node.node_at(0).accept(Current)
  174. logger_name := last_class_name.intern
  175. last_entity_name := Void
  176. node.node_at(3).accept(Current)
  177. if last_entity_name = Void then
  178. output := default_output
  179. else
  180. output_name := last_entity_name.intern
  181. output := outputs.fast_reference_at(output_name)
  182. if output = Void then
  183. on_error.call(["Unknown output: " + output_name])
  184. end
  185. end
  186. create logger.make(output, logger_name, generation_id)
  187. loggers.put(logger, logger_name)
  188. when "Logger_Output" then
  189. if not node.is_empty then
  190. node.node_at(1).accept(Current)
  191. end
  192. when "Level" then
  193. if not node.is_empty then
  194. node.node_at(1).accept(Current)
  195. end
  196. end
  197. end
  198. do_pass_2 (on_error: PROCEDURE[TUPLE[STRING]]; node: EIFFEL_NON_TERMINAL_NODE_IMPL)
  199. -- Attach loggers' parents
  200. require
  201. on_error /= Void
  202. local
  203. logger_name: FIXED_STRING; logger, parent: LOGGER
  204. do
  205. inspect
  206. node.name
  207. when "Configuration" then
  208. node.node_at(4).accept(Current)
  209. when "Loggers" then
  210. node.node_at(1).accept(Current)
  211. when "Logger" then
  212. node.node_at(0).accept(Current)
  213. logger_name := last_class_name.intern
  214. current_logger := loggers.fast_reference_at(logger_name)
  215. last_level := Void
  216. last_format := Void
  217. check
  218. current_logger /= Void
  219. end
  220. node.node_at(2).accept(Current)
  221. node.node_at(4).accept(Current)
  222. if last_level /= Void then
  223. current_logger.set_level(last_level)
  224. end
  225. current_logger := Void
  226. when "Level" then
  227. if not node.is_empty then
  228. node.node_at(1).accept(Current)
  229. end
  230. when "Parent" then
  231. check
  232. current_logger /= Void
  233. end
  234. last_level := levels.trace
  235. if not node.is_empty then
  236. if logger = root then
  237. on_error.call([once "The root logger cannot have a parent"])
  238. else
  239. last_class_name := Void
  240. node.node_at(1).accept(Current)
  241. if last_class_name /= Void then
  242. parent := loggers.fast_reference_at(last_class_name.intern)
  243. if parent /= Void then
  244. current_logger.set_parent(parent)
  245. last_level := parent.level
  246. else
  247. on_error.call(["Unknown logger " + last_class_name + " for logger " + logger_name])
  248. end
  249. else
  250. logger.set_parent(root)
  251. end
  252. end
  253. end
  254. end
  255. end
  256. file_options: LOG_FILE_OPTIONS
  257. each_days (stream: FILE_STREAM; number: INTEGER_64): BOOLEAN
  258. require
  259. number > 0
  260. local
  261. now, last_change: TIME; elapsed: REAL
  262. do
  263. now.update
  264. last_change := ft.last_change_of(stream.path)
  265. if now < last_change then
  266. Result := True
  267. elseif now.year /= last_change.year or else now.year_day /= last_change.year_day then
  268. elapsed := last_change.elapsed_seconds(now)
  269. Result := elapsed > ((number - 1) * 86400).force_to_real_64
  270. end
  271. end
  272. each_weeks (stream: FILE_STREAM; number: INTEGER_64; week_day: INTEGER): BOOLEAN
  273. require
  274. number > 0
  275. week_day.in_range(0, 6)
  276. local
  277. now, last_change: TIME; elapsed: REAL
  278. do
  279. now.update
  280. last_change := ft.last_change_of(stream.path)
  281. if now < last_change then
  282. Result := True
  283. elseif now.year /= last_change.year or else now.year_day /= last_change.year_day then
  284. -- TODO: not sure of that algorithm (thought power exhausted)
  285. elapsed := last_change.elapsed_seconds(now)
  286. if last_change.week_day = week_day then
  287. Result := elapsed > ((number - 1) * 86400 * 7).force_to_real_64
  288. else
  289. Result := elapsed > (number * 86400 * 7).force_to_real_64
  290. end
  291. end
  292. end
  293. each_months (stream: FILE_STREAM; number: INTEGER_64): BOOLEAN
  294. require
  295. number > 0
  296. local
  297. now, last_change: TIME
  298. do
  299. now.update
  300. last_change := ft.last_change_of(stream.path)
  301. if now < last_change then
  302. Result := True
  303. else
  304. Result := (now.year - last_change.year) * 12 + (now.month - last_change.month) >= number
  305. end
  306. end
  307. each_years (stream: FILE_STREAM; number: INTEGER_64): BOOLEAN
  308. require
  309. number > 0
  310. local
  311. now, last_change: TIME
  312. do
  313. now.update
  314. last_change := ft.last_change_of(stream.path)
  315. if now < last_change then
  316. Result := True
  317. else
  318. Result := (now.year - last_change.year) >= number
  319. end
  320. end
  321. each_hours (stream: FILE_STREAM; number: INTEGER_64): BOOLEAN
  322. require
  323. number > 0
  324. local
  325. now, last_change: TIME; elapsed: REAL
  326. do
  327. now.update
  328. last_change := ft.last_change_of(stream.path)
  329. if now < last_change then
  330. Result := True
  331. elseif now.year /= last_change.year or else now.year_day /= last_change.year_day or else now.hour /= last_change.hour then
  332. elapsed := last_change.elapsed_seconds(now)
  333. Result := elapsed > ((number - 1) * 3600).force_to_real_64
  334. end
  335. end
  336. each_minutes (stream: FILE_STREAM; number: INTEGER_64): BOOLEAN
  337. require
  338. number > 0
  339. local
  340. now, last_change: TIME; elapsed: REAL
  341. do
  342. now.update
  343. last_change := ft.last_change_of(stream.path)
  344. if now < last_change then
  345. Result := True
  346. elseif now.year /= last_change.year or else now.year_day /= last_change.year_day or else now.hour /= last_change.hour or else now.minute /= last_change.minute then
  347. elapsed := last_change.elapsed_seconds(now)
  348. Result := elapsed > ((number - 1) * 60).force_to_real_64
  349. end
  350. end
  351. each_bytes (stream: FILE_STREAM; number: INTEGER_64): BOOLEAN
  352. require
  353. number > 0
  354. do
  355. Result := ft.size_of(stream.path) >= number
  356. end
  357. ft: FILE_TOOLS
  358. feature {EIFFEL_TERMINAL_NODE_IMPL}
  359. visit_eiffel_terminal_node_impl (node: EIFFEL_TERMINAL_NODE_IMPL)
  360. local
  361. string: TYPED_EIFFEL_IMAGE[STRING]
  362. do
  363. inspect
  364. node.name
  365. when "KW class name" then
  366. last_class_name := node.image.image
  367. when "KW entity name" then
  368. last_entity_name := node.image.image
  369. when "KW string" then
  370. string ::= node.image
  371. last_string := string.decoded
  372. when "KW number" then
  373. last_number := node.image
  374. when "KW error" then
  375. last_level := levels.error
  376. when "KW warning" then
  377. last_level := levels.warning
  378. when "KW info" then
  379. last_level := levels.info
  380. when "KW trace" then
  381. last_level := levels.trace
  382. else
  383. -- nothing (syntax sugar)
  384. end
  385. end
  386. fatal_error (message: STRING)
  387. do
  388. std_error.put_line(message)
  389. die_with_code(1)
  390. end
  391. default_path_resolver: FUNCTION[TUPLE[STRING], STRING]
  392. once
  393. Result := agent resolve_path(?)
  394. end
  395. resolve_path (a_path: STRING): STRING
  396. do
  397. Result := a_path
  398. end
  399. feature {EIFFEL_LIST_NODE_IMPL}
  400. visit_eiffel_list_node_impl (node: EIFFEL_LIST_NODE_IMPL)
  401. local
  402. i: INTEGER
  403. do
  404. from
  405. i := node.lower
  406. until
  407. i > node.upper
  408. loop
  409. node.item(i).accept(Current)
  410. i := i + 1
  411. end
  412. end
  413. feature {LOG_CONFIGURATION}
  414. load (a_stream: INPUT_STREAM; when_error: PROCEDURE[TUPLE[STRING]]; a_path_resolver: FUNCTION[TUPLE[STRING], STRING]; a_load_completion: PROCEDURE[TUPLE])
  415. local
  416. load_item: TUPLE[INPUT_STREAM, PROCEDURE[TUPLE[STRING]], FUNCTION[TUPLE[STRING], STRING], PROCEDURE[TUPLE]]
  417. do
  418. if loading then
  419. load_queue.add_last([a_stream, when_error, a_path_resolver, a_load_completion])
  420. else
  421. load_default
  422. loading := True
  423. from
  424. load_(a_stream, when_error, a_path_resolver, a_load_completion)
  425. until
  426. load_queue.is_empty
  427. loop
  428. load_item := load_queue.first
  429. load_queue.remove_first
  430. load_(load_item.first, load_item.second, load_item.third, load_item.fourth)
  431. end
  432. loading := False
  433. end
  434. end
  435. conf_logger (a_tag: FIXED_STRING): LOGGER
  436. require
  437. a_tag.intern = a_tag
  438. local
  439. i: INTEGER; parent: LOGGER; parent_tag: FIXED_STRING
  440. do
  441. load_default
  442. Result := loggers.fast_reference_at(a_tag)
  443. if Result = Void then
  444. i := a_tag.first_index_of('[')
  445. if a_tag.valid_index(i) then
  446. parent_tag := a_tag.substring(a_tag.lower, i - 1).intern
  447. parent := loggers.fast_reference_at(parent_tag)
  448. if parent = Void then
  449. create parent.make(root.output, parent_tag, generation_id)
  450. parent.set_parent(root)
  451. loggers.put(parent, parent_tag)
  452. end
  453. else
  454. parent := root
  455. end
  456. check
  457. parent /= Void
  458. end
  459. create Result.make(parent.output, a_tag, generation_id)
  460. Result.set_parent(parent)
  461. loggers.put(Result, a_tag)
  462. end
  463. ensure
  464. Result.tag = a_tag
  465. end
  466. generation_id: INTEGER
  467. do
  468. Result := generations.item
  469. end
  470. feature {}
  471. load_queue: RING_ARRAY[TUPLE[INPUT_STREAM, PROCEDURE[TUPLE[STRING]], FUNCTION[TUPLE[STRING], STRING], PROCEDURE[TUPLE]]]
  472. loading: BOOLEAN
  473. load_ (a_stream: INPUT_STREAM; when_error: PROCEDURE[TUPLE[STRING]]; a_path_resolver: FUNCTION[TUPLE[STRING], STRING]; a_load_completion: PROCEDURE[TUPLE])
  474. require
  475. a_stream.is_connected
  476. local
  477. conf: STRING
  478. on_error: like when_error
  479. path_resolver: FUNCTION[TUPLE[STRING], STRING]
  480. do
  481. if when_error = Void then
  482. on_error := agent fatal_error(?)
  483. else
  484. on_error := when_error
  485. end
  486. if a_path_resolver = Void then
  487. path_resolver := default_path_resolver
  488. else
  489. path_resolver := a_path_resolver
  490. end
  491. conf := once ""
  492. conf.clear_count
  493. from
  494. a_stream.read_line
  495. until
  496. a_stream.end_of_input
  497. loop
  498. conf.append(a_stream.last_string)
  499. conf.extend('%N')
  500. a_stream.read_line
  501. end
  502. conf.append(a_stream.last_string)
  503. parser_buffer.initialize_with(conf)
  504. grammar.reset
  505. if parser.eval(parser_buffer, grammar.table, once "Configuration") then
  506. if parser.error /= Void then
  507. on_error.call([parser.error.message])
  508. else
  509. generations.next
  510. loggers.clear_count
  511. outputs.clear_count
  512. pass := agent do_pass_1(on_error, path_resolver, ?)
  513. grammar.root_node.accept(Current)
  514. pass := agent do_pass_2(on_error, ?)
  515. grammar.root_node.accept(Current)
  516. end
  517. else
  518. if when_error /= Void then
  519. when_error.call(["Truncated log configuration file"])
  520. else
  521. std_error.put_line("Truncated log configuration file")
  522. die_with_code(1)
  523. end
  524. end
  525. if a_load_completion /= Void then
  526. a_load_completion.call([])
  527. end
  528. end
  529. feature {}
  530. make
  531. do
  532. create loggers.make
  533. create outputs.make
  534. create generations
  535. create load_queue.make(1, 0)
  536. end
  537. load_default
  538. local
  539. in: TEXT_FILE_READ
  540. o: LOG_OUTPUT
  541. root0: like root
  542. once
  543. -- This very basic initialization ensures that a root always exists, which is useful while parsing
  544. -- the log file (the parsing engine itself uses the logging framework...)
  545. create o.make(agent pass_through(std_output), "root".intern)
  546. create root0.make(o, "root".intern, generation_id)
  547. root0.set_level(levels.info)
  548. root := root0
  549. if ft.file_exists("log.rc") then
  550. create in.connect_to("log.rc")
  551. if in.is_connected then
  552. load(in, Void, Void, agent (a_in: TEXT_FILE_READ) do a_in.disconnect end(in))
  553. end
  554. if root = Void then
  555. std_error.put_line(once "Could not initialize the logging framework.%NPlease check your log.rc file or explicitly call LOG_CONFIGURATION.load")
  556. die_with_code(1)
  557. end
  558. end
  559. end
  560. last_class_name: STRING
  561. last_entity_name: STRING
  562. last_string: STRING
  563. last_number: EIFFEL_IMAGE
  564. last_level: LOG_LEVEL
  565. last_format: FIXED_STRING
  566. last_retention: INTEGER_64
  567. current_logger: LOGGER
  568. pass: PROCEDURE[TUPLE[EIFFEL_NON_TERMINAL_NODE_IMPL]]
  569. levels: LOG_LEVELS
  570. grammar: LOG_GRAMMAR
  571. once
  572. create Result.make(create {LOG_NODE_FACTORY}.make)
  573. end
  574. parser_buffer: MINI_PARSER_BUFFER
  575. once
  576. create Result
  577. end
  578. parser: DESCENDING_PARSER
  579. once
  580. create Result.make
  581. end
  582. default_output: LOG_OUTPUT
  583. once
  584. create Result.make(agent pass_through(std_output), "*default*".intern)
  585. end
  586. generations: COUNTER
  587. pass_through (a_output: OUTPUT_STREAM): OUTPUT_STREAM
  588. do
  589. Result := a_output
  590. end
  591. end -- class LOG_INTERNAL_CONF
  592. --
  593. -- Copyright (C) 2009-2017: by all the people cited in the AUTHORS file.
  594. --
  595. -- Permission is hereby granted, free of charge, to any person obtaining a copy
  596. -- of this software and associated documentation files (the "Software"), to deal
  597. -- in the Software without restriction, including without limitation the rights
  598. -- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  599. -- copies of the Software, and to permit persons to whom the Software is
  600. -- furnished to do so, subject to the following conditions:
  601. --
  602. -- The above copyright notice and this permission notice shall be included in
  603. -- all copies or substantial portions of the Software.
  604. --
  605. -- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  606. -- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  607. -- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  608. -- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  609. -- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  610. -- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  611. -- THE SOFTWARE.