github.com/mysteriumnetwork/node@v0.0.0-20240516044423-365054f76801/trace/trace.go (about) 1 /* 2 * Copyright (C) 2020 The "MysteriumNetwork/node" Authors. 3 * 4 * This program is free software: you can redistribute it and/or modify 5 * it under the terms of the GNU General Public License as published by 6 * the Free Software Foundation, either version 3 of the License, or 7 * (at your option) any later version. 8 * 9 * This program is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU General Public License for more details. 13 * 14 * You should have received a copy of the GNU General Public License 15 * along with this program. If not, see <http://www.gnu.org/licenses/>. 16 */ 17 18 package trace 19 20 import ( 21 "fmt" 22 "strings" 23 "sync" 24 "time" 25 26 "github.com/mysteriumnetwork/node/eventbus" 27 "github.com/rs/zerolog/log" 28 ) 29 30 const ( 31 // AppTopicTraceEvent represents event topic for Trace events 32 AppTopicTraceEvent = "Trace" 33 ) 34 35 // NewTracer returns new tracer instance. 36 func NewTracer(name string) *Tracer { 37 tracer := &Tracer{ 38 stages: make([]*stage, 0), 39 } 40 tracer.name = tracer.StartStage(name) 41 return tracer 42 } 43 44 // Tracer represents tracer which records stages durations. It can be used 45 // to record stages times for tracing how long it took time. 46 type Tracer struct { 47 name string 48 mu sync.Mutex 49 stages []*stage 50 finished bool 51 } 52 53 // StartStage starts tracing stage for given key. 54 func (t *Tracer) StartStage(key string) string { 55 t.mu.Lock() 56 defer t.mu.Unlock() 57 58 if t.finished { 59 log.Error().Msg("Tracer is already finished") 60 return "" 61 } 62 if _, ok := t.findStage(key); ok { 63 log.Error().Msgf("Stage %s was already started", key) 64 return "" 65 } 66 67 t.stages = append(t.stages, &stage{ 68 key: key, 69 start: time.Now(), 70 }) 71 return key 72 } 73 74 // EndStage ends tracing stage for given key. 75 func (t *Tracer) EndStage(key string) { 76 t.mu.Lock() 77 defer t.mu.Unlock() 78 79 if t.finished { 80 log.Error().Msg("Tracer is already finished") 81 return 82 } 83 s, ok := t.findStage(key) 84 if !ok { 85 log.Error().Msgf("Stage %s was not started", key) 86 return 87 } 88 89 s.end = time.Now() 90 } 91 92 // Finish finishes tracing and returns formatted string with stages durations. 93 func (t *Tracer) Finish(eventPublisher eventbus.Publisher, id string) string { 94 t.EndStage(t.name) 95 96 t.mu.Lock() 97 defer t.mu.Unlock() 98 t.finished = true 99 100 var strs []string 101 for _, s := range t.stages { 102 if s.end.After(time.Time{}) { 103 t.publishStageEvent(eventPublisher, id, *s) 104 strs = append(strs, fmt.Sprintf("%q took %s", s.key, s.end.Sub(s.start).String())) 105 } else { 106 strs = append(strs, fmt.Sprintf("%q did not start", s.key)) 107 } 108 } 109 110 return strings.Join(strs, ", ") 111 } 112 113 func (t *Tracer) findStage(key string) (*stage, bool) { 114 for _, s := range t.stages { 115 if s.key == key { 116 return s, true 117 } 118 } 119 return nil, false 120 } 121 122 func (t *Tracer) publishStageEvent(eventPublisher eventbus.Publisher, id string, stage stage) { 123 if eventPublisher == nil { 124 return 125 } 126 127 eventPublisher.Publish(AppTopicTraceEvent, 128 Event{ 129 ID: id, 130 Key: stage.key, 131 Duration: stage.end.Sub(stage.start), 132 }, 133 ) 134 } 135 136 type stage struct { 137 key string 138 start, end time.Time 139 } 140 141 // Event represents a published Trace event. 142 type Event struct { 143 ID string 144 Key string 145 Duration time.Duration 146 }