Skip to content

Commit

Permalink
implement user defined logger (with sensible defaults)
Browse files Browse the repository at this point in the history
  • Loading branch information
Nick Randall authored and neelance committed Apr 4, 2017
1 parent b357f46 commit 3f1cb6f
Show file tree
Hide file tree
Showing 3 changed files with 45 additions and 14 deletions.
11 changes: 11 additions & 0 deletions graphql.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import (
"github.com/neelance/graphql-go/internal/schema"
"github.com/neelance/graphql-go/internal/validation"
"github.com/neelance/graphql-go/introspection"
"github.com/neelance/graphql-go/log"
"github.com/neelance/graphql-go/trace"
)

Expand Down Expand Up @@ -49,6 +50,7 @@ func ParseSchema(schemaString string, resolver interface{}, opts ...SchemaOpt) (
schema: schema.New(),
maxParallelism: 10,
tracer: trace.OpenTracingTracer{},
logger: &log.DefaultLogger{},
}
for _, opt := range opts {
opt(s)
Expand Down Expand Up @@ -85,6 +87,7 @@ type Schema struct {

maxParallelism int
tracer trace.Tracer
logger log.Logger
}

// SchemaOpt is an option to pass to ParseSchema or MustParseSchema.
Expand All @@ -104,6 +107,13 @@ func Tracer(tracer trace.Tracer) SchemaOpt {
}
}

// Logger is used to log panics durring query execution. It defaults to exec.DefaultLogger.
func Logger(logger log.Logger) SchemaOpt {
return func(s *Schema) {
s.logger = logger
}
}

// Response represents a typical response of a GraphQL server. It may be encoded to JSON directly or
// it may be further processed to a custom response type, for example to include custom error data.
type Response struct {
Expand Down Expand Up @@ -146,6 +156,7 @@ func (s *Schema) exec(ctx context.Context, queryString string, operationName str
},
Limiter: make(chan struct{}, s.maxParallelism),
Tracer: s.tracer,
Logger: s.logger,
}
varTypes := make(map[string]*introspection.Type)
for _, v := range op.Vars {
Expand Down
25 changes: 11 additions & 14 deletions internal/exec/exec.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,7 @@ package exec
import (
"bytes"
"context"
"log"
"reflect"
"runtime"
"strconv"
"sync"

Expand All @@ -15,39 +13,37 @@ import (
"github.com/neelance/graphql-go/internal/exec/selected"
"github.com/neelance/graphql-go/internal/query"
"github.com/neelance/graphql-go/internal/schema"
"github.com/neelance/graphql-go/log"
"github.com/neelance/graphql-go/trace"
)

type Request struct {
selected.Request
Limiter chan struct{}
Tracer trace.Tracer
Logger log.Logger
}

type fieldResult struct {
name string
value []byte
}

func (r *Request) handlePanic() {
if err := recover(); err != nil {
r.AddError(makePanicError(err))
func (r *Request) handlePanic(ctx context.Context) {
if value := recover(); value != nil {
r.Logger.LogPanic(ctx, value)
r.AddError(makePanicError(value))
}
}

func makePanicError(value interface{}) *errors.QueryError {
err := errors.Errorf("graphql: panic occurred: %v", value)
const size = 64 << 10
buf := make([]byte, size)
buf = buf[:runtime.Stack(buf, false)]
log.Printf("%s\n%s", err, buf)
return err
return errors.Errorf("graphql: panic occurred: %v", value)
}

func (r *Request) Execute(ctx context.Context, s *resolvable.Schema, op *query.Operation) ([]byte, []*errors.QueryError) {
var out bytes.Buffer
func() {
defer r.handlePanic()
defer r.handlePanic(ctx)
sels := selected.ApplyOperation(&r.Request, s, op)
r.execSelections(ctx, sels, s.Resolver, &out, op.Type == query.Mutation)
}()
Expand Down Expand Up @@ -76,7 +72,7 @@ func (r *Request) execSelections(ctx context.Context, sels []selected.Selection,
wg.Add(len(fields))
for _, f := range fields {
go func(f *fieldWithResolver) {
defer r.handlePanic()
defer r.handlePanic(ctx)
r.execFieldSelection(ctx, f.field, f.resolver, &f.out, false)
wg.Done()
}(f)
Expand Down Expand Up @@ -158,6 +154,7 @@ func (r *Request) execFieldSelection(ctx context.Context, field *selected.Schema
err = func() (err *errors.QueryError) {
defer func() {
if panicValue := recover(); panicValue != nil {
r.Logger.LogPanic(ctx, panicValue)
err = makePanicError(panicValue)
}
}()
Expand Down Expand Up @@ -236,7 +233,7 @@ func (r *Request) execSelectionSet(ctx context.Context, sels []selected.Selectio
entryouts := make([]bytes.Buffer, l)
for i := 0; i < l; i++ {
go func(i int) {
defer r.handlePanic()
defer r.handlePanic(ctx)
r.execSelectionSet(ctx, sels, t.OfType, resolver.Index(i), &entryouts[i])
wg.Done()
}(i)
Expand Down
23 changes: 23 additions & 0 deletions log/log.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package log

import (
"context"
"log"
"runtime"
)

// Logger is the interface used to log panics that occur durring query execution. It is setable via graphql.ParseSchema
type Logger interface {
LogPanic(ctx context.Context, value interface{})
}

// DefaultLogger is the default logger used to log panics that occur durring query execution
type DefaultLogger struct{}

// LogPanic is used to log recovered panic values that occur durring query execution
func (l *DefaultLogger) LogPanic(_ context.Context, value interface{}) {
const size = 64 << 10
buf := make([]byte, size)
buf = buf[:runtime.Stack(buf, false)]
log.Printf("graphql: panic occurred: %v\n%s", value, buf)
}

0 comments on commit 3f1cb6f

Please sign in to comment.