diff --git a/chromedp.go b/chromedp.go index dec9585..1baf97b 100644 --- a/chromedp.go +++ b/chromedp.go @@ -34,6 +34,9 @@ type CDP struct { // handlerMap is the map of target IDs to its active handler. handlerMap map[string]int + // logging funcs + logf, debugf, errorf LogFunc + sync.RWMutex } @@ -44,6 +47,9 @@ func New(ctxt context.Context, opts ...Option) (*CDP, error) { c := &CDP{ handlers: make([]*TargetHandler, 0), handlerMap: make(map[string]int), + logf: log.Printf, + debugf: func(string, ...interface{}) {}, + errorf: func(s string, v ...interface{}) { log.Printf("error: "+s, v...) }, } // apply options @@ -108,16 +114,16 @@ func (c *CDP) AddTarget(ctxt context.Context, t client.Target) { defer c.Unlock() // create target manager - h, err := NewTargetHandler(t) + h, err := NewTargetHandler(t, c.logf, c.debugf, c.errorf) if err != nil { - log.Printf("error: could not create handler for %s, got: %v", t, err) + c.errorf("could not create handler for %s, got: %v", t, err) return } // run err = h.Run(ctxt) if err != nil { - log.Printf("error: could not start handler for %s, got: %v", t, err) + c.errorf("could not start handler for %s, got: %v", t, err) return } @@ -344,8 +350,8 @@ func (c *CDP) Run(ctxt context.Context, a Action) error { // Option is a Chrome Debugging Protocol option. type Option func(*CDP) error -// WithRunner is a option to specify the underlying Chrome runner to monitor -// for page handlers. +// WithRunner is a CDP option to specify the underlying Chrome runner to +// monitor for page handlers. func WithRunner(r *runner.Runner) Option { return func(c *CDP) error { c.r = r @@ -353,8 +359,8 @@ func WithRunner(r *runner.Runner) Option { } } -// WithTargets is an option to specify the incoming targets to monitor for page -// handlers. +// WithTargets is a CDP option to specify the incoming targets to monitor for +// page handlers. func WithTargets(watch <-chan client.Target) Option { return func(c *CDP) error { c.watch = watch @@ -362,7 +368,7 @@ func WithTargets(watch <-chan client.Target) Option { } } -// WithRunnerOptions is a option to specify the options to pass to a newly +// WithRunnerOptions is a CDP option to specify the options to pass to a newly // created Chrome process runner. func WithRunnerOptions(opts ...runner.CommandLineOption) Option { return func(c *CDP) error { @@ -370,3 +376,51 @@ func WithRunnerOptions(opts ...runner.CommandLineOption) Option { return nil } } + +// LogFunc is the common logging func type. +type LogFunc func(string, ...interface{}) + +// WithLogf is a CDP option to specify a func to receive general logging. +func WithLogf(f LogFunc) Option { + return func(c *CDP) error { + c.logf = f + return nil + } +} + +// WithDebugf is a CDP option to specify a func to receive debug logging (ie, +// protocol information). +func WithDebugf(f LogFunc) Option { + return func(c *CDP) error { + c.debugf = f + return nil + } +} + +// WithErrorf is a CDP option to specify a func to receive error logging. +func WithErrorf(f LogFunc) Option { + return func(c *CDP) error { + c.errorf = f + return nil + } +} + +// WithLog is a CDP option that sets the logging, debugging, and error funcs to +// f. +func WithLog(f LogFunc) Option { + return func(c *CDP) error { + c.logf = f + c.debugf = f + c.errorf = f + return nil + } +} + +// WithConsolef is a CDP option to specify a func to receive chrome log events. +// +// Note: NOT YET IMPLEMENTED. +func WithConsolef(f LogFunc) Option { + return func(c *CDP) error { + return nil + } +} diff --git a/handler.go b/handler.go index 4678c5b..79b3061 100644 --- a/handler.go +++ b/handler.go @@ -3,7 +3,6 @@ package chromedp import ( "context" "fmt" - "log" "reflect" "runtime" "strings" @@ -54,17 +53,25 @@ type TargetHandler struct { res map[int64]chan interface{} resrw sync.RWMutex + // logging funcs + logf, debugf, errorf LogFunc + sync.RWMutex } // NewTargetHandler creates a new handler for the specified client target. -func NewTargetHandler(t client.Target) (*TargetHandler, error) { +func NewTargetHandler(t client.Target, logf, debugf, errorf LogFunc) (*TargetHandler, error) { conn, err := client.Dial(t) if err != nil { return nil, err } - return &TargetHandler{conn: conn}, nil + return &TargetHandler{ + conn: conn, + logf: logf, + debugf: debugf, + errorf: errorf, + }, nil } // Run starts the processing of commands and events of the client target @@ -154,7 +161,7 @@ func (h *TargetHandler) run(ctxt context.Context) { h.qres <- msg default: - log.Printf("ignoring malformed incoming message (missing id or method): %#v", msg) + h.errorf("ignoring malformed incoming message (missing id or method): %#v", msg) } case <-h.detached: @@ -175,19 +182,19 @@ func (h *TargetHandler) run(ctxt context.Context) { case ev := <-h.qevents: err = h.processEvent(ctxt, ev) if err != nil { - log.Printf("could not process event, got: %v", err) + h.errorf("could not process event, got: %v", err) } case res := <-h.qres: err = h.processResult(res) if err != nil { - log.Printf("could not process command result, got: %v", err) + h.errorf("could not process command result, got: %v", err) } case cmd := <-h.qcmd: err = h.processCommand(cmd) if err != nil { - log.Printf("could not process command, got: %v", err) + h.errorf("could not process command, got: %v", err) } case <-ctxt.Done(): @@ -204,7 +211,7 @@ func (h *TargetHandler) read() (*cdp.Message, error) { return nil, err } - log.Printf("-> %s", string(buf)) + h.debugf("-> %s", string(buf)) // unmarshal msg := new(cdp.Message) @@ -264,7 +271,7 @@ func (h *TargetHandler) processEvent(ctxt context.Context, msg *cdp.Message) err func (h *TargetHandler) documentUpdated(ctxt context.Context) { f, err := h.WaitFrame(ctxt, EmptyFrameID) if err != nil { - log.Printf("could not get current frame, got: %v", err) + h.errorf("could not get current frame, got: %v", err) return } @@ -279,7 +286,7 @@ func (h *TargetHandler) documentUpdated(ctxt context.Context) { f.Nodes = make(map[cdp.NodeID]*cdp.Node) f.Root, err = dom.GetDocument().WithPierce(true).Do(ctxt, h) if err != nil { - log.Printf("error could not retrieve document root for %s, got: %v", f.ID, err) + h.errorf("could not retrieve document root for %s, got: %v", f.ID, err) return } f.Root.Invalidated = make(chan struct{}) @@ -293,7 +300,9 @@ func (h *TargetHandler) processResult(msg *cdp.Message) error { res, ok := h.res[msg.ID] if !ok { - panic(fmt.Sprintf("expected result to be present for message id %d", msg.ID)) + err := fmt.Errorf("expected result to be present for message id %d", msg.ID) + h.errorf(err.Error()) + return err } if msg.Error != nil { @@ -316,7 +325,7 @@ func (h *TargetHandler) processCommand(cmd *cdp.Message) error { return err } - log.Printf("<- %s", string(buf)) + h.debugf("<- %s", string(buf)) // write return h.conn.Write(buf) @@ -539,12 +548,13 @@ func (h *TargetHandler) pageEvent(ctxt context.Context, ev interface{}) { return default: - panic(fmt.Sprintf("unhandled page event %s", reflect.TypeOf(ev))) + h.errorf("unhandled page event %s", reflect.TypeOf(ev)) + return } f, err := h.WaitFrame(ctxt, id) if err != nil { - log.Printf("error could not get frame %s, got: %v", id, err) + h.errorf("could not get frame %s, got: %v", id, err) return } @@ -564,7 +574,7 @@ func (h *TargetHandler) domEvent(ctxt context.Context, ev interface{}) { // wait current frame f, err := h.WaitFrame(ctxt, EmptyFrameID) if err != nil { - log.Printf("error processing DOM event %s: error waiting for frame, got: %v", reflect.TypeOf(ev), err) + h.errorf("error processing DOM event %s: error waiting for frame, got: %v", reflect.TypeOf(ev), err) return } @@ -624,7 +634,8 @@ func (h *TargetHandler) domEvent(ctxt context.Context, ev interface{}) { return default: - panic(fmt.Sprintf("unhandled node event %s", reflect.TypeOf(ev))) + h.errorf("unhandled node event %s", reflect.TypeOf(ev)) + return } s := strings.TrimPrefix(strings.TrimSuffix(runtime.FuncForPC(reflect.ValueOf(op).Pointer()).Name(), ".func1"), "github.com/knq/chromedp.") @@ -632,7 +643,7 @@ func (h *TargetHandler) domEvent(ctxt context.Context, ev interface{}) { // retrieve node n, err := h.WaitNode(ctxt, f, id) if err != nil { - log.Printf("error could not perform (%s) operation on node %d (wait node error), got: %v", s, id, err) + h.errorf("error could not perform (%s) operation on node %d (wait node error), got: %v", s, id, err) return } diff --git a/pool.go b/pool.go index 97c561e..bde807e 100644 --- a/pool.go +++ b/pool.go @@ -3,6 +3,7 @@ package chromedp import ( "context" "fmt" + "log" "sync" "github.com/knq/chromedp/runner" @@ -19,6 +20,9 @@ type Pool struct { // res are the running chrome resources. res map[int]*Res + // logging funcs + logf, debugf, errorf LogFunc + rw sync.RWMutex } @@ -27,9 +31,12 @@ func NewPool(opts ...PoolOption) (*Pool, error) { var err error p := &Pool{ - start: DefaultPoolStartPort, - end: DefaultPoolEndPort, - res: make(map[int]*Res), + start: DefaultPoolStartPort, + end: DefaultPoolEndPort, + res: make(map[int]*Res), + logf: log.Printf, + debugf: func(string, ...interface{}) {}, + errorf: func(s string, v ...interface{}) { log.Printf("error: "+s, v...) }, } // apply opts @@ -85,7 +92,10 @@ func (p *Pool) Allocate(ctxt context.Context, opts ...runner.CommandLineOption) } // setup cdp - r.c, err = New(ctxt, WithRunner(r.r)) + r.c, err = New( + ctxt, WithRunner(r.r), + WithLogf(p.logf), WithDebugf(p.debugf), WithErrorf(p.errorf), + ) if err != nil { cancel() return nil, err @@ -173,3 +183,13 @@ func PortRange(start, end int) PoolOption { return nil } } + +// PoolLog is a pool option to set the logging to use for the pool. +func PoolLog(logf, debugf, errorf LogFunc) PoolOption { + return func(p *Pool) error { + p.logf = logf + p.debugf = debugf + p.errorf = errorf + return nil + } +}