Skip to content

fix: Generate psedurandom execution id if Function-Execution-Id is not provided; Update README with examples #260

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jan 27, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 51 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,57 @@ handling logic.
curl localhost:8080
# Output: Hello, World!
```
## Quickstart: Enable Exeuction Id Logging

[Cloud Run Functions(1st gen)](https://cloud.google.com/functions/1stgendocs/deploy) provides an execution id in the logs at `labels.execution_id`, which customers can use to filter their logs for each execution. [Cloud Run Functions](https://cloud.google.com/functions/docs/deploy) doesn't have the same feature embedded.

To have exeuction id logged for `Cloud Run Functions` executions, users can either:

* Provide a custom execution Id in the Http Header `Function-Execution-Id`.

```sh
curl -H "Function-Execution-Id: 123456" localhost:8080
# Output: Hello, World!
```

Example Log:
```
{"message":"Try logging with executionID!","logging.googleapis.com/labels":{"execution_id":"123456"}}
```


OR
* Leverage `LogWriter` provided in function-framework-go(v1.9.0 or higher) library to generate logs. If `Function-Exeuction-Id` is empty, a pseduorandom execution id will be auto-generated if `LogWriter` is used.

```golang
package function

import (
"fmt"
"net/http"
"log"
"github.com/GoogleCloudPlatform/functions-framework-go/functions"
"github.com/GoogleCloudPlatform/functions-framework-go/funcframework"
)

func init() {
functions.HTTP("HelloWorld", helloWorld)
}

// helloWorld writes "Hello, World!" to the HTTP response.
func helloWorld(w http.ResponseWriter, r *http.Request) {
l := log.New(funcframework.LogWriter(r.Context()), "", 0)

l.Println("Try logging with executionID!")
fmt.Fprintln(w, "Hello, World!")
}
```

Example Log:
```
{"message":"Try logging with executionID!","logging.googleapis.com/labels":{"execution_id":"181dbb5b096549313d470dd68fa64d96"}}
```


## Go further: build a deployable container

Expand Down
10 changes: 9 additions & 1 deletion funcframework/logging.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,14 @@ import (
"bufio"
"context"
"encoding/json"
"fmt"
"io"
"math/rand"
"net/http"
"os"
"regexp"
"sync"
"time"
)

var (
Expand All @@ -32,6 +35,11 @@ type contextKey string

func addLoggingIDsToRequest(r *http.Request) *http.Request {
executionID := r.Header.Get("Function-Execution-Id")
if executionID == "" {
timestamp := time.Now().UnixNano()
random := rand.Int63()
executionID = fmt.Sprintf("%06x%06x", timestamp, random)
}
traceID, spanID, _ := deconstructXCloudTraceContext(r.Header.Get("X-Cloud-Trace-Context"))

if executionID == "" && traceID == "" && spanID == "" {
Expand Down Expand Up @@ -182,7 +190,7 @@ func (w *structuredLogWriter) Close() error {
// )
// ...
// func helloWorld(w http.ResponseWriter, r *http.Request) {
// l := logger.New(funcframework.LogWriter(r.Context()))
// l := log.New(funcframework.LogWriter(r.Context()), "", 0)
// l.Println("hello world!")
// }
func LogWriter(ctx context.Context) io.WriteCloser {
Expand Down
13 changes: 11 additions & 2 deletions funcframework/logging_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,15 @@ func TestLoggingIDExtraction(t *testing.T) {
wantTraceID string
wantSpanID string
wantExecutionID string
randomExecutionIdGenerated bool
}{
{
name: "no IDs",
headers: map[string]string{},
randomExecutionIdGenerated: true,
},
{
name: "execution ID only",
name: "provided execution ID only",
headers: map[string]string{
"Function-Execution-Id": "exec id",
},
Expand All @@ -32,13 +34,15 @@ func TestLoggingIDExtraction(t *testing.T) {
headers: map[string]string{
"X-Cloud-Trace-Context": "$*#$(v434)",
},
randomExecutionIdGenerated: true,
},
{
name: "trace ID only",
headers: map[string]string{
"X-Cloud-Trace-Context": "0123456789abcdef",
},
wantTraceID: "0123456789abcdef",
randomExecutionIdGenerated: true,
},
{
name: "trace ID and span ID",
Expand All @@ -47,6 +51,7 @@ func TestLoggingIDExtraction(t *testing.T) {
},
wantTraceID: "0123456789abcdef",
wantSpanID: "aaaaaa",
randomExecutionIdGenerated: true,
},
{
name: "all",
Expand Down Expand Up @@ -77,9 +82,13 @@ func TestLoggingIDExtraction(t *testing.T) {
t.Errorf("expected span id %q but got %q", tc.wantSpanID, spid)
}

if eid := ExecutionIDFromContext(ctx); eid != tc.wantExecutionID {
eid := ExecutionIDFromContext(ctx);
if tc.wantExecutionID != "" && eid != tc.wantExecutionID {
t.Errorf("expected execution id %q but got %q", tc.wantExecutionID, eid)
}
if tc.randomExecutionIdGenerated && eid == "" {
t.Errorf("expected random execution id generated but got %q", eid)
}
})
}
}
Expand Down
Loading