clogger 0.4.0 → 0.5.0

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.
@@ -1,7 +1,7 @@
1
1
  #!/bin/sh
2
2
 
3
3
  GVF=GIT-VERSION-FILE
4
- DEF_VER=v0.4.0.GIT
4
+ DEF_VER=v0.5.0.GIT
5
5
 
6
6
  LF='
7
7
  '
@@ -22,11 +22,24 @@ clean:
22
22
  -$(MAKE) -C ext/clogger_ext clean
23
23
  $(RM) ext/clogger_ext/Makefile lib/clogger_ext.$(DLEXT)
24
24
 
25
- test-ext: ext/clogger_ext/clogger.$(DLEXT)
26
- $(RUBY) -Iext/clogger_ext:lib test/test_clogger.rb
25
+ test_unit := $(wildcard test/test_*.rb)
26
+ test-unit: $(test_unit)
27
+
28
+ ifeq ($(CLOGGER_PURE),)
29
+ $(test_unit): export RUBYLIB := ext/clogger_ext:lib
30
+ $(test_unit): ext/clogger_ext/clogger.$(DLEXT)
31
+ else
32
+ $(test_unit): export RUBYLIB := lib
33
+ endif
34
+
35
+ $(test_unit):
36
+ $(RUBY) $@
37
+
38
+ test-ext:
39
+ CLOGGER_PURE=0 $(MAKE) test-unit
27
40
 
28
41
  test-pure:
29
- CLOGGER_PURE=t $(RUBY) -Ilib test/test_clogger.rb
42
+ CLOGGER_PURE=1 $(MAKE) test-unit
30
43
 
31
44
  test: test-ext test-pure
32
45
 
@@ -46,7 +59,7 @@ NEWS: GIT-VERSION-FILE .manifest
46
59
  $(RAKE) -s news_rdoc > $@+
47
60
  mv $@+ $@
48
61
 
49
- SINCE = 0.3.2
62
+ SINCE = 0.4.0
50
63
  ChangeLog: log_range = $(shell test -n "$(SINCE)" && echo v$(SINCE)..)
51
64
  ChangeLog: GIT-VERSION-FILE
52
65
  @echo "ChangeLog from $(GIT_URL) ($(SINCE)..$(GIT_VERSION))" > $@+
@@ -60,7 +73,7 @@ atom = <link rel="alternate" title="Atom feed" href="$(1)" \
60
73
  type="application/atom+xml"/>
61
74
 
62
75
  doc: .document NEWS ChangeLog
63
- rdoc -Na -t "$(shell sed -ne '1s/^= //p' README)"
76
+ rdoc -a -t "$(shell sed -ne '1s/^= //p' README)"
64
77
  install -m644 COPYING doc/COPYING
65
78
  install -m644 $(shell grep '^[A-Z]' .document) doc/
66
79
  $(RUBY) -i -p -e \
@@ -145,3 +158,4 @@ gem install-gem: GIT-VERSION-FILE
145
158
  endif
146
159
 
147
160
  .PHONY: .FORCE-GIT-VERSION-FILE test doc manifest
161
+ .PHONY: test test-ext test-pure $(test_unit)
data/LICENSE ADDED
@@ -0,0 +1,16 @@
1
+ \Clogger is copyrighted Free Software by all contributors, see logs in
2
+ revision control for names and email addresses of all of them.
3
+
4
+ You can redistribute it and/or modify it under the terms of the GNU Lesser
5
+ General Public License as published by the Free Software Foundation,
6
+ version 2.1 or later {LGPLv2.1}[http://www.gnu.org/licenses/lgpl-2.1.txt]
7
+ (see link:COPYING).
8
+
9
+ \Clogger is distributed in the hope that it will be useful, but WITHOUT
10
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
12
+ License for more details.
13
+
14
+ You should have received a copy of the GNU Lesser General Public License
15
+ along with this library; if not, write to the Free Software
16
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
data/README CHANGED
@@ -1,8 +1,8 @@
1
- = Clogger - configurable request logging for Rack
1
+ = \Clogger - configurable request logging for Rack
2
2
 
3
3
  == DESCRIPTION
4
4
 
5
- Clogger is Rack middleware for logging HTTP requests. The log format
5
+ \Clogger is Rack middleware for logging HTTP requests. The log format
6
6
  is customizable so you can specify exactly which fields to log.
7
7
 
8
8
  == FEATURES
@@ -21,13 +21,18 @@ is customizable so you can specify exactly which fields to log.
21
21
  all bytes in the range of \x00-\x1F
22
22
 
23
23
  * multi-instance capable and (optionally) reentrant. You can use
24
- Clogger in a multi-threaded server, and even multiple Cloggers logging
24
+ \Clogger in a multi-threaded server, and even multiple Cloggers logging
25
25
  to different locations and different formats in the same process.
26
26
 
27
+ * Pure Ruby version for non-MRI versions of Ruby (or via CLOGGER_PURE=1
28
+ in the environment). The optional C extension is loaded by default
29
+ and supported under MRI 1.8.7, 1.9.1, and 1.9.2.
30
+
27
31
  == SYNOPSIS
28
32
 
29
- Clogger may be loaded as Rack middleware in your config.ru:
33
+ \Clogger may be loaded as Rack middleware in your config.ru:
30
34
 
35
+ # ENV['CLOGGER_PURE'] = '1' # uncomment to disable C extension
31
36
  require "clogger"
32
37
  use Clogger,
33
38
  :format => Clogger::Format::Combined,
@@ -81,7 +86,7 @@ somewhere inside the "Rails::Initializer.run do |config|" block:
81
86
 
82
87
  == REQUIREMENTS
83
88
 
84
- * Ruby, Rack
89
+ * {Ruby}[http://ruby-lang.org/], {Rack}[http://rack.rubyforge.org/]
85
90
 
86
91
  == DEVELOPMENT
87
92
 
@@ -126,20 +131,3 @@ There is an optional C extension that should be compatible with MRI
126
131
  other Ruby implementations, but be sure to let us know if that's not the
127
132
  case. No pre-built binaries are currently distributed, let us know if
128
133
  you're interested in helping with the release/support effort.
129
-
130
- == LICENSE
131
-
132
- Copyright (C) 2009 Eric Wong and contributors.
133
-
134
- Clogger is free software; you can redistribute it and/or modify it under
135
- the terms of the GNU Lesser General Public License as published by the
136
- Free Software Foundation, version 3.0.
137
-
138
- Clogger is distributed in the hope that it will be useful, but WITHOUT ANY
139
- WARRANTY; without even the implied warranty of MERCHANTABILITY or
140
- FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
141
- License in the COPYING file for more details.
142
-
143
- You should have received a copy of the GNU Lesser General Public License
144
- along with Clogger; if not, write to the Free Software Foundation, Inc.,
145
- 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
@@ -35,11 +35,11 @@ is customizable so you can specify exactly which fields to log.
35
35
  s.require_paths = %w(lib ext)
36
36
  s.rubyforge_project = %q{clogger}
37
37
  s.summary = %q{configurable request logging for Rack}
38
- s.test_files = %w(test/test_clogger.rb)
38
+ s.test_files = %w(test/test_clogger.rb test/test_clogger_to_path.rb)
39
39
 
40
40
  # HeaderHash wasn't case-insensitive in old versions
41
41
  s.add_dependency(%q<rack>, ["> 0.9"])
42
42
  s.extensions = %w(ext/clogger_ext/extconf.rb)
43
43
 
44
- # s.license = "LGPLv3" # disabled for compatibility with older RubyGems
44
+ # s.license = "LGPLv2.1+" # disabled for compatibility with older RubyGems
45
45
  end
@@ -1,8 +1,14 @@
1
1
  #define _BSD_SOURCE
2
2
  #include <ruby.h>
3
+ #ifdef HAVE_RUBY_IO_H
4
+ # include <ruby/io.h>
5
+ #else
6
+ # include <rubyio.h>
7
+ #endif
3
8
  #include <assert.h>
4
9
  #include <unistd.h>
5
10
  #include <sys/types.h>
11
+ #include <sys/stat.h>
6
12
  #include <sys/time.h>
7
13
  #include <time.h>
8
14
  #include <errno.h>
@@ -89,7 +95,10 @@ static ID to_s_id;
89
95
  static ID size_id;
90
96
  static ID sq_brace_id;
91
97
  static ID new_id;
98
+ static ID to_path_id;
99
+ static ID to_io_id;
92
100
  static VALUE cClogger;
101
+ static VALUE cToPath;
93
102
  static VALUE mFormat;
94
103
  static VALUE cHeaderHash;
95
104
 
@@ -237,21 +246,31 @@ static void write_full(int fd, const void *buf, size_t count)
237
246
  * allow us to use write_full() iff we detect a blocking file
238
247
  * descriptor that wouldn't play nicely with Ruby threading/fibers
239
248
  */
240
- static int raw_fd(VALUE my_fileno)
249
+ static int raw_fd(VALUE my_fd)
241
250
  {
242
251
  #if defined(HAVE_FCNTL) && defined(F_GETFL) && defined(O_NONBLOCK)
243
252
  int fd;
244
253
  int flags;
245
254
 
246
- if (NIL_P(my_fileno))
255
+ if (NIL_P(my_fd))
247
256
  return -1;
248
- fd = NUM2INT(my_fileno);
257
+ fd = NUM2INT(my_fd);
249
258
 
250
259
  flags = fcntl(fd, F_GETFL);
251
260
  if (flags < 0)
252
261
  rb_sys_fail("fcntl");
253
262
 
254
- return (flags & O_NONBLOCK) ? -1 : fd;
263
+ if (flags & O_NONBLOCK) {
264
+ struct stat sb;
265
+
266
+ if (fstat(fd, &sb) < 0)
267
+ return -1;
268
+
269
+ /* O_NONBLOCK is no-op for regular files: */
270
+ if (! S_ISREG(sb.st_mode))
271
+ return -1;
272
+ }
273
+ return fd;
255
274
  #else /* platforms w/o fcntl/F_GETFL/O_NONBLOCK */
256
275
  return -1;
257
276
  #endif /* platforms w/o fcntl/F_GETFL/O_NONBLOCK */
@@ -638,12 +657,11 @@ static VALUE body_iter_i(VALUE str, VALUE memop)
638
657
  return rb_yield(str);
639
658
  }
640
659
 
641
- static VALUE wrap_each(struct clogger *c)
660
+ static VALUE body_close(struct clogger *c)
642
661
  {
643
- c->body_bytes_sent = 0;
644
- rb_iterate(rb_each, c->body, body_iter_i, (VALUE)&c->body_bytes_sent);
645
-
646
- return c->body;
662
+ if (rb_respond_to(c->body, close_id))
663
+ return rb_funcall(c->body, close_id, 0);
664
+ return Qnil;
647
665
  }
648
666
 
649
667
  /**
@@ -659,8 +677,10 @@ static VALUE clogger_each(VALUE self)
659
677
  struct clogger *c = clogger_get(self);
660
678
 
661
679
  rb_need_block();
680
+ c->body_bytes_sent = 0;
681
+ rb_iterate(rb_each, c->body, body_iter_i, (VALUE)&c->body_bytes_sent);
662
682
 
663
- return rb_ensure(wrap_each, (VALUE)c, cwrite, (VALUE)c);
683
+ return self;
664
684
  }
665
685
 
666
686
  /**
@@ -675,9 +695,7 @@ static VALUE clogger_close(VALUE self)
675
695
  {
676
696
  struct clogger *c = clogger_get(self);
677
697
 
678
- if (rb_respond_to(c->body, close_id))
679
- return rb_funcall(c->body, close_id, 0);
680
- return Qnil;
698
+ return rb_ensure(body_close, (VALUE)c, cwrite, (VALUE)c);
681
699
  }
682
700
 
683
701
  /* :nodoc: */
@@ -749,6 +767,9 @@ static VALUE clogger_call(VALUE self, VALUE env)
749
767
 
750
768
  rv = ccall(c, env);
751
769
  assert(!OBJ_FROZEN(rv) && "frozen response array");
770
+
771
+ if (rb_respond_to(c->body, to_path_id))
772
+ self = rb_funcall(cToPath, new_id, 1, self);
752
773
  rb_ary_store(rv, 2, self);
753
774
 
754
775
  return rv;
@@ -779,6 +800,39 @@ static VALUE clogger_init_copy(VALUE clone, VALUE orig)
779
800
 
780
801
  #define CONST_GLOBAL_STR(val) CONST_GLOBAL_STR2(val, #val)
781
802
 
803
+ static VALUE to_path(VALUE self)
804
+ {
805
+ struct clogger *c = clogger_get(RSTRUCT_PTR(self)[0]);
806
+ VALUE path = rb_funcall(c->body, to_path_id, 0);
807
+ struct stat sb;
808
+ int rv;
809
+ unsigned devfd;
810
+ const char *cpath;
811
+
812
+ Check_Type(path, T_STRING);
813
+ cpath = RSTRING_PTR(path);
814
+
815
+ /* try to avoid an extra path lookup */
816
+ if (rb_respond_to(c->body, to_io_id))
817
+ rv = fstat(my_fileno(c->body), &sb);
818
+ /*
819
+ * Rainbows! can use "/dev/fd/%u" in to_path output to avoid
820
+ * extra open() syscalls, too.
821
+ */
822
+ else if (sscanf(cpath, "/dev/fd/%u", &devfd) == 1)
823
+ rv = fstat((int)devfd, &sb);
824
+ else
825
+ rv = stat(cpath, &sb);
826
+
827
+ /*
828
+ * calling this method implies the web server will bypass
829
+ * the each method where body_bytes_sent is calculated,
830
+ * so we stat and set that value here.
831
+ */
832
+ c->body_bytes_sent = rv == 0 ? sb.st_size : 0;
833
+ return path;
834
+ }
835
+
782
836
  void Init_clogger_ext(void)
783
837
  {
784
838
  VALUE tmp;
@@ -792,6 +846,8 @@ void Init_clogger_ext(void)
792
846
  size_id = rb_intern("size");
793
847
  sq_brace_id = rb_intern("[]");
794
848
  new_id = rb_intern("new");
849
+ to_path_id = rb_intern("to_path");
850
+ to_io_id = rb_intern("to_io");
795
851
  cClogger = rb_define_class("Clogger", rb_cObject);
796
852
  mFormat = rb_define_module_under(cClogger, "Format");
797
853
  rb_define_alloc_func(cClogger, clogger_alloc);
@@ -821,4 +877,6 @@ void Init_clogger_ext(void)
821
877
  tmp = rb_const_get(rb_cObject, rb_intern("Rack"));
822
878
  tmp = rb_const_get(tmp, rb_intern("Utils"));
823
879
  cHeaderHash = rb_const_get(tmp, rb_intern("HeaderHash"));
880
+ cToPath = rb_const_get(cClogger, rb_intern("ToPath"));
881
+ rb_define_method(cToPath, "to_path", to_path, 0);
824
882
  }
@@ -11,6 +11,12 @@
11
11
  #ifndef RARRAY_LEN
12
12
  # define RARRAY_LEN(s) (RARRAY(s)->len)
13
13
  #endif
14
+ #ifndef RSTRUCT_PTR
15
+ # define RSTRUCT_PTR(s) (RSTRUCT(s)->ptr)
16
+ #endif
17
+ #ifndef RSTRUCT_LEN
18
+ # define RSTRUCT_LEN(s) (RSTRUCT(s)->len)
19
+ #endif
14
20
 
15
21
  #ifndef HAVE_RB_STR_SET_LEN
16
22
  /* this is taken from Ruby 1.8.7, 1.8.6 may not have it */
@@ -21,3 +27,34 @@ static void rb_18_str_set_len(VALUE str, long len)
21
27
  }
22
28
  #define rb_str_set_len(str,len) rb_18_str_set_len(str,len)
23
29
  #endif
30
+
31
+ #if ! HAVE_RB_IO_T
32
+ # define rb_io_t OpenFile
33
+ #endif
34
+
35
+ #ifdef GetReadFile
36
+ # define FPTR_TO_FD(fptr) (fileno(GetReadFile(fptr)))
37
+ #else
38
+ # if !HAVE_RB_IO_T || (RUBY_VERSION_MAJOR == 1 && RUBY_VERSION_MINOR == 8)
39
+ # define FPTR_TO_FD(fptr) fileno(fptr->f)
40
+ # else
41
+ # define FPTR_TO_FD(fptr) fptr->fd
42
+ # endif
43
+ #endif
44
+
45
+ static int my_fileno(VALUE io)
46
+ {
47
+ rb_io_t *fptr;
48
+
49
+ for (;;) {
50
+ switch (TYPE(io)) {
51
+ case T_FILE: {
52
+ GetOpenFile(io, fptr);
53
+ return FPTR_TO_FD(fptr);
54
+ }
55
+ default:
56
+ io = rb_convert_type(io, T_FILE, "IO", "to_io");
57
+ /* retry */
58
+ }
59
+ }
60
+ }
@@ -1,9 +1,13 @@
1
1
  # -*- encoding: binary -*-
2
- autoload :Rack, 'rack'
2
+ require 'rack'
3
3
 
4
+ # See the README for usage instructions
4
5
  class Clogger
5
- VERSION = '0.4.0'
6
6
 
7
+ # the version of Clogger, currently 0.5.0
8
+ VERSION = '0.5.0'
9
+
10
+ # :stopdoc:
7
11
  OP_LITERAL = 0
8
12
  OP_REQUEST = 1
9
13
  OP_RESPONSE = 2
@@ -36,6 +40,15 @@ class Clogger
36
40
  :request_uri => 7
37
41
  }
38
42
 
43
+ # proxy class to avoid clobbering the +to_path+ optimization when
44
+ # using static files
45
+ class ToPath < Struct.new(:clogger)
46
+ def each(&block); clogger.each(&block); end
47
+ def close; clogger.close; end
48
+
49
+ # to_path is defined in Clogger::Pure or the C extension
50
+ end
51
+
39
52
  private
40
53
 
41
54
  CGI_ENV = Regexp.new('\A\$(' <<
@@ -132,12 +145,13 @@ private
132
145
  end
133
146
  end
134
147
 
148
+ # :startdoc:
135
149
  end
136
150
 
137
151
  require 'clogger/format'
138
152
 
139
153
  begin
140
- raise LoadError if ENV['CLOGGER_PURE']
154
+ raise LoadError if ENV['CLOGGER_PURE'].to_i != 0
141
155
  require 'clogger_ext'
142
156
  rescue LoadError
143
157
  require 'clogger/pure'
@@ -5,6 +5,9 @@
5
5
  # the original C extension code so it's not very Ruby-ish...
6
6
  class Clogger
7
7
 
8
+ attr_accessor :env, :status, :headers, :body
9
+ attr_writer :body_bytes_sent
10
+
8
11
  def initialize(app, opts = {})
9
12
  # trigger autoload to avoid thread-safety issues later on
10
13
  Rack::Utils::HeaderHash.new({})
@@ -30,8 +33,13 @@ class Clogger
30
33
  headers = Rack::Utils::HeaderHash.new(headers) if @need_resp
31
34
  if @wrap_body
32
35
  @reentrant = env['rack.multithread'] if @reentrant.nil?
33
- @env, @status, @headers, @body = env, status, headers, body
34
- return [ status, headers, @reentrant ? self.dup : self ]
36
+ wbody = @reentrant ? self.dup : self
37
+ wbody.env = env
38
+ wbody.status = status
39
+ wbody.headers = headers
40
+ wbody.body = body
41
+ wbody = Clogger::ToPath.new(wbody) if body.respond_to?(:to_path)
42
+ return [ status, headers, wbody ]
35
43
  end
36
44
  log(env, status, headers)
37
45
  [ status, headers, body ]
@@ -43,12 +51,13 @@ class Clogger
43
51
  @body_bytes_sent += Rack::Utils.bytesize(part)
44
52
  yield part
45
53
  end
46
- ensure
47
- log(@env, @status, @headers)
54
+ self
48
55
  end
49
56
 
50
57
  def close
51
58
  @body.close if @body.respond_to?(:close)
59
+ ensure
60
+ log(@env, @status, @headers)
52
61
  end
53
62
 
54
63
  def reentrant?
@@ -138,4 +147,16 @@ private
138
147
  }.join('')
139
148
  end
140
149
 
150
+ class ToPath
151
+ def to_path
152
+ rv = (body = clogger.body).to_path
153
+
154
+ # try to avoid unnecessary path lookups with to_io.stat instead of
155
+ # File.stat
156
+ clogger.body_bytes_sent =
157
+ (body.respond_to?(:to_io) ? body.to_io.stat : File.stat(rv)).size
158
+ rv
159
+ end
160
+ end
161
+
141
162
  end