github.com/Axway/agent-sdk@v1.1.101/pkg/transaction/metric/metricscollector.go (about)

     1  package metric
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"os"
     7  	"strconv"
     8  	"strings"
     9  	"sync"
    10  	"time"
    11  
    12  	"github.com/golang-jwt/jwt"
    13  	"github.com/google/uuid"
    14  	"github.com/rcrowley/go-metrics"
    15  
    16  	"github.com/Axway/agent-sdk/pkg/agent"
    17  	"github.com/Axway/agent-sdk/pkg/agent/cache"
    18  	v1 "github.com/Axway/agent-sdk/pkg/apic/apiserver/models/api/v1"
    19  	catalog "github.com/Axway/agent-sdk/pkg/apic/apiserver/models/catalog/v1alpha1"
    20  	management "github.com/Axway/agent-sdk/pkg/apic/apiserver/models/management/v1alpha1"
    21  	"github.com/Axway/agent-sdk/pkg/cmd"
    22  	"github.com/Axway/agent-sdk/pkg/config"
    23  	"github.com/Axway/agent-sdk/pkg/jobs"
    24  	"github.com/Axway/agent-sdk/pkg/traceability"
    25  	"github.com/Axway/agent-sdk/pkg/transaction/models"
    26  	transutil "github.com/Axway/agent-sdk/pkg/transaction/util"
    27  	"github.com/Axway/agent-sdk/pkg/util"
    28  	"github.com/Axway/agent-sdk/pkg/util/healthcheck"
    29  	"github.com/Axway/agent-sdk/pkg/util/log"
    30  )
    31  
    32  const (
    33  	startTimestampStr = "start-timestamp"
    34  	endTimestampStr   = "end-timestamp"
    35  	eventTypeStr      = "event-type"
    36  	usageStr          = "usage"
    37  	metricStr         = "metric"
    38  	volumeStr         = "volume"
    39  	countStr          = "count"
    40  )
    41  
    42  var exitMetricInit = false
    43  var exitMutex = &sync.RWMutex{}
    44  
    45  func ExitMetricInit() {
    46  	exitMutex.Lock()
    47  	defer exitMutex.Unlock()
    48  	exitMetricInit = true
    49  }
    50  
    51  // Collector - interface for collecting metrics
    52  type Collector interface {
    53  	InitializeBatch()
    54  	AddMetric(apiDetails models.APIDetails, statusCode string, duration, bytes int64, appName string)
    55  	AddMetricDetail(metricDetail Detail)
    56  	AddAPIMetricDetail(metric MetricDetail)
    57  	AddAPIMetric(apiMetric *APIMetric)
    58  	Publish()
    59  	ShutdownPublish()
    60  }
    61  
    62  // collector - collects the metrics for transactions events
    63  type collector struct {
    64  	jobs.Job
    65  	usageStartTime   time.Time
    66  	usageEndTime     time.Time
    67  	metricStartTime  time.Time
    68  	metricEndTime    time.Time
    69  	orgGUID          string
    70  	lock             *sync.Mutex
    71  	batchLock        *sync.Mutex
    72  	registry         metrics.Registry
    73  	metricBatch      *EventBatch
    74  	metricMap        map[string]map[string]map[string]map[string]*APIMetric
    75  	metricMapLock    *sync.Mutex
    76  	publishItemQueue []publishQueueItem
    77  	jobID            string
    78  	usagePublisher   *usagePublisher
    79  	storage          storageCache
    80  	reports          *usageReportCache
    81  	metricConfig     config.MetricReportingConfig
    82  	usageConfig      config.UsageReportingConfig
    83  	logger           log.FieldLogger
    84  	metricLogger     log.FieldLogger
    85  }
    86  
    87  type publishQueueItem interface {
    88  	GetEvent() interface{}
    89  	GetUsageMetric() interface{}
    90  	GetVolumeMetric() interface{}
    91  }
    92  
    93  type usageEventPublishItem interface {
    94  	publishQueueItem
    95  }
    96  
    97  type usageEventQueueItem struct {
    98  	usageEventPublishItem
    99  	event        UsageEvent
   100  	usageMetric  metrics.Counter
   101  	volumeMetric metrics.Counter
   102  }
   103  
   104  func init() {
   105  	go func() {
   106  		// Wait for the datadir to be set and exist
   107  		dataDir := ""
   108  		_, err := os.Stat(dataDir)
   109  		for dataDir == "" || os.IsNotExist(err) {
   110  			time.Sleep(time.Millisecond * 50)
   111  			exitMutex.RLock()
   112  			if exitMetricInit {
   113  				exitMutex.RUnlock()
   114  				return
   115  			}
   116  			exitMutex.RUnlock()
   117  
   118  			dataDir = traceability.GetDataDirPath()
   119  			_, err = os.Stat(dataDir)
   120  		}
   121  		GetMetricCollector()
   122  	}()
   123  }
   124  
   125  func (qi *usageEventQueueItem) GetEvent() interface{} {
   126  	return qi.event
   127  }
   128  
   129  func (qi *usageEventQueueItem) GetUsageMetric() interface{} {
   130  	return qi.usageMetric
   131  }
   132  
   133  func (qi *usageEventQueueItem) GetVolumeMetric() interface{} {
   134  	return qi.volumeMetric
   135  }
   136  
   137  var globalMetricCollector Collector
   138  
   139  // GetMetricCollector - Create metric collector
   140  func GetMetricCollector() Collector {
   141  	// There are beat params on execution that doesn't require central config to be instantiated
   142  	if agent.GetCentralConfig() == nil {
   143  		// if this is the case, check central config and if not instantiated, return nil
   144  		return nil
   145  	}
   146  
   147  	if globalMetricCollector == nil && util.IsNotTest() {
   148  		globalMetricCollector = createMetricCollector()
   149  	}
   150  
   151  	return globalMetricCollector
   152  }
   153  
   154  func createMetricCollector() Collector {
   155  	logger := log.NewFieldLogger().
   156  		WithPackage("sdk.transaction.metric").
   157  		WithComponent("collector")
   158  	metricCollector := &collector{
   159  		// Set the initial start time to be minimum 1m behind, so that the job can generate valid event
   160  		// if any usage event are to be generated on startup
   161  		usageStartTime:   now().Add(-1 * time.Minute),
   162  		metricStartTime:  now().Add(-1 * time.Minute),
   163  		lock:             &sync.Mutex{},
   164  		batchLock:        &sync.Mutex{},
   165  		metricMapLock:    &sync.Mutex{},
   166  		registry:         metrics.NewRegistry(),
   167  		metricMap:        make(map[string]map[string]map[string]map[string]*APIMetric),
   168  		publishItemQueue: make([]publishQueueItem, 0),
   169  		metricConfig:     agent.GetCentralConfig().GetMetricReportingConfig(),
   170  		usageConfig:      agent.GetCentralConfig().GetUsageReportingConfig(),
   171  		logger:           logger,
   172  		metricLogger:     log.NewMetricFieldLogger(),
   173  	}
   174  
   175  	// Create and initialize the storage cache for usage/metric and offline report cache by loading from disk
   176  	metricCollector.storage = newStorageCache(metricCollector)
   177  	metricCollector.storage.initialize()
   178  	metricCollector.reports = newReportCache()
   179  	metricCollector.usagePublisher = newUsagePublisher(metricCollector.storage, metricCollector.reports)
   180  
   181  	if util.IsNotTest() {
   182  		var err error
   183  		if !metricCollector.usageConfig.IsOfflineMode() {
   184  			metricCollector.jobID, err = jobs.RegisterScheduledJobWithName(metricCollector, metricCollector.metricConfig.GetSchedule(), "Metric Collector")
   185  		} else {
   186  			metricCollector.jobID, err = jobs.RegisterScheduledJobWithName(metricCollector, metricCollector.usageConfig.GetOfflineSchedule(), "Metric Collector")
   187  		}
   188  		if err != nil {
   189  			panic(err)
   190  		}
   191  	}
   192  
   193  	return metricCollector
   194  }
   195  
   196  // Status - returns the status of the metric collector
   197  func (c *collector) Status() error {
   198  	return nil
   199  }
   200  
   201  // Ready - indicates that the collector job is ready to process
   202  func (c *collector) Ready() bool {
   203  	// Wait until any existing offline reports are saved
   204  	if c.usageConfig.IsOfflineMode() && !c.usagePublisher.isReady() {
   205  		return false
   206  	}
   207  	return agent.GetCentralConfig().GetEnvironmentID() != ""
   208  }
   209  
   210  // Execute - process the metric collection and generation of usage/metric event
   211  func (c *collector) Execute() error {
   212  	c.lock.Lock()
   213  	defer c.lock.Unlock()
   214  
   215  	if !c.usagePublisher.offline && healthcheck.GetStatus(traceability.HealthCheckEndpoint) != healthcheck.OK {
   216  		c.logger.Warn("traceability is not connected, can not publish metrics at this time")
   217  		return nil
   218  	}
   219  
   220  	c.usageEndTime = now()
   221  	c.metricEndTime = now()
   222  	c.orgGUID = c.getOrgGUID()
   223  
   224  	usageMsg := "updating working usage report file"
   225  	if !c.usageConfig.IsOfflineMode() {
   226  		usageMsg = "caching usage event"
   227  		c.logger.
   228  			WithField(startTimestampStr, util.ConvertTimeToMillis(c.metricStartTime)).
   229  			WithField(endTimestampStr, util.ConvertTimeToMillis(c.metricEndTime)).
   230  			WithField(eventTypeStr, metricStr).
   231  			Debug("generating metric events")
   232  	}
   233  
   234  	c.logger.
   235  		WithField(startTimestampStr, util.ConvertTimeToMillis(c.usageStartTime)).
   236  		WithField(endTimestampStr, util.ConvertTimeToMillis(c.usageEndTime)).
   237  		WithField(eventTypeStr, usageStr).
   238  		Debug(usageMsg)
   239  
   240  	defer c.cleanup()
   241  	c.generateEvents()
   242  	c.publishEvents()
   243  	return nil
   244  }
   245  
   246  func (c *collector) InitializeBatch() {
   247  	c.lock.Lock()
   248  	defer c.lock.Unlock()
   249  	c.metricBatch = NewEventBatch(c)
   250  }
   251  
   252  // AddMetric - add metric for API transaction to collection
   253  func (c *collector) AddMetric(apiDetails models.APIDetails, statusCode string, duration, bytes int64, appName string) {
   254  	c.lock.Lock()
   255  	defer c.lock.Unlock()
   256  	c.batchLock.Lock()
   257  	defer c.batchLock.Unlock()
   258  	c.updateUsage(1)
   259  	c.updateVolume(bytes)
   260  }
   261  
   262  // AddMetricDetail - add metric for API transaction and consumer subscription to collection
   263  func (c *collector) AddMetricDetail(metricDetail Detail) {
   264  	c.AddMetric(metricDetail.APIDetails, metricDetail.StatusCode, metricDetail.Duration, metricDetail.Bytes, metricDetail.APIDetails.Name)
   265  	c.createOrUpdateMetric(metricDetail)
   266  }
   267  
   268  // AddMetricDetailSet - add metric details for several response codes and transactions
   269  func (c *collector) AddAPIMetricDetail(detail MetricDetail) {
   270  	if !c.metricConfig.CanPublish() || c.usageConfig.IsOfflineMode() {
   271  		return
   272  	}
   273  
   274  	newMetric, _ := c.createMetric(TransactionContext{detail.APIDetails, detail.AppDetails, detail.StatusCode})
   275  	// update the new metric with all the necessary details
   276  	newMetric.Count = detail.Count
   277  	newMetric.Response = detail.Response
   278  	newMetric.StartTime = time.UnixMilli(detail.Observation.Start)
   279  	newMetric.Observation = detail.Observation
   280  
   281  	c.AddAPIMetric(newMetric)
   282  }
   283  
   284  // AddAPIMetric - add api metric for API transaction
   285  func (c *collector) AddAPIMetric(metric *APIMetric) {
   286  	if metric.EventID == "" {
   287  		metric.EventID = uuid.NewString()
   288  	}
   289  	metric.Status = c.getStatusText(metric.StatusCode)
   290  
   291  	v4Event := c.createV4Event(metric.Observation.Start, metric)
   292  	metricData, _ := json.Marshal(v4Event)
   293  	pubEvent, err := (&CondorMetricEvent{
   294  		Message:   string(metricData),
   295  		Fields:    make(map[string]interface{}),
   296  		Timestamp: v4Event.Data.GetStartTime(),
   297  		ID:        v4Event.ID,
   298  	}).CreateEvent()
   299  	if err != nil {
   300  		return
   301  	}
   302  	c.updateUsage(metric.Count)
   303  	c.metricBatch.AddEventWithoutHistogram(pubEvent)
   304  }
   305  
   306  func (c *collector) Publish() {
   307  	c.metricBatch.Publish()
   308  }
   309  
   310  func (c *collector) ShutdownPublish() {
   311  	c.Execute()
   312  	c.usagePublisher.Execute()
   313  }
   314  
   315  func (c *collector) updateVolume(bytes int64) {
   316  	if !agent.GetCentralConfig().IsAxwayManaged() {
   317  		return // no need to update volume for customer managed
   318  	}
   319  	transactionVolume := c.getOrRegisterCounter(transactionVolumeMetric)
   320  	transactionVolume.Inc(bytes)
   321  	c.storage.updateVolume(transactionVolume.Count())
   322  }
   323  
   324  func (c *collector) updateUsage(count int64) {
   325  	transactionCount := c.getOrRegisterCounter(transactionCountMetric)
   326  	transactionCount.Inc(count)
   327  	c.storage.updateUsage(int(transactionCount.Count()))
   328  }
   329  
   330  func (c *collector) createMetric(detail TransactionContext) (*APIMetric, []string) {
   331  	// Go get the access request and managed app
   332  	accessRequest, managedApp := c.getAccessRequestAndManagedApp(agent.GetCacheManager(), detail)
   333  
   334  	// Update consumer details
   335  	subRef := v1.Reference{
   336  		ID:   unknown,
   337  		Name: unknown,
   338  	}
   339  	if accessRequest != nil {
   340  		accessReqSub := accessRequest.GetReferenceByGVK(catalog.SubscriptionGVK())
   341  		if accessReqSub.ID != "" {
   342  			subRef = accessReqSub
   343  		}
   344  	}
   345  
   346  	appDetail := c.createAppDetail(managedApp)
   347  
   348  	// consumer, subscriptionID, appID, apiID, status
   349  	histogramKeyParts := []string{"consumer", subRef.ID, appDetail.ID, strings.ReplaceAll(detail.APIDetails.ID, ".", "#"), detail.StatusCode}
   350  
   351  	return &APIMetric{
   352  		Subscription:  c.createSubscriptionDetail(subRef),
   353  		App:           appDetail,
   354  		Product:       c.getProduct(accessRequest, c.logger),
   355  		API:           c.createAPIDetail(detail.APIDetails, accessRequest),
   356  		AssetResource: c.getAssetResource(accessRequest, c.logger),
   357  		ProductPlan:   c.getProductPlan(accessRequest, c.logger),
   358  		Quota:         c.getQuota(accessRequest, c.logger),
   359  		StatusCode:    detail.StatusCode,
   360  		Status:        c.getStatusText(detail.StatusCode),
   361  		StartTime:     now(),
   362  		EventID:       uuid.NewString(),
   363  	}, histogramKeyParts
   364  }
   365  
   366  func (c *collector) createOrUpdateMetric(detail Detail) *APIMetric {
   367  	if !c.metricConfig.CanPublish() || c.usageConfig.IsOfflineMode() {
   368  		return nil // no need to update metrics with publish off
   369  	}
   370  
   371  	metric, keyParts := c.createMetric(TransactionContext{detail.APIDetails, detail.AppDetails, detail.StatusCode})
   372  	histogram := c.getOrRegisterHistogram(strings.Join(keyParts, "."))
   373  
   374  	c.metricMapLock.Lock()
   375  	defer c.metricMapLock.Unlock()
   376  	if _, ok := c.metricMap[keyParts[1]]; !ok {
   377  		c.metricMap[keyParts[1]] = make(map[string]map[string]map[string]*APIMetric)
   378  	}
   379  	if _, ok := c.metricMap[keyParts[1]][keyParts[2]]; !ok {
   380  		c.metricMap[keyParts[1]][keyParts[2]] = make(map[string]map[string]*APIMetric)
   381  	}
   382  	if _, ok := c.metricMap[keyParts[1]][keyParts[2]][keyParts[3]]; !ok {
   383  		c.metricMap[keyParts[1]][keyParts[2]][keyParts[3]] = make(map[string]*APIMetric)
   384  	}
   385  	if _, ok := c.metricMap[keyParts[1]][keyParts[2]][keyParts[3]][keyParts[4]]; !ok {
   386  		// First api metric for sub+app+api+statuscode,
   387  		// setup the start time to be used for reporting metric event
   388  		c.metricMap[keyParts[1]][keyParts[2]][keyParts[3]][keyParts[4]] = metric
   389  	}
   390  
   391  	histogram.Update(detail.Duration)
   392  	c.storage.updateMetric(histogram, c.metricMap[keyParts[1]][keyParts[2]][keyParts[3]][keyParts[4]])
   393  	return c.metricMap[keyParts[1]][keyParts[2]][keyParts[3]][keyParts[4]]
   394  }
   395  
   396  // getAccessRequest -
   397  func (c *collector) getAccessRequestAndManagedApp(cacheManager cache.Manager, detail TransactionContext) (*management.AccessRequest, *v1.ResourceInstance) {
   398  	if detail.AppDetails.Name == "" {
   399  		return nil, nil
   400  	}
   401  
   402  	c.logger.
   403  		WithField("apiID", detail.APIDetails.ID).
   404  		WithField("stage", detail.APIDetails.Stage).
   405  		Trace("metric collector information")
   406  
   407  	// get the managed application
   408  	managedApp := cacheManager.GetManagedApplicationByName(detail.AppDetails.Name)
   409  	if managedApp == nil {
   410  		c.logger.
   411  			WithField("appName", detail.AppDetails.Name).
   412  			Trace("could not get managed application by name, return empty API metrics")
   413  		return nil, nil
   414  	}
   415  	c.logger.
   416  		WithField("appName", detail.AppDetails.Name).
   417  		WithField("managed-app-name", managedApp.Name).
   418  		Trace("managed application info")
   419  
   420  	// get the access request
   421  	accessRequest := transutil.GetAccessRequest(cacheManager, managedApp, detail.APIDetails.ID, detail.APIDetails.Stage, detail.APIDetails.Version)
   422  	if accessRequest == nil {
   423  		c.logger.
   424  			Debug("could not get access request, return empty API metrics")
   425  		return nil, nil
   426  	}
   427  	c.logger.
   428  		WithField("managed-app-name", managedApp.Name).
   429  		WithField("apiID", detail.APIDetails.ID).
   430  		WithField("stage", detail.APIDetails.Stage).
   431  		WithField("access-request-name", accessRequest.Name).
   432  		Trace("managed application info")
   433  
   434  	return accessRequest, managedApp
   435  }
   436  
   437  func (c *collector) createSubscriptionDetail(subRef v1.Reference) models.Subscription {
   438  	detail := models.Subscription{
   439  		ID:   unknown,
   440  		Name: unknown,
   441  	}
   442  
   443  	if subRef.ID != "" && subRef.Name != "" {
   444  		detail.ID = subRef.ID
   445  		detail.Name = subRef.Name
   446  	}
   447  	return detail
   448  }
   449  
   450  func (c *collector) createAppDetail(app *v1.ResourceInstance) models.AppDetails {
   451  	detail := models.AppDetails{
   452  		ID:   unknown,
   453  		Name: unknown,
   454  	}
   455  
   456  	if app != nil {
   457  		detail.ID, detail.Name = c.getConsumerApplication(app)
   458  		detail.ConsumerOrgID = c.getConsumerOrgID(app)
   459  	}
   460  	return detail
   461  }
   462  
   463  func (c *collector) createAPIDetail(api models.APIDetails, accessReq *management.AccessRequest) models.APIDetails {
   464  	detail := models.APIDetails{
   465  		ID:                 api.ID,
   466  		Name:               api.Name,
   467  		Revision:           api.Revision,
   468  		TeamID:             api.TeamID,
   469  		APIServiceInstance: unknown,
   470  	}
   471  
   472  	if accessReq != nil {
   473  		detail.APIServiceInstance = accessReq.Spec.ApiServiceInstance
   474  	}
   475  	return detail
   476  }
   477  
   478  func (c *collector) getAssetResource(accessRequest *management.AccessRequest, log log.FieldLogger) models.AssetResource {
   479  	// Set default to provider details in case access request or managed apps comes back nil
   480  	assetResource := models.AssetResource{
   481  		ID:   unknown,
   482  		Name: unknown,
   483  	}
   484  
   485  	if accessRequest == nil {
   486  		log.Trace("access request is nil. Setting default values to unknown")
   487  		return assetResource
   488  	}
   489  
   490  	assetResourceRef := accessRequest.GetReferenceByGVK(catalog.AssetResourceGVK())
   491  	if assetResourceRef.ID == "" || assetResourceRef.Name == "" {
   492  		log.Trace("could not get asset resource, setting asset resource to unknown")
   493  	} else {
   494  		assetResource.ID = assetResourceRef.ID
   495  		assetResource.Name = assetResourceRef.Name
   496  	}
   497  	log.WithField("asset-resource-id", assetResource.ID).
   498  		WithField("asset-resource-name", assetResource.Name).
   499  		Trace("asset resource information")
   500  
   501  	return assetResource
   502  }
   503  
   504  func (c *collector) getProduct(accessRequest *management.AccessRequest, log log.FieldLogger) models.Product {
   505  	product := models.Product{
   506  		ID:          unknown,
   507  		Name:        unknown,
   508  		VersionID:   unknown,
   509  		VersionName: unknown,
   510  	}
   511  
   512  	if accessRequest == nil {
   513  		log.Trace("access request is nil. Setting default values to unknown")
   514  		return product
   515  	}
   516  
   517  	productRef := accessRequest.GetReferenceByGVK(catalog.ProductGVK())
   518  	if productRef.ID == "" || productRef.Name == "" {
   519  		log.Trace("could not get product information, setting product to unknown")
   520  	} else {
   521  		product.ID = productRef.ID
   522  		product.Name = productRef.Name
   523  	}
   524  
   525  	productReleaseRef := accessRequest.GetReferenceByGVK(catalog.ProductReleaseGVK())
   526  	if productReleaseRef.ID == "" || productReleaseRef.Name == "" {
   527  		log.Trace("could not get product release information, setting product release to unknown")
   528  	} else {
   529  		product.VersionID = productReleaseRef.ID
   530  		product.VersionName = productReleaseRef.Name
   531  	}
   532  	log.WithField("product-id", product.ID).
   533  		WithField("product-name", product.Name).
   534  		WithField("product-version-id", product.VersionID).
   535  		WithField("product-version-name", product.VersionName).
   536  		Trace("product information")
   537  	return product
   538  
   539  }
   540  
   541  func (c *collector) getProductPlan(accessRequest *management.AccessRequest, log log.FieldLogger) models.ProductPlan {
   542  	productPlan := models.ProductPlan{
   543  		ID: unknown,
   544  	}
   545  
   546  	if accessRequest == nil {
   547  		log.Trace("access request is nil. Setting default values to unknown")
   548  		return productPlan
   549  	}
   550  
   551  	productPlanRef := accessRequest.GetReferenceByGVK(catalog.ProductPlanGVK())
   552  	if productPlanRef.ID == "" {
   553  		log.Debug("could not get product plan ID, setting product plan to unknown")
   554  	} else {
   555  		productPlan.ID = productPlanRef.ID
   556  	}
   557  	log.WithField("product-plan-id", productPlan.ID).
   558  		Trace("product plan ID information")
   559  
   560  	return productPlan
   561  }
   562  
   563  func (c *collector) getQuota(accessRequest *management.AccessRequest, log log.FieldLogger) models.Quota {
   564  	quota := models.Quota{
   565  		ID: unknown,
   566  	}
   567  	if accessRequest == nil {
   568  		log.Trace("access request or managed app is nil. Setting default values to unknown")
   569  		return quota
   570  	}
   571  	quotaRef := accessRequest.GetReferenceByGVK(catalog.QuotaGVK())
   572  	if quotaRef.ID == "" {
   573  		log.Debug("could not get quota ID, setting quota to unknown")
   574  	} else {
   575  		quota.ID = quotaRef.ID
   576  	}
   577  	log.WithField("quota-id", quota.ID).
   578  		Trace("quota ID information")
   579  
   580  	return quota
   581  }
   582  
   583  func (c *collector) cleanup() {
   584  	c.publishItemQueue = make([]publishQueueItem, 0)
   585  }
   586  
   587  func (c *collector) getOrgGUID() string {
   588  	authToken, _ := agent.GetCentralAuthToken()
   589  	parser := new(jwt.Parser)
   590  	parser.SkipClaimsValidation = true
   591  
   592  	claims := jwt.MapClaims{}
   593  	_, _, err := parser.ParseUnverified(authToken, claims)
   594  	if err != nil {
   595  		return ""
   596  	}
   597  
   598  	claim, ok := claims["org_guid"]
   599  	if ok {
   600  		return claim.(string)
   601  	}
   602  	return ""
   603  }
   604  
   605  func (c *collector) generateEvents() {
   606  	if agent.GetCentralConfig().GetEnvironmentID() == "" || cmd.GetBuildDataPlaneType() == "" {
   607  		c.logger.Warn("Unable to process usage and metric event generation. Please verify the agent config")
   608  		return
   609  	}
   610  
   611  	c.metricBatch = NewEventBatch(c)
   612  	c.registry.Each(c.processRegistry)
   613  
   614  	if len(c.metricBatch.events) == 0 && !c.usageConfig.IsOfflineMode() {
   615  		c.logger.
   616  			WithField(startTimestampStr, util.ConvertTimeToMillis(c.metricStartTime)).
   617  			WithField(endTimestampStr, util.ConvertTimeToMillis(c.metricEndTime)).
   618  			WithField(eventTypeStr, metricStr).
   619  			Info("no metric events generated as no transactions recorded")
   620  	}
   621  
   622  	if c.metricConfig.CanPublish() {
   623  		err := c.metricBatch.Publish()
   624  		if err != nil {
   625  			c.logger.WithError(err).Errorf("could not send metric event, data is kept and will be added to the next trigger interval")
   626  		}
   627  	}
   628  }
   629  
   630  func (c *collector) processRegistry(name string, metric interface{}) {
   631  	switch {
   632  	case name == transactionCountMetric:
   633  		if c.usageConfig.CanPublish() {
   634  			c.generateUsageEvent(c.orgGUID)
   635  		} else {
   636  			c.logger.Info("Publishing the usage event is turned off")
   637  		}
   638  
   639  	// case transactionVolumeMetric:
   640  	case name == transactionVolumeMetric:
   641  		return // skip volume metric as it is handled with Count metric
   642  	default:
   643  		c.processMetric(name, metric)
   644  	}
   645  }
   646  
   647  func (c *collector) generateUsageEvent(orgGUID string) {
   648  	// skip generating a report if no usage when online
   649  	if c.getOrRegisterCounter(transactionCountMetric).Count() == 0 && !c.usageConfig.IsOfflineMode() {
   650  		return
   651  	}
   652  
   653  	usageMap := map[string]int64{
   654  		fmt.Sprintf("%s.%s", cmd.GetBuildDataPlaneType(), lighthouseTransactions): c.getOrRegisterCounter(transactionCountMetric).Count(),
   655  	}
   656  	c.logger.
   657  		WithField(startTimestampStr, util.ConvertTimeToMillis(c.usageStartTime)).
   658  		WithField(endTimestampStr, util.ConvertTimeToMillis(c.usageEndTime)).
   659  		WithField(countStr, c.getOrRegisterCounter(transactionCountMetric).Count()).
   660  		WithField(eventTypeStr, usageStr).
   661  		Info("creating usage event for cache")
   662  
   663  	if agent.GetCentralConfig().IsAxwayManaged() {
   664  		usageMap[fmt.Sprintf("%s.%s", cmd.GetBuildDataPlaneType(), lighthouseVolume)] = c.getOrRegisterCounter(transactionVolumeMetric).Count()
   665  		c.logger.
   666  			WithField(eventTypeStr, volumeStr).
   667  			WithField("total-bytes", c.getOrRegisterCounter(transactionVolumeMetric).Count()).
   668  			WithField(startTimestampStr, util.ConvertTimeToMillis(c.usageStartTime)).
   669  			WithField(endTimestampStr, util.ConvertTimeToMillis(c.usageEndTime)).
   670  			Infof("creating volume event for cache")
   671  	}
   672  
   673  	granularity := c.usageConfig.GetReportGranularity()
   674  	// for offline usage reporting granularity computed with offline schedule
   675  	if granularity == 0 {
   676  		granularity = c.metricConfig.GetReportGranularity()
   677  	}
   678  
   679  	reportTime := c.usageStartTime.Format(ISO8601)
   680  	if c.usageConfig.IsOfflineMode() {
   681  		reportTime = c.usageEndTime.Add(time.Duration(-1*granularity) * time.Millisecond).Format(ISO8601)
   682  	}
   683  
   684  	usageEvent := UsageEvent{
   685  		OrgGUID:     orgGUID,
   686  		EnvID:       agent.GetCentralConfig().GetEnvironmentID(),
   687  		Timestamp:   ISO8601Time(c.usageEndTime),
   688  		SchemaID:    c.usageConfig.GetURL() + schemaPath,
   689  		Granularity: granularity,
   690  		Report: map[string]UsageReport{
   691  			reportTime: {
   692  				Product: cmd.GetBuildDataPlaneType(),
   693  				Usage:   usageMap,
   694  				Meta:    make(map[string]interface{}),
   695  			},
   696  		},
   697  		Meta: map[string]interface{}{
   698  			"AgentName":       agent.GetCentralConfig().GetAgentName(),
   699  			"AgentVersion":    cmd.BuildVersion,
   700  			"AgentType":       cmd.BuildAgentName,
   701  			"AgentSDKVersion": cmd.SDKBuildVersion,
   702  		},
   703  	}
   704  
   705  	queueItem := &usageEventQueueItem{
   706  		event:        usageEvent,
   707  		usageMetric:  c.getOrRegisterCounter(transactionCountMetric),
   708  		volumeMetric: c.getOrRegisterCounter(transactionVolumeMetric),
   709  	}
   710  	c.publishItemQueue = append(c.publishItemQueue, queueItem)
   711  }
   712  
   713  func (c *collector) processMetric(metricName string, metric interface{}) {
   714  	c.metricMapLock.Lock()
   715  	defer c.metricMapLock.Unlock()
   716  	elements := strings.Split(metricName, ".")
   717  	if len(elements) == 5 {
   718  		subscriptionID := elements[1]
   719  		appID := elements[2]
   720  		apiID := strings.ReplaceAll(elements[3], "#", ".")
   721  		statusCode := elements[4]
   722  		if appMap, ok := c.metricMap[subscriptionID]; ok {
   723  			if apiMap, ok := appMap[appID]; ok {
   724  				if statusMap, ok := apiMap[apiID]; ok {
   725  					if statusDetail, ok := statusMap[statusCode]; ok {
   726  						statusMetric := (metric.(metrics.Histogram))
   727  						c.setMetricsFromHistogram(statusDetail, statusMetric)
   728  						c.generateMetricEvent(statusMetric, statusDetail)
   729  					}
   730  				}
   731  			}
   732  		}
   733  	}
   734  }
   735  
   736  func (c *collector) setMetricsFromHistogram(metrics *APIMetric, histogram metrics.Histogram) {
   737  	metrics.Count = histogram.Count()
   738  	metrics.Response.Max = histogram.Max()
   739  	metrics.Response.Min = histogram.Min()
   740  	metrics.Response.Avg = histogram.Mean()
   741  }
   742  
   743  func (c *collector) generateMetricEvent(histogram metrics.Histogram, metric *APIMetric) {
   744  	if metric.Count == 0 {
   745  		return
   746  	}
   747  
   748  	metric.Observation.Start = util.ConvertTimeToMillis(c.metricStartTime)
   749  	metric.Observation.End = util.ConvertTimeToMillis(c.metricEndTime)
   750  	// Generate app subscription metric
   751  	c.generateV4Event(histogram, metric)
   752  }
   753  
   754  func (c *collector) createV4Event(startTime int64, v4data V4Data) V4Event {
   755  	return V4Event{
   756  		ID:        v4data.GetEventID(),
   757  		Timestamp: startTime,
   758  		Event:     metricEvent,
   759  		App:       c.orgGUID,
   760  		Version:   "4",
   761  		Distribution: &V4EventDistribution{
   762  			Environment: agent.GetCentralConfig().GetEnvironmentID(),
   763  			Version:     "1",
   764  		},
   765  		Data: v4data,
   766  	}
   767  }
   768  
   769  func (c *collector) generateV4Event(histogram metrics.Histogram, v4data V4Data) {
   770  	generatedEvent := c.createV4Event(c.metricStartTime.UnixMilli(), v4data)
   771  	c.metricLogger.WithFields(generatedEvent.getLogFields()).Info("generated")
   772  	AddCondorMetricEventToBatch(generatedEvent, c.metricBatch, histogram)
   773  }
   774  
   775  func (c *collector) getOrRegisterCounter(name string) metrics.Counter {
   776  	counter := c.registry.Get(name)
   777  	if counter == nil {
   778  		counter = metrics.NewCounter()
   779  		c.registry.Register(name, counter)
   780  	}
   781  	return counter.(metrics.Counter)
   782  }
   783  
   784  func (c *collector) getOrRegisterHistogram(name string) metrics.Histogram {
   785  	histogram := c.registry.Get(name)
   786  	if histogram == nil {
   787  		sampler := metrics.NewUniformSample(2048)
   788  		histogram = metrics.NewHistogram(sampler)
   789  		c.registry.Register(name, histogram)
   790  	}
   791  	return histogram.(metrics.Histogram)
   792  }
   793  
   794  func (c *collector) publishEvents() {
   795  	if len(c.publishItemQueue) > 0 {
   796  		defer c.storage.save()
   797  
   798  		for _, eventQueueItem := range c.publishItemQueue {
   799  			err := c.usagePublisher.publishEvent(eventQueueItem.GetEvent())
   800  			if err != nil {
   801  				c.logger.
   802  					WithError(err).
   803  					WithField(startTimestampStr, util.ConvertTimeToMillis(c.usageStartTime)).
   804  					WithField(endTimestampStr, util.ConvertTimeToMillis(c.usageEndTime)).
   805  					WithField(eventTypeStr, usageStr).
   806  					Error("failed to add usage report to cache. Current usage report is kept and will be added to the next interval")
   807  			} else {
   808  				c.logger.
   809  					WithField(startTimestampStr, util.ConvertTimeToMillis(c.usageStartTime)).
   810  					WithField(endTimestampStr, util.ConvertTimeToMillis(c.usageEndTime)).
   811  					Info("added usage report to cache")
   812  				c.cleanupCounters(eventQueueItem)
   813  			}
   814  		}
   815  	}
   816  }
   817  
   818  func (c *collector) cleanupCounters(eventQueueItem publishQueueItem) {
   819  	usageEventItem, ok := eventQueueItem.(usageEventPublishItem)
   820  	if ok {
   821  		c.cleanupUsageCounter(usageEventItem)
   822  	}
   823  }
   824  
   825  func (c *collector) cleanupUsageCounter(usageEventItem usageEventPublishItem) {
   826  	itemUsageMetric := usageEventItem.GetUsageMetric()
   827  	if usage, ok := itemUsageMetric.(metrics.Counter); ok {
   828  		// Clean up the usage counter and reset the start time to current endTime
   829  		usage.Clear()
   830  		itemVolumeMetric := usageEventItem.GetVolumeMetric()
   831  		if volume, ok := itemVolumeMetric.(metrics.Counter); ok {
   832  			volume.Clear()
   833  		}
   834  		c.usageStartTime = c.usageEndTime
   835  		c.storage.updateUsage(0)
   836  		c.storage.updateVolume(0)
   837  	}
   838  }
   839  
   840  func (c *collector) logMetric(msg string, metric *APIMetric) {
   841  	c.metricLogger.WithField("id", metric.EventID).Info(msg)
   842  }
   843  
   844  func (c *collector) cleanupMetricCounter(histogram metrics.Histogram, metric *APIMetric) {
   845  	c.metricMapLock.Lock()
   846  	defer c.metricMapLock.Unlock()
   847  	subID := metric.Subscription.ID
   848  	appID := metric.App.ID
   849  	apiID := metric.API.ID
   850  	statusCode := metric.StatusCode
   851  	if consumerAppMap, ok := c.metricMap[subID]; ok {
   852  		if apiMap, ok := consumerAppMap[appID]; ok {
   853  			if apiStatusMap, ok := apiMap[apiID]; ok {
   854  				c.storage.removeMetric(apiStatusMap[statusCode])
   855  				delete(c.metricMap[subID][appID][apiID], statusCode)
   856  				histogram.Clear()
   857  			}
   858  			if len(c.metricMap[subID][appID][apiID]) == 0 {
   859  				delete(c.metricMap[subID][appID], apiID)
   860  			}
   861  		}
   862  		if len(c.metricMap[subID][appID]) == 0 {
   863  			delete(c.metricMap[subID], appID)
   864  		}
   865  	}
   866  	if len(c.metricMap[subID]) == 0 {
   867  		delete(c.metricMap, subID)
   868  	}
   869  	c.logger.
   870  		WithField(startTimestampStr, util.ConvertTimeToMillis(c.usageStartTime)).
   871  		WithField(endTimestampStr, util.ConvertTimeToMillis(c.usageEndTime)).
   872  		WithField("api-name", metric.API.Name).
   873  		Info("Published metrics report for API")
   874  }
   875  
   876  func (c *collector) getStatusText(statusCode string) string {
   877  	httpStatusCode, _ := strconv.Atoi(statusCode)
   878  	switch {
   879  	case httpStatusCode >= 100 && httpStatusCode < 400:
   880  		return "Success"
   881  	case httpStatusCode >= 400 && httpStatusCode < 500:
   882  		return "Failure"
   883  	default:
   884  		return "Exception"
   885  	}
   886  }
   887  
   888  func (c *collector) getConsumerOrgID(ri *v1.ResourceInstance) string {
   889  	if ri == nil {
   890  		return ""
   891  	}
   892  
   893  	// Lookup Subscription
   894  	app := &management.ManagedApplication{}
   895  	app.FromInstance(ri)
   896  
   897  	if app.Marketplace.Resource.Owner != nil {
   898  		return app.Marketplace.Resource.Owner.Organization.ID
   899  	}
   900  	return ""
   901  }
   902  
   903  func (c *collector) getConsumerApplication(ri *v1.ResourceInstance) (string, string) {
   904  	if ri == nil {
   905  		return "", ""
   906  	}
   907  
   908  	for _, ref := range ri.Metadata.References {
   909  		// get the ID of the Catalog Application
   910  		if ref.Kind == catalog.ApplicationGVK().Kind {
   911  			return ref.ID, ref.Name
   912  		}
   913  	}
   914  
   915  	return ri.Metadata.ID, ri.Name // default to the managed app id
   916  }