hayabusa 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (85) hide show
  1. data/.document +5 -0
  2. data/.rspec +1 -0
  3. data/Gemfile +20 -0
  4. data/Gemfile.lock +59 -0
  5. data/LICENSE.txt +20 -0
  6. data/README.rdoc +19 -0
  7. data/Rakefile +49 -0
  8. data/VERSION +1 -0
  9. data/bin/check_running.rb +69 -0
  10. data/bin/hayabusa_benchmark.rb +82 -0
  11. data/bin/hayabusa_cgi.rb +84 -0
  12. data/bin/hayabusa_fcgi.fcgi +159 -0
  13. data/bin/hayabusa_fcgi.rb +159 -0
  14. data/bin/knjappserver_start.rb +42 -0
  15. data/conf/apache2_cgi_rhtml_conf.conf +10 -0
  16. data/conf/apache2_fcgi_rhtml_conf.conf +22 -0
  17. data/hayabusa.gemspec +151 -0
  18. data/lib/hayabusa.rb +518 -0
  19. data/lib/hayabusa_cgi_session.rb +128 -0
  20. data/lib/hayabusa_cgi_tools.rb +102 -0
  21. data/lib/hayabusa_custom_io.rb +22 -0
  22. data/lib/hayabusa_database.rb +125 -0
  23. data/lib/hayabusa_erb_handler.rb +27 -0
  24. data/lib/hayabusa_ext/cleaner.rb +140 -0
  25. data/lib/hayabusa_ext/cmdline.rb +52 -0
  26. data/lib/hayabusa_ext/errors.rb +135 -0
  27. data/lib/hayabusa_ext/logging.rb +404 -0
  28. data/lib/hayabusa_ext/mailing.rb +158 -0
  29. data/lib/hayabusa_ext/sessions.rb +71 -0
  30. data/lib/hayabusa_ext/threadding.rb +96 -0
  31. data/lib/hayabusa_ext/threadding_timeout.rb +101 -0
  32. data/lib/hayabusa_ext/translations.rb +43 -0
  33. data/lib/hayabusa_ext/web.rb +190 -0
  34. data/lib/hayabusa_http_server.rb +102 -0
  35. data/lib/hayabusa_http_session.rb +361 -0
  36. data/lib/hayabusa_http_session_contentgroup.rb +176 -0
  37. data/lib/hayabusa_http_session_page_environment.rb +66 -0
  38. data/lib/hayabusa_http_session_post_multipart.rb +135 -0
  39. data/lib/hayabusa_http_session_request.rb +219 -0
  40. data/lib/hayabusa_http_session_response.rb +144 -0
  41. data/lib/hayabusa_models.rb +8 -0
  42. data/lib/kernel_ext/gettext_methods.rb +22 -0
  43. data/lib/kernel_ext/magic_methods.rb +61 -0
  44. data/lib/models/log.rb +130 -0
  45. data/lib/models/log_access.rb +88 -0
  46. data/lib/models/log_data.rb +27 -0
  47. data/lib/models/log_data_link.rb +3 -0
  48. data/lib/models/log_data_value.rb +21 -0
  49. data/lib/models/log_link.rb +65 -0
  50. data/lib/models/session.rb +35 -0
  51. data/pages/benchmark.rhtml +0 -0
  52. data/pages/benchmark_print.rhtml +14 -0
  53. data/pages/benchmark_simple.rhtml +3 -0
  54. data/pages/benchmark_threadded_content.rhtml +21 -0
  55. data/pages/debug_database_connections.rhtml +46 -0
  56. data/pages/debug_http_sessions.rhtml +40 -0
  57. data/pages/debug_memory_usage.rhtml +16 -0
  58. data/pages/error_notfound.rhtml +12 -0
  59. data/pages/logs_latest.rhtml +57 -0
  60. data/pages/logs_show.rhtml +32 -0
  61. data/pages/spec.rhtml +41 -0
  62. data/pages/spec_post.rhtml +3 -0
  63. data/pages/spec_test_multiple_clients.rhtml +3 -0
  64. data/pages/spec_thread_joins.rhtml +21 -0
  65. data/pages/spec_threadded_content.rhtml +40 -0
  66. data/pages/tests.rhtml +14 -0
  67. data/spec/cgi_spec.rb +47 -0
  68. data/spec/custom_urls_spec.rb +35 -0
  69. data/spec/fcgi_multiple_processes_spec.rb +32 -0
  70. data/spec/fcgi_spec.rb +69 -0
  71. data/spec/hayabusa_spec.rb +194 -0
  72. data/spec/spec_helper.rb +12 -0
  73. data/tests/cgi_test/config_cgi.rb +6 -0
  74. data/tests/cgi_test/threadded_content_test.rhtml +23 -0
  75. data/tests/cgi_test/vars_get_test.rhtml +4 -0
  76. data/tests/cgi_test/vars_header_test.rhtml +3 -0
  77. data/tests/cgi_test/vars_post_test.rhtml +4 -0
  78. data/tests/fcgi_test/config_fcgi.rb +6 -0
  79. data/tests/fcgi_test/index.rhtml +3 -0
  80. data/tests/fcgi_test/sleeper.rhtml +4 -0
  81. data/tests/fcgi_test/threadded_content_test.rhtml +23 -0
  82. data/tests/fcgi_test/vars_get_test.rhtml +4 -0
  83. data/tests/fcgi_test/vars_header_test.rhtml +3 -0
  84. data/tests/fcgi_test/vars_post_test.rhtml +4 -0
  85. metadata +257 -0
@@ -0,0 +1,52 @@
1
+ class Hayabusa
2
+ def initialize_cmdline
3
+ @cmds = {}
4
+
5
+ Thread.new do
6
+ begin
7
+ $stdin.each_line do |line|
8
+ called = 0
9
+ @cmds.each do |key, connects|
10
+ data = {}
11
+
12
+ if key.is_a?(Regexp)
13
+ if line.match(key)
14
+ connects.each do |conn|
15
+ called += 1
16
+ conn[:block].call(data)
17
+ end
18
+ end
19
+ else
20
+ raise "Unknown class for 'cmd_connect': '#{key.class.name}'."
21
+ end
22
+ end
23
+
24
+ if called == 0
25
+ print "Unknown command: '#{line.strip}'.\n"
26
+ end
27
+ end
28
+ rescue => e
29
+ self.handle_error(e)
30
+ end
31
+ end
32
+
33
+ self.cmd_connect(/^\s*restart\s*$/i, &self.method(:cmdline_on_restart_cmd))
34
+ self.cmd_connect(/^\s*stop\s*$/i, &self.method(:cmdline_on_stop_cmd))
35
+ end
36
+
37
+ def cmdline_on_restart_cmd(data)
38
+ print "Restart will begin shortly.\n"
39
+ self.should_restart = true
40
+ end
41
+
42
+ def cmdline_on_stop_cmd(data)
43
+ print "Stopping appserver.\n"
44
+ self.stop
45
+ end
46
+
47
+ #Connects a proc to a specific command in the command-line (key should be a regex).
48
+ def cmd_connect(cmd, &block)
49
+ @cmds[cmd] = [] if !@cmds.key?(cmd)
50
+ @cmds[cmd] << {:block => block}
51
+ end
52
+ end
@@ -0,0 +1,135 @@
1
+ class Hayabusa
2
+ attr_reader :error_emails_pending
3
+
4
+ def initialize_errors
5
+ @error_emails_pending = {}
6
+ @error_emails_pending_mutex = Mutex.new
7
+
8
+ if @config[:error_emails_time]
9
+ @error_emails_time = @config[:error_emails_time]
10
+ elsif @config[:debug]
11
+ @error_emails_time = 5
12
+ else
13
+ @error_emails_time = 180
14
+ end
15
+
16
+ self.timeout(:time => @error_emails_time, &self.method(:flush_error_emails))
17
+ end
18
+
19
+ #Send error-emails based on error-emails-cache (cached so the same error isnt send out every time it occurrs to prevent spamming).
20
+ def flush_error_emails
21
+ @error_emails_pending_mutex.synchronize do
22
+ send_time_older_than = Time.new.to_i - @error_emails_time
23
+
24
+ @error_emails_pending.each do |backtrace_hash, error_email|
25
+ if send_time_older_than < error_email[:last_time].to_i and error_email[:messages].length < 1000
26
+ next
27
+ end
28
+
29
+ @config[:error_report_emails].each do |email|
30
+ next if !email or error_email[:messages].length <= 0
31
+
32
+ if error_email[:messages].length == 1
33
+ html = error_email[:messages].first
34
+ else
35
+ html = "<b>First time:</b> #{Datet.in(error_email[:first_time]).out}<br />"
36
+ html << "<b>Last time:</b> #{Datet.in(error_email[:last_time]).out}<br />"
37
+ html << "<b>Number of errors:</b> #{error_email[:messages].length}<br />"
38
+ count = 0
39
+
40
+ error_email[:messages].each do |error_msg|
41
+ count += 1
42
+
43
+ if count > 10
44
+ html << "<br /><br /><b><i>Limiting to showing 10 out of #{error_email[:messages].length} messages.</i></b>"
45
+ break
46
+ end
47
+
48
+ html << "<br /><br />"
49
+ html << "<b>Message #{count}</b><br />"
50
+ html << error_msg
51
+ end
52
+ end
53
+
54
+ self.mail(
55
+ :to => email,
56
+ :subject => error_email[:subject],
57
+ :html => html,
58
+ :from => @config[:error_report_from]
59
+ )
60
+ end
61
+
62
+ @error_emails_pending.delete(backtrace_hash)
63
+ end
64
+ end
65
+ end
66
+
67
+ #Handels a given error. Sends to the admin-emails.
68
+ def handle_error(e, args = {})
69
+ @error_emails_pending_mutex.synchronize do
70
+ if !Thread.current[:hayabusa] or !Thread.current[:hayabusa][:httpsession]
71
+ STDOUT.print "#{Knj::Errors.error_str(e)}\n\n"
72
+ end
73
+
74
+ browser = _httpsession.browser if _httpsession
75
+
76
+ send_email = true
77
+ send_email = false if !@config[:smtp_args]
78
+ send_email = false if !@config[:error_report_emails]
79
+ send_email = false if args.has_key?(:email) and !args[:email]
80
+ send_email = false if @config.key?(:error_report_bots) and !@config[:error_report_bots] and browser and browser["browser"] == "bot"
81
+
82
+ if send_email
83
+ backtrace_hash = Knj::ArrayExt.array_hash(e.backtrace)
84
+
85
+ if !@error_emails_pending.has_key?(backtrace_hash)
86
+ @error_emails_pending[backtrace_hash] = {
87
+ :first_time => Time.new,
88
+ :messages => [],
89
+ :subject => sprintf("Error @ %s", @config[:title]) + " (#{Knj::Strings.shorten(e.message, 100)})"
90
+ }
91
+ end
92
+
93
+ html = "An error occurred.<br /><br />"
94
+ html << "<b>#{Knj::Web.html(e.class.name)}: #{Knj::Web.html(e.message)}</b><br /><br />"
95
+
96
+ e.backtrace.each do |line|
97
+ html << "#{Knj::Web.html(line)}<br />"
98
+ end
99
+
100
+ html << "<br /><b>Post:</b><br /><pre>#{Php4r.print_r(_post, true)}</pre>" if _post
101
+ html << "<br /><b>Get:</b><br /><pre>#{Php4r.print_r(_get, true)}</pre>" if _get
102
+ html << "<br /><b>Server:</b><br /><pre>#{Php4r.print_r(_server, true).html}</pre>" if _server
103
+ html << "<br /><b>Cookie:</b><br /><pre>#{Php4r.print_r(_cookie, true).html}</pre>" if _meta
104
+ html << "<br /><b>Session:</b><br /><pre>#{Php4r.print_r(_session, true).html}</pre>" if _session
105
+ html << "<br /><b>Session hash:</b><br /><pre>#{Php4r.print_r(_session_hash, true).html}</pre>" if _session_hash
106
+
107
+ error_hash = @error_emails_pending[backtrace_hash]
108
+ error_hash[:last_time] = Time.new
109
+ error_hash[:messages] << html
110
+ end
111
+ end
112
+ end
113
+
114
+ #Takes a proc and executes it. On error it alerts the error-message with javascript to the server, sends a javascript back and exits.
115
+ def on_error_go_back(&block)
116
+ begin
117
+ block.call
118
+ rescue => e
119
+ self.alert(e.message).back
120
+ end
121
+ end
122
+
123
+ #Prints a detailed overview of the object in the terminal from where the appserver was started. This can be used for debugging.
124
+ def dprint(obj)
125
+ STDOUT.print Php4r.print_r(obj, true)
126
+ end
127
+
128
+ #Prints a string with a single file-line-backtrace prepended which is useful for debugging.
129
+ def debugs(str)
130
+ #Get backtrace.
131
+ backtrace_str = caller[0]
132
+ backtrace_match = backtrace_str.match(/^(.+):(\d+):in /)
133
+ STDOUT.print "#{File.basename(backtrace_match[1])}:#{backtrace_match[2]}: #{str}\n"
134
+ end
135
+ end
@@ -0,0 +1,404 @@
1
+ class Hayabusa
2
+ def initialize_logging
3
+ @logs_access_pending = []
4
+ @logs_mutex = Mutex.new
5
+
6
+ if @config[:logging] and @config[:logging][:access_db]
7
+ self.timeout(:time => 30, &self.method(:flush_access_log))
8
+ end
9
+ end
10
+
11
+ #Writes all queued access-logs to the database.
12
+ def flush_access_log
13
+ return nil if @logs_access_pending.empty?
14
+
15
+ @logs_mutex.synchronize do
16
+ ins_arr = @logs_access_pending
17
+ @logs_access_pending = []
18
+ inserts = []
19
+ inserts_links = []
20
+
21
+ ins_arr.each do |ins|
22
+ gothrough = [{
23
+ :col => :get_keys_data_id,
24
+ :hash => ins[:get],
25
+ :type => :keys
26
+ },{
27
+ :col => :get_values_data_id,
28
+ :hash => ins[:get],
29
+ :type => :values
30
+ },{
31
+ :col => :post_keys_data_id,
32
+ :hash => ins[:post],
33
+ :type => :keys
34
+ },{
35
+ :col => :post_values_data_id,
36
+ :hash => ins[:post],
37
+ :type => :values
38
+ },{
39
+ :col => :cookie_keys_data_id,
40
+ :hash => ins[:cookie],
41
+ :type => :keys
42
+ },{
43
+ :col => :cookie_values_data_id,
44
+ :hash => ins[:cookie],
45
+ :type => :values
46
+ },{
47
+ :col => :meta_keys_data_id,
48
+ :hash => ins[:meta],
49
+ :type => :keys
50
+ },{
51
+ :col => :meta_values_data_id,
52
+ :hash => ins[:meta],
53
+ :type => :values
54
+ }]
55
+ ins_hash = {
56
+ :session_id => ins[:session_id],
57
+ :date_request => ins[:date_request]
58
+ }
59
+
60
+ gothrough.each do |data|
61
+ if data[:type] == :keys
62
+ hash = Knj::ArrayExt.hash_keys_hash(data[:hash])
63
+ else
64
+ hash = Knj::ArrayExt.hash_values_hash(data[:hash])
65
+ end
66
+
67
+ data_id = @ob.static(:Log_data, :by_id_hash, hash)
68
+ if !data_id
69
+ data_id = @db.insert(:Log_data, {"id_hash" => hash}, {:return_id => true})
70
+
71
+ link_count = 0
72
+ data[:hash].keys.sort.each do |key|
73
+ if data[:type] == :keys
74
+ ins_data = "#{key.to_s}"
75
+ else
76
+ ins_data = "#{data[:hash][key]}"
77
+ end
78
+
79
+ ins_data = ins_data.force_encoding("UTF-8") if ins_data.respond_to?(:force_encoding)
80
+ data_value_id = @ob.static(:Log_data_value, :force_id, ins_data)
81
+ inserts_links << {:no => link_count, :data_id => data_id, :value_id => data_value_id}
82
+ link_count += 1
83
+ end
84
+ end
85
+
86
+ ins_hash[data[:col]] = data_id
87
+ end
88
+
89
+ hash = Knj::ArrayExt.array_hash(ins[:ips])
90
+ data_id = @ob.static(:Log_data, :by_id_hash, hash)
91
+
92
+ if !data_id
93
+ data_id = @db.insert(:Log_data, {"id_hash" => hash}, {:return_id => true})
94
+
95
+ link_count = 0
96
+ ins[:ips].each do |ip|
97
+ data_value_id = @ob.static(:Log_data_value, :force_id, ip)
98
+ inserts_links << {:no => link_count, :data_id => data_id, :value_id => data_value_id}
99
+ link_count += 1
100
+ end
101
+ end
102
+
103
+ ins_hash[:ip_data_id] = data_id
104
+ inserts << ins_hash
105
+ end
106
+
107
+ @db.insert_multi(:Log_access, inserts)
108
+ @db.insert_multi(:Log_data_link, inserts_links)
109
+ @ob.unset_class([:Log_access, :Log_data, :Log_data_link, :Log_data_value])
110
+ end
111
+ end
112
+
113
+ #Converts fileuploads into strings so logging wont be crazy big.
114
+ def log_hash_safe(hash)
115
+ hash_obj = {}
116
+ hash.each do |key, val|
117
+ if val.is_a?(Hayabusa::Http_session::Post_multipart::File_upload)
118
+ hash_obj[key] = "<Fileupload>"
119
+ elsif val.is_a?(Hash)
120
+ hash_obj[key] = self.log_hash_safe(val)
121
+ else
122
+ hash_obj[key] = val
123
+ end
124
+ end
125
+
126
+ return hash_obj
127
+ end
128
+
129
+ #Handles the hashes that should be logged.
130
+ def log_hash_ins(hash_obj)
131
+ #Sort out fileuploads - it would simply bee too big to log this.
132
+ hash_obj = self.log_hash_safe(hash_obj)
133
+
134
+ inserts_links = []
135
+ ret = {}
136
+ [:keys, :values].each do |type|
137
+ if type == :keys
138
+ hash = Knj::ArrayExt.hash_keys_hash(hash_obj)
139
+ else
140
+ hash = Knj::ArrayExt.hash_values_hash(hash_obj)
141
+ end
142
+
143
+ data_id = @db.single(:Log_data, {"id_hash" => hash})
144
+ data_id = data_id[:id] if data_id
145
+
146
+ if !data_id
147
+ data_id = @db.insert(:Log_data, {"id_hash" => hash}, {:return_id => true})
148
+
149
+ link_count = 0
150
+ hash_obj.keys.sort.each do |key|
151
+ if type == :keys
152
+ ins_data = "#{key.to_s}"
153
+ else
154
+ ins_data = "#{hash_obj[key].to_s}"
155
+ end
156
+
157
+ ins_data = ins_data.force_encoding("UTF-8") if ins_data.respond_to?(:force_encoding)
158
+ data_value_id = @ob.static(:Log_data_value, :force_id, ins_data)
159
+ inserts_links << {:no => link_count, :data_id => data_id, :value_id => data_value_id}
160
+ link_count += 1
161
+ end
162
+ end
163
+
164
+ if type == :keys
165
+ ret[:keys_data_id] = data_id
166
+ else
167
+ ret[:values_data_id] = data_id
168
+ end
169
+ end
170
+
171
+ @db.insert_multi(:Log_data_link, inserts_links)
172
+
173
+ return ret
174
+ end
175
+
176
+ def log_data_hash(keys_id, values_id)
177
+ begin
178
+ keys_data_obj = @ob.get(:Log_data, keys_id)
179
+ values_data_obj = @ob.get(:Log_data, values_id)
180
+ rescue Errno::ENOENT
181
+ return {}
182
+ end
183
+
184
+ sql = "
185
+ SELECT
186
+ key_value.value AS `key`,
187
+ value_value.value AS value
188
+
189
+ FROM
190
+ Log_data_link AS key_links,
191
+ Log_data_link AS value_links,
192
+ Log_data_value AS key_value,
193
+ Log_data_value AS value_value
194
+
195
+ WHERE
196
+ key_links.data_id = '#{keys_id}' AND
197
+ value_links.data_id = '#{values_id}' AND
198
+ key_links.no = value_links.no AND
199
+ key_value.id = key_links.value_id AND
200
+ value_value.id = value_links.value_id
201
+
202
+ ORDER BY
203
+ key_links.no
204
+ "
205
+
206
+ hash = {}
207
+ db.q(sql) do |d_hash|
208
+ hash[d_hash[:key].to_sym] = d_hash[:value]
209
+ end
210
+
211
+ return hash
212
+ end
213
+
214
+ #Writes a custom log to the database.
215
+ def log(msg, objs, args = {})
216
+ #This can come in handy if migrating logs to appserver-database.
217
+ if args[:date_saved]
218
+ date_saved = args[:date_saved]
219
+ else
220
+ date_saved = Time.now
221
+ end
222
+
223
+ objs = [objs] if !objs.is_a?(Array)
224
+
225
+ @logs_mutex.synchronize do
226
+ log_value_id = @ob.static(:Log_data_value, :force_id, msg)
227
+
228
+ ins_data = {
229
+ :date_saved => date_saved,
230
+ :text_value_id => log_value_id
231
+ }
232
+
233
+ get_hash = log_hash_ins(_get) if _get
234
+ if get_hash
235
+ ins_data[:get_keys_data_id] = get_hash[:keys_data_id]
236
+ ins_data[:get_values_data_id] = get_hash[:values_data_id]
237
+ end
238
+
239
+ post_hash = log_hash_ins(_post) if _post
240
+ if post_hash
241
+ ins_data[:post_keys_data_id] = post_hash[:keys_data_id]
242
+ ins_data[:post_values_data_id] = post_hash[:values_data_id]
243
+ end
244
+
245
+ cookie_hash = log_hash_ins(_cookie) if _cookie
246
+ if cookie_hash
247
+ ins_data[:post_keys_data_id] = cookie_hash[:keys_data_id]
248
+ ins_data[:post_values_data_id] = cookie_hash[:values_data_id]
249
+ end
250
+
251
+ meta_hash = log_hash_ins(_meta) if _meta
252
+ if cookie_hash
253
+ ins_data[:meta_keys_data_id] = meta_hash[:keys_data_id]
254
+ ins_data[:meta_values_data_id] = meta_hash[:values_data_id]
255
+ end
256
+
257
+ session_hash = log_hash_ins(_session) if _session
258
+ if session_hash
259
+ ins_data[:session_keys_data_id] = session_hash[:keys_data_id]
260
+ ins_data[:session_values_data_id] = session_hash[:values_data_id]
261
+ end
262
+
263
+ if args[:tag]
264
+ tag_value_id = @ob.static(:Log_data_value, :force_id, args[:tag])
265
+ ins_data[:tag_data_id] = tag_value_id
266
+ end
267
+
268
+ if args[:comment]
269
+ comment_value_id = @ob.static(:Log_data_value, :force_id, args[:comment])
270
+ ins_data[:comment_data_id] = comment_value_id
271
+ end
272
+
273
+ log_id = @db.insert(:Log, ins_data, {:return_id => true})
274
+
275
+ log_links = []
276
+ objs.each do |obj|
277
+ class_data_id = @ob.static(:Log_data_value, :force_id, obj.class.name)
278
+
279
+ log_links << {
280
+ :object_class_value_id => class_data_id,
281
+ :object_id => obj.id,
282
+ :log_id => log_id
283
+ }
284
+ end
285
+
286
+ @db.insert_multi(:Log_link, log_links)
287
+ end
288
+ end
289
+
290
+ #Deletes all logs for an object.
291
+ def logs_delete(obj)
292
+ @db.q_buffer do |db_buffer|
293
+ buffer_hash = {:db_buffer => db_buffer}
294
+
295
+ @ob.list(:Log_link, {"object_class" => obj.class.name, "object_id" => obj.id}) do |log_link|
296
+ log = log_link.log
297
+ @ob.delete(log_link, buffer_hash)
298
+ @ob.delete(log, buffer_hash) if log and log.links("count" => true) <= 0
299
+ end
300
+ end
301
+ end
302
+
303
+ #Returns the HTML for a table with logs from a given object.
304
+ def logs_table(obj, args = {})
305
+ if args[:out]
306
+ html = args[:out]
307
+ else
308
+ html = $stdout
309
+ end
310
+
311
+ html = ""
312
+
313
+ html << "<table class=\"list hayabusa_log_table\">"
314
+ html << "<thead>"
315
+ html << "<tr>"
316
+ html << "<th>ID</th>"
317
+ html << "<th>Message</th>"
318
+ html << "<th style=\"width: 130px;\">Date &amp; time</th>"
319
+ html << "<th>Tag</th>"
320
+ html << "<th>Objects</th>" if args[:ob_use]
321
+ html << "<th>IP</th>" if args[:show_ip]
322
+ html << "</tr>"
323
+ html << "</thead>"
324
+ html << "<tbody>"
325
+
326
+ count = 0
327
+ @ob.list(:Log_link, {"object_class" => obj.class.name, "object_id" => obj.id, "limit" => 500, "orderby" => [["id", "desc"]]}) do |link|
328
+ count += 1
329
+ log = link.log
330
+
331
+ msg_lines = log.text.split("\n")
332
+ first_line = msg_lines[0].to_s
333
+
334
+ classes = ["hayabusa_log", "hayabusa_log_#{log.id}"]
335
+ classes << "hayabusa_log_multiple_lines" if msg_lines.length > 1
336
+
337
+ html << "<tr class=\"#{classes.join(" ")}\">"
338
+ html << "<td>#{log.id}</td>"
339
+ html << "<td>#{first_line.html}</td>"
340
+ html << "<td>#{log.date_saved_str}</td>"
341
+ html << "<td>#{log.tag.html}</td>"
342
+
343
+ if args[:ob_use]
344
+ begin
345
+ html << "<td>#{log.objects_html(args[:ob_use])}</td>"
346
+ rescue => e
347
+ html << "<td>#{e.message.html}</td>"
348
+ end
349
+ end
350
+
351
+ html << "<td>#{log.ip}</td>" if args[:show_ip]
352
+ html << "</tr>"
353
+ end
354
+
355
+ if count <= 0
356
+ html << "<tr>"
357
+ html << "<td colspan=\"2\" class=\"error\">No logs were found for that object.</td>"
358
+ html << "</tr>"
359
+ end
360
+
361
+ html << "</tbody>"
362
+ html << "</table>"
363
+
364
+ return nil
365
+ end
366
+
367
+ #Removes all logs for objects that have been deleted.
368
+ #===Examples
369
+ #Remember to pass Knj::Objects-object handler to the method.
370
+ # appsrv.logs_delete_dead(:ob => ob, :debug => false)
371
+ def logs_delete_dead(args)
372
+ raise "No :ob-argument given." if !args[:ob]
373
+
374
+ @db.q_buffer do |db_buffer|
375
+ STDOUT.puts "Starting to look for dead log-links." if @debug or args[:debug]
376
+ @ob.list(:Log_link, :cloned_ubuf => true) do |log_link|
377
+ classname = log_link.object_class.to_s.split("::").last
378
+ obj_exists = args[:ob].exists?(classname, log_link[:object_id])
379
+ next if obj_exists
380
+
381
+ log = log_link.log
382
+
383
+ STDOUT.puts "Deleting log-link #{log_link.id} for #{classname}(#{log_link[:object_id]})." if @debug or args[:debug]
384
+ @ob.delete(log_link, :db_buffer => db_buffer)
385
+
386
+ links_count = log.links("count" => true)
387
+
388
+ if links_count <= 0
389
+ STDOUT.puts "Deleting log #{log.id} because it has no more links." if @debug or args[:debug]
390
+ @ob.delete(log, :db_buffer => db_buffer)
391
+ end
392
+ end
393
+
394
+ STDOUT.puts "Starting to look for logs with no links." if @debug or args[:debug]
395
+ @ob.list(:Log, {
396
+ [:Log_link, "id"] => {:type => :sqlval, :val => :null},
397
+ :cloned_ubuf => true
398
+ }) do |log|
399
+ STDOUT.puts "Deleting log #{log.id} because it has no links: '#{log.text}'." if @debug or args[:debug]
400
+ @ob.delete(log, :db_buffer => db_buffer)
401
+ end
402
+ end
403
+ end
404
+ end