Skip to content

Commit 8de421e

Browse files
Accept custom logger + log binding errors (#5)
1 parent 995309a commit 8de421e

1 file changed

Lines changed: 48 additions & 8 deletions

File tree

graph.go

Lines changed: 48 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,7 @@ type graphNode struct {
122122
dependents []*graphNode
123123
dependentsByKey map[ID][]*graphNode
124124
tracer trace.Tracer
125+
logger Logger
125126
}
126127

127128
const (
@@ -150,8 +151,8 @@ func (gn *graphNode) execute(ctx context.Context, rs *runState) (err error) {
150151
tCtx, span := gn.tracer.Start(ctx, gn.task.Name())
151152
defer span.End()
152153

153-
log.Debugf("Starting task %s", gn.task.Name())
154-
defer log.Debugf("Finished task %s", gn.task.Name())
154+
gn.logger.Debugf("Starting task %s", gn.task.Name())
155+
defer gn.logger.Debugf("Finished task %s", gn.task.Name())
155156

156157
bindings, err := gn.task.Execute(tCtx, rs)
157158
if err != nil {
@@ -169,20 +170,28 @@ func (gn *graphNode) execute(ctx context.Context, rs *runState) (err error) {
169170
}
170171
}
171172
var extra []string
173+
errors := []string{}
172174
providesSet := set.NewSet[ID](gn.task.Provides()...)
173175
for _, binding := range bindings {
174176
if !providesSet.Contains(binding.ID()) {
175177
extra = append(extra, binding.ID().String())
176178
}
179+
177180
if binding.Status() == Absent {
181+
err := binding.Error()
182+
if err != nil {
183+
errors = append(errors, fmt.Sprintf("[%s: %s]", binding.ID().String(), err))
184+
}
185+
178186
span.SetAttributes(
179187
attribute.String(
180188
traceTaskgraphAbsentKeysPrefix+binding.ID().String(),
181-
fmt.Sprintf("%v", binding.Error()),
189+
fmt.Sprintf("%v", err),
182190
),
183191
)
184192
}
185193
}
194+
186195
if len(extra) > 0 || len(missing) > 0 {
187196
return wrapStackErrorf(
188197
"task %s: mismatch between task Provides declaration and returned bindings: missing bindings [%s], got extra bindings [%s]",
@@ -192,8 +201,16 @@ func (gn *graphNode) execute(ctx context.Context, rs *runState) (err error) {
192201
)
193202
}
194203

204+
if len(errors) > 0 {
205+
gn.logger.Debugf(
206+
"task %s has binding errors: %s",
207+
gn.task.Name(),
208+
strings.Join(errors, ", "),
209+
)
210+
}
211+
195212
for _, dependent := range gn.dependents {
196-
log.Debugf("task %s signalling dependent %s\n", gn.task.Name(), dependent.task.Name())
213+
gn.logger.Debugf("task %s signalling dependent %s\n", gn.task.Name(), dependent.task.Name())
197214
if err := rs.signal(tCtx, dependent.id); err != nil {
198215
return err
199216
}
@@ -212,10 +229,11 @@ func (gn *graphNode) canExecute(b Binder) bool {
212229
func (gn *graphNode) runFunc(ctx context.Context, rs *runState) func() error {
213230
return func() error {
214231
if gn.canExecute(rs) {
215-
log.Debugf("task %s starting immediately\n", gn.task.Name())
232+
gn.logger.Debugf("task %s starting immediately\n", gn.task.Name())
216233
return gn.execute(ctx, rs)
217234
}
218-
log.Debugf("task %s has dependencies missing; cannot start immediately\n", gn.task.Name())
235+
gn.logger.Debugf("task %s has dependencies missing; cannot start immediately\n",
236+
gn.task.Name())
219237
signal, ok := rs.signals[gn.id]
220238
if !ok {
221239
return wrapStackErrorf("signal channel missing for id %q", gn.id)
@@ -224,10 +242,10 @@ func (gn *graphNode) runFunc(ctx context.Context, rs *runState) func() error {
224242
select {
225243
case <-signal:
226244
if gn.canExecute(rs) {
227-
log.Debugf("task %s starting\n", gn.task.Name())
245+
gn.logger.Debugf("task %s starting\n", gn.task.Name())
228246
return gn.execute(ctx, rs)
229247
}
230-
log.Debugf("task %s still has dependencies missing\n", gn.task.Name())
248+
gn.logger.Debugf("task %s still has dependencies missing\n", gn.task.Name())
231249
case <-ctx.Done():
232250
return nil
233251
}
@@ -241,6 +259,7 @@ type graph struct {
241259
allDependencies, allProvided set.Set[ID]
242260
nodes []*graphNode
243261
tracer trace.Tracer
262+
logger Logger
244263
}
245264

246265
func (g *graph) buildInputBinder(inputs ...Binding) (Binder, error) {
@@ -429,9 +448,15 @@ func (g *graph) Graphviz(includeInputs bool) string {
429448
return buf.String()
430449
}
431450

451+
// Logger logger interface for the graph.
452+
type Logger interface {
453+
Debugf(format string, args ...interface{})
454+
}
455+
432456
type graphOptions struct {
433457
tasks []Task
434458
tracer trace.Tracer
459+
logger Logger
435460
}
436461

437462
// A GraphOption is used to configure a new Graph.
@@ -459,6 +484,15 @@ func WithTracer(tracer trace.Tracer) GraphOption {
459484
}
460485
}
461486

487+
// WithLogger sets a logger for the graph.
488+
func WithLogger(logger Logger) GraphOption {
489+
return func(opts *graphOptions) error {
490+
opts.logger = logger
491+
492+
return nil
493+
}
494+
}
495+
462496
// New creates a new Graph. Exactly one WithTasks option should be passed.
463497
//
464498
// Ideally, Graphs should be created on program startup, rather than creating them dynamically.
@@ -473,12 +507,17 @@ func New(name string, opts ...GraphOption) (Graph, error) {
473507
}
474508
}
475509

510+
if o.logger == nil {
511+
o.logger = log
512+
}
513+
476514
g := &graph{
477515
name: name,
478516
tasks: o.tasks,
479517
allDependencies: set.NewSet[ID](),
480518
allProvided: set.NewSet[ID](),
481519
tracer: o.tracer,
520+
logger: o.logger,
482521
}
483522

484523
provideTasks := map[string][]string{}
@@ -498,6 +537,7 @@ func New(name string, opts ...GraphOption) (Graph, error) {
498537
task: t,
499538
dependentsByKey: map[ID][]*graphNode{},
500539
tracer: g.tracer,
540+
logger: g.logger,
501541
}
502542
g.nodes = append(g.nodes, node)
503543

0 commit comments

Comments
 (0)