Class: Metanorma::Utils::Log
- Inherits:
-
Object
- Object
- Metanorma::Utils::Log
- Defined in:
- lib/utils/log.rb,
lib/utils/log_html.rb
Instance Attribute Summary collapse
-
#suppress_log ⇒ Object
Returns the value of attribute suppress_log.
Instance Method Summary collapse
- #abort_messages ⇒ Object
- #add(id, loc, display: true, params: []) ⇒ Object
-
#add_error_ranges(xml) ⇒ Object
pass Nokogiri XML in, to record where all the anchors and ids are in the target document.
- #add_msg(messages) ⇒ Object
- #add_prep(id) ⇒ Object
- #break_up_long_str(str, threshold, punct) ⇒ Object
- #compare_key_parts(a_parts, b_parts) ⇒ Object
- #context(node) ⇒ Object
- #context_render(entry) ⇒ Object
- #create_entry(loc, msg, severity, error_id, params) ⇒ Object
- #current_location(node) ⇒ Object
- #display_messages ⇒ Object
- #entry_in_suppress_range?(entry, id) ⇒ Boolean
- #entry_in_suppress_range_prep(entry) ⇒ Object
- #filter_locations ⇒ Object
- #filter_locations? ⇒ Boolean
- #format_category_section(category, keys) ⇒ Object
- #format_error_line(key) ⇒ Object
- #group_messages_by_category ⇒ Object
-
#human_readable_xml(node) ⇒ Object
try to approximate input, at least for maths.
- #index_severities(entries) ⇒ Object
-
#initialize(messages = {}) ⇒ Log
constructor
messages: hash of message IDs to severity, category severity: 0: abort; 1: serious; 2: not serious; 3: info only.
- #interpolate_msg(msg, params) ⇒ Object
- #line(node, msg) ⇒ Object
- #loc_link(entry) ⇒ Object
- #loc_to_url(loc) ⇒ Object
- #log_hdr(file) ⇒ Object
- #log_index ⇒ Object
- #mapid(old, new) ⇒ Object
- #messages ⇒ Object
- #parse_message_key(key) ⇒ Object
- #render_preproc_entry(entry) ⇒ Object
- #save_to(filename, dir = nil) ⇒ Object
- #sort_messages_by_category_and_key ⇒ Object
- #suppress_display?(category, _loc, _msg, display) ⇒ Boolean
- #suppress_log?(id) ⇒ Boolean
- #to_ncname(tag) ⇒ Object
- #write(file = nil) ⇒ Object
- #write_entry(file, entry) ⇒ Object
- #write_key(file, key) ⇒ Object
- #xml_current_location(node) ⇒ Object
Constructor Details
#initialize(messages = {}) ⇒ Log
messages: hash of message IDs to severity, category severity: 0: abort; 1: serious; 2: not serious; 3: info only
11 12 13 14 15 16 17 18 19 20 21 |
# File 'lib/utils/log.rb', line 11 def initialize( = {}) @log = {} @c = HTMLEntities.new @mapid = {} @suppress_log = { severity: 4, category: [], error_ids: [], locations: [] } @msg = .each_value do |v| v[:error] = v[:error] .encode("UTF-8", invalid: :replace, undef: :replace) end end |
Instance Attribute Details
#suppress_log ⇒ Object
Returns the value of attribute suppress_log.
7 8 9 |
# File 'lib/utils/log.rb', line 7 def suppress_log @suppress_log end |
Instance Method Details
#abort_messages ⇒ Object
58 59 60 61 62 63 64 |
# File 'lib/utils/log.rb', line 58 def @log.values.each_with_object([]) do |v, m| v.each do |e| e[:severity].zero? and m << e[:error] end end end |
#add(id, loc, display: true, params: []) ⇒ Object
49 50 51 52 53 54 55 56 |
# File 'lib/utils/log.rb', line 49 def add(id, loc, display: true, params: []) m = add_prep(id) or return msg = create_entry(loc, m[:error], m[:severity], id, params) @log[m[:category]] << msg loc = loc.nil? ? "" : "(#{current_location(loc)[0]}): " suppress_display?(m[:category], loc, msg, display) or warn "#{m[:category]}: #{loc}#{msg[:error]}" end |
#add_error_ranges(xml) ⇒ Object
pass Nokogiri XML in, to record where all the anchors and ids are in the target document
29 30 31 |
# File 'lib/utils/log.rb', line 29 def add_error_ranges(xml) @anchor_ranges = AnchorRanges.new(xml) end |
#add_msg(messages) ⇒ Object
23 24 25 |
# File 'lib/utils/log.rb', line 23 def add_msg() @msg.merge!() end |
#add_prep(id) ⇒ Object
41 42 43 44 45 46 47 |
# File 'lib/utils/log.rb', line 41 def add_prep(id) id = id.to_sym @msg[id] or raise "Logging: Error #{id} is not defined!" @novalid || suppress_log?(id) and return nil @log[@msg[id][:category]] ||= [] @msg[id] end |
#break_up_long_str(str, threshold, punct) ⇒ Object
104 105 106 |
# File 'lib/utils/log_html.rb', line 104 def break_up_long_str(str, threshold, punct) Metanorma::Utils.break_up_long_str(str, threshold, punct) end |
#compare_key_parts(a_parts, b_parts) ⇒ Object
153 154 155 156 157 158 159 160 161 162 |
# File 'lib/utils/log_html.rb', line 153 def compare_key_parts(a_parts, b_parts) a_str, a_num = a_parts b_str, b_num = b_parts if a_num.nil? || b_num.nil? a_str <=> b_str else str_cmp = a_str <=> b_str str_cmp.zero? ? a_num <=> b_num : str_cmp end end |
#context(node) ⇒ Object
166 167 168 169 170 171 |
# File 'lib/utils/log.rb', line 166 def context(node) node.is_a? String and return nil node.respond_to?(:to_xml) and return human_readable_xml(node) node.respond_to?(:to_s) and return node.to_s nil end |
#context_render(entry) ⇒ Object
77 78 79 80 81 |
# File 'lib/utils/log_html.rb', line 77 def context_render(entry) entry[:context] or return nil entry[:context].split("\n").first(5) .join("\n").gsub("><", "> <") end |
#create_entry(loc, msg, severity, error_id, params) ⇒ Object
87 88 89 90 91 92 93 94 95 96 97 98 |
# File 'lib/utils/log.rb', line 87 def create_entry(loc, msg, severity, error_id, params) loc_str, anchor, node_id = current_location(loc) item = { error_id: error_id, location: loc_str, severity: severity, error: interpolate_msg(msg, params), context: context(loc), line: line(loc, msg), anchor: anchor, id: node_id } if item[:error].include?(" :: ") a = item[:error].split(" :: ", 2) item[:context] = a[1] item[:error] = a[0] end item end |
#current_location(node) ⇒ Object
111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 |
# File 'lib/utils/log.rb', line 111 def current_location(node) anchor = nil id = nil ret = if node.nil? then "" elsif node.respond_to?(:id) && !node.id.nil? then "ID #{node.id}" elsif node.respond_to?(:id) && node.id.nil? && node.respond_to?(:parent) while !node.nil? && node.id.nil? node = node.parent end node.nil? ? "" : "ID #{node.id}" elsif node.respond_to?(:to_xml) && node.respond_to?(:parent) loc, anchor, id = xml_current_location(node) loc elsif node.is_a? String then node elsif node.respond_to?(:lineno) && !node.lineno.nil? && !node.lineno.empty? "Asciidoctor Line #{'%06d' % node.lineno}" elsif node.respond_to?(:line) && !node.line.nil? "XML Line #{'%06d' % node.line}" elsif node.respond_to?(:parent) while !node.nil? && (!node.respond_to?(:level) || node.level.positive?) && (!node.respond_to?(:context) || node.context != :section) node = node.parent node.respond_to?(:context) && node&.context == :section and return "Section: #{node.title}" end "??" else "??" end [ret, anchor, id] end |
#display_messages ⇒ Object
117 118 119 120 121 |
# File 'lib/utils/log_html.rb', line 117 def grouped = grouped.map { |cat, keys| format_category_section(cat, keys) } .join("\n\n") end |
#entry_in_suppress_range?(entry, id) ⇒ Boolean
206 207 208 209 210 211 212 213 214 215 216 |
# File 'lib/utils/log.rb', line 206 def entry_in_suppress_range?(entry, id) # Use anchor if present, otherwise use id id.nil? and return false @suppress_log[:locations].each do |loc| entry_in_suppress_range_prep(loc) @anchor_ranges.in_range?(id, loc[:from], loc[:to]) or next loc[:error_ids].empty? || loc[:error_ids] .include?(entry[:error_id].to_s) and return true end false end |
#entry_in_suppress_range_prep(entry) ⇒ Object
200 201 202 203 204 |
# File 'lib/utils/log.rb', line 200 def entry_in_suppress_range_prep(entry) entry[:to] ||= entry[:from] entry[:error_ids] ||= [] entry end |
#filter_locations ⇒ Object
190 191 192 193 194 195 196 197 198 |
# File 'lib/utils/log.rb', line 190 def filter_locations filter_locations? or return @log.transform_values! do |entries| entries.reject do |entry| # Use anchor if present, otherwise use id entry_in_suppress_range?(entry, entry[:anchor] || entry[:id]) end end end |
#filter_locations? ⇒ Boolean
184 185 186 187 188 |
# File 'lib/utils/log.rb', line 184 def filter_locations? @suppress_log[:locations] && !@suppress_log[:locations].empty? or return @anchor_ranges or return true end |
#format_category_section(category, keys) ⇒ Object
129 130 131 132 |
# File 'lib/utils/log_html.rb', line 129 def format_category_section(category, keys) lines = keys.map { |k| format_error_line(k) } "#{category}:\n#{lines.join("\n")}" end |
#format_error_line(key) ⇒ Object
134 135 136 137 |
# File 'lib/utils/log_html.rb', line 134 def format_error_line(key) padded_key = key.to_s.ljust(12) "\t#{padded_key}: #{@msg[key][:error].gsub("\n", ' ')}" end |
#group_messages_by_category ⇒ Object
123 124 125 126 127 |
# File 'lib/utils/log_html.rb', line 123 def .group_by { |k| @msg[k][:category] } .sort_by { |cat, _| cat } end |
#human_readable_xml(node) ⇒ Object
try to approximate input, at least for maths
174 175 176 177 178 179 180 181 182 |
# File 'lib/utils/log.rb', line 174 def human_readable_xml(node) ret = node.dup ret.xpath(".//*[local-name() = 'stem']").each do |s| sub = s.at("./*[local-name() = 'asciimath'] | " \ "./*[local-name() = 'latexmath']") sub and s.replace(sub) end ret.to_xml end |
#index_severities(entries) ⇒ Object
32 33 34 35 36 37 38 39 40 41 |
# File 'lib/utils/log_html.rb', line 32 def index_severities(entries) s = entries.each_with_object({}) do |e, m| m[e[:severity]] ||= 0 m[e[:severity]] += 1 end.compact s.keys.sort.map do |k| error = s[k] == 1 ? "error" : "errors" "Severity #{k}: <b>#{s[k]}</b> #{error}" end.join("; ") end |
#interpolate_msg(msg, params) ⇒ Object
100 101 102 103 104 105 106 107 108 109 |
# File 'lib/utils/log.rb', line 100 def interpolate_msg(msg, params) # Count %s placeholders in the message placeholder_count = msg.scan(/%s/).length interpolation_params = if params.empty? ::Array.new(placeholder_count, "") else params end placeholder_count.zero? ? msg : (msg % interpolation_params) end |
#line(node, msg) ⇒ Object
156 157 158 159 160 161 162 163 164 |
# File 'lib/utils/log.rb', line 156 def line(node, msg) if node.respond_to?(:line) && !node.line.nil? "#{'%06d' % node.line}" elsif /^XML Line /.match?(msg) msg.sub(/^XML Line /, "").sub(/(^[^:]+):.*$/, "\\1") else "000000" end end |
#loc_link(entry) ⇒ Object
87 88 89 90 91 92 93 94 |
# File 'lib/utils/log_html.rb', line 87 def loc_link(entry) loc = entry[:location] loc.nil? || loc.empty? and loc = "--" loc, url = loc_to_url(loc) loc &&= break_up_long_str(loc, 10, 2) url and loc = "<a href='#{url}'>#{loc}</a>" loc end |
#loc_to_url(loc) ⇒ Object
96 97 98 99 100 101 102 |
# File 'lib/utils/log_html.rb', line 96 def loc_to_url(loc) /^ID /.match?(loc) or return [loc, nil] loc.sub!(/^ID /, "") loc = @mapid[loc] while @mapid[loc] url = "#{@htmlfilename}##{to_ncname loc}" [loc, url] end |
#log_hdr(file) ⇒ Object
8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
# File 'lib/utils/log_html.rb', line 8 def log_hdr(file) " <html><head><title>\#{file} errors</title>\n <meta charset=\"UTF-8\"/>\n <style> pre { white-space: pre-wrap; }\n thead th { font-weight: bold; background-color: aqua; }\n .severity0 { font-weight: bold; background-color: lightpink }\n .severity1 { font-weight: bold; }\n .severity2 { }\n .severity3 { font-style: italic; color: grey; }\n </style>\n </head><body><h1>\#{file} errors</h1>\n <ul>\#{log_index}</ul>\n HTML\nend\n" |
#log_index ⇒ Object
24 25 26 27 28 29 30 |
# File 'lib/utils/log_html.rb', line 24 def log_index @log.each_with_object([]) do |(k, v), m| m << " <li><p><b><a href=\"#\#{to_ncname(k)}\">\#{k}</a></b>: \#{index_severities(v)}</p></li>\n HTML\n end.join(\"\\n\")\nend\n" |
#mapid(old, new) ⇒ Object
83 84 85 |
# File 'lib/utils/log_html.rb', line 83 def mapid(old, new) @mapid[old] = new end |
#messages ⇒ Object
66 67 68 69 70 71 72 |
# File 'lib/utils/log.rb', line 66 def @log.values.each_with_object([]) do |v, m| v.each do |e| m << e end end end |
#parse_message_key(key) ⇒ Object
148 149 150 151 |
# File 'lib/utils/log_html.rb', line 148 def (key) match = key.to_s.match(/^(.+?)_(\d+)$/) match ? [match[1], match[2].to_i] : [key.to_s, nil] end |
#render_preproc_entry(entry) ⇒ Object
67 68 69 70 71 72 73 74 75 |
# File 'lib/utils/log_html.rb', line 67 def render_preproc_entry(entry) ret = entry.dup ret[:line] = nil if ret[:line] == "000000" ret[:location] = loc_link(entry) ret[:error] = break_up_long_str(entry[:error], 10, 2) .gsub(/`([^`]+)`/, "<code>\\1</code>") ret[:context] = context_render(entry) ret.compact end |
#save_to(filename, dir = nil) ⇒ Object
33 34 35 36 37 38 39 |
# File 'lib/utils/log.rb', line 33 def save_to(filename, dir = nil) dir ||= File.dirname(filename) new_fn = filename.sub(/\.err\.html$/, ".html") b = File.join(dir, File.basename(new_fn, ".*")) @filename = "#{b}.err.html" @htmlfilename = "#{b}.html" end |
#sort_messages_by_category_and_key ⇒ Object
139 140 141 142 143 144 145 146 |
# File 'lib/utils/log_html.rb', line 139 def @msg.keys.sort do |a, b| cat_cmp = @msg[a][:category] <=> @msg[b][:category] a_parts = (a) b_parts = (b) cat_cmp.zero? ? compare_key_parts(a_parts, b_parts) : cat_cmp end end |
#suppress_display?(category, _loc, _msg, display) ⇒ Boolean
82 83 84 85 |
# File 'lib/utils/log.rb', line 82 def suppress_display?(category, _loc, _msg, display) ["Metanorma XML Syntax", "Relaton"].include?(category) || !display end |
#suppress_log?(id) ⇒ Boolean
74 75 76 77 78 79 80 |
# File 'lib/utils/log.rb', line 74 def suppress_log?(id) category = @msg[id][:category] category && /^Fetching /.match?(@msg[id][:error]) || @suppress_log[:severity] <= @msg[id][:severity] || @suppress_log[:category].include?(category) || @suppress_log[:error_ids].include?(id.to_s) end |
#to_ncname(tag) ⇒ Object
4 5 6 |
# File 'lib/utils/log_html.rb', line 4 def to_ncname(tag) ::Metanorma::Utils.to_ncname(tag) end |
#write(file = nil) ⇒ Object
43 44 45 46 47 48 49 50 51 |
# File 'lib/utils/log_html.rb', line 43 def write(file = nil) (!file && @filename) or save_to(file || "metanorma", nil) filter_locations File.open(@filename, "w:UTF-8") do |f| f.puts log_hdr(@filename) @log.each_key { |key| write_key(f, key) } f.puts "</body></html>\n" end end |
#write_entry(file, entry) ⇒ Object
108 109 110 111 112 113 114 115 |
# File 'lib/utils/log_html.rb', line 108 def write_entry(file, entry) entry[:context] &&= @c.encode(break_up_long_str(entry[:context], 40, 2)) file.print " <tr class=\"severity\#{entry[:severity]}\">\n <td>\#{entry[:line]}</td><th><code>\#{entry[:location]}</code></th><td>\#{entry[:error_id]}</td>\n <td>\#{entry[:error]}</td><td><pre>\#{entry[:context]}</pre></td><td>\#{entry[:severity]}</td></tr>\n HTML\nend\n" |
#write_key(file, key) ⇒ Object
53 54 55 56 57 58 59 60 61 62 63 64 65 |
# File 'lib/utils/log_html.rb', line 53 def write_key(file, key) file.puts " <h2 id=\"\#{to_ncname(key)}\">\#{key}</h2>\\n<table border=\"1\">\n <thead><th width=\"5%\">Line</th><th width=\"20%\">ID</th><th width=\"10%\">Error</th>\n <th width=\"20%\">Message</th><th width=\"40%\">Context</th><th width=\"5%\">Severity</th></thead>\n <tbody>\n HTML\n @log[key].sort_by { |a| [a[:line], a[:location], a[:error]] }\n .each do |n|\n write_entry(file, render_preproc_entry(n))\n end\n file.puts \"</tbody></table>\\n\"\nend\n" |
#xml_current_location(node) ⇒ Object
145 146 147 148 149 150 151 152 153 154 |
# File 'lib/utils/log.rb', line 145 def xml_current_location(node) while !node.nil? && node["id"].nil? && node.respond_to?(:parent) node.parent.nil? and break node = node.parent end anchor = node["anchor"] id = node["id"] loc = node.respond_to?(:parent) ? "ID #{anchor || id}" : "" [loc, anchor, id] end |