Log a message with each HTTP Cloud Function error response

73 Views Asked by At

I'm using google cloud HTTP functions and it seems like cloud functions automatically writes a single log line for each response. When an error occurs, I'd have expected perhaps a snippet of the body response to be logged, but there's nothing besides the status code and response size.

Here's some example code:

func init() {
    functions.HTTP("Scrape", ScrapeHTTP)
}

func Scrape(w http.ResponseWriter, r *http.Request) {
    err := validateRequestData(r)
    if err != nil {
        // Doesn't actually log "my error message" in `textPayload` on logs.
        http.Error(w, "my error message", http.StatusBadRequest)
        return
    }
}

Here's an example of what gets logged:

{
  "insertId": "65aea4130000c5a9b9b6bad2",
  "httpRequest": {
    "requestMethod": "GET",
    "requestUrl": "<removed>",
    "requestSize": "1355",
    "status": 500,
    "responseSize": "782",
    "userAgent": "Google-Cloud-Tasks",
    "latency": "0.143171908s",
    "protocol": "HTTP/1.1"
  },
  "resource": {
    "type": "cloud_run_revision",
    "labels": {
      "project_id": "<removed>",
      "location": "<removed>",
      "revision_name": "<removed>",
      "service_name": "scrape",
      "configuration_name": "scrape"
    }
  },
  "timestamp": "2024-01-22T17:21:22.905611Z",
  "severity": "ERROR",
  "labels": {
    "goog-managed-by": "cloudfunctions",
    "instanceId": "<removed>"
  },
  "logName": "projects/<removed>/logs/run.googleapis.com%2Frequests",
  "trace": "<removed>",
  "receiveTimestamp": "2024-01-22T17:21:23.141132352Z",
  "spanId": "1278497214486517307"
}

I would have expected "my error message" to appear somewhere in the logs but it doesn't. Is there a way to get that logged besides manually logging the error using the logging library?

When a function times out, the same automatic log response line has a textPayload with the message: "The request has been terminated because it has reached the maximum request timeout.", so perhaps there's a way of passing a textPayload to http.Error?

1

There are 1 best solutions below

1
Clash On

Here's what http.Error does:

// Error replies to the request with the specified error message and HTTP code.
// It does not otherwise end the request; the caller should ensure no further
// writes are done to w.
// The error message should be plain text.
func Error(w ResponseWriter, error string, code int) {
    w.Header().Set("Content-Type", "text/plain; charset=utf-8")
    w.Header().Set("X-Content-Type-Options", "nosniff")
    w.WriteHeader(code)
    fmt.Fprintln(w, error)
}

There's actually no special magic logging from google cloud functions, the log line I'm seeing is actually coming from fmt.Fprintln(w, error).

The solution is to actually simply write your own error function that writes structured logs. It's unfortunate that this practice is never mentioned anywhere in the documentation, which uses http.Error everywhere.

Thanks a lot for the hint, @JohnHanley!