class Clogger

See the README for usage instructions

Public Class Methods

new(app, :logger => $stderr, :format => string) → obj click to toggle source

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

call(env) → [ status, headers, body ] click to toggle source

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;
}
close click to toggle source

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);
}
each { |part| socket.write(part) } click to toggle source

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;
}
respond_to?(:to_path) → true or false click to toggle source
respond_to?(:close) → true

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);
}
to_path click to toggle source

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