github.com/anuvu/tyk@v2.9.0-beta9-dl-apic+incompatible/rpc/rpc_analytics_purger.go (about)

     1  package rpc
     2  
     3  import (
     4  	"encoding/json"
     5  	"time"
     6  
     7  	msgpack "gopkg.in/vmihailenco/msgpack.v2"
     8  
     9  	"github.com/TykTechnologies/tyk/storage"
    10  )
    11  
    12  type AnalyticsRecord struct {
    13  	Method        string
    14  	Path          string
    15  	RawPath       string
    16  	ContentLength int64
    17  	UserAgent     string
    18  	Day           int
    19  	Month         time.Month
    20  	Year          int
    21  	Hour          int
    22  	ResponseCode  int
    23  	APIKey        string
    24  	TimeStamp     time.Time
    25  	APIVersion    string
    26  	APIName       string
    27  	APIID         string
    28  	OrgID         string
    29  	OauthID       string
    30  	RequestTime   int64
    31  	RawRequest    string
    32  	RawResponse   string
    33  	IPAddress     string
    34  	Geo           GeoData
    35  	Tags          []string
    36  	Alias         string
    37  	TrackPath     bool
    38  	ExpireAt      time.Time `bson:"expireAt" json:"expireAt"`
    39  }
    40  type GeoData struct {
    41  	Country struct {
    42  		ISOCode string `maxminddb:"iso_code"`
    43  	} `maxminddb:"country"`
    44  
    45  	City struct {
    46  		GeoNameID uint              `maxminddb:"geoname_id"`
    47  		Names     map[string]string `maxminddb:"names"`
    48  	} `maxminddb:"city"`
    49  
    50  	Location struct {
    51  		Latitude  float64 `maxminddb:"latitude"`
    52  		Longitude float64 `maxminddb:"longitude"`
    53  		TimeZone  string  `maxminddb:"time_zone"`
    54  	} `maxminddb:"location"`
    55  }
    56  
    57  const analyticsKeyName = "tyk-system-analytics"
    58  
    59  // RPCPurger will purge analytics data into a Mongo database, requires that the Mongo DB string is specified
    60  // in the Config object
    61  type Purger struct {
    62  	Store storage.Handler
    63  }
    64  
    65  // Connect Connects to RPC
    66  func (r *Purger) Connect() {
    67  	if !clientIsConnected {
    68  		Log.Error("RPC client is not connected, use Connect method 1st")
    69  	}
    70  
    71  	// setup RPC func if needed
    72  	if !addedFuncs["Ping"] {
    73  		dispatcher.AddFunc("Ping", func() bool {
    74  			return false
    75  		})
    76  		addedFuncs["Ping"] = true
    77  	}
    78  	if !addedFuncs["PurgeAnalyticsData"] {
    79  		dispatcher.AddFunc("PurgeAnalyticsData", func(data string) error {
    80  			return nil
    81  		})
    82  		addedFuncs["PurgeAnalyticsData"] = true
    83  	}
    84  
    85  	Log.Info("RPC Analytics client using singleton")
    86  }
    87  
    88  // PurgeLoop starts the loop that will pull data out of the in-memory
    89  // store and into RPC.
    90  func (r Purger) PurgeLoop(ticker <-chan time.Time) {
    91  	for {
    92  		<-ticker
    93  		r.PurgeCache()
    94  	}
    95  }
    96  
    97  // PurgeCache will pull the data from the in-memory store and drop it into the specified MongoDB collection
    98  func (r *Purger) PurgeCache() {
    99  	if !clientIsConnected {
   100  		Log.Error("RPC client is not connected, use Connect method 1st")
   101  	}
   102  
   103  	if _, err := FuncClientSingleton("Ping", nil); err != nil {
   104  		Log.WithError(err).Error("Can't purge cache, failed to ping RPC")
   105  		return
   106  	}
   107  
   108  	analyticsValues := r.Store.GetAndDeleteSet(analyticsKeyName)
   109  	if len(analyticsValues) == 0 {
   110  		return
   111  	}
   112  	keys := make([]interface{}, len(analyticsValues))
   113  
   114  	for i, v := range analyticsValues {
   115  		decoded := AnalyticsRecord{}
   116  		if err := msgpack.Unmarshal(v.([]byte), &decoded); err != nil {
   117  			Log.WithError(err).Error("Couldn't unmarshal analytics data")
   118  		} else {
   119  			Log.WithField("decoded", decoded).Debug("Decoded Record")
   120  			keys[i] = decoded
   121  		}
   122  	}
   123  
   124  	data, err := json.Marshal(keys)
   125  	if err != nil {
   126  		Log.WithError(err).Error("Failed to marshal analytics data")
   127  		return
   128  	}
   129  
   130  	// Send keys to RPC
   131  	if _, err := FuncClientSingleton("PurgeAnalyticsData", string(data)); err != nil {
   132  		EmitErrorEvent(FuncClientSingletonCall, "PurgeAnalyticsData", err)
   133  		Log.Warn("Failed to call purge, retrying: ", err)
   134  	}
   135  }