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 ADDED
@@ -0,0 +1,4 @@
1
+ README
2
+ History
3
+ lib
4
+ ext/clogger_ext/clogger.c
data/.gitignore CHANGED
@@ -11,3 +11,5 @@ Makefile
11
11
  /doc
12
12
  /local.mk
13
13
  /pkg
14
+ /.manifest
15
+ /History
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
- Manifest.txt:
33
- git ls-files > $@+
34
- cmp $@+ $@ || mv $@+ $@
35
- $(RM) $@+
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
- $(RM) -r pkg
64
- unset CLOGGER_EXT; rake package VERSION=$(VERSION)
65
- CLOGGER_EXT=1 rake package VERSION=$(VERSION)
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 Manifest.txt $(release_notes) $(release_changes)
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
- .PHONY: test doc Manifest.txt release
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
@@ -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 <normalperson@yhbt.net> and contributors.
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
- common = lambda do |hoe|
12
- title = hoe.paragraphs_of("README.txt", 0).first.sub(/^= /, '')
13
- hoe.version = Clogger::VERSION
14
- hoe.summary = title.split(/\s*-\s*/, 2).last
15
- hoe.description = hoe.paragraphs_of("README.txt", 3)
16
- hoe.rubyforge_name = 'clogger'
17
- hoe.author = 'Eric Wong'
18
- hoe.email = 'clogger@librelist.com'
19
- hoe.spec_extras.merge!('rdoc_options' => [ "--title", title ])
20
- hoe.remote_rdoc_dir = ''
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
- if ENV['CLOGGER_EXT']
24
- Hoe.spec('clogger_ext') do
25
- common.call(self)
26
- self.spec_extras.merge!(:extensions => Dir.glob('ext/*/extconf.rb'))
27
- end
28
- else
29
- Hoe.spec('clogger') { common.call(self) }
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
@@ -67,7 +67,9 @@ struct clogger {
67
67
 
68
68
  VALUE env;
69
69
  VALUE cookies;
70
- VALUE response;
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->response);
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 = RARRAY_PTR(c->response)[0];
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 = sent_headers_ops(c);
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
- /* headers we found already got swapped for literals */
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"), 1, fmt);
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(RARRAY_PTR(c->response)[2], close_id, 0);
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 void ccall(struct clogger *c, VALUE env)
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
- c->response = rv;
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->response = g_bad_app_response;
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
- tmp = rb_ary_dup(c->response);
786
- rb_ary_store(tmp, 2, self);
787
- return tmp;
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 c->response;
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 init_bad_response(void)
758
+ static void init_rack_utils_header_hash(void)
816
759
  {
817
- g_bad_app_response = rb_ary_new();
818
- rb_ary_store(g_bad_app_response, 0, INT2NUM(500));
819
- rb_ary_store(g_bad_app_response, 1, rb_obj_freeze(rb_hash_new()));
820
- rb_ary_store(g_bad_app_response, 2, rb_obj_freeze(rb_ary_new()));
821
- rb_obj_freeze(g_bad_app_response);
822
- rb_global_variable(&g_bad_app_response);
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
- init_bad_response();
808
+ init_rack_utils_header_hash();
861
809
  }
@@ -1,12 +1,34 @@
1
- require 'mkmf'
1
+ begin
2
+ require 'mkmf'
2
3
 
3
- if have_header('fcntl.h')
4
- have_macro('F_GETFL', %w(fcntl.h))
5
- have_macro('O_NONBLOCK', %w(unistd.h fcntl.h))
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 abort "localtime_r needed"
9
- have_func('gmtime_r', 'time.h') or abort "gmtime_r needed"
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.5'
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).join('|') << ')\z').freeze
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
- if (op == OP_LITERAL && /\n\z/ !~ last.last) || op != OP_LITERAL
101
- rv << [ OP_LITERAL, "\n" ]
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
- @fmt_ops = compile_format(opts[:format] || Format::Common)
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(get_sent_header(headers, op[1]))
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
- assert_raise(TypeError) { cl.call(@req) }
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.5
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-02 00:00:00 -07:00
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: hoe
17
- type: :development
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: 2.3.3
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
- - History.txt
35
- - Manifest.txt
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.txt
42
- - Manifest.txt
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
- - --title
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