mirror of
https://github.com/cgrates/cgrates.git
synced 2026-02-11 18:16:24 +05:00
building new stats packages
This commit is contained in:
@@ -18,12 +18,15 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
package cdrstats
|
||||
|
||||
import "time"
|
||||
|
||||
type Metric interface {
|
||||
AddCDR(*QCDR)
|
||||
RemoveCDR(*QCDR)
|
||||
GetValue() float64
|
||||
}
|
||||
|
||||
func CreateMetric(metric string) *Metric {
|
||||
func CreateMetric(metric string) Metric {
|
||||
switch metric {
|
||||
case "ASR":
|
||||
return &ASRMetric{}
|
||||
@@ -38,23 +41,74 @@ func CreateMetric(metric string) *Metric {
|
||||
// ASR - Answer-Seizure Ratio
|
||||
// successfully answered Calls divided by the total number of Calls attempted and multiplied by 100
|
||||
type ASRMetric struct {
|
||||
sum float64
|
||||
count int64
|
||||
answered float64
|
||||
total float64
|
||||
}
|
||||
|
||||
func (asr *ASRMetric) AddCdr(cdr *QCDR) {
|
||||
func (asr *ASRMetric) AddCDR(cdr *QCDR) {
|
||||
if !cdr.AnswerTime.IsZero() {
|
||||
asr.answered += 1
|
||||
}
|
||||
asr.total += 1
|
||||
}
|
||||
|
||||
func (asr *ASRMetric) RemoveCDR(cdr *QCDR) {
|
||||
if !cdr.AnswerTime.IsZero() {
|
||||
asr.answered -= 1
|
||||
}
|
||||
asr.total -= 1
|
||||
}
|
||||
|
||||
func (asr *ASRMetric) GetValue() float64 {
|
||||
return asr.answered / asr.total * 100
|
||||
}
|
||||
|
||||
// ACD – Average Call Duration
|
||||
// the sum of billable seconds (billsec) of answered calls divided by the number of these answered calls.
|
||||
type ACDMetric struct {
|
||||
sum float64
|
||||
count int64
|
||||
sum time.Duration
|
||||
count float64
|
||||
}
|
||||
|
||||
func (acd *ACDMetric) AddCDR(cdr *QCDR) {
|
||||
if !cdr.AnswerTime.IsZero() {
|
||||
acd.sum += cdr.Usage
|
||||
acd.count += 1
|
||||
}
|
||||
}
|
||||
|
||||
func (acd *ACDMetric) RemoveCDR(cdr *QCDR) {
|
||||
if !cdr.AnswerTime.IsZero() {
|
||||
acd.sum -= cdr.Usage
|
||||
acd.count -= 1
|
||||
}
|
||||
}
|
||||
|
||||
func (acd *ACDMetric) GetValue() float64 {
|
||||
return acd.sum.Seconds() / acd.count
|
||||
}
|
||||
|
||||
// ACC – Average Call Cost
|
||||
// the sum of cost of answered calls divided by the number of these answered calls.
|
||||
type ACCMetric struct {
|
||||
sum float64
|
||||
count int64
|
||||
count float64
|
||||
}
|
||||
|
||||
func (acc *ACCMetric) AddCDR(cdr *QCDR) {
|
||||
if !cdr.AnswerTime.IsZero() && cdr.Cost >= 0 {
|
||||
acc.sum += cdr.Cost
|
||||
acc.count += 1
|
||||
}
|
||||
}
|
||||
|
||||
func (acc *ACCMetric) RemoveCDR(cdr *QCDR) {
|
||||
if !cdr.AnswerTime.IsZero() && cdr.Cost >= 0 {
|
||||
acc.sum -= cdr.Cost
|
||||
acc.count -= 1
|
||||
}
|
||||
}
|
||||
|
||||
func (acc *ACCMetric) GetValue() float64 {
|
||||
return acc.sum / acc.count
|
||||
}
|
||||
|
||||
@@ -18,149 +18,158 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
package cdrstats
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/cgrates/cgrates/config"
|
||||
"github.com/cgrates/cgrates/utils"
|
||||
)
|
||||
|
||||
type StatsQueue struct {
|
||||
cdrs []*QCDR
|
||||
config *CdrStatsConfig
|
||||
metrics map[string]*Metric
|
||||
cdrs []*QCDR
|
||||
conf *config.CdrStatsConfig
|
||||
metrics map[string]Metric
|
||||
}
|
||||
|
||||
// Simplified cdr structure containing only the necessary info
|
||||
type QCDR struct {
|
||||
SetupTime time.Time
|
||||
AnswerTime time.Time
|
||||
Usage time.Duration
|
||||
Cost float64
|
||||
SetupTime time.Time
|
||||
AnswerTime time.Time
|
||||
Usage time.Duration
|
||||
Cost float64
|
||||
}
|
||||
|
||||
func NewStatsQueue(config *CdrStatsConfig) *StatsQueue{
|
||||
sq := &StatsQueue{
|
||||
config:config
|
||||
metrics:=make(map[string]*Metric, len(config.Metrics))
|
||||
}
|
||||
for _, m := range config.Metrics {
|
||||
metric := CreateMetric(m)
|
||||
if metric != nil{
|
||||
func NewStatsQueue(conf *config.CdrStatsConfig) *StatsQueue {
|
||||
sq := &StatsQueue{
|
||||
conf: conf,
|
||||
metrics: make(map[string]Metric, len(conf.Metrics)),
|
||||
}
|
||||
for _, m := range conf.Metrics {
|
||||
metric := CreateMetric(m)
|
||||
if metric != nil {
|
||||
sq.metrics[m] = metric
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return sq
|
||||
}
|
||||
|
||||
func (sq *StatsQueue) AppendCDR(cdr *utils.StoredCdr){
|
||||
if sq.AcceptCDR(cdr){
|
||||
qcdr := sq.SimplifyCDR(cdr)
|
||||
sq.cdrs = append(sq.cdrs, qcdr)
|
||||
sq.AddToMetrics(qcdr)
|
||||
}
|
||||
func (sq *StatsQueue) AppendCDR(cdr *utils.StoredCdr) {
|
||||
if sq.AcceptCDR(cdr) {
|
||||
qcdr := sq.SimplifyCDR(cdr)
|
||||
sq.cdrs = append(sq.cdrs, qcdr)
|
||||
sq.AddToMetrics(qcdr)
|
||||
}
|
||||
}
|
||||
|
||||
func (sq *StatsQueue) AddToMetrics(cdr *QCDR) {
|
||||
for _, metric:= range sq.metrics {
|
||||
metric.AddCdr(cdr)
|
||||
}
|
||||
for _, metric := range sq.metrics {
|
||||
metric.AddCDR(cdr)
|
||||
}
|
||||
}
|
||||
|
||||
func (sq *StatsQueue) RemoveFromMetrics(cdr *QCDR) {
|
||||
for _, metric:= range sq.metrics {
|
||||
metric.RemoveCdr(cdr)
|
||||
}
|
||||
for _, metric := range sq.metrics {
|
||||
metric.RemoveCDR(cdr)
|
||||
}
|
||||
}
|
||||
|
||||
func (sq *StatsQueue) SimplifyCDR(cdr *utils.StoredCdr) *QCDR{
|
||||
return &QCDR{
|
||||
SetupTime: cdr.SetupTime,
|
||||
AnswerTime: cdr.AnswerTime,
|
||||
Usage: cdr.Usage,
|
||||
Cost: cdr.Cost
|
||||
}
|
||||
func (sq *StatsQueue) SimplifyCDR(cdr *utils.StoredCdr) *QCDR {
|
||||
return &QCDR{
|
||||
SetupTime: cdr.SetupTime,
|
||||
AnswerTime: cdr.AnswerTime,
|
||||
Usage: cdr.Usage,
|
||||
Cost: cdr.Cost,
|
||||
}
|
||||
}
|
||||
|
||||
func (sq *StatsQueue) PurgeObsoleteCDRs {
|
||||
currentLength := len(sq.cdrs)
|
||||
if currentLength>sq.config.QueuedItems{
|
||||
for _, cdr := range sq.cdrs[:currentLength-sq.config.QueuedItems] {
|
||||
sq.RemoveFromMetrics(cdr)
|
||||
}
|
||||
sq.cdrs = sq.cdrs[currentLength-sq.config.QueuedItems:]
|
||||
}
|
||||
for i, cdr := range sq.cdrs {
|
||||
if time.Now().Sub(cdr.SetupTime) > sq.config.TimeWindow {
|
||||
sq.RemoveFromMetrics(cdr)
|
||||
continue
|
||||
} else {
|
||||
if i > 0 {
|
||||
sq.cdrs = sq.cdrs[i:]
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
func (sq *StatsQueue) PurgeObsoleteCDRs() {
|
||||
currentLength := len(sq.cdrs)
|
||||
if currentLength > sq.conf.QueuedItems {
|
||||
for _, cdr := range sq.cdrs[:currentLength-sq.conf.QueuedItems] {
|
||||
sq.RemoveFromMetrics(cdr)
|
||||
}
|
||||
sq.cdrs = sq.cdrs[currentLength-sq.conf.QueuedItems:]
|
||||
}
|
||||
for i, cdr := range sq.cdrs {
|
||||
if time.Now().Sub(cdr.SetupTime) > sq.conf.TimeWindow {
|
||||
sq.RemoveFromMetrics(cdr)
|
||||
continue
|
||||
} else {
|
||||
if i > 0 {
|
||||
sq.cdrs = sq.cdrs[i:]
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (sq *StatsQueue) AcceptCDR(cdr *utils.StoredCdr) bool {
|
||||
if len(sq.config.SetupInterval) > 0 {
|
||||
if cdr.SetupTime.Before(sq.config.SetupInterval[0]){
|
||||
return false
|
||||
}
|
||||
if len(sq.config.SetupInterval) > 1 && (cdr.SetupTime.Equals(sq.config.SetupInterval[1]) || cdr.SetupTime.After(sq.config.SetupInterval[1])){
|
||||
return false
|
||||
}
|
||||
}
|
||||
if len(sq.config.TOR) >0 && !utils.IsSliceMember(sq.config.TOR, cdr.TOR){
|
||||
return false
|
||||
}
|
||||
if len(sq.config.CdrHost) >0 && !utils.IsSliceMember(sq.config.CdrHost, cdr.CdrHost){
|
||||
return false
|
||||
}
|
||||
if len(sq.config.CdrSource) >0 && !utils.IsSliceMember(sq.config.CdrSource, cdr.CdrSource){
|
||||
return false
|
||||
}
|
||||
if len(sq.config.ReqType) >0 && !utils.IsSliceMember(sq.config.ReqType, cdr.ReqType){
|
||||
return false
|
||||
}
|
||||
if len(sq.config.Direction) >0 && !utils.IsSliceMember(sq.config.Direction, cdr.Direction){
|
||||
return false
|
||||
}
|
||||
if len(sq.config.Tenant) >0 && !utils.IsSliceMember(sq.config.Tenant, cdr.Tenant){
|
||||
return false
|
||||
}
|
||||
if len(sq.config.Category) >0 && !utils.IsSliceMember(sq.config.Category, cdr.Category){
|
||||
return false
|
||||
}
|
||||
if len(sq.config.Account) >0 && !utils.IsSliceMember(sq.config.Account, cdr.Account){
|
||||
return false
|
||||
}
|
||||
if len(sq.config.Subject) >0 && !utils.IsSliceMember(sq.config.Subject, cdr.Subject){
|
||||
return false
|
||||
}
|
||||
if len(sq.config.DestinationPrefix)>0 {
|
||||
found := false
|
||||
for _, prefix := range sq.config.DestinationPrefix {
|
||||
if cdr.Destination.HasPrefix(prefix){
|
||||
found=true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found{
|
||||
return false
|
||||
}
|
||||
}
|
||||
if len(sq.config.UsageInterval) > 0 {
|
||||
if cdr.Usage < sq.config.UsageInterval[0]{
|
||||
return false
|
||||
}
|
||||
if len(sq.config.UsageInterval) > 1 && cdr.Usage >= sq.config.UsageInterval[1]{
|
||||
return false
|
||||
}
|
||||
}
|
||||
if len(sq.config.MediationRunIds) >0 && !utils.IsSliceMember(sq.config.MediationRunIds, cdr.MediationRunId){
|
||||
return false
|
||||
}
|
||||
if len(sq.config.CostInterval) > 0 {
|
||||
if cdr.Cost < sq.config.CostInterval[0]{
|
||||
return false
|
||||
}
|
||||
if len(sq.config.CostInterval) > 1 && cdr.Cost >= sq.config.CostInterval[1]{
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
if len(sq.conf.SetupInterval) > 0 {
|
||||
if cdr.SetupTime.Before(sq.conf.SetupInterval[0]) {
|
||||
return false
|
||||
}
|
||||
if len(sq.conf.SetupInterval) > 1 && (cdr.SetupTime.Equal(sq.conf.SetupInterval[1]) || cdr.SetupTime.After(sq.conf.SetupInterval[1])) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
if len(sq.conf.TOR) > 0 && !utils.IsSliceMember(sq.conf.TOR, cdr.TOR) {
|
||||
return false
|
||||
}
|
||||
if len(sq.conf.CdrHost) > 0 && !utils.IsSliceMember(sq.conf.CdrHost, cdr.CdrHost) {
|
||||
return false
|
||||
}
|
||||
if len(sq.conf.CdrSource) > 0 && !utils.IsSliceMember(sq.conf.CdrSource, cdr.CdrSource) {
|
||||
return false
|
||||
}
|
||||
if len(sq.conf.ReqType) > 0 && !utils.IsSliceMember(sq.conf.ReqType, cdr.ReqType) {
|
||||
return false
|
||||
}
|
||||
if len(sq.conf.Direction) > 0 && !utils.IsSliceMember(sq.conf.Direction, cdr.Direction) {
|
||||
return false
|
||||
}
|
||||
if len(sq.conf.Tenant) > 0 && !utils.IsSliceMember(sq.conf.Tenant, cdr.Tenant) {
|
||||
return false
|
||||
}
|
||||
if len(sq.conf.Category) > 0 && !utils.IsSliceMember(sq.conf.Category, cdr.Category) {
|
||||
return false
|
||||
}
|
||||
if len(sq.conf.Account) > 0 && !utils.IsSliceMember(sq.conf.Account, cdr.Account) {
|
||||
return false
|
||||
}
|
||||
if len(sq.conf.Subject) > 0 && !utils.IsSliceMember(sq.conf.Subject, cdr.Subject) {
|
||||
return false
|
||||
}
|
||||
if len(sq.conf.DestinationPrefix) > 0 {
|
||||
found := false
|
||||
for _, prefix := range sq.conf.DestinationPrefix {
|
||||
if strings.HasPrefix(cdr.Destination, prefix) {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
return false
|
||||
}
|
||||
}
|
||||
if len(sq.conf.UsageInterval) > 0 {
|
||||
if cdr.Usage < sq.conf.UsageInterval[0] {
|
||||
return false
|
||||
}
|
||||
if len(sq.conf.UsageInterval) > 1 && cdr.Usage >= sq.conf.UsageInterval[1] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
if len(sq.conf.MediationRunIds) > 0 && !utils.IsSliceMember(sq.conf.MediationRunIds, cdr.MediationRunId) {
|
||||
return false
|
||||
}
|
||||
if len(sq.conf.CostInterval) > 0 {
|
||||
if cdr.Cost < sq.conf.CostInterval[0] {
|
||||
return false
|
||||
}
|
||||
if len(sq.conf.CostInterval) > 1 && cdr.Cost >= sq.conf.CostInterval[1] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ import (
|
||||
|
||||
type CdrStatsConfig struct {
|
||||
Id string // Config id, unique per config instance
|
||||
QueuedItems int64 // Number of items in the stats buffer
|
||||
QueuedItems int // Number of items in the stats buffer
|
||||
TimeWindow time.Duration // Will only keep the CDRs who's call setup time is not older than time.Now()-TimeWindow
|
||||
Metrics []string // ASR, ACD, ACC
|
||||
SetupInterval []time.Time // 2 or less items (>= start interval,< stop_interval)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
*utilssssss/*
|
||||
/*
|
||||
Real-time Charging System for Telecom & ISP environments
|
||||
Copyright (C) 2012-2014 ITsysCOM GmbH
|
||||
|
||||
|
||||
Reference in New Issue
Block a user