clogger 0.0.5 → 0.0.6
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +4 -0
- data/.gitignore +2 -0
- data/GNUmakefile +43 -19
- data/{README.txt → README} +14 -9
- data/Rakefile +27 -20
- data/clogger.gemspec +32 -0
- data/ext/clogger_ext/clogger.c +75 -127
- data/ext/clogger_ext/extconf.rb +32 -10
- data/lib/clogger.rb +10 -5
- data/lib/clogger/pure.rb +8 -13
- data/test/test_clogger.rb +36 -1
- metadata +17 -15
- data/History.txt +0 -45
- data/Manifest.txt +0 -15
data/.document
ADDED
data/.gitignore
CHANGED
data/GNUmakefile
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
all:: test
|
2
2
|
ruby = ruby
|
3
|
+
rake = rake
|
3
4
|
|
4
5
|
-include local.mk
|
5
6
|
|
@@ -7,10 +8,6 @@ ifeq ($(DLEXT),) # "so" for Linux
|
|
7
8
|
DLEXT := $(shell $(ruby) -rrbconfig -e 'puts Config::CONFIG["DLEXT"]')
|
8
9
|
endif
|
9
10
|
|
10
|
-
ifeq ($(RUBY_VERSION),)
|
11
|
-
RUBY_VERSION := $(shell $(ruby) -e 'puts RUBY_VERSION')
|
12
|
-
endif
|
13
|
-
|
14
11
|
ext/clogger_ext/Makefile: ext/clogger_ext/clogger.c ext/clogger_ext/extconf.rb
|
15
12
|
cd ext/clogger_ext && $(ruby) extconf.rb
|
16
13
|
|
@@ -25,14 +22,17 @@ test-ext: ext/clogger_ext/clogger.$(DLEXT)
|
|
25
22
|
$(ruby) -Iext/clogger_ext:lib test/test_clogger.rb
|
26
23
|
|
27
24
|
test-pure:
|
28
|
-
$(ruby) -Ilib test/test_clogger.rb
|
25
|
+
CLOGGER_PURE=t $(ruby) -Ilib test/test_clogger.rb
|
29
26
|
|
30
27
|
test: test-ext test-pure
|
31
28
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
29
|
+
History:
|
30
|
+
$(rake) -s history > $@+
|
31
|
+
mv $@+ $@
|
32
|
+
|
33
|
+
.manifest: History
|
34
|
+
(git ls-files; for i in $^ $@; do echo $$i; done) | LC_ALL=C sort > $@+
|
35
|
+
mv $@+ $@
|
36
36
|
|
37
37
|
VERSION := $(shell git describe 2>/dev/null | sed 's/^v//')
|
38
38
|
|
@@ -41,6 +41,8 @@ v := /^v$(VERSION)$$/
|
|
41
41
|
vPREV := $(shell git tag -l 2>/dev/null | sed -n -e '$(v)!h' -e '$(v){x;p;q}')
|
42
42
|
release_notes := release_notes-$(VERSION).txt
|
43
43
|
release_changes := release_changes-$(VERSION).txt
|
44
|
+
release-notes: $(release_notes)
|
45
|
+
release-changes: $(release_changes)
|
44
46
|
$(release_changes): verify
|
45
47
|
git diff --stat $(vPREV) v$(VERSION) > $@+
|
46
48
|
echo >> $@+
|
@@ -55,24 +57,46 @@ verify:
|
|
55
57
|
@test -n "$(VERSION)" || { echo >&2 VERSION= not defined; exit 1; }
|
56
58
|
git rev-parse --verify refs/tags/v$(VERSION)^{}
|
57
59
|
@test -n "$(VISUAL)" || { echo >&2 VISUAL= not defined; exit 1; }
|
58
|
-
|
59
|
-
package: verify
|
60
60
|
git diff-index --quiet HEAD^0
|
61
61
|
test `git rev-parse --verify HEAD^0` = \
|
62
62
|
`git rev-parse --verify refs/tags/v$(VERSION)^{}`
|
63
|
-
|
64
|
-
|
65
|
-
|
63
|
+
|
64
|
+
pkg/clogger-$(VERSION).gem: .manifest History clogger.gemspec
|
65
|
+
gem build clogger.gemspec
|
66
|
+
mkdir -p pkg
|
67
|
+
mv $(@F) $@
|
68
|
+
|
69
|
+
pkg/clogger-$(VERSION).tgz: HEAD = v$(VERSION)
|
70
|
+
pkg/clogger-$(VERSION).tgz: .manifest History
|
71
|
+
$(RM) -r $(basename $@)
|
72
|
+
git archive --format=tar --prefix=$(basename $@)/ $(HEAD) | tar xv
|
73
|
+
install -m644 $^ $(basename $@)
|
74
|
+
cd pkg && tar cv $(basename $(@F)) | gzip -9 > $(@F)+
|
75
|
+
mv $@+ $@
|
76
|
+
|
77
|
+
package: pkg/clogger-$(VERSION).gem pkg/clogger-$(VERSION).tgz
|
66
78
|
|
67
79
|
# not using Hoe's release system since we release 2 gems but only one tgz
|
68
|
-
release: package
|
80
|
+
release: package $(release_notes) $(release_changes)
|
69
81
|
rubyforge add_release -f -n $(release_notes) -a $(release_changes) \
|
70
82
|
clogger clogger $(VERSION) pkg/clogger-$(VERSION).gem
|
71
83
|
rubyforge add_file \
|
72
84
|
clogger clogger $(VERSION) pkg/clogger-$(VERSION).tgz
|
73
|
-
rubyforge add_release -f -n $(release_notes) -a $(release_changes) \
|
74
|
-
clogger clogger_ext $(VERSION) pkg/clogger_ext-$(VERSION).gem
|
75
|
-
rake post_news
|
76
85
|
endif
|
77
86
|
|
78
|
-
|
87
|
+
doc: .document History
|
88
|
+
rdoc -Na -t "$(shell sed -ne '1s/^= //p' README)"
|
89
|
+
install -m644 COPYING doc/COPYING
|
90
|
+
cd doc && ln README.html tmp.html && mv tmp.html index.html
|
91
|
+
|
92
|
+
# publishes docs to http://clogger.rubyforge.org/
|
93
|
+
# this preserves timestamps as best as possible to help HTTP caches out
|
94
|
+
# git set-file-times can is here: http://git-scm.org/gitwiki/ExampleScripts
|
95
|
+
publish_doc:
|
96
|
+
git set-file-times
|
97
|
+
$(RM) -r doc
|
98
|
+
$(MAKE) doc
|
99
|
+
rsync -av --delete doc/ rubyforge.org:/var/www/gforge-projects/clogger/
|
100
|
+
git ls-files | xargs touch
|
101
|
+
|
102
|
+
.PHONY: test doc .manifest release History
|
data/{README.txt → README}
RENAMED
@@ -1,8 +1,5 @@
|
|
1
1
|
= Clogger - configurable request logging for Rack
|
2
2
|
|
3
|
-
* http://clogger.rubyforge.org/
|
4
|
-
* mailto:clogger@librelist.com
|
5
|
-
|
6
3
|
== DESCRIPTION
|
7
4
|
|
8
5
|
Clogger is Rack middleware for logging HTTP requests. The log format
|
@@ -23,6 +20,10 @@ is customizable so you can specify exactly which fields to log.
|
|
23
20
|
" (double quote)
|
24
21
|
all bytes in the range of \x00-\x1F
|
25
22
|
|
23
|
+
* multi-instance capable and reentrant. You can use Clogger in a
|
24
|
+
multi-threaded server, and even multiple Cloggers logging to
|
25
|
+
different locations and different formats in the same process.
|
26
|
+
|
26
27
|
== SYNOPSIS
|
27
28
|
|
28
29
|
Clogger may be loaded as Rack middleware in your config.ru:
|
@@ -104,24 +105,28 @@ requests) go to the mailing list.
|
|
104
105
|
|
105
106
|
Do not send HTML mail or attachments. Do not top post.
|
106
107
|
|
108
|
+
Homepage: http://clogger.rubyforge.org/
|
109
|
+
|
107
110
|
== INSTALL
|
108
111
|
|
109
112
|
For all Rubygems users:
|
110
113
|
|
111
114
|
gem install clogger
|
112
115
|
|
113
|
-
If you're using MRI 1.8/1.9 and have a build environment, you can also try:
|
114
|
-
|
115
|
-
gem install clogger_ext
|
116
|
-
|
117
116
|
If you do not use Rubygems, you may also use setup.rb from tarballs from
|
118
117
|
the Rubyforge project page:
|
119
118
|
|
120
119
|
* http://rubyforge.org/frs/?group_id=8896
|
121
120
|
|
121
|
+
There is an optional C extension that should be compatible with MRI
|
122
|
+
1.8/1.9. The extensions should automatically be disabled for users of
|
123
|
+
other Ruby implementations, but be sure to let us know if that's not the
|
124
|
+
case. No pre-built currently distributed, let us know if you're
|
125
|
+
interested in helping with the release/support effort.
|
126
|
+
|
122
127
|
== LICENSE
|
123
128
|
|
124
|
-
Copyright (C) 2009 Eric Wong
|
129
|
+
Copyright (C) 2009 Eric Wong and contributors.
|
125
130
|
|
126
131
|
Clogger is free software; you can redistribute it and/or modify it under
|
127
132
|
the terms of the GNU Lesser General Public License as published by the
|
@@ -130,7 +135,7 @@ Free Software Foundation, version 3.0.
|
|
130
135
|
Clogger is distributed in the hope that it will be useful, but WITHOUT ANY
|
131
136
|
WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
132
137
|
FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
|
133
|
-
License for more details.
|
138
|
+
License in the COPYING file for more details.
|
134
139
|
|
135
140
|
You should have received a copy of the GNU Lesser General Public License
|
136
141
|
along with Clogger; if not, write to the Free Software Foundation, Inc.,
|
data/Rakefile
CHANGED
@@ -1,6 +1,3 @@
|
|
1
|
-
require 'hoe'
|
2
|
-
$LOAD_PATH << 'lib'
|
3
|
-
require 'clogger'
|
4
1
|
begin
|
5
2
|
require 'rake/extensiontask'
|
6
3
|
Rake::ExtensionTask.new('clogger_ext')
|
@@ -8,23 +5,33 @@ rescue LoadError
|
|
8
5
|
warn "rake-compiler not available, cross compiling disabled"
|
9
6
|
end
|
10
7
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
8
|
+
desc 'prints RDoc-formatted history'
|
9
|
+
task :history do
|
10
|
+
tags = `git tag -l`.split(/\n/).grep(/^v/).reverse
|
11
|
+
timefmt = '%Y-%m-%d %H:%M UTC'
|
12
|
+
tags.each do |tag|
|
13
|
+
header, subject, body = `git cat-file tag #{tag}`.split(/\n\n/)
|
14
|
+
tagger = header.split(/\n/).grep(/^tagger /).first.split(/\s/)
|
15
|
+
time = Time.at(tagger[-2].to_i).utc
|
16
|
+
puts "=== #{tag.sub(/^v/, '')} / #{time.strftime(timefmt)}"
|
17
|
+
puts ""
|
18
|
+
puts body
|
19
|
+
puts ""
|
20
|
+
end
|
21
21
|
end
|
22
22
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
23
|
+
desc "read news article from STDIN and post to rubyforge"
|
24
|
+
task :publish_news do
|
25
|
+
require 'rubyforge'
|
26
|
+
IO.select([STDIN], nil, nil, 1) or abort "E: news must be read from stdin"
|
27
|
+
msg = STDIN.readlines
|
28
|
+
subject = msg.shift
|
29
|
+
blank = msg.shift
|
30
|
+
blank == "\n" or abort "no newline after subject!"
|
31
|
+
subject.strip!
|
32
|
+
body = msg.join("").strip!
|
33
|
+
|
34
|
+
rf = RubyForge.new.configure
|
35
|
+
rf.login
|
36
|
+
rf.post_news('clogger', subject, body)
|
30
37
|
end
|
data/clogger.gemspec
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
ENV["VERSION"] or abort "VERSION= must be specified"
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = %q{clogger}
|
5
|
+
s.version = ENV["VERSION"]
|
6
|
+
|
7
|
+
if s.respond_to? :required_rubygems_version=
|
8
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0")
|
9
|
+
end
|
10
|
+
s.homepage = 'http://clogger.rubyforge.org/'
|
11
|
+
s.authors = ["Eric Wong"]
|
12
|
+
s.date = Time.now.utc.strftime('%Y-%m-%d')
|
13
|
+
s.description = %q{
|
14
|
+
Clogger is Rack middleware for logging HTTP requests. The log format
|
15
|
+
is customizable so you can specify exactly which fields to log.
|
16
|
+
}.strip
|
17
|
+
s.email = %q{clogger@librelist.com}
|
18
|
+
s.extra_rdoc_files = %w(README History)
|
19
|
+
s.files = File.readlines('.manifest').map! { |x| x.chomp! }
|
20
|
+
s.rdoc_options = [ "-Na",
|
21
|
+
"-t", "Clogger - configurable request logging for Rack"
|
22
|
+
]
|
23
|
+
s.require_paths = %w(lib ext)
|
24
|
+
s.rubyforge_project = %q{clogger}
|
25
|
+
s.summary = %q{configurable request logging for Rack}
|
26
|
+
s.test_files = %w(test/test_clogger.rb)
|
27
|
+
|
28
|
+
# HeaderHash wasn't case-insensitive in old versions
|
29
|
+
s.add_dependency(%q<rack>, ["> 0.9"])
|
30
|
+
s.extensions = %w(ext/clogger_ext/extconf.rb)
|
31
|
+
|
32
|
+
end
|
data/ext/clogger_ext/clogger.c
CHANGED
@@ -67,7 +67,9 @@ struct clogger {
|
|
67
67
|
|
68
68
|
VALUE env;
|
69
69
|
VALUE cookies;
|
70
|
-
VALUE
|
70
|
+
VALUE status;
|
71
|
+
VALUE headers;
|
72
|
+
VALUE body;
|
71
73
|
|
72
74
|
off_t body_bytes_sent;
|
73
75
|
struct timeval tv_start;
|
@@ -85,8 +87,11 @@ static ID close_id;
|
|
85
87
|
static ID to_i_id;
|
86
88
|
static ID to_s_id;
|
87
89
|
static ID size_id;
|
90
|
+
static ID sq_brace_id;
|
91
|
+
static ID new_id;
|
88
92
|
static VALUE cClogger;
|
89
93
|
static VALUE mFormat;
|
94
|
+
static VALUE cHeaderHash;
|
90
95
|
|
91
96
|
/* common hash lookup keys */
|
92
97
|
static VALUE g_HTTP_X_FORWARDED_FOR;
|
@@ -104,7 +109,6 @@ static VALUE g_empty;
|
|
104
109
|
static VALUE g_space;
|
105
110
|
static VALUE g_question_mark;
|
106
111
|
static VALUE g_rack_request_cookie_hash;
|
107
|
-
static VALUE g_bad_app_response;
|
108
112
|
|
109
113
|
#define LOG_BUF_INIT_SIZE 128
|
110
114
|
|
@@ -160,99 +164,6 @@ static VALUE byte_xs(VALUE from)
|
|
160
164
|
return rv;
|
161
165
|
}
|
162
166
|
|
163
|
-
/* strcasecmp isn't locale independent, so we roll our own */
|
164
|
-
static int str_case_eq(VALUE a, VALUE b)
|
165
|
-
{
|
166
|
-
long alen = RSTRING_LEN(a);
|
167
|
-
long blen = RSTRING_LEN(b);
|
168
|
-
|
169
|
-
if (alen == blen) {
|
170
|
-
const char *aptr = RSTRING_PTR(a);
|
171
|
-
const char *bptr = RSTRING_PTR(b);
|
172
|
-
|
173
|
-
for (; alen--; ++aptr, ++bptr) {
|
174
|
-
if ((*bptr == *aptr)
|
175
|
-
|| (*aptr >= 'A' && *aptr <= 'Z' &&
|
176
|
-
(*aptr | 0x20) == *bptr))
|
177
|
-
continue;
|
178
|
-
return 0;
|
179
|
-
}
|
180
|
-
return 1;
|
181
|
-
}
|
182
|
-
return 0;
|
183
|
-
}
|
184
|
-
|
185
|
-
struct response_ops { long nr; VALUE ops; };
|
186
|
-
|
187
|
-
/* this can be worse than O(M*N) :<... but C loops are fast ... */
|
188
|
-
static VALUE swap_sent_headers_unsafe(VALUE kv, VALUE memo)
|
189
|
-
{
|
190
|
-
struct response_ops *tmp = (struct response_ops *)memo;
|
191
|
-
VALUE key = rb_obj_as_string(RARRAY_PTR(kv)[0]);
|
192
|
-
long i = RARRAY_LEN(tmp->ops);
|
193
|
-
VALUE *ary = RARRAY_PTR(tmp->ops);
|
194
|
-
VALUE value;
|
195
|
-
|
196
|
-
for (; --i >= 0; ary++) {
|
197
|
-
VALUE *op = RARRAY_PTR(*ary);
|
198
|
-
enum clogger_opcode opcode = NUM2INT(op[0]);
|
199
|
-
|
200
|
-
if (opcode != CL_OP_RESPONSE)
|
201
|
-
continue;
|
202
|
-
assert(RARRAY_LEN(*ary) == 2);
|
203
|
-
if (!str_case_eq(key, op[1]))
|
204
|
-
continue;
|
205
|
-
|
206
|
-
value = RARRAY_PTR(kv)[1];
|
207
|
-
op[0] = INT2NUM(CL_OP_LITERAL);
|
208
|
-
op[1] = byte_xs(rb_obj_as_string(value));
|
209
|
-
|
210
|
-
if (!--tmp->nr)
|
211
|
-
rb_iter_break();
|
212
|
-
return Qnil;
|
213
|
-
}
|
214
|
-
return Qnil;
|
215
|
-
}
|
216
|
-
|
217
|
-
static VALUE swap_sent_headers(VALUE kv, VALUE memo)
|
218
|
-
{
|
219
|
-
if (TYPE(kv) != T_ARRAY)
|
220
|
-
rb_raise(rb_eTypeError, "headers not returning pairs");
|
221
|
-
if (RARRAY_LEN(kv) < 2)
|
222
|
-
rb_raise(rb_eTypeError, "headers not returning pairs");
|
223
|
-
return swap_sent_headers_unsafe(kv, memo);
|
224
|
-
}
|
225
|
-
|
226
|
-
static VALUE sent_headers_ops(struct clogger *c)
|
227
|
-
{
|
228
|
-
struct response_ops tmp;
|
229
|
-
long i, len;
|
230
|
-
VALUE *ary;
|
231
|
-
|
232
|
-
if (!c->need_resp)
|
233
|
-
return c->fmt_ops;
|
234
|
-
|
235
|
-
tmp.nr = 0;
|
236
|
-
tmp.ops = rb_ary_dup(c->fmt_ops);
|
237
|
-
len = RARRAY_LEN(tmp.ops);
|
238
|
-
ary = RARRAY_PTR(tmp.ops);
|
239
|
-
|
240
|
-
for (i = 0; i < len; ++i) {
|
241
|
-
VALUE *op = RARRAY_PTR(ary[i]);
|
242
|
-
|
243
|
-
if (NUM2INT(op[0]) == CL_OP_RESPONSE) {
|
244
|
-
assert(RARRAY_LEN(ary[i]) == 2);
|
245
|
-
ary[i] = rb_ary_dup(ary[i]);
|
246
|
-
++tmp.nr;
|
247
|
-
}
|
248
|
-
}
|
249
|
-
|
250
|
-
rb_iterate(rb_each, RARRAY_PTR(c->response)[1],
|
251
|
-
swap_sent_headers, (VALUE)&tmp);
|
252
|
-
|
253
|
-
return tmp.ops;
|
254
|
-
}
|
255
|
-
|
256
167
|
static void clogger_mark(void *ptr)
|
257
168
|
{
|
258
169
|
struct clogger *c = ptr;
|
@@ -263,7 +174,9 @@ static void clogger_mark(void *ptr)
|
|
263
174
|
rb_gc_mark(c->log_buf);
|
264
175
|
rb_gc_mark(c->env);
|
265
176
|
rb_gc_mark(c->cookies);
|
266
|
-
rb_gc_mark(c->
|
177
|
+
rb_gc_mark(c->status);
|
178
|
+
rb_gc_mark(c->headers);
|
179
|
+
rb_gc_mark(c->body);
|
267
180
|
}
|
268
181
|
|
269
182
|
static VALUE clogger_alloc(VALUE klass)
|
@@ -287,6 +200,11 @@ static VALUE obj_fileno(VALUE obj)
|
|
287
200
|
return rb_funcall(obj, rb_intern("fileno"), 0);
|
288
201
|
}
|
289
202
|
|
203
|
+
static VALUE obj_enable_sync(VALUE obj)
|
204
|
+
{
|
205
|
+
return rb_funcall(obj, rb_intern("sync="), 1, Qtrue);
|
206
|
+
}
|
207
|
+
|
290
208
|
/* only for writing to regular files, not stupid crap like NFS */
|
291
209
|
static void write_full(int fd, const void *buf, size_t count)
|
292
210
|
{
|
@@ -350,7 +268,7 @@ static void append_status(struct clogger *c)
|
|
350
268
|
{
|
351
269
|
char buf[sizeof("999")];
|
352
270
|
int nr;
|
353
|
-
VALUE status =
|
271
|
+
VALUE status = c->status;
|
354
272
|
|
355
273
|
if (TYPE(status) != T_FIXNUM) {
|
356
274
|
status = rb_funcall(status, to_i_id, 0);
|
@@ -545,6 +463,17 @@ static void append_request_env(struct clogger *c, VALUE key)
|
|
545
463
|
rb_str_buf_append(c->log_buf, tmp);
|
546
464
|
}
|
547
465
|
|
466
|
+
static void append_response(struct clogger *c, VALUE key)
|
467
|
+
{
|
468
|
+
VALUE v;
|
469
|
+
|
470
|
+
assert(rb_obj_class(c->headers) == cHeaderHash);
|
471
|
+
|
472
|
+
v = rb_funcall(c->headers, sq_brace_id, 1, key);
|
473
|
+
v = NIL_P(v) ? g_dash : byte_xs(rb_obj_as_string(v));
|
474
|
+
rb_str_buf_append(c->log_buf, v);
|
475
|
+
}
|
476
|
+
|
548
477
|
static void special_var(struct clogger *c, enum clogger_special var)
|
549
478
|
{
|
550
479
|
switch (var) {
|
@@ -579,7 +508,7 @@ static void special_var(struct clogger *c, enum clogger_special var)
|
|
579
508
|
|
580
509
|
static VALUE cwrite(struct clogger *c)
|
581
510
|
{
|
582
|
-
const VALUE ops =
|
511
|
+
const VALUE ops = c->fmt_ops;
|
583
512
|
const VALUE *ary = RARRAY_PTR(ops);
|
584
513
|
long i = RARRAY_LEN(ops);
|
585
514
|
VALUE dst = c->log_buf;
|
@@ -598,8 +527,7 @@ static VALUE cwrite(struct clogger *c)
|
|
598
527
|
append_request_env(c, op[1]);
|
599
528
|
break;
|
600
529
|
case CL_OP_RESPONSE:
|
601
|
-
|
602
|
-
rb_str_buf_append(dst, g_dash);
|
530
|
+
append_response(c, op[1]);
|
603
531
|
break;
|
604
532
|
case CL_OP_SPECIAL:
|
605
533
|
special_var(c, NUM2INT(op[1]));
|
@@ -658,8 +586,10 @@ static VALUE clogger_init(int argc, VALUE *argv, VALUE self)
|
|
658
586
|
VALUE tmp;
|
659
587
|
|
660
588
|
c->logger = rb_hash_aref(o, ID2SYM(rb_intern("logger")));
|
661
|
-
if (!NIL_P(c->logger))
|
589
|
+
if (!NIL_P(c->logger)) {
|
590
|
+
rb_rescue(obj_enable_sync, c->logger, 0, 0);
|
662
591
|
c->fd = raw_fd(rb_rescue(obj_fileno, c->logger, 0, 0));
|
592
|
+
}
|
663
593
|
|
664
594
|
tmp = rb_hash_aref(o, ID2SYM(rb_intern("format")));
|
665
595
|
if (!NIL_P(tmp))
|
@@ -667,7 +597,7 @@ static VALUE clogger_init(int argc, VALUE *argv, VALUE self)
|
|
667
597
|
}
|
668
598
|
|
669
599
|
init_buffers(c);
|
670
|
-
c->fmt_ops = rb_funcall(self, rb_intern("compile_format"),
|
600
|
+
c->fmt_ops = rb_funcall(self, rb_intern("compile_format"), 2, fmt, o);
|
671
601
|
|
672
602
|
if (Qtrue == rb_funcall(self, rb_intern("need_response_headers?"),
|
673
603
|
1, c->fmt_ops))
|
@@ -691,12 +621,10 @@ static VALUE body_iter_i(VALUE str, VALUE memop)
|
|
691
621
|
|
692
622
|
static VALUE wrap_each(struct clogger *c)
|
693
623
|
{
|
694
|
-
VALUE body = RARRAY_PTR(c->response)[2];
|
695
|
-
|
696
624
|
c->body_bytes_sent = 0;
|
697
|
-
rb_iterate(rb_each, body, body_iter_i, (VALUE)&c->body_bytes_sent);
|
625
|
+
rb_iterate(rb_each, c->body, body_iter_i, (VALUE)&c->body_bytes_sent);
|
698
626
|
|
699
|
-
return body;
|
627
|
+
return c->body;
|
700
628
|
}
|
701
629
|
|
702
630
|
/**
|
@@ -728,7 +656,7 @@ static VALUE clogger_close(VALUE self)
|
|
728
656
|
{
|
729
657
|
struct clogger *c = clogger_get(self);
|
730
658
|
|
731
|
-
return rb_funcall(
|
659
|
+
return rb_funcall(c->body, close_id, 0);
|
732
660
|
}
|
733
661
|
|
734
662
|
/* :nodoc: */
|
@@ -739,7 +667,7 @@ static VALUE clogger_fileno(VALUE self)
|
|
739
667
|
return c->fd < 0 ? Qnil : INT2NUM(c->fd);
|
740
668
|
}
|
741
669
|
|
742
|
-
static
|
670
|
+
static VALUE ccall(struct clogger *c, VALUE env)
|
743
671
|
{
|
744
672
|
VALUE rv;
|
745
673
|
|
@@ -748,14 +676,28 @@ static void ccall(struct clogger *c, VALUE env)
|
|
748
676
|
c->cookies = Qfalse;
|
749
677
|
rv = rb_funcall(c->app, call_id, 1, env);
|
750
678
|
if (TYPE(rv) == T_ARRAY && RARRAY_LEN(rv) == 3) {
|
751
|
-
|
679
|
+
VALUE *tmp = RARRAY_PTR(rv);
|
680
|
+
|
681
|
+
c->status = tmp[0];
|
682
|
+
c->headers = tmp[1];
|
683
|
+
c->body = tmp[2];
|
684
|
+
|
685
|
+
if (c->need_resp && cHeaderHash != rb_obj_class(c->headers)) {
|
686
|
+
c->headers = rb_funcall(cHeaderHash, new_id, 1, tmp[1]);
|
687
|
+
if (OBJ_FROZEN(rv))
|
688
|
+
rv = rb_ary_dup(rv);
|
689
|
+
rb_ary_store(rv, 1, c->headers);
|
690
|
+
}
|
752
691
|
} else {
|
753
|
-
c->
|
692
|
+
c->status = INT2NUM(500);
|
693
|
+
c->headers = c->body = rb_ary_new();
|
754
694
|
cwrite(c);
|
755
695
|
rb_raise(rb_eTypeError,
|
756
696
|
"app response not a 3 element Array: %s",
|
757
697
|
RSTRING_PTR(rb_inspect(rv)));
|
758
698
|
}
|
699
|
+
|
700
|
+
return rv;
|
759
701
|
}
|
760
702
|
|
761
703
|
/*
|
@@ -768,12 +710,11 @@ static void ccall(struct clogger *c, VALUE env)
|
|
768
710
|
static VALUE clogger_call(VALUE self, VALUE env)
|
769
711
|
{
|
770
712
|
struct clogger *c = clogger_get(self);
|
713
|
+
VALUE rv;
|
771
714
|
|
772
715
|
if (c->wrap_body) {
|
773
|
-
VALUE tmp;
|
774
|
-
|
775
716
|
if (c->reentrant < 0) {
|
776
|
-
tmp = rb_hash_aref(env, g_rack_multithread);
|
717
|
+
VALUE tmp = rb_hash_aref(env, g_rack_multithread);
|
777
718
|
c->reentrant = Qfalse == tmp ? 0 : 1;
|
778
719
|
}
|
779
720
|
if (c->reentrant) {
|
@@ -781,16 +722,18 @@ static VALUE clogger_call(VALUE self, VALUE env)
|
|
781
722
|
c = clogger_get(self);
|
782
723
|
}
|
783
724
|
|
784
|
-
ccall(c, env);
|
785
|
-
|
786
|
-
|
787
|
-
|
725
|
+
rv = ccall(c, env);
|
726
|
+
if (OBJ_FROZEN(rv))
|
727
|
+
rv = rb_ary_dup(rv);
|
728
|
+
rb_ary_store(rv, 2, self);
|
729
|
+
|
730
|
+
return rv;
|
788
731
|
}
|
789
732
|
|
790
|
-
ccall(c, env);
|
733
|
+
rv = ccall(c, env);
|
791
734
|
cwrite(c);
|
792
735
|
|
793
|
-
return
|
736
|
+
return rv;
|
794
737
|
}
|
795
738
|
|
796
739
|
/* :nodoc */
|
@@ -812,14 +755,17 @@ static VALUE clogger_init_copy(VALUE clone, VALUE orig)
|
|
812
755
|
|
813
756
|
#define CONST_GLOBAL_STR(val) CONST_GLOBAL_STR2(val, #val)
|
814
757
|
|
815
|
-
static void
|
758
|
+
static void init_rack_utils_header_hash(void)
|
816
759
|
{
|
817
|
-
|
818
|
-
|
819
|
-
|
820
|
-
|
821
|
-
|
822
|
-
|
760
|
+
VALUE mRack, mUtils;
|
761
|
+
#if 0
|
762
|
+
extra double quotes below are to disable rdoc (and so is avoiding comments)
|
763
|
+
let me know if there is a better way...
|
764
|
+
#endif
|
765
|
+
rb_require("rack");
|
766
|
+
mRack = rb_define_module("Rack""");
|
767
|
+
mUtils = rb_define_module_under(mRack, "Utils""");
|
768
|
+
cHeaderHash = rb_define_class_under(mUtils, "HeaderHash""", rb_cHash);
|
823
769
|
}
|
824
770
|
|
825
771
|
void Init_clogger_ext(void)
|
@@ -831,6 +777,8 @@ void Init_clogger_ext(void)
|
|
831
777
|
to_i_id = rb_intern("to_i");
|
832
778
|
to_s_id = rb_intern("to_s");
|
833
779
|
size_id = rb_intern("size");
|
780
|
+
sq_brace_id = rb_intern("[]");
|
781
|
+
new_id = rb_intern("new");
|
834
782
|
cClogger = rb_define_class("Clogger", rb_cObject);
|
835
783
|
mFormat = rb_define_module_under(cClogger, "Format");
|
836
784
|
rb_define_alloc_func(cClogger, clogger_alloc);
|
@@ -857,5 +805,5 @@ void Init_clogger_ext(void)
|
|
857
805
|
CONST_GLOBAL_STR2(space, " ");
|
858
806
|
CONST_GLOBAL_STR2(question_mark, "?");
|
859
807
|
CONST_GLOBAL_STR2(rack_request_cookie_hash, "rack.request.cookie_hash");
|
860
|
-
|
808
|
+
init_rack_utils_header_hash();
|
861
809
|
}
|
data/ext/clogger_ext/extconf.rb
CHANGED
@@ -1,12 +1,34 @@
|
|
1
|
-
|
1
|
+
begin
|
2
|
+
require 'mkmf'
|
2
3
|
|
3
|
-
if
|
4
|
-
|
5
|
-
|
6
|
-
end
|
4
|
+
# XXX let me know if this works for you...
|
5
|
+
if ! defined?(RUBY_VERSION) || RUBY_VERSION !~ /\A1\.[89]\./
|
6
|
+
raise "Invalid RUBY_VERSION for C extension"
|
7
|
+
end
|
8
|
+
|
9
|
+
have_header('ruby.h') or raise "ruby.h header not found!"
|
10
|
+
|
11
|
+
if have_header('fcntl.h')
|
12
|
+
have_macro('F_GETFL', %w(fcntl.h))
|
13
|
+
have_macro('O_NONBLOCK', %w(unistd.h fcntl.h))
|
14
|
+
end
|
7
15
|
|
8
|
-
have_func('localtime_r', 'time.h') or
|
9
|
-
have_func('gmtime_r', 'time.h') or
|
10
|
-
have_func('rb_str_set_len', 'ruby.h')
|
11
|
-
dir_config('clogger_ext')
|
12
|
-
create_makefile('clogger_ext')
|
16
|
+
have_func('localtime_r', 'time.h') or raise "localtime_r needed"
|
17
|
+
have_func('gmtime_r', 'time.h') or raise "gmtime_r needed"
|
18
|
+
have_func('rb_str_set_len', 'ruby.h')
|
19
|
+
dir_config('clogger_ext')
|
20
|
+
create_makefile('clogger_ext')
|
21
|
+
rescue Object => err
|
22
|
+
warn "E: #{err.inspect}"
|
23
|
+
warn "Skipping C extension, pure Ruby version will be used instead"
|
24
|
+
|
25
|
+
# generate a dummy Makefile to fool rubygems installer
|
26
|
+
targets = %w(all static clean distclean realclean
|
27
|
+
install install-so install-rb install-rb-default
|
28
|
+
pre-install-rb pre-install-rb-default
|
29
|
+
site-install site-install-so site-install-rb)
|
30
|
+
File.open(File.dirname(__FILE__) << "/Makefile", "wb") do |fp|
|
31
|
+
fp.puts targets.join(' ') << ":"
|
32
|
+
fp.puts "\techo >&2 extension disabled"
|
33
|
+
end
|
34
|
+
end
|
data/lib/clogger.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# -*- encoding: binary -*-
|
2
2
|
class Clogger
|
3
|
-
VERSION = '0.0.
|
3
|
+
VERSION = '0.0.6'
|
4
4
|
|
5
5
|
OP_LITERAL = 0
|
6
6
|
OP_REQUEST = 1
|
@@ -40,7 +40,9 @@ private
|
|
40
40
|
%w(request_method content_length content_type
|
41
41
|
remote_addr remote_ident remote_user
|
42
42
|
path_info query_string script_name
|
43
|
-
server_name server_port
|
43
|
+
server_name server_port
|
44
|
+
auth_type gateway_interface server_software path_translated
|
45
|
+
).join('|') << ')\z').freeze
|
44
46
|
|
45
47
|
SCAN = /([^$]*)(\$+(?:env\{\w+(?:\.[\w\.]+)?\}|
|
46
48
|
e\{[^\}]+\}|
|
@@ -48,8 +50,9 @@ private
|
|
48
50
|
time_(?:utc|local)\{[^\}]+\}|
|
49
51
|
\w*))?([^$]*)/x
|
50
52
|
|
51
|
-
def compile_format(str)
|
53
|
+
def compile_format(str, opt = {})
|
52
54
|
rv = []
|
55
|
+
opt ||= {}
|
53
56
|
str.scan(SCAN).each do |pre,tok,post|
|
54
57
|
rv << [ OP_LITERAL, pre ] if pre && pre != ""
|
55
58
|
|
@@ -97,8 +100,9 @@ private
|
|
97
100
|
# auto-append a newline
|
98
101
|
last = rv.last or return rv
|
99
102
|
op = last.first
|
100
|
-
|
101
|
-
|
103
|
+
ors = opt[:ORS] || "\n"
|
104
|
+
if (op == OP_LITERAL && /#{ors}\z/ !~ last.last) || op != OP_LITERAL
|
105
|
+
rv << [ OP_LITERAL, ors ] if ors.size > 0
|
102
106
|
end
|
103
107
|
|
104
108
|
rv
|
@@ -131,6 +135,7 @@ end
|
|
131
135
|
require 'clogger/format'
|
132
136
|
|
133
137
|
begin
|
138
|
+
raise LoadError if ENV['CLOGGER_PURE']
|
134
139
|
require 'clogger_ext'
|
135
140
|
rescue LoadError
|
136
141
|
require 'clogger/pure'
|
data/lib/clogger/pure.rb
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
# -*- encoding: binary -*-
|
2
2
|
# :stopdoc:
|
3
|
-
|
3
|
+
|
4
|
+
require 'rack'
|
5
|
+
|
4
6
|
# Not at all optimized for performance, this was written based on
|
5
7
|
# the original C extension code so it's not very Ruby-ish...
|
6
8
|
class Clogger
|
@@ -8,9 +10,11 @@ class Clogger
|
|
8
10
|
def initialize(app, opts = {})
|
9
11
|
@app = app
|
10
12
|
@logger = opts[:logger]
|
11
|
-
@
|
13
|
+
(@logger.sync = true) rescue nil
|
14
|
+
@fmt_ops = compile_format(opts[:format] || Format::Common, opts)
|
12
15
|
@wrap_body = need_wrap_body?(@fmt_ops)
|
13
16
|
@reentrant = nil
|
17
|
+
@need_resp = need_response_headers?(@fmt_ops)
|
14
18
|
@body_bytes_sent = 0
|
15
19
|
end
|
16
20
|
|
@@ -22,6 +26,7 @@ class Clogger
|
|
22
26
|
raise TypeError, "app response not a 3 element Array: #{resp.inspect}"
|
23
27
|
end
|
24
28
|
status, headers, body = resp
|
29
|
+
headers = Rack::Utils::HeaderHash.new(headers) if @need_resp
|
25
30
|
if wrap_body?
|
26
31
|
@reentrant = env['rack.multithread']
|
27
32
|
@env, @status, @headers, @body = env, status, headers, body
|
@@ -113,7 +118,7 @@ private
|
|
113
118
|
case op[0]
|
114
119
|
when OP_LITERAL; op[1]
|
115
120
|
when OP_REQUEST; byte_xs(env[op[1]] || "-")
|
116
|
-
when OP_RESPONSE; byte_xs(
|
121
|
+
when OP_RESPONSE; byte_xs(headers[op[1]] || "-")
|
117
122
|
when OP_SPECIAL; special_var(op[1], env, status, headers)
|
118
123
|
when OP_EVAL; eval(op[1]).to_s rescue "-"
|
119
124
|
when OP_TIME_LOCAL; Time.now.strftime(op[1])
|
@@ -132,14 +137,4 @@ private
|
|
132
137
|
}.join('')
|
133
138
|
end
|
134
139
|
|
135
|
-
def get_sent_header(headers, match)
|
136
|
-
headers.each do |pair|
|
137
|
-
Array === pair && pair.size >= 2 or
|
138
|
-
raise TypeError, "headers not returning pairs"
|
139
|
-
key, value = pair
|
140
|
-
match == key.downcase and return value
|
141
|
-
end
|
142
|
-
"-"
|
143
|
-
end
|
144
|
-
|
145
140
|
end
|
data/test/test_clogger.rb
CHANGED
@@ -389,7 +389,7 @@ class TestClogger < Test::Unit::TestCase
|
|
389
389
|
str = StringIO.new
|
390
390
|
app = lambda { |env| [302, [ %w(a) ], []] }
|
391
391
|
cl = Clogger.new(app, :logger => str, :format => '$sent_http_set_cookie')
|
392
|
-
|
392
|
+
assert_nothing_raised { cl.call(@req) }
|
393
393
|
end
|
394
394
|
|
395
395
|
def test_http_09_request
|
@@ -434,4 +434,39 @@ class TestClogger < Test::Unit::TestCase
|
|
434
434
|
assert_equal "text/plain\n", str.string
|
435
435
|
end
|
436
436
|
|
437
|
+
def test_clogger_synced
|
438
|
+
io = StringIO.new
|
439
|
+
logger = Struct.new(:sync, :io).new(false, io)
|
440
|
+
assert ! logger.sync
|
441
|
+
def logger.<<(str)
|
442
|
+
io << str
|
443
|
+
end
|
444
|
+
app = lambda { |env| [302, [ %w(a) ], []] }
|
445
|
+
cl = Clogger.new(app, :logger => logger)
|
446
|
+
assert logger.sync
|
447
|
+
end
|
448
|
+
|
449
|
+
def test_clogger_unsyncable
|
450
|
+
logger = ''
|
451
|
+
assert ! logger.respond_to?('sync=')
|
452
|
+
app = lambda { |env| [302, [ %w(a) ], []] }
|
453
|
+
assert_nothing_raised { Clogger.new(app, :logger => logger) }
|
454
|
+
end
|
455
|
+
|
456
|
+
def test_clogger_no_ORS
|
457
|
+
s = ''
|
458
|
+
app = lambda { |env| [302, [ %w(a) ], []] }
|
459
|
+
cl = Clogger.new(app, :logger => s, :format => "$request", :ORS => "")
|
460
|
+
cl.call(@req)
|
461
|
+
assert_equal "GET /hello?goodbye=true HTTP/1.0", s
|
462
|
+
end
|
463
|
+
|
464
|
+
def test_clogger_weird_ORS
|
465
|
+
s = ''
|
466
|
+
app = lambda { |env| [302, [ %w(a) ], []] }
|
467
|
+
cl = Clogger.new(app, :logger => s, :format => "<$request", :ORS => ">")
|
468
|
+
cl.call(@req)
|
469
|
+
assert_equal "<GET /hello?goodbye=true HTTP/1.0>", s
|
470
|
+
end
|
471
|
+
|
437
472
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: clogger
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.6
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Eric Wong
|
@@ -9,18 +9,18 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-09-
|
12
|
+
date: 2009-09-08 00:00:00 -07:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
|
-
name:
|
17
|
-
type: :
|
16
|
+
name: rack
|
17
|
+
type: :runtime
|
18
18
|
version_requirement:
|
19
19
|
version_requirements: !ruby/object:Gem::Requirement
|
20
20
|
requirements:
|
21
|
-
- - "
|
21
|
+
- - ">"
|
22
22
|
- !ruby/object:Gem::Version
|
23
|
-
version:
|
23
|
+
version: "0.9"
|
24
24
|
version:
|
25
25
|
description: |-
|
26
26
|
Clogger is Rack middleware for logging HTTP requests. The log format
|
@@ -28,20 +28,21 @@ description: |-
|
|
28
28
|
email: clogger@librelist.com
|
29
29
|
executables: []
|
30
30
|
|
31
|
-
extensions:
|
32
|
-
|
31
|
+
extensions:
|
32
|
+
- ext/clogger_ext/extconf.rb
|
33
33
|
extra_rdoc_files:
|
34
|
-
-
|
35
|
-
-
|
36
|
-
- README.txt
|
34
|
+
- README
|
35
|
+
- History
|
37
36
|
files:
|
37
|
+
- .document
|
38
38
|
- .gitignore
|
39
|
+
- .manifest
|
39
40
|
- COPYING
|
40
41
|
- GNUmakefile
|
41
|
-
- History
|
42
|
-
-
|
43
|
-
- README.txt
|
42
|
+
- History
|
43
|
+
- README
|
44
44
|
- Rakefile
|
45
|
+
- clogger.gemspec
|
45
46
|
- ext/clogger_ext/clogger.c
|
46
47
|
- ext/clogger_ext/extconf.rb
|
47
48
|
- ext/clogger_ext/ruby_1_9_compat.h
|
@@ -56,7 +57,8 @@ licenses: []
|
|
56
57
|
|
57
58
|
post_install_message:
|
58
59
|
rdoc_options:
|
59
|
-
-
|
60
|
+
- -Na
|
61
|
+
- -t
|
60
62
|
- Clogger - configurable request logging for Rack
|
61
63
|
require_paths:
|
62
64
|
- lib
|
data/History.txt
DELETED
@@ -1,45 +0,0 @@
|
|
1
|
-
=== 0.0.5 / 2009-09-02
|
2
|
-
|
3
|
-
The following variables are now exposed: $request_method,
|
4
|
-
$content_length and $content_type. Additionally, attempts
|
5
|
-
to use $http_content_length or $http_content_type will be
|
6
|
-
remapped to use the non-"$http_"-prefixed variable instead
|
7
|
-
since the "$http_"-variants of those variables is not allowed
|
8
|
-
by Rack.
|
9
|
-
|
10
|
-
=== 0.0.4 / 2009-09-02
|
11
|
-
|
12
|
-
The pure Ruby version now escapes with uppercase A-F
|
13
|
-
characters to match nginx log output. There are now extra
|
14
|
-
checks against badly behaving Rack applications and 500
|
15
|
-
errors will be logged before TypeError is raised if the
|
16
|
-
application response does not conform (minimally) to Rack
|
17
|
-
expectations. Finally, handling of $request (requests
|
18
|
-
without "HTTP_VERSION" set in the Rack env) should now be
|
19
|
-
logged correctly without unnecessary trailing characters.
|
20
|
-
|
21
|
-
Hackers: the primary git repository has been moved to
|
22
|
-
git://git.bogomips.org/clogger.git for now since I'm having
|
23
|
-
issues with pushing to Rubyforge (seems related to Support
|
24
|
-
Requests item #26185).
|
25
|
-
|
26
|
-
=== 0.0.3 / 2009-08-29
|
27
|
-
|
28
|
-
The MRI extension clogger_ext gets GC bug fix and
|
29
|
-
cleanups/robustness improvements. It should now be more
|
30
|
-
tolerant of misbehaving applications in that it'll be less
|
31
|
-
likely to segfault. No changes to the (recommended) pure
|
32
|
-
implementation.
|
33
|
-
|
34
|
-
=== 0.0.2 / 2009-08-29
|
35
|
-
|
36
|
-
* 2 bug fixes
|
37
|
-
|
38
|
-
* support "$request_uri" as a log variable
|
39
|
-
* Log bad/invalid app responses as 500 errors
|
40
|
-
|
41
|
-
=== 0.0.1 / 2009-08-28
|
42
|
-
|
43
|
-
* 1 major enhancement
|
44
|
-
|
45
|
-
* initial release
|
data/Manifest.txt
DELETED
@@ -1,15 +0,0 @@
|
|
1
|
-
.gitignore
|
2
|
-
COPYING
|
3
|
-
GNUmakefile
|
4
|
-
History.txt
|
5
|
-
Manifest.txt
|
6
|
-
README.txt
|
7
|
-
Rakefile
|
8
|
-
ext/clogger_ext/clogger.c
|
9
|
-
ext/clogger_ext/extconf.rb
|
10
|
-
ext/clogger_ext/ruby_1_9_compat.h
|
11
|
-
lib/clogger.rb
|
12
|
-
lib/clogger/format.rb
|
13
|
-
lib/clogger/pure.rb
|
14
|
-
setup.rb
|
15
|
-
test/test_clogger.rb
|