clogger 0.4.0 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +1 -0
- data/COPYING +497 -160
- data/GIT-VERSION-GEN +1 -1
- data/GNUmakefile +19 -5
- data/LICENSE +16 -0
- data/README +10 -22
- data/clogger.gemspec +2 -2
- data/ext/clogger_ext/clogger.c +71 -13
- data/ext/clogger_ext/ruby_1_9_compat.h +37 -0
- data/lib/clogger.rb +17 -3
- data/lib/clogger/pure.rb +25 -4
- data/test/test_clogger.rb +10 -4
- data/test/test_clogger_to_path.rb +140 -0
- metadata +6 -2
data/GIT-VERSION-GEN
CHANGED
data/GNUmakefile
CHANGED
@@ -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
|
26
|
-
|
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=
|
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.
|
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 -
|
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
|
data/clogger.gemspec
CHANGED
@@ -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 = "
|
44
|
+
# s.license = "LGPLv2.1+" # disabled for compatibility with older RubyGems
|
45
45
|
end
|
data/ext/clogger_ext/clogger.c
CHANGED
@@ -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
|
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(
|
255
|
+
if (NIL_P(my_fd))
|
247
256
|
return -1;
|
248
|
-
fd = NUM2INT(
|
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
|
-
|
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
|
660
|
+
static VALUE body_close(struct clogger *c)
|
642
661
|
{
|
643
|
-
c->
|
644
|
-
|
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
|
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
|
-
|
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
|
+
}
|
data/lib/clogger.rb
CHANGED
@@ -1,9 +1,13 @@
|
|
1
1
|
# -*- encoding: binary -*-
|
2
|
-
|
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'
|
data/lib/clogger/pure.rb
CHANGED
@@ -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
|
-
|
34
|
-
|
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
|
-
|
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
|