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