clogger 0.0.5 → 0.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|