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