Skip to content
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

fix: Add GCP error reporting log format to cloudevent functions and include panic message in stack trace. #230

Merged
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
5 changes: 4 additions & 1 deletion .github/workflows/buildpack-integration-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ jobs:
prerun: ${{format('testdata/conformance/prerun.sh {0} testdata/conformance/function', github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.sha )}}
builder-runtime: 'go113'
builder-runtime-version: '1.13'
builder-url: gcr.io/gae-runtimes/buildpacks/google-gae-22/go/builder:latest
go116-buildpack-test:
if: github.event.pull_request.head.repo.full_name == github.repository
uses: GoogleCloudPlatform/functions-framework-conformance/.github/workflows/buildpack-integration-test.yml@main
Expand All @@ -36,6 +37,7 @@ jobs:
prerun: ${{format('testdata/conformance/prerun.sh {0} testdata/conformance/function', github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.sha )}}
builder-runtime: 'go116'
builder-runtime-version: '1.16'
builder-url: gcr.io/gae-runtimes/buildpacks/google-gae-22/go/builder:latest
non-declarative-buildpack-test:
if: github.event.pull_request.head.repo.full_name == github.repository
uses: GoogleCloudPlatform/functions-framework-conformance/.github/workflows/buildpack-integration-test.yml@main
Expand All @@ -46,4 +48,5 @@ jobs:
cloudevent-builder-target: 'CloudEvent'
prerun: ${{format('testdata/conformance/prerun.sh {0} testdata/conformance/nondeclarative', github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.sha )}}
builder-runtime: 'go116'
builder-runtime-version: '1.16'
builder-runtime-version: '1.16'
builder-url: gcr.io/gae-runtimes/buildpacks/google-gae-22/go/builder:latest
21 changes: 13 additions & 8 deletions funcframework/framework.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,21 +45,25 @@ var errorType = reflect.TypeOf((*error)(nil)).Elem()
// describe what was happening when the panic was encountered, for example
// "user function execution". w is an http.ResponseWriter to write a generic
// response body to that does not expose the details of the panic; w can be
// nil to skip this.
func recoverPanic(w http.ResponseWriter, panicSrc string) {
// nil to skip this. If panic needs to be recovered by different caller
// set shouldPanic to true.
func recoverPanic(w http.ResponseWriter, panicSrc string, shouldPanic bool) {
if r := recover(); r != nil {
genericMsg := fmt.Sprintf(panicMessageTmpl, panicSrc)
fmt.Fprintf(os.Stderr, "%s\npanic message: %v\nstack trace: \n%s", genericMsg, r, debug.Stack())
fmt.Fprintf(os.Stderr, "%s\npanic message: %v\nstack trace: %v\n%s", genericMsg, r, r, debug.Stack())
if w != nil {
writeHTTPErrorResponse(w, http.StatusInternalServerError, crashStatus, genericMsg)
}
if shouldPanic {
panic(r)
}
}
}

// RegisterHTTPFunction registers fn as an HTTP function.
// Maintained for backward compatibility. Please use RegisterHTTPFunctionContext instead.
func RegisterHTTPFunction(path string, fn interface{}) {
defer recoverPanic(nil, "function registration")
defer recoverPanic(nil, "function registration", false)

fnHTTP, ok := fn.(func(http.ResponseWriter, *http.Request))
if !ok {
Expand All @@ -76,7 +80,7 @@ func RegisterHTTPFunction(path string, fn interface{}) {
// Maintained for backward compatibility. Please use RegisterEventFunctionContext instead.
func RegisterEventFunction(path string, fn interface{}) {
ctx := context.Background()
defer recoverPanic(nil, "function registration")
defer recoverPanic(nil, "function registration", false)
if err := RegisterEventFunctionContext(ctx, path, fn); err != nil {
panic(fmt.Sprintf("unexpected error in RegisterEventFunctionContext: %v", err))
}
Expand Down Expand Up @@ -192,7 +196,7 @@ func wrapHTTPFunction(fn func(http.ResponseWriter, *http.Request)) (http.Handler
defer fmt.Println()
defer fmt.Fprintln(os.Stderr)
}
defer recoverPanic(w, "user function execution")
defer recoverPanic(w, "user function execution", false)
fn(w, r)
}), nil
}
Expand Down Expand Up @@ -237,7 +241,7 @@ func wrapTypedFunction(fn interface{}) (http.Handler, error) {
return
}

defer recoverPanic(w, "user function execution")
defer recoverPanic(w, "user function execution", false)
funcReturn := reflect.ValueOf(fn).Call([]reflect.Value{
argVal.Elem(),
})
Expand Down Expand Up @@ -286,6 +290,7 @@ func wrapCloudEventFunction(ctx context.Context, fn func(context.Context, cloude

// Always log errors returned by the function to stderr
logErrFn := func(ctx context.Context, ce cloudevents.Event) error {
defer recoverPanic(nil, "user function execution", true)
err := fn(ctx, ce)
if err != nil {
fmt.Fprintf(os.Stderr, fmtFunctionError(err))
Expand Down Expand Up @@ -345,7 +350,7 @@ func runUserFunctionWithContext(ctx context.Context, w http.ResponseWriter, r *h
return
}

defer recoverPanic(w, "user function execution")
defer recoverPanic(w, "user function execution", false)
userFunErr := reflect.ValueOf(fn).Call([]reflect.Value{
reflect.ValueOf(ctx),
argVal.Elem(),
Expand Down
1 change: 1 addition & 0 deletions funcframework/framework_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -767,6 +767,7 @@ func TestRegisterCloudEventFunctionContext(t *testing.T) {
ceHeaders: map[string]string{
"Content-Type": "application/cloudevents+json",
},
wantStderr: fmt.Sprintf(panicMessageTmpl, "user function execution"),
},
{
name: "error returns 500",
Expand Down
6 changes: 4 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ module github.com/GoogleCloudPlatform/functions-framework-go
go 1.11

require (
cloud.google.com/go/functions v1.15.4
cloud.google.com/go v0.110.8 // indirect
cloud.google.com/go/functions v1.15.3
github.com/cloudevents/sdk-go/v2 v2.14.0
github.com/google/go-cmp v0.6.0
github.com/google/go-cmp v0.5.9
github.com/google/uuid v1.4.0 // indirect
)
Loading
Loading