GoogleCloudPlatform / functions-framework-go

FaaS (Function as a service) framework for writing portable Go functions
https://godoc.org/github.com/GoogleCloudPlatform/functions-framework-go
Apache License 2.0
458 stars 63 forks source link

feat: add structured log writer #246

Closed garethgeorge closed 2 months ago

garethgeorge commented 2 months ago

Background

When using a standard go logger e.g. "log" package's log.Default() we don't get a "spanId" / "trace" in the logs generated:

// HTTP is a simple HTTP function that writes the request body to the response body.
func HTTPNoLog(w http.ResponseWriter, r *http.Request) {
    log.Default().Println("handling HTTP request without span / trace ID logging")
    body, err := ioutil.ReadAll(r.Body)
    if err != nil {
        w.WriteHeader(http.StatusInternalServerError)
        return
    }
    if err := ioutil.WriteFile(outputFile, body, 0644); err != nil {
        w.WriteHeader(http.StatusInternalServerError)
        return
    }
}

This can make it hard to associate logs printed with the invocation generating them e.g.

image

No span ID / trace ID is captured for logs generated by default when using concurrency.

Proposal

Include a funcframework package utility for creating io.Writers that produce structured logs and that are compatible with common logging frameworks (e.g. "log" by default.


// HTTP is a simple HTTP function that writes the request body to the response body.
func HTTP(w http.ResponseWriter, r *http.Request) {
    l := log.New(funcframework.LogWriter(r.Context()), "", log.Lshortfile)
    l.Println("handling HTTP request")
    body, err := ioutil.ReadAll(r.Body)
    if err != nil {
        w.WriteHeader(http.StatusInternalServerError)
        return
    }
    if err := ioutil.WriteFile(outputFile, body, 0644); err != nil {
        w.WriteHeader(http.StatusInternalServerError)
        return
    }
}
image

Or, alternatively a CX can extract the trace ID and other logging IDs from the request context if needed using the utilities:

funcframework.ExecutionIDFromContext(ctx context.Context)
funcframework.TraceIDFromContext(ctx context.Context)
funcframework.SpanIDFromContext(ctx context.Context)