This repository has been archived by the owner on Jan 24, 2019. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
improve request logging (closer to Apache Common Log)
- Loading branch information
Showing
4 changed files
with
153 additions
and
7 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -135,3 +135,11 @@ Google Auth Proxy responds directly to the following endpoints. All other endpoi | |
* /oauth2/sign_in - the login page, which also doubles as a sign out page (it clears cookies) | ||
* /oauth2/start - a URL that will redirect to start the OAuth cycle | ||
* /oauth2/callback - the URL used at the end of the OAuth cycle. The oauth app will be configured with this ass the callback url. | ||
|
||
## Logging Format | ||
|
||
Google Auth Proxy logs requests to stdout in a format similar to Apache Combined Log. | ||
|
||
``` | ||
<REMOTE_ADDRESS> - <[email protected]> [19/Mar/2015:17:20:19 -0400] <HOST_HEADER> GET <UPSTREAM_HOST> "/path/" HTTP/1.1 "<USER_AGENT>" <RESPONSE_CODE> <RESPONSE_BYTES> <REQUEST_DURATION> | ||
```` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,125 @@ | ||
// largely adapted from https://github.com/gorilla/handlers/blob/master/handlers.go | ||
// to add logging of request duration as last value (and drop referrer) | ||
|
||
package main | ||
|
||
import ( | ||
"fmt" | ||
"io" | ||
"net" | ||
"net/http" | ||
"net/url" | ||
"time" | ||
) | ||
|
||
// responseLogger is wrapper of http.ResponseWriter that keeps track of its HTTP status | ||
// code and body size | ||
type responseLogger struct { | ||
w http.ResponseWriter | ||
status int | ||
size int | ||
upstream string | ||
authInfo string | ||
} | ||
|
||
func (l *responseLogger) Header() http.Header { | ||
return l.w.Header() | ||
} | ||
|
||
func (l *responseLogger) ExtractGAPMetadata() { | ||
upstream := l.w.Header().Get("GAP-Upstream-Address") | ||
if upstream != "" { | ||
l.upstream = upstream | ||
l.w.Header().Del("GAP-Upstream-Address") | ||
} | ||
authInfo := l.w.Header().Get("GAP-Auth") | ||
if authInfo != "" { | ||
l.authInfo = authInfo | ||
l.w.Header().Del("GAP-Auth") | ||
} | ||
} | ||
|
||
func (l *responseLogger) Write(b []byte) (int, error) { | ||
if l.status == 0 { | ||
// The status will be StatusOK if WriteHeader has not been called yet | ||
l.status = http.StatusOK | ||
} | ||
l.ExtractGAPMetadata() | ||
size, err := l.w.Write(b) | ||
l.size += size | ||
return size, err | ||
} | ||
|
||
func (l *responseLogger) WriteHeader(s int) { | ||
l.ExtractGAPMetadata() | ||
l.w.WriteHeader(s) | ||
l.status = s | ||
} | ||
|
||
func (l *responseLogger) Status() int { | ||
return l.status | ||
} | ||
|
||
func (l *responseLogger) Size() int { | ||
return l.size | ||
} | ||
|
||
// loggingHandler is the http.Handler implementation for LoggingHandlerTo and its friends | ||
type loggingHandler struct { | ||
writer io.Writer | ||
handler http.Handler | ||
} | ||
|
||
func LoggingHandler(out io.Writer, h http.Handler) http.Handler { | ||
return loggingHandler{out, h} | ||
} | ||
|
||
func (h loggingHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { | ||
t := time.Now() | ||
logger := &responseLogger{w: w} | ||
url := *req.URL | ||
h.handler.ServeHTTP(logger, req) | ||
logLine := buildLogLine(logger.authInfo, logger.upstream, req, url, t, logger.Status(), logger.Size()) | ||
h.writer.Write(logLine) | ||
} | ||
|
||
// Log entry for req similar to Apache Common Log Format. | ||
// ts is the timestamp with which the entry should be logged. | ||
// status, size are used to provide the response HTTP status and size. | ||
func buildLogLine(username, upstream string, req *http.Request, url url.URL, ts time.Time, status int, size int) []byte { | ||
if username == "" { | ||
username = "-" | ||
} | ||
if upstream == "" { | ||
upstream = "-" | ||
} | ||
if url.User != nil && username == "-" { | ||
if name := url.User.Username(); name != "" { | ||
username = name | ||
} | ||
} | ||
|
||
client, _, err := net.SplitHostPort(req.RemoteAddr) | ||
|
||
if err != nil { | ||
client = req.RemoteAddr | ||
} | ||
|
||
duration := float64(time.Now().Sub(ts)) / float64(time.Second) | ||
|
||
logLine := fmt.Sprintf("%s - %s [%s] %s %s %s %q %s %q %d %d %0.3f\n", | ||
client, | ||
username, | ||
ts.Format("02/Jan/2006:15:04:05 -0700"), | ||
req.Host, | ||
req.Method, | ||
upstream, | ||
url.RequestURI(), | ||
req.Proto, | ||
req.UserAgent(), | ||
status, | ||
size, | ||
duration, | ||
) | ||
return []byte(logLine) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters