github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/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 "github.com/juju/clock" 10 "github.com/juju/loggo" 11 "gopkg.in/juju/names.v2" 12 13 "github.com/juju/juju/pubsub/apiserver" 14 "github.com/juju/juju/rpc" 15 "github.com/juju/juju/rpc/jsoncodec" 16 ) 17 18 // Hub defines the only method of the apiserver centralhub that 19 // the observer uses. 20 type Hub interface { 21 Publish(topic string, data interface{}) (<-chan struct{}, error) 22 } 23 24 // RequestObserver serves as a sink for API server requests and 25 // responses. 26 type RequestObserver struct { 27 clock clock.Clock 28 hub Hub 29 logger loggo.Logger 30 connLogger loggo.Logger 31 pingLogger loggo.Logger 32 apiConnectionCount func() int64 33 34 // state represents information that's built up as methods on this 35 // type are called. We segregate this to ensure it's clear what 36 // information is transient in case we want to extract it 37 // later. It's an anonymous struct so this doesn't leak outside 38 // this type. 39 state struct { 40 id uint64 41 websocketConnected time.Time 42 tag string 43 model string 44 agent bool 45 fromController bool 46 } 47 } 48 49 // RequestObservercontext provides information needed for a 50 // RequestObserverContext to operate correctly. 51 type RequestObserverContext struct { 52 53 // Clock is the clock to use for all time operations on this type. 54 Clock clock.Clock 55 56 // Hub refers to the pubsub Hub which will have connection 57 // and disconnection events published. 58 Hub Hub 59 60 // Logger is the log to use to write log statements. 61 Logger loggo.Logger 62 } 63 64 // NewRequestObserver returns a new RPCObserver. 65 func NewRequestObserver(ctx RequestObserverContext) *RequestObserver { 66 // Ideally we should have a logging context so we can log into the correct 67 // model rather than the api server for everything. 68 module := ctx.Logger.Name() 69 return &RequestObserver{ 70 clock: ctx.Clock, 71 hub: ctx.Hub, 72 logger: ctx.Logger, 73 connLogger: loggo.GetLogger(module + ".connection"), 74 pingLogger: loggo.GetLogger(module + ".ping"), 75 } 76 } 77 78 func (n *RequestObserver) isAgent(entity names.Tag) bool { 79 switch entity.(type) { 80 case names.UnitTag, names.MachineTag, names.ApplicationTag: 81 return true 82 default: 83 return false 84 } 85 } 86 87 // Login implements Observer. 88 func (n *RequestObserver) Login(entity names.Tag, model names.ModelTag, fromController bool, userData string) { 89 n.state.tag = entity.String() 90 n.state.fromController = fromController 91 if n.isAgent(entity) { 92 n.state.agent = true 93 n.state.model = model.Id() 94 // Don't log connections from the controller to the model. 95 if !n.state.fromController { 96 n.connLogger.Infof("agent login: %s for %s", n.state.tag, n.state.model) 97 } 98 n.hub.Publish(apiserver.ConnectTopic, apiserver.APIConnection{ 99 AgentTag: n.state.tag, 100 ControllerAgent: fromController, 101 ModelUUID: model.Id(), 102 ConnectionID: n.state.id, 103 UserData: userData, 104 }) 105 } 106 } 107 108 // Join implements Observer. 109 func (n *RequestObserver) Join(req *http.Request, connectionID uint64) { 110 n.state.id = connectionID 111 n.state.websocketConnected = n.clock.Now() 112 113 n.logger.Debugf( 114 "[%X] API connection from %s", 115 n.state.id, 116 req.RemoteAddr, 117 ) 118 } 119 120 // Leave implements Observer. 121 func (n *RequestObserver) Leave() { 122 if n.state.agent { 123 // Don't log disconnections from the controller to the model. 124 if !n.state.fromController { 125 n.connLogger.Infof("agent disconnected: %s for %s", n.state.tag, n.state.model) 126 } 127 n.hub.Publish(apiserver.DisconnectTopic, apiserver.APIConnection{ 128 AgentTag: n.state.tag, 129 ControllerAgent: n.state.fromController, 130 ModelUUID: n.state.model, 131 ConnectionID: n.state.id, 132 }) 133 } 134 n.logger.Debugf( 135 "[%X] %s API connection terminated after %v", 136 n.state.id, 137 n.state.tag, 138 time.Since(n.state.websocketConnected), 139 ) 140 } 141 142 // RPCObserver implements Observer. 143 func (n *RequestObserver) RPCObserver() rpc.Observer { 144 return &rpcObserver{ 145 clock: n.clock, 146 logger: n.logger, 147 pingLogger: n.pingLogger, 148 id: n.state.id, 149 tag: n.state.tag, 150 } 151 } 152 153 // rpcObserver serves as a sink for RPC requests and responses. 154 type rpcObserver struct { 155 clock clock.Clock 156 logger loggo.Logger 157 pingLogger loggo.Logger 158 id uint64 159 tag string 160 requestStart time.Time 161 } 162 163 // ServerRequest implements rpc.Observer. 164 func (n *rpcObserver) ServerRequest(hdr *rpc.Header, body interface{}) { 165 n.requestStart = n.clock.Now() 166 167 if hdr.Request.Type == "Pinger" && hdr.Request.Action == "Ping" { 168 n.logRequestTrace(n.pingLogger, hdr, body) 169 return 170 } 171 172 // TODO(rog) 2013-10-11 remove secrets from some requests. 173 // Until secrets are removed, we only log the body of the requests at trace level 174 // which is below the default level of debug. 175 if n.logger.IsTraceEnabled() { 176 n.logRequestTrace(n.logger, hdr, body) 177 } else { 178 n.logger.Debugf("<- [%X] %s %s", n.id, n.tag, jsoncodec.DumpRequest(hdr, "'params redacted'")) 179 } 180 } 181 182 // ServerReply implements rpc.Observer. 183 func (n *rpcObserver) ServerReply(req rpc.Request, hdr *rpc.Header, body interface{}) { 184 if req.Type == "Pinger" && req.Action == "Ping" { 185 n.logReplyTrace(n.pingLogger, hdr, body) 186 return 187 } 188 189 // TODO(rog) 2013-10-11 remove secrets from some responses. 190 // Until secrets are removed, we only log the body of the requests at trace level 191 // which is below the default level of debug. 192 if n.logger.IsTraceEnabled() { 193 n.logReplyTrace(n.logger, hdr, body) 194 } else { 195 n.logger.Debugf( 196 "-> [%X] %s %s %s %s[%q].%s", 197 n.id, 198 n.tag, 199 time.Since(n.requestStart), 200 jsoncodec.DumpRequest(hdr, "'body redacted'"), 201 req.Type, 202 req.Id, 203 req.Action, 204 ) 205 } 206 } 207 208 func (n *rpcObserver) logRequestTrace(logger loggo.Logger, hdr *rpc.Header, body interface{}) { 209 n.logTrace(logger, "<-", hdr, body) 210 } 211 212 func (n *rpcObserver) logReplyTrace(logger loggo.Logger, hdr *rpc.Header, body interface{}) { 213 n.logTrace(logger, "->", hdr, body) 214 } 215 216 func (n *rpcObserver) logTrace(logger loggo.Logger, prefix string, hdr *rpc.Header, body interface{}) { 217 logger.Tracef("%s [%X] %s %s", prefix, n.id, n.tag, jsoncodec.DumpRequest(hdr, body)) 218 }