go-micro.dev/v5@v5.12.0/debug/handler/debug.go (about) 1 // Package handler implements service debug handler embedded in go-micro services 2 package handler 3 4 import ( 5 "context" 6 "errors" 7 "io" 8 "time" 9 10 "go-micro.dev/v5/client" 11 "go-micro.dev/v5/debug/log" 12 proto "go-micro.dev/v5/debug/proto" 13 "go-micro.dev/v5/debug/stats" 14 "go-micro.dev/v5/debug/trace" 15 ) 16 17 // NewHandler returns an instance of the Debug Handler. 18 func NewHandler(c client.Client) *Debug { 19 return &Debug{ 20 log: log.DefaultLog, 21 stats: stats.DefaultStats, 22 trace: trace.DefaultTracer, 23 } 24 } 25 26 var _ proto.DebugHandler = (*Debug)(nil) 27 28 type Debug struct { 29 // must honor the debug handler 30 proto.DebugHandler 31 // the logger for retrieving logs 32 log log.Log 33 // the stats collector 34 stats stats.Stats 35 // the tracer 36 trace trace.Tracer 37 } 38 39 func (d *Debug) Health(ctx context.Context, req *proto.HealthRequest, rsp *proto.HealthResponse) error { 40 rsp.Status = "ok" 41 return nil 42 } 43 44 func (d *Debug) MessageBus(ctx context.Context, stream proto.Debug_MessageBusStream) error { 45 for { 46 _, err := stream.Recv() 47 if errors.Is(err, io.EOF) { 48 return nil 49 } else if err != nil { 50 return err 51 } 52 53 rsp := proto.BusMsg{ 54 Msg: "Request received!", 55 } 56 57 if err := stream.Send(&rsp); err != nil { 58 return err 59 } 60 } 61 } 62 63 func (d *Debug) Stats(ctx context.Context, req *proto.StatsRequest, rsp *proto.StatsResponse) error { 64 stats, err := d.stats.Read() 65 if err != nil { 66 return err 67 } 68 69 if len(stats) == 0 { 70 return nil 71 } 72 73 // write the response values 74 rsp.Timestamp = uint64(stats[0].Timestamp) 75 rsp.Started = uint64(stats[0].Started) 76 rsp.Uptime = uint64(stats[0].Uptime) 77 rsp.Memory = stats[0].Memory 78 rsp.Gc = stats[0].GC 79 rsp.Threads = stats[0].Threads 80 rsp.Requests = stats[0].Requests 81 rsp.Errors = stats[0].Errors 82 83 return nil 84 } 85 86 func (d *Debug) Trace(ctx context.Context, req *proto.TraceRequest, rsp *proto.TraceResponse) error { 87 traces, err := d.trace.Read(trace.ReadTrace(req.Id)) 88 if err != nil { 89 return err 90 } 91 92 for _, t := range traces { 93 var typ proto.SpanType 94 95 switch t.Type { 96 case trace.SpanTypeRequestInbound: 97 typ = proto.SpanType_INBOUND 98 case trace.SpanTypeRequestOutbound: 99 typ = proto.SpanType_OUTBOUND 100 } 101 102 rsp.Spans = append(rsp.Spans, &proto.Span{ 103 Trace: t.Trace, 104 Id: t.Id, 105 Parent: t.Parent, 106 Name: t.Name, 107 Started: uint64(t.Started.UnixNano()), 108 Duration: uint64(t.Duration.Nanoseconds()), 109 Type: typ, 110 Metadata: t.Metadata, 111 }) 112 } 113 114 return nil 115 } 116 117 func (d *Debug) Log(ctx context.Context, req *proto.LogRequest, stream proto.Debug_LogStream) error { 118 119 var options []log.ReadOption 120 121 since := time.Unix(req.Since, 0) 122 if !since.IsZero() { 123 options = append(options, log.Since(since)) 124 } 125 126 count := int(req.Count) 127 if count > 0 { 128 options = append(options, log.Count(count)) 129 } 130 131 if req.Stream { 132 // TODO: we need to figure out how to close the log stream 133 // It seems like when a client disconnects, 134 // the connection stays open until some timeout expires 135 // or something like that; that means the map of streams 136 // might end up leaking memory if not cleaned up properly 137 lgStream, err := d.log.Stream() 138 if err != nil { 139 return err 140 } 141 defer lgStream.Stop() 142 143 for record := range lgStream.Chan() { 144 // copy metadata 145 metadata := make(map[string]string) 146 for k, v := range record.Metadata { 147 metadata[k] = v 148 } 149 // send record 150 if err := stream.Send(&proto.Record{ 151 Timestamp: record.Timestamp.Unix(), 152 Message: record.Message.(string), 153 Metadata: metadata, 154 }); err != nil { 155 return err 156 } 157 } 158 159 // done streaming, return 160 return nil 161 } 162 163 // get the log records 164 records, err := d.log.Read(options...) 165 if err != nil { 166 return err 167 } 168 169 // send all the logs downstream 170 for _, record := range records { 171 // copy metadata 172 metadata := make(map[string]string) 173 for k, v := range record.Metadata { 174 metadata[k] = v 175 } 176 // send record 177 if err := stream.Send(&proto.Record{ 178 Timestamp: record.Timestamp.Unix(), 179 Message: record.Message.(string), 180 Metadata: metadata, 181 }); err != nil { 182 return err 183 } 184 } 185 186 return nil 187 }