class Clogger
See the README for usage instructions
Public Class Methods
Creates a new Clogger object that wraps app
. :logger
may be any object that responds to the “<<” method with a string argument. Instead of :logger
, :path
may be specified to be a :path of a File that will be opened in append mode.
static VALUE clogger_init(int argc, VALUE *argv, VALUE self) { struct clogger *c = clogger_get(self); VALUE o = Qnil; VALUE fmt = rb_const_get(mFormat, rb_intern("Common")); rb_scan_args(argc, argv, "11", &c->app, &o); c->fd = -1; c->logger = Qnil; c->reentrant = -1; /* auto-detect */ if (TYPE(o) == T_HASH) { VALUE tmp; tmp = rb_hash_aref(o, ID2SYM(rb_intern("path"))); c->logger = rb_hash_aref(o, ID2SYM(rb_intern("logger"))); init_logger(c, tmp); tmp = rb_hash_aref(o, ID2SYM(rb_intern("format"))); if (!NIL_P(tmp)) fmt = tmp; tmp = rb_hash_aref(o, ID2SYM(rb_intern("reentrant"))); switch (TYPE(tmp)) { case T_TRUE: c->reentrant = 1; break; case T_FALSE: c->reentrant = 0; case T_NIL: break; default: rb_raise(rb_eArgError, ":reentrant must be boolean"); } } init_buffers(c); c->fmt_ops = rb_funcall(self, rb_intern("compile_format"), 2, fmt, o); if (Qtrue == rb_funcall(self, rb_intern("need_response_headers?"), 1, c->fmt_ops)) c->need_resp = 1; if (Qtrue == rb_funcall(self, rb_intern("need_wrap_body?"), 1, c->fmt_ops)) c->wrap_body = 1; return self; }
Public Instance Methods
calls the wrapped Rack application with env
, returns the
- status, headers, body
-
tuplet required by Rack.
static VALUE clogger_call(VALUE self, VALUE env) { struct clogger *c = clogger_get(self); VALUE rv; env = rb_check_convert_type(env, T_HASH, "Hash", "to_hash"); if (c->wrap_body) { /* XXX: we assume the existence of the GVL here: */ if (c->reentrant < 0) { VALUE tmp = rb_hash_aref(env, g_rack_multithread); c->reentrant = Qfalse == tmp ? 0 : 1; } if (c->reentrant) { self = rb_obj_dup(self); c = clogger_get(self); } rv = ccall(c, env); assert(!OBJ_FROZEN(rv) && "frozen response array"); rb_ary_store(rv, 2, self); return rv; } rv = ccall(c, env); cwrite(c); return rv; }
Delegates the body#close call to the underlying body
object. This is only used when Clogger is wrapping the body
of a Rack response and should be automatically called by the web server.
static VALUE clogger_close(VALUE self) { return rb_ensure(body_close, self, clogger_write, self); }
Delegates the body#each call to the underlying body
object while tracking the number of bytes yielded. This will log the request.
static VALUE clogger_each(VALUE self) { struct clogger *c = clogger_get(self); rb_need_block(); c->body_bytes_sent = 0; rb_iterate(rb_each, c->body, body_iter_i, self); return self; }
used to delegate :to_path
checks for Rack webservers that optimize static file serving
static VALUE respond_to(int argc, VALUE *argv, VALUE self) { struct clogger *c = clogger_get(self); VALUE method, include_all; ID id; rb_scan_args(argc, argv, "11", &method, &include_all); id = rb_to_id(method); if (close_id == id) return Qtrue; #ifdef HAVE_RB_OBJ_RESPOND_TO return rb_obj_respond_to(c->body, id, RTEST(include_all)); #endif if (argc == 1) return rb_respond_to(c->body, id); return rb_funcallv(c->body, respond_to_id, argc, argv); }
used to proxy :to_path
method calls to the wrapped response body.
static VALUE to_path(VALUE self) { struct clogger *c = clogger_get(self); struct stat sb; int rv; VALUE path = rb_funcall(c->body, to_path_id, 0); const char *cpath = StringValueCStr(path); unsigned devfd; /* * Rainbows! can use "/dev/fd/%u" in to_path output to avoid * extra open() syscalls, too. */ if (sscanf(cpath, "/dev/fd/%u", &devfd) == 1) rv = fstat((int)devfd, &sb); else rv = nogvl_stat(cpath, &sb); /* * calling this method implies the web server will bypass * the each method where body_bytes_sent is calculated, * so we stat and set that value here. */ c->body_bytes_sent = rv == 0 ? sb.st_size : 0; return path; }