github.com/wallyworld/juju@v0.0.0-20161013125918-6cf1bc9d917a/apiserver/observer/request_notifier.go (about)

     1  // Copyright 2016 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  package observer
     4  
     5  import (
     6  	"net/http"
     7  	"time"
     8  
     9  	"gopkg.in/juju/names.v2"
    10  
    11  	"github.com/juju/loggo"
    12  	"github.com/juju/utils/clock"
    13  
    14  	"github.com/juju/juju/rpc"
    15  	"github.com/juju/juju/rpc/jsoncodec"
    16  )
    17  
    18  // RequestObserver serves as a sink for API server requests and
    19  // responses.
    20  type RequestObserver struct {
    21  	clock              clock.Clock
    22  	logger             loggo.Logger
    23  	apiConnectionCount func() int64
    24  
    25  	// state represents information that's built up as methods on this
    26  	// type are called. We segregate this to ensure it's clear what
    27  	// information is transient in case we want to extract it
    28  	// later. It's an anonymous struct so this doesn't leak outside
    29  	// this type.
    30  	state struct {
    31  		id                 uint64
    32  		websocketConnected time.Time
    33  		tag                string
    34  	}
    35  }
    36  
    37  // RequestObservercontext provides information needed for a
    38  // RequestObserverContext to operate correctly.
    39  type RequestObserverContext struct {
    40  
    41  	// Clock is the clock to use for all time operations on this type.
    42  	Clock clock.Clock
    43  
    44  	// Logger is the log to use to write log statements.
    45  	Logger loggo.Logger
    46  }
    47  
    48  // NewRequestObserver returns a new RPCObserver.
    49  func NewRequestObserver(ctx RequestObserverContext) *RequestObserver {
    50  	return &RequestObserver{
    51  		clock:  ctx.Clock,
    52  		logger: ctx.Logger,
    53  	}
    54  }
    55  
    56  // Login implements Observer.
    57  func (n *RequestObserver) Login(entity names.Tag, _ names.ModelTag, _ bool, _ string) {
    58  	n.state.tag = entity.String()
    59  }
    60  
    61  // Join implements Observer.
    62  func (n *RequestObserver) Join(req *http.Request, connectionID uint64) {
    63  	n.state.id = connectionID
    64  	n.state.websocketConnected = n.clock.Now()
    65  
    66  	n.logger.Debugf(
    67  		"[%X] API connection from %s",
    68  		n.state.id,
    69  		req.RemoteAddr,
    70  	)
    71  }
    72  
    73  // Leave implements Observer.
    74  func (n *RequestObserver) Leave() {
    75  	n.logger.Debugf(
    76  		"[%X] %s API connection terminated after %v",
    77  		n.state.id,
    78  		n.state.tag,
    79  		time.Since(n.state.websocketConnected),
    80  	)
    81  }
    82  
    83  // RPCObserver implements Observer.
    84  func (n *RequestObserver) RPCObserver() rpc.Observer {
    85  	return &rpcObserver{
    86  		clock:  n.clock,
    87  		logger: n.logger,
    88  		id:     n.state.id,
    89  		tag:    n.state.tag,
    90  	}
    91  }
    92  
    93  // rpcObserver serves as a sink for RPC requests and responses.
    94  type rpcObserver struct {
    95  	clock        clock.Clock
    96  	logger       loggo.Logger
    97  	id           uint64
    98  	tag          string
    99  	requestStart time.Time
   100  }
   101  
   102  // ServerReques timplements rpc.Observer.
   103  func (n *rpcObserver) ServerRequest(hdr *rpc.Header, body interface{}) {
   104  	n.requestStart = n.clock.Now()
   105  
   106  	if hdr.Request.Type == "Pinger" && hdr.Request.Action == "Ping" {
   107  		return
   108  	}
   109  	// TODO(rog) 2013-10-11 remove secrets from some requests.
   110  	// Until secrets are removed, we only log the body of the requests at trace level
   111  	// which is below the default level of debug.
   112  	if n.logger.IsTraceEnabled() {
   113  		n.logger.Tracef("<- [%X] %s %s", n.id, n.tag, jsoncodec.DumpRequest(hdr, body))
   114  	} else {
   115  		n.logger.Debugf("<- [%X] %s %s", n.id, n.tag, jsoncodec.DumpRequest(hdr, "'params redacted'"))
   116  	}
   117  }
   118  
   119  // ServerReply implements rpc.Observer.
   120  func (n *rpcObserver) ServerReply(req rpc.Request, hdr *rpc.Header, body interface{}) {
   121  	if req.Type == "Pinger" && req.Action == "Ping" {
   122  		return
   123  	}
   124  
   125  	// TODO(rog) 2013-10-11 remove secrets from some responses.
   126  	// Until secrets are removed, we only log the body of the requests at trace level
   127  	// which is below the default level of debug.
   128  	if n.logger.IsTraceEnabled() {
   129  		n.logger.Tracef("-> [%X] %s %s", n.id, n.tag, jsoncodec.DumpRequest(hdr, body))
   130  	} else {
   131  		n.logger.Debugf(
   132  			"-> [%X] %s %s %s %s[%q].%s",
   133  			n.id,
   134  			n.tag,
   135  			time.Since(n.requestStart),
   136  			jsoncodec.DumpRequest(hdr, "'body redacted'"),
   137  			req.Type,
   138  			req.Id,
   139  			req.Action,
   140  		)
   141  	}
   142  }