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 }