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 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