blob: 20490b28dd383d2abd5996644086199e701d4e9b [file] [log] [blame]
// 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
package main
import (
// newGCELogger returns a handler that wraps h but logs each request
// using Google Cloud Logging service.
func newGCELogger(cli *logging.Client) *GCELogger {
return &GCELogger{cli}
type GCELogger struct {
cli *logging.Client
// 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)
// 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.
Time: time.Now().UTC(),
Payload: payload,
func randomString() (string, error) {
b := make([]byte, 8)
_, err := rand.Read(b)
return hex.EncodeToString(b), err