| // Copyright 2016 The Go Authors. All rights reserved. |
| // |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file or at |
| // https://developers.google.com/open-source/licenses/bsd. |
| |
| package main |
| |
| import ( |
| "crypto/rand" |
| "encoding/hex" |
| "log" |
| "net/http" |
| "time" |
| |
| "cloud.google.com/go/logging" |
| "github.com/golang/gddo/database" |
| ) |
| |
| // newGCELogger returns a handler that wraps h but logs each request |
| // using Google Cloud Logging service. |
| func newGCELogger(cli *logging.Logger) *GCELogger { |
| return &GCELogger{cli} |
| } |
| |
| type GCELogger struct { |
| cli *logging.Logger |
| } |
| |
| // LogEvent creates an entry in Cloud Logging to record user's behavior. We should only |
| // use this to log events we are interested in. General request logs are handled by GAE |
| // automatically in request_log and stderr. |
| func (g *GCELogger) LogEvent(w http.ResponseWriter, r *http.Request, content interface{}) { |
| const sessionCookieName = "GODOC_ORG_SESSION_ID" |
| cookie, err := r.Cookie(sessionCookieName) |
| if err != nil { |
| // Generates a random session id and sends it in response. |
| rs, err := randomString() |
| if err != nil { |
| log.Println("error generating a random session id: ", err) |
| return |
| } |
| // This cookie is intentionally short-lived and contains no information |
| // that might identify the user. Its sole purpose is to tie query |
| // terms and destination pages together to measure search quality. |
| cookie = &http.Cookie{ |
| Name: sessionCookieName, |
| Value: rs, |
| Expires: time.Now().Add(time.Hour), |
| } |
| http.SetCookie(w, cookie) |
| } |
| |
| // We must not record the client's IP address, or any other information |
| // that might compromise the user's privacy. |
| payload := map[string]interface{}{ |
| sessionCookieName: cookie.Value, |
| "path": r.URL.RequestURI(), |
| "method": r.Method, |
| "referer": r.Referer(), |
| } |
| if pkgs, ok := content.([]database.Package); ok { |
| payload["packages"] = pkgs |
| } |
| |
| // Log queues the entry to its internal buffer, or discarding the entry |
| // if the buffer was full. |
| g.cli.Log(logging.Entry{ |
| Payload: payload, |
| }) |
| } |
| |
| func randomString() (string, error) { |
| b := make([]byte, 8) |
| _, err := rand.Read(b) |
| return hex.EncodeToString(b), err |
| } |