Skip to content

Commit 7107a52

Browse files
authored
fix: Generate psedurandom execution id if Function-Execution-Id is not provided; Update README with examples (#260)
* fix: Generate psedurandom execution id if Function-Execution-Id is missing; Update README with exeamples to generate execution Id * Update README.md to address comments
1 parent a1ec99e commit 7107a52

File tree

3 files changed

+71
-3
lines changed

3 files changed

+71
-3
lines changed

README.md

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,57 @@ handling logic.
133133
curl localhost:8080
134134
# Output: Hello, World!
135135
```
136+
## Quickstart: Enable Exeuction Id Logging
137+
138+
[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.
139+
140+
To have exeuction id logged for `Cloud Run Functions` executions, users can either:
141+
142+
* Provide a custom execution Id in the Http Header `Function-Execution-Id`.
143+
144+
```sh
145+
curl -H "Function-Execution-Id: 123456" localhost:8080
146+
# Output: Hello, World!
147+
```
148+
149+
Example Log:
150+
```
151+
{"message":"Try logging with executionID!","logging.googleapis.com/labels":{"execution_id":"123456"}}
152+
```
153+
154+
155+
OR
156+
* 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.
157+
158+
```golang
159+
package function
160+
161+
import (
162+
"fmt"
163+
"net/http"
164+
"log"
165+
"github.com/GoogleCloudPlatform/functions-framework-go/functions"
166+
"github.com/GoogleCloudPlatform/functions-framework-go/funcframework"
167+
)
168+
169+
func init() {
170+
functions.HTTP("HelloWorld", helloWorld)
171+
}
172+
173+
// helloWorld writes "Hello, World!" to the HTTP response.
174+
func helloWorld(w http.ResponseWriter, r *http.Request) {
175+
l := log.New(funcframework.LogWriter(r.Context()), "", 0)
176+
177+
l.Println("Try logging with executionID!")
178+
fmt.Fprintln(w, "Hello, World!")
179+
}
180+
```
181+
182+
Example Log:
183+
```
184+
{"message":"Try logging with executionID!","logging.googleapis.com/labels":{"execution_id":"181dbb5b096549313d470dd68fa64d96"}}
185+
```
186+
136187

137188
## Go further: build a deployable container
138189

funcframework/logging.go

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,14 @@ import (
44
"bufio"
55
"context"
66
"encoding/json"
7+
"fmt"
78
"io"
9+
"math/rand"
810
"net/http"
911
"os"
1012
"regexp"
1113
"sync"
14+
"time"
1215
)
1316

1417
var (
@@ -32,6 +35,11 @@ type contextKey string
3235

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

3745
if executionID == "" && traceID == "" && spanID == "" {
@@ -182,7 +190,7 @@ func (w *structuredLogWriter) Close() error {
182190
// )
183191
// ...
184192
// func helloWorld(w http.ResponseWriter, r *http.Request) {
185-
// l := logger.New(funcframework.LogWriter(r.Context()))
193+
// l := log.New(funcframework.LogWriter(r.Context()), "", 0)
186194
// l.Println("hello world!")
187195
// }
188196
func LogWriter(ctx context.Context) io.WriteCloser {

funcframework/logging_test.go

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,15 @@ func TestLoggingIDExtraction(t *testing.T) {
1515
wantTraceID string
1616
wantSpanID string
1717
wantExecutionID string
18+
randomExecutionIdGenerated bool
1819
}{
1920
{
2021
name: "no IDs",
2122
headers: map[string]string{},
23+
randomExecutionIdGenerated: true,
2224
},
2325
{
24-
name: "execution ID only",
26+
name: "provided execution ID only",
2527
headers: map[string]string{
2628
"Function-Execution-Id": "exec id",
2729
},
@@ -32,13 +34,15 @@ func TestLoggingIDExtraction(t *testing.T) {
3234
headers: map[string]string{
3335
"X-Cloud-Trace-Context": "$*#$(v434)",
3436
},
37+
randomExecutionIdGenerated: true,
3538
},
3639
{
3740
name: "trace ID only",
3841
headers: map[string]string{
3942
"X-Cloud-Trace-Context": "0123456789abcdef",
4043
},
4144
wantTraceID: "0123456789abcdef",
45+
randomExecutionIdGenerated: true,
4246
},
4347
{
4448
name: "trace ID and span ID",
@@ -47,6 +51,7 @@ func TestLoggingIDExtraction(t *testing.T) {
4751
},
4852
wantTraceID: "0123456789abcdef",
4953
wantSpanID: "aaaaaa",
54+
randomExecutionIdGenerated: true,
5055
},
5156
{
5257
name: "all",
@@ -77,9 +82,13 @@ func TestLoggingIDExtraction(t *testing.T) {
7782
t.Errorf("expected span id %q but got %q", tc.wantSpanID, spid)
7883
}
7984

80-
if eid := ExecutionIDFromContext(ctx); eid != tc.wantExecutionID {
85+
eid := ExecutionIDFromContext(ctx);
86+
if tc.wantExecutionID != "" && eid != tc.wantExecutionID {
8187
t.Errorf("expected execution id %q but got %q", tc.wantExecutionID, eid)
8288
}
89+
if tc.randomExecutionIdGenerated && eid == "" {
90+
t.Errorf("expected random execution id generated but got %q", eid)
91+
}
8392
})
8493
}
8594
}

0 commit comments

Comments
 (0)