Index: java/org/apache/coyote/Response.java =================================================================== --- java/org/apache/coyote/Response.java (revision 1493385) +++ java/org/apache/coyote/Response.java (working copy) @@ -107,6 +107,7 @@ // General informations private long contentWritten = 0; + private long commitTime = -1; /** * Holds request error exception. @@ -219,9 +220,20 @@ public void setCommitted(boolean v) { + if (v && !this.commited) { + this.commitTime = System.currentTimeMillis(); + } this.commited = v; } + /** + * Return the time the response was committed (based on System.currentTimeMillis). + * + * @return the time the response was committed + */ + public long getCommitTime() { + return commitTime; + } // -----------------Error State -------------------- @@ -354,7 +366,7 @@ */ public void sendHeaders() { action(ActionCode.COMMIT, this); - commited = true; + setCommitted(true); } @@ -522,6 +534,7 @@ status = 200; message = null; commited = false; + commitTime = -1; errorException = null; headers.clear(); listener = null; Index: java/org/apache/catalina/valves/AccessLogValve.java =================================================================== --- java/org/apache/catalina/valves/AccessLogValve.java (revision 1493385) +++ java/org/apache/catalina/valves/AccessLogValve.java (working copy) @@ -1754,6 +1754,22 @@ } /** + * write time until first byte is written (commit time) in millis - %F + */ + protected static class FirstByteTimeElement implements AccessLogElement { + @Override + public void addElement(CharArrayWriter buf, Date date, Request request, Response response, long time) { + long commitTime = response.getCoyoteResponse().getCommitTime(); + if (commitTime == -1) { + buf.append('-'); + } else { + long delta = commitTime - request.getCoyoteRequest().getStartTime(); + buf.append(Long.toString(delta)); + } + } + } + + /** * write Query string (prepended with a '?' if it exists) - %q */ protected static class QueryElement implements AccessLogElement { @@ -2065,6 +2081,8 @@ return new ByteSentElement(false); case 'D': return new ElapsedTimeElement(true); + case 'F': + return new FirstByteTimeElement(); case 'h': return new HostElement(); case 'H':