| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| |
| package datareq |
| |
| import ( |
| "net/http" |
| "net/url" |
| "time" |
| |
| "github.com/apache/trafficcontrol/lib/go-log" |
| "github.com/apache/trafficcontrol/lib/go-tc" |
| "github.com/apache/trafficcontrol/lib/go-util" |
| "github.com/apache/trafficcontrol/traffic_monitor/cache" |
| "github.com/apache/trafficcontrol/traffic_monitor/srvhttp" |
| "github.com/apache/trafficcontrol/traffic_monitor/threadsafe" |
| "github.com/apache/trafficcontrol/traffic_monitor/todata" |
| jsoniter "github.com/json-iterator/go" |
| ) |
| |
| // msPerNs is the number of milliseconds in a nanosecond. |
| const msPerNs = 1000000 |
| |
| // for whatever reason, all stats are prefixed with this |
| const statPrefix = "ats." |
| |
| // StatSummaryStat represents a summary of a stat's values and changes over a |
| // period of time. |
| type StatSummaryStat struct { |
| // Average is the stat's value's arithmetic mean within the time period. |
| Average float64 `json:"average"` |
| // DataPointCount is the number of measurements of the stat that have been |
| // cataloged in the stat's history. |
| DataPointCount int64 `json:"dpCount"` |
| // End is the final value of the stat at the time of the most recent |
| // measurement. |
| End float64 `json:"end"` |
| // EndTime is the time of the most recent measurement, and defines the end |
| // point of the time period. |
| EndTime int64 `json:"endTime"` |
| // High is the stat's maximum value within the time period. |
| High float64 `json:"high"` |
| // Low is the stat's minimum value within the time period. |
| Low float64 `json:"low"` |
| // Start is the initial value of the stat at the time of the first |
| // measurement. |
| Start float64 `json:"start"` |
| // StartTime is the time of the first measurement, and defines the beginning |
| // of the time period. |
| StartTime int64 `json:"startTime"` |
| } |
| |
| // CacheStatSummary is a summary of a cache server's measured statistics and the |
| // measured statistics of its network interfaces. |
| type CacheStatSummary struct { |
| // InterfaceStats is a map of network interface names to a map of statistic |
| // names to their summaries. |
| InterfaceStats map[string]map[string]StatSummaryStat `json:"interfaceStats"` |
| // Stats is a map of statistic names to summaries of those statistics. |
| Stats map[string]StatSummaryStat `json:"stats"` |
| } |
| |
| type StatSummary struct { |
| Caches map[string]CacheStatSummary `json:"caches"` |
| tc.CommonAPIData |
| } |
| |
| func srvStatSummary(params url.Values, errorCount threadsafe.Uint, path string, toData todata.TODataThreadsafe, statResultHistory threadsafe.ResultStatHistory) ([]byte, int) { |
| filter, err := NewCacheStatFilter(path, params, toData.Get().ServerTypes) |
| if err != nil { |
| HandleErr(errorCount, path, err) |
| return []byte(err.Error()), http.StatusBadRequest |
| } |
| |
| json := jsoniter.ConfigFastest |
| bytes, err := json.Marshal(createStatSummary(statResultHistory, filter, params)) |
| return WrapErrCode(errorCount, path, bytes, err) |
| } |
| |
| func createStatSummary(statResultHistory threadsafe.ResultStatHistory, filter cache.Filter, params url.Values) StatSummary { |
| ss := StatSummary{ |
| Caches: map[string]CacheStatSummary{}, |
| CommonAPIData: srvhttp.GetCommonAPIData(params, time.Now()), |
| } |
| |
| statResultHistory.Range(func(cacheName string, stats threadsafe.CacheStatHistory) bool { |
| if !filter.UseCache(tc.CacheName(cacheName)) { |
| return true |
| } |
| |
| var cacheStats CacheStatSummary |
| |
| ssStats := map[string]StatSummaryStat{} |
| stats.Stats.Range(func(statName string, statHistory []tc.ResultStatVal) bool { |
| if !filter.UseStat(statName) || len(statHistory) == 0 { |
| return true |
| } |
| |
| ssStat := StatSummaryStat{ |
| EndTime: statHistory[0].Time.UnixNano() / msPerNs, |
| DataPointCount: 0, |
| StartTime: statHistory[len(statHistory)-1].Time.UnixNano() / msPerNs, |
| } |
| |
| oldestVal, isOldestValNumeric := util.ToNumeric(statHistory[len(statHistory)-1].Val) |
| newestVal, isNewestValNumeric := util.ToNumeric(statHistory[0].Val) |
| if !isOldestValNumeric || !isNewestValNumeric { |
| return true // skip non-numeric stats |
| } |
| |
| ssStat.End = newestVal |
| ssStat.High = newestVal |
| ssStat.Low = newestVal |
| ssStat.Start = oldestVal |
| |
| for _, val := range statHistory { |
| fVal, ok := util.ToNumeric(val.Val) |
| if !ok { |
| log.Warnf("threshold stat %v value %v is not a number, cannot use.", statName, val.Val) |
| return true |
| } |
| |
| for i := uint64(0); i < val.Span; i++ { |
| ssStat.DataPointCount++ |
| ssStat.Average -= ssStat.Average / float64(ssStat.DataPointCount) |
| ssStat.Average += fVal / float64(ssStat.DataPointCount) |
| } |
| if fVal < ssStat.Low { |
| ssStat.Low = fVal |
| } |
| if fVal > ssStat.High { |
| ssStat.High = fVal |
| } |
| } |
| ssStats[statPrefix+statName] = ssStat |
| return true |
| }) |
| |
| cacheStats.Stats = ssStats |
| |
| infStats := make(map[string]map[string]StatSummaryStat, len(stats.Interfaces)) |
| for infName, infStatHistory := range stats.Interfaces { |
| if _, ok := infStats[infName]; ok { |
| log.Warnf("Somehow found duplicate interface '%s' in stat history for cache server '%s'", infName, cacheName) |
| continue |
| } |
| infStatMap := map[string]StatSummaryStat{} |
| |
| infStatHistory.Range(func(statName string, statHistory []tc.ResultStatVal) bool { |
| if !filter.UseInterfaceStat(statName) || len(statHistory) == 0 { |
| return true |
| } |
| |
| ssStat := StatSummaryStat{ |
| EndTime: statHistory[0].Time.UnixNano() / msPerNs, |
| DataPointCount: 0, |
| StartTime: statHistory[len(statHistory)-1].Time.UnixNano() / msPerNs, |
| } |
| |
| oldestVal, isOldestValNumeric := util.ToNumeric(statHistory[len(statHistory)-1].Val) |
| newestVal, isNewestValNumeric := util.ToNumeric(statHistory[0].Val) |
| if !isOldestValNumeric || !isNewestValNumeric { |
| return true // skip non-numeric stats |
| } |
| |
| ssStat.End = newestVal |
| ssStat.High = newestVal |
| ssStat.Low = newestVal |
| ssStat.Start = oldestVal |
| |
| for _, val := range statHistory { |
| fVal, ok := util.ToNumeric(val.Val) |
| if !ok { |
| log.Warnf("threshold stat %v value %v is not a number, cannot use.", statName, val.Val) |
| return true |
| } |
| |
| for i := uint64(0); i < val.Span; i++ { |
| ssStat.DataPointCount++ |
| ssStat.Average -= ssStat.Average / float64(ssStat.DataPointCount) |
| ssStat.Average += fVal / float64(ssStat.DataPointCount) |
| } |
| if fVal < ssStat.Low { |
| ssStat.Low = fVal |
| } |
| if fVal > ssStat.High { |
| ssStat.High = fVal |
| } |
| } |
| infStatMap[statPrefix+statName] = ssStat |
| return true |
| }) |
| |
| infStats[infName] = infStatMap |
| } |
| |
| cacheStats.InterfaceStats = infStats |
| ss.Caches[cacheName] = cacheStats |
| return true |
| }) |
| return ss |
| } |