Node/CCQServer: log responses (#3752)
This commit is contained in:
parent
3f074f30ee
commit
e98bb64436
|
@ -38,6 +38,7 @@ type httpServer struct {
|
||||||
permissions *Permissions
|
permissions *Permissions
|
||||||
signerKey *ecdsa.PrivateKey
|
signerKey *ecdsa.PrivateKey
|
||||||
pendingResponses *PendingResponses
|
pendingResponses *PendingResponses
|
||||||
|
loggingMap *LoggingMap
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *httpServer) handleQuery(w http.ResponseWriter, r *http.Request) {
|
func (s *httpServer) handleQuery(w http.ResponseWriter, r *http.Request) {
|
||||||
|
@ -147,6 +148,10 @@ func (s *httpServer) handleQuery(w http.ResponseWriter, r *http.Request) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if permEntry.logResponses {
|
||||||
|
s.loggingMap.AddRequest(requestId)
|
||||||
|
}
|
||||||
|
|
||||||
s.logger.Info("posting request to gossip", zap.String("userId", permEntry.userName), zap.String("requestId", requestId))
|
s.logger.Info("posting request to gossip", zap.String("userId", permEntry.userName), zap.String("requestId", requestId))
|
||||||
err = s.topic.Publish(r.Context(), b)
|
err = s.topic.Publish(r.Context(), b)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -202,7 +207,7 @@ func (s *httpServer) handleQuery(w http.ResponseWriter, r *http.Request) {
|
||||||
s.pendingResponses.Remove(pendingResponse)
|
s.pendingResponses.Remove(pendingResponse)
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewHTTPServer(addr string, t *pubsub.Topic, permissions *Permissions, signerKey *ecdsa.PrivateKey, p *PendingResponses, logger *zap.Logger, env common.Environment) *http.Server {
|
func NewHTTPServer(addr string, t *pubsub.Topic, permissions *Permissions, signerKey *ecdsa.PrivateKey, p *PendingResponses, logger *zap.Logger, env common.Environment, loggingMap *LoggingMap) *http.Server {
|
||||||
s := &httpServer{
|
s := &httpServer{
|
||||||
topic: t,
|
topic: t,
|
||||||
permissions: permissions,
|
permissions: permissions,
|
||||||
|
@ -210,6 +215,7 @@ func NewHTTPServer(addr string, t *pubsub.Topic, permissions *Permissions, signe
|
||||||
pendingResponses: p,
|
pendingResponses: p,
|
||||||
logger: logger,
|
logger: logger,
|
||||||
env: env,
|
env: env,
|
||||||
|
loggingMap: loggingMap,
|
||||||
}
|
}
|
||||||
r := mux.NewRouter()
|
r := mux.NewRouter()
|
||||||
r.HandleFunc("/v1/query", s.handleQuery).Methods("PUT", "POST", "OPTIONS")
|
r.HandleFunc("/v1/query", s.handleQuery).Methods("PUT", "POST", "OPTIONS")
|
||||||
|
|
|
@ -0,0 +1,69 @@
|
||||||
|
package ccq
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/certusone/wormhole/node/pkg/common"
|
||||||
|
"go.uber.org/zap"
|
||||||
|
)
|
||||||
|
|
||||||
|
// LoggingMap is used to track the requests for which we should log response. It contains a map keyed by the request signature
|
||||||
|
// where the payload is time the request was received. Requests will be removed from the map after two minutes.
|
||||||
|
type LoggingMap struct {
|
||||||
|
loggingLock sync.Mutex
|
||||||
|
loggingMap map[string]time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewLoggingMap creates the map used to track requests for which we should log responses.
|
||||||
|
func NewLoggingMap() *LoggingMap {
|
||||||
|
return &LoggingMap{
|
||||||
|
loggingMap: make(map[string]time.Time),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start starts a go routine to clean up requests that have been in the map for two minutes.
|
||||||
|
func (lm *LoggingMap) Start(ctx context.Context, logger *zap.Logger, errC chan error) {
|
||||||
|
common.RunWithScissors(ctx, errC, "logging_cleanup", func(ctx context.Context) error {
|
||||||
|
ticker := time.NewTicker(1 * time.Minute)
|
||||||
|
defer ticker.Stop()
|
||||||
|
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return nil
|
||||||
|
case <-ticker.C:
|
||||||
|
lm.CleanUp(logger)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// CleanUp iterates over the map and removes all entries that are more than two minutes old.
|
||||||
|
func (lm *LoggingMap) CleanUp(logger *zap.Logger) {
|
||||||
|
lm.loggingLock.Lock()
|
||||||
|
defer lm.loggingLock.Unlock()
|
||||||
|
for requestId, cleanUpTime := range lm.loggingMap {
|
||||||
|
if time.Now().After(cleanUpTime) {
|
||||||
|
delete(lm.loggingMap, requestId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddRequest adds a request to the map, giving it an expiration time two minutes into the future.
|
||||||
|
func (lm *LoggingMap) AddRequest(requestSignature string) {
|
||||||
|
lm.loggingLock.Lock()
|
||||||
|
defer lm.loggingLock.Unlock()
|
||||||
|
lm.loggingMap[requestSignature] = time.Now().Add(2 * time.Minute)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ShouldLogResponse returns true if the request is in the map.
|
||||||
|
func (lm *LoggingMap) ShouldLogResponse(requestSignature string) bool {
|
||||||
|
lm.loggingLock.Lock()
|
||||||
|
defer lm.loggingLock.Unlock()
|
||||||
|
if _, exists := lm.loggingMap[requestSignature]; exists {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
|
@ -37,7 +37,7 @@ type P2PSub struct {
|
||||||
host host.Host
|
host host.Host
|
||||||
}
|
}
|
||||||
|
|
||||||
func runP2P(ctx context.Context, priv crypto.PrivKey, port uint, networkID, bootstrapPeers, ethRpcUrl, ethCoreAddr string, pendingResponses *PendingResponses, logger *zap.Logger, monitorPeers bool) (*P2PSub, error) {
|
func runP2P(ctx context.Context, priv crypto.PrivKey, port uint, networkID, bootstrapPeers, ethRpcUrl, ethCoreAddr string, pendingResponses *PendingResponses, logger *zap.Logger, monitorPeers bool, loggingMap *LoggingMap) (*P2PSub, error) {
|
||||||
// p2p setup
|
// p2p setup
|
||||||
components := p2p.DefaultComponents()
|
components := p2p.DefaultComponents()
|
||||||
components.Port = port
|
components.Port = port
|
||||||
|
@ -168,6 +168,9 @@ func runP2P(ctx context.Context, priv crypto.PrivKey, port uint, networkID, boot
|
||||||
}
|
}
|
||||||
requestSignature := hex.EncodeToString(queryResponse.Request.Signature)
|
requestSignature := hex.EncodeToString(queryResponse.Request.Signature)
|
||||||
logger.Info("query response received from gossip", zap.String("peerId", peerId), zap.Any("requestId", requestSignature))
|
logger.Info("query response received from gossip", zap.String("peerId", peerId), zap.Any("requestId", requestSignature))
|
||||||
|
if loggingMap.ShouldLogResponse(requestSignature) {
|
||||||
|
logger.Info("logging response", zap.Any("requestId", requestSignature), zap.Any("response", queryResponse))
|
||||||
|
}
|
||||||
// Check that we're handling the request for this response
|
// Check that we're handling the request for this response
|
||||||
pendingResponse := pendingResponses.Get(requestSignature)
|
pendingResponse := pendingResponses.Get(requestSignature)
|
||||||
if pendingResponse == nil {
|
if pendingResponse == nil {
|
||||||
|
|
|
@ -28,6 +28,7 @@ type (
|
||||||
UserName string `json:"userName"`
|
UserName string `json:"userName"`
|
||||||
ApiKey string `json:"apiKey"`
|
ApiKey string `json:"apiKey"`
|
||||||
AllowUnsigned bool `json:"allowUnsigned"`
|
AllowUnsigned bool `json:"allowUnsigned"`
|
||||||
|
LogResponses bool `json:"logResponses"`
|
||||||
AllowedCalls []AllowedCall `json:"allowedCalls"`
|
AllowedCalls []AllowedCall `json:"allowedCalls"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -67,6 +68,7 @@ type (
|
||||||
userName string
|
userName string
|
||||||
apiKey string
|
apiKey string
|
||||||
allowUnsigned bool
|
allowUnsigned bool
|
||||||
|
logResponses bool
|
||||||
allowedCalls allowedCallsForUser // Key is something like "ethCall:2:000000000000000000000000b4fbf271143f4fbf7b91a5ded31805e42b2208d6:06fdde03"
|
allowedCalls allowedCallsForUser // Key is something like "ethCall:2:000000000000000000000000b4fbf271143f4fbf7b91a5ded31805e42b2208d6:06fdde03"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -267,6 +269,7 @@ func parseConfig(byteValue []byte) (PermissionsMap, error) {
|
||||||
userName: user.UserName,
|
userName: user.UserName,
|
||||||
apiKey: apiKey,
|
apiKey: apiKey,
|
||||||
allowUnsigned: user.AllowUnsigned,
|
allowUnsigned: user.AllowUnsigned,
|
||||||
|
logResponses: user.LogResponses,
|
||||||
allowedCalls: allowedCalls,
|
allowedCalls: allowedCalls,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -148,6 +148,8 @@ func runQueryServer(cmd *cobra.Command, args []string) {
|
||||||
logger.Fatal("Failed to load permissions file", zap.String("permFile", *permFile), zap.Error(err))
|
logger.Fatal("Failed to load permissions file", zap.String("permFile", *permFile), zap.Error(err))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
loggingMap := NewLoggingMap()
|
||||||
|
|
||||||
// Load p2p private key
|
// Load p2p private key
|
||||||
var priv crypto.PrivKey
|
var priv crypto.PrivKey
|
||||||
priv, err = common.GetOrCreateNodeKey(logger, *nodeKeyPath)
|
priv, err = common.GetOrCreateNodeKey(logger, *nodeKeyPath)
|
||||||
|
@ -170,14 +172,14 @@ func runQueryServer(cmd *cobra.Command, args []string) {
|
||||||
|
|
||||||
// Run p2p
|
// Run p2p
|
||||||
pendingResponses := NewPendingResponses()
|
pendingResponses := NewPendingResponses()
|
||||||
p2p, err := runP2P(ctx, priv, *p2pPort, networkID, *p2pBootstrap, *ethRPC, *ethContract, pendingResponses, logger, *monitorPeers)
|
p2p, err := runP2P(ctx, priv, *p2pPort, networkID, *p2pBootstrap, *ethRPC, *ethContract, pendingResponses, logger, *monitorPeers, loggingMap)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Fatal("Failed to start p2p", zap.Error(err))
|
logger.Fatal("Failed to start p2p", zap.Error(err))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start the HTTP server
|
// Start the HTTP server
|
||||||
go func() {
|
go func() {
|
||||||
s := NewHTTPServer(*listenAddr, p2p.topic_req, permissions, signerKey, pendingResponses, logger, env)
|
s := NewHTTPServer(*listenAddr, p2p.topic_req, permissions, signerKey, pendingResponses, logger, env, loggingMap)
|
||||||
logger.Sugar().Infof("Server listening on %s", *listenAddr)
|
logger.Sugar().Infof("Server listening on %s", *listenAddr)
|
||||||
err := s.ListenAndServe()
|
err := s.ListenAndServe()
|
||||||
if err != nil && err != http.ErrServerClosed {
|
if err != nil && err != http.ErrServerClosed {
|
||||||
|
@ -249,6 +251,9 @@ func runQueryServer(cmd *cobra.Command, args []string) {
|
||||||
errC := make(chan error)
|
errC := make(chan error)
|
||||||
permissions.StartWatcher(ctx, logger, errC)
|
permissions.StartWatcher(ctx, logger, errC)
|
||||||
|
|
||||||
|
// Star logging cleanup process.
|
||||||
|
loggingMap.Start(ctx, logger, errC)
|
||||||
|
|
||||||
// Wait for either a shutdown or a fatal error from the permissions watcher.
|
// Wait for either a shutdown or a fatal error from the permissions watcher.
|
||||||
select {
|
select {
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
|
|
Loading…
Reference in New Issue