eintopf.info@v0.13.16/service/status/status.go (about) 1 // Copyright (C) 2022 The Eintopf authors 2 // 3 // This program is free software: you can redistribute it and/or modify 4 // it under the terms of the GNU Affero General Public License as 5 // published by the Free Software Foundation, either version 3 of the 6 // License, or (at your option) any later version. 7 // 8 // This program is distributed in the hope that it will be useful, 9 // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 // GNU Affero General Public License for more details. 12 // 13 // You should have received a copy of the GNU Affero General Public License 14 // along with this program. If not, see <https://www.gnu.org/licenses/>. 15 16 package status 17 18 import ( 19 "sync" 20 "time" 21 ) 22 23 // Status contains information about the status of a server. 24 type Status struct { 25 Uptime time.Duration `json:"uptime"` 26 Requests uint `json:"requests"` 27 RequestDuration RequestDuration `json:"requestDuration"` 28 } 29 30 // RequestDuration tracks the duration of requests in different metrics. 31 type RequestDuration struct { 32 Min time.Duration `json:"min"` 33 Max time.Duration `json:"max"` 34 Avg time.Duration `json:"avg"` 35 } 36 37 func addDurations(r RequestDuration, durations []time.Duration) RequestDuration { 38 if len(durations) == 0 { 39 return r 40 } 41 avg := time.Duration(0) 42 43 for _, d := range durations { 44 if d < r.Min { 45 r.Min = d 46 } 47 if d > r.Max { 48 r.Max = d 49 } 50 avg += d 51 } 52 53 avg /= time.Duration(len(durations)) 54 if r.Avg == 0 { 55 r.Avg = avg 56 } else { 57 r.Avg = (r.Avg + avg) / 2 58 } 59 60 return r 61 } 62 63 // Service defines a a servies, that collects and provides status information 64 // about a server. 65 // 66 // -go:generate go run github.com/petergtz/pegomock/pegomock generate eintopf.info/service/status Service --output=../../internal/mock/status_service.go --package=mock --mock-name=StatusService 67 type Service interface { 68 // Status returns the current status of the server. 69 Status() Status 70 71 // RequestServed tells the service, that a request was served, with a given 72 // request duration. 73 RequestServed(duration time.Duration) 74 } 75 76 // NewService returns a new status service. 77 func NewService() Service { 78 return &service{ 79 started: time.Now(), 80 requests: 0, 81 requestDurations: make([]time.Duration, 0), 82 m: &sync.Mutex{}, 83 requestDuration: RequestDuration{ 84 Min: 99999999, 85 }, 86 } 87 } 88 89 type service struct { 90 started time.Time 91 92 requests uint 93 requestDurations []time.Duration 94 m *sync.Mutex // protects request/response information 95 96 requestDuration RequestDuration 97 } 98 99 func (s *service) Status() Status { 100 return Status{ 101 Uptime: time.Now().Sub(s.started), 102 Requests: s.requests, 103 RequestDuration: addDurations(s.requestDuration, s.requestDurations), 104 } 105 } 106 107 func (s *service) RequestServed(duration time.Duration) { 108 s.m.Lock() 109 defer s.m.Unlock() 110 111 if len(s.requestDurations) > 1000 { 112 s.requestDuration = addDurations(s.requestDuration, s.requestDurations) 113 s.requestDurations = make([]time.Duration, 0) 114 } 115 s.requests += 1 116 s.requestDurations = append(s.requestDurations, duration) 117 }