func (lg Logger) Printf(msg string, args ...interface{}) {
	if lg != nil {
		lg(fmt.Sprintf(msg, args...))
	}
}
Printf writes a formatted message to the logger. If lg == nil, the message
is discarded.
package jrpc2
import (
	"context"
	"encoding/json"
	"fmt"
	"log"
	"runtime"
	"time"
	"github.com/creachadair/jrpc2/code"
	"github.com/creachadair/jrpc2/metrics"
)
type ServerOptions struct {
	Logger Logger
	RPCLog RPCLogger
	AllowV1 bool
	AllowPush bool
	DisableBuiltin bool
	Concurrency int
	NewContext func() context.Context
	DecodeContext func(context.Context, string, json.RawMessage) (context.Context, json.RawMessage, error)
	CheckRequest func(ctx context.Context, req *Request) error
	Metrics *metrics.M
	StartTime time.Time
}
func (s *ServerOptions) logFunc() func(string, ...interface{}) {
	if s == nil || s.Logger == nil {
		return func(string, ...interface{}) {}
	}
	return s.Logger.Printf
}
func (s *ServerOptions) allowV1() bool      { return s != nil && s.AllowV1 }
func (s *ServerOptions) allowPush() bool    { return s != nil && s.AllowPush }
func (s *ServerOptions) allowBuiltin() bool { return s == nil || !s.DisableBuiltin }
func (s *ServerOptions) concurrency() int64 {
	if s == nil || s.Concurrency < 1 {
		return int64(runtime.NumCPU())
	}
	return int64(s.Concurrency)
}
func (s *ServerOptions) startTime() time.Time {
	if s == nil {
		return time.Time{}
	}
	return s.StartTime
}
func (o *ServerOptions) newContext() func() context.Context {
	if o == nil || o.NewContext == nil {
		return context.Background
	}
	return o.NewContext
}
type decoder = func(context.Context, string, json.RawMessage) (context.Context, json.RawMessage, error)
func (s *ServerOptions) decodeContext() (decoder, bool) {
	if s == nil || s.DecodeContext == nil {
		return func(ctx context.Context, method string, params json.RawMessage) (context.Context, json.RawMessage, error) {
			return ctx, params, nil
		}, false
	}
	return s.DecodeContext, true
}
type verifier = func(context.Context, *Request) error
func (s *ServerOptions) checkRequest() verifier {
	if s == nil || s.CheckRequest == nil {
		return func(context.Context, *Request) error { return nil }
	}
	return s.CheckRequest
}
func (s *ServerOptions) metrics() *metrics.M {
	if s == nil || s.Metrics == nil {
		return metrics.New()
	}
	return s.Metrics
}
func (s *ServerOptions) rpcLog() RPCLogger {
	if s == nil || s.RPCLog == nil {
		return nullRPCLogger{}
	}
	return s.RPCLog
}
type ClientOptions struct {
	Logger Logger
	AllowV1 bool
	EncodeContext func(context.Context, string, json.RawMessage) (json.RawMessage, error)
	OnNotify func(*Request)
	OnCallback func(context.Context, *Request) (interface{}, error)
	OnCancel func(cli *Client, rsp *Response)
}
func (c *ClientOptions) logFunc() func(string, ...interface{}) {
	if c == nil || c.Logger == nil {
		return func(string, ...interface{}) {}
	}
	return c.Logger.Printf
}
func (c *ClientOptions) allowV1() bool { return c != nil && c.AllowV1 }
type encoder = func(context.Context, string, json.RawMessage) (json.RawMessage, error)
func (c *ClientOptions) encodeContext() encoder {
	if c == nil || c.EncodeContext == nil {
		return func(_ context.Context, methods string, params json.RawMessage) (json.RawMessage, error) {
			return params, nil
		}
	}
	return c.EncodeContext
}
func (c *ClientOptions) handleNotification() func(*jmessage) {
	if c == nil || c.OnNotify == nil {
		return nil
	}
	h := c.OnNotify
	return func(req *jmessage) { h(&Request{method: req.M, params: req.P}) }
}
func (c *ClientOptions) handleCancel() func(*Client, *Response) {
	if c == nil {
		return nil
	}
	return c.OnCancel
}
func (c *ClientOptions) handleCallback() func(*jmessage) []byte {
	if c == nil || c.OnCallback == nil {
		return nil
	}
	cb := c.OnCallback
	return func(req *jmessage) []byte {
		ctx, cancel := context.WithCancel(context.Background())
		defer cancel()
		rsp := &jmessage{ID: req.ID}
		v, err := panicToError(func() (interface{}, error) {
			return cb(ctx, &Request{
				id:     req.ID,
				method: req.M,
				params: req.P,
			})
		})
		if err == nil {
			rsp.R, err = json.Marshal(v)
		}
		if err != nil {
			rsp.R = nil
			if e, ok := err.(*Error); ok {
				rsp.E = e
			} else {
				rsp.E = &Error{Code: code.FromError(err), Message: err.Error()}
			}
		}
		bits, _ := rsp.toJSON()
		return bits
	}
}
func panicToError(f func() (interface{}, error)) (v interface{}, err error) {
	defer func() {
		if p := recover(); p != nil {
			err = fmt.Errorf("panic in callback handler: %v", p)
		}
	}()
	return f()
}
type Logger func(text string)