chore: import upstream snapshot with attribution
govulncheck / govulncheck (push) Has been cancelled
Lint / golangci-lint (push) Has been cancelled
Run Tests / Unit Tests (push) Has been cancelled
Run Tests / Etcd Integration Tests (push) Has been cancelled
Harness (E2E) / Harnesses (mock LLM) (push) Has been cancelled
Harness (E2E) / Provider harnesses (live LLM conformance) (push) Has been cancelled
govulncheck / govulncheck (push) Has been cancelled
Lint / golangci-lint (push) Has been cancelled
Run Tests / Unit Tests (push) Has been cancelled
Run Tests / Etcd Integration Tests (push) Has been cancelled
Harness (E2E) / Harnesses (mock LLM) (push) Has been cancelled
Harness (E2E) / Provider harnesses (live LLM conformance) (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,187 @@
|
||||
// Package handler implements service debug handler embedded in go-micro services
|
||||
package handler
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"io"
|
||||
"time"
|
||||
|
||||
"go-micro.dev/v6/client"
|
||||
"go-micro.dev/v6/debug/log"
|
||||
proto "go-micro.dev/v6/debug/proto"
|
||||
"go-micro.dev/v6/debug/stats"
|
||||
"go-micro.dev/v6/debug/trace"
|
||||
)
|
||||
|
||||
// NewHandler returns an instance of the Debug Handler.
|
||||
func NewHandler(c client.Client) *Debug {
|
||||
return &Debug{
|
||||
log: log.DefaultLog,
|
||||
stats: stats.DefaultStats,
|
||||
trace: trace.DefaultTracer,
|
||||
}
|
||||
}
|
||||
|
||||
var _ proto.DebugHandler = (*Debug)(nil)
|
||||
|
||||
type Debug struct {
|
||||
// must honor the debug handler
|
||||
proto.DebugHandler
|
||||
// the logger for retrieving logs
|
||||
log log.Log
|
||||
// the stats collector
|
||||
stats stats.Stats
|
||||
// the tracer
|
||||
trace trace.Tracer
|
||||
}
|
||||
|
||||
func (d *Debug) Health(ctx context.Context, req *proto.HealthRequest, rsp *proto.HealthResponse) error {
|
||||
rsp.Status = "ok"
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *Debug) MessageBus(ctx context.Context, stream proto.Debug_MessageBusStream) error {
|
||||
for {
|
||||
_, err := stream.Recv()
|
||||
if errors.Is(err, io.EOF) {
|
||||
return nil
|
||||
} else if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
rsp := proto.BusMsg{
|
||||
Msg: "Request received!",
|
||||
}
|
||||
|
||||
if err := stream.Send(&rsp); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (d *Debug) Stats(ctx context.Context, req *proto.StatsRequest, rsp *proto.StatsResponse) error {
|
||||
stats, err := d.stats.Read()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(stats) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
// write the response values
|
||||
rsp.Timestamp = uint64(stats[0].Timestamp)
|
||||
rsp.Started = uint64(stats[0].Started)
|
||||
rsp.Uptime = uint64(stats[0].Uptime)
|
||||
rsp.Memory = stats[0].Memory
|
||||
rsp.Gc = stats[0].GC
|
||||
rsp.Threads = stats[0].Threads
|
||||
rsp.Requests = stats[0].Requests
|
||||
rsp.Errors = stats[0].Errors
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *Debug) Trace(ctx context.Context, req *proto.TraceRequest, rsp *proto.TraceResponse) error {
|
||||
traces, err := d.trace.Read(trace.ReadTrace(req.Id))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, t := range traces {
|
||||
var typ proto.SpanType
|
||||
|
||||
switch t.Type {
|
||||
case trace.SpanTypeRequestInbound:
|
||||
typ = proto.SpanType_INBOUND
|
||||
case trace.SpanTypeRequestOutbound:
|
||||
typ = proto.SpanType_OUTBOUND
|
||||
}
|
||||
|
||||
rsp.Spans = append(rsp.Spans, &proto.Span{
|
||||
Trace: t.Trace,
|
||||
Id: t.Id,
|
||||
Parent: t.Parent,
|
||||
Name: t.Name,
|
||||
Started: uint64(t.Started.UnixNano()),
|
||||
Duration: uint64(t.Duration.Nanoseconds()),
|
||||
Type: typ,
|
||||
Metadata: t.Metadata,
|
||||
})
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *Debug) Log(ctx context.Context, req *proto.LogRequest, stream proto.Debug_LogStream) error {
|
||||
|
||||
var options []log.ReadOption
|
||||
|
||||
since := time.Unix(req.Since, 0)
|
||||
if !since.IsZero() {
|
||||
options = append(options, log.Since(since))
|
||||
}
|
||||
|
||||
count := int(req.Count)
|
||||
if count > 0 {
|
||||
options = append(options, log.Count(count))
|
||||
}
|
||||
|
||||
if req.Stream {
|
||||
// TODO: we need to figure out how to close the log stream
|
||||
// It seems like when a client disconnects,
|
||||
// the connection stays open until some timeout expires
|
||||
// or something like that; that means the map of streams
|
||||
// might end up leaking memory if not cleaned up properly
|
||||
lgStream, err := d.log.Stream()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() { _ = lgStream.Stop() }()
|
||||
|
||||
for record := range lgStream.Chan() {
|
||||
// copy metadata
|
||||
metadata := make(map[string]string)
|
||||
for k, v := range record.Metadata {
|
||||
metadata[k] = v
|
||||
}
|
||||
// send record
|
||||
if err := stream.Send(&proto.Record{
|
||||
Timestamp: record.Timestamp.Unix(),
|
||||
Message: record.Message.(string),
|
||||
Metadata: metadata,
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// done streaming, return
|
||||
return nil
|
||||
}
|
||||
|
||||
// get the log records
|
||||
records, err := d.log.Read(options...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// send all the logs downstream
|
||||
for _, record := range records {
|
||||
// copy metadata
|
||||
metadata := make(map[string]string)
|
||||
for k, v := range record.Metadata {
|
||||
metadata[k] = v
|
||||
}
|
||||
// send record
|
||||
if err := stream.Send(&proto.Record{
|
||||
Timestamp: record.Timestamp.Unix(),
|
||||
Message: record.Message.(string),
|
||||
Metadata: metadata,
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
// Package log provides debug logging
|
||||
package log
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
// Default buffer size if any.
|
||||
DefaultSize = 1024
|
||||
// DefaultLog logger.
|
||||
DefaultLog = NewLog()
|
||||
// Default formatter.
|
||||
DefaultFormat = TextFormat
|
||||
)
|
||||
|
||||
// Log is debug log interface for reading and writing logs.
|
||||
type Log interface {
|
||||
// Read reads log entries from the logger
|
||||
Read(...ReadOption) ([]Record, error)
|
||||
// Write writes records to log
|
||||
Write(Record) error
|
||||
// Stream log records
|
||||
Stream() (Stream, error)
|
||||
}
|
||||
|
||||
// Record is log record entry.
|
||||
type Record struct {
|
||||
// Timestamp of logged event
|
||||
Timestamp time.Time `json:"timestamp"`
|
||||
// Metadata to enrich log record
|
||||
Metadata map[string]string `json:"metadata"`
|
||||
// Value contains log entry
|
||||
Message interface{} `json:"message"`
|
||||
}
|
||||
|
||||
// Stream returns a log stream.
|
||||
type Stream interface {
|
||||
Chan() <-chan Record
|
||||
Stop() error
|
||||
}
|
||||
|
||||
// Format is a function which formats the output.
|
||||
type FormatFunc func(Record) string
|
||||
|
||||
// TextFormat returns text format.
|
||||
func TextFormat(r Record) string {
|
||||
t := r.Timestamp.Format("2006-01-02 15:04:05")
|
||||
return fmt.Sprintf("%s %v", t, r.Message)
|
||||
}
|
||||
|
||||
// JSONFormat is a json Format func.
|
||||
func JSONFormat(r Record) string {
|
||||
b, _ := json.Marshal(r)
|
||||
return string(b)
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
// Package memory provides an in memory log buffer
|
||||
package memory
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"go-micro.dev/v6/debug/log"
|
||||
"go-micro.dev/v6/internal/util/ring"
|
||||
)
|
||||
|
||||
var (
|
||||
// DefaultSize of the logger buffer.
|
||||
DefaultSize = 1024
|
||||
)
|
||||
|
||||
// memoryLog is default micro log.
|
||||
type memoryLog struct {
|
||||
*ring.Buffer
|
||||
}
|
||||
|
||||
// NewLog returns default Logger with.
|
||||
func NewLog(opts ...log.Option) log.Log {
|
||||
// get default options
|
||||
options := log.DefaultOptions()
|
||||
|
||||
// apply requested options
|
||||
for _, o := range opts {
|
||||
o(&options)
|
||||
}
|
||||
|
||||
return &memoryLog{
|
||||
Buffer: ring.New(options.Size),
|
||||
}
|
||||
}
|
||||
|
||||
// Write writes logs into logger.
|
||||
func (l *memoryLog) Write(r log.Record) error {
|
||||
l.Put(fmt.Sprint(r.Message))
|
||||
return nil
|
||||
}
|
||||
|
||||
// Read reads logs and returns them.
|
||||
func (l *memoryLog) Read(opts ...log.ReadOption) ([]log.Record, error) {
|
||||
options := log.ReadOptions{}
|
||||
// initialize the read options
|
||||
for _, o := range opts {
|
||||
o(&options)
|
||||
}
|
||||
|
||||
var entries []*ring.Entry
|
||||
// if Since options ha sbeen specified we honor it
|
||||
if !options.Since.IsZero() {
|
||||
entries = l.Since(options.Since)
|
||||
}
|
||||
|
||||
// only if we specified valid count constraint
|
||||
// do we end up doing some serious if-else kung-fu
|
||||
// if since constraint has been provided
|
||||
// we return *count* number of logs since the given timestamp;
|
||||
// otherwise we return last count number of logs
|
||||
if options.Count > 0 {
|
||||
switch len(entries) > 0 {
|
||||
case true:
|
||||
// if we request fewer logs than what since constraint gives us
|
||||
if options.Count < len(entries) {
|
||||
entries = entries[0:options.Count]
|
||||
}
|
||||
default:
|
||||
entries = l.Get(options.Count)
|
||||
}
|
||||
}
|
||||
|
||||
records := make([]log.Record, 0, len(entries))
|
||||
for _, entry := range entries {
|
||||
record := log.Record{
|
||||
Timestamp: entry.Timestamp,
|
||||
Message: entry.Value,
|
||||
}
|
||||
records = append(records, record)
|
||||
}
|
||||
|
||||
return records, nil
|
||||
}
|
||||
|
||||
// Stream returns channel for reading log records
|
||||
// along with a stop channel, close it when done.
|
||||
func (l *memoryLog) Stream() (log.Stream, error) {
|
||||
// get stream channel from ring buffer
|
||||
stream, stop := l.Buffer.Stream()
|
||||
// make a buffered channel
|
||||
records := make(chan log.Record, 128)
|
||||
// get last 10 records
|
||||
last10 := l.Get(10)
|
||||
|
||||
// stream the log records
|
||||
go func() {
|
||||
// first send last 10 records
|
||||
for _, entry := range last10 {
|
||||
records <- log.Record{
|
||||
Timestamp: entry.Timestamp,
|
||||
Message: entry.Value,
|
||||
Metadata: make(map[string]string),
|
||||
}
|
||||
}
|
||||
// now stream continuously
|
||||
for entry := range stream {
|
||||
records <- log.Record{
|
||||
Timestamp: entry.Timestamp,
|
||||
Message: entry.Value,
|
||||
Metadata: make(map[string]string),
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
return &logStream{
|
||||
stream: records,
|
||||
stop: stop,
|
||||
}, nil
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package memory
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"go-micro.dev/v6/debug/log"
|
||||
)
|
||||
|
||||
func TestLogger(t *testing.T) {
|
||||
// set size to some value
|
||||
size := 100
|
||||
// override the global logger
|
||||
lg := NewLog(log.Size(size))
|
||||
// make sure we have the right size of the logger ring buffer
|
||||
if lg.(*memoryLog).Size() != size {
|
||||
t.Errorf("expected buffer size: %d, got: %d", size, lg.(*memoryLog).Size())
|
||||
}
|
||||
|
||||
// Log some cruft
|
||||
lg.Write(log.Record{Message: "foobar"})
|
||||
lg.Write(log.Record{Message: "foo bar"})
|
||||
|
||||
// Check if the logs are stored in the logger ring buffer
|
||||
expected := []string{"foobar", "foo bar"}
|
||||
entries, _ := lg.Read(log.Count(len(expected)))
|
||||
for i, entry := range entries {
|
||||
if !reflect.DeepEqual(entry.Message, expected[i]) {
|
||||
t.Errorf("expected %s, got %s", expected[i], entry.Message)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package memory
|
||||
|
||||
import (
|
||||
"go-micro.dev/v6/debug/log"
|
||||
)
|
||||
|
||||
type logStream struct {
|
||||
stream <-chan log.Record
|
||||
stop chan bool
|
||||
}
|
||||
|
||||
func (l *logStream) Chan() <-chan log.Record {
|
||||
return l.stream
|
||||
}
|
||||
|
||||
func (l *logStream) Stop() error {
|
||||
select {
|
||||
case <-l.stop:
|
||||
return nil
|
||||
default:
|
||||
close(l.stop)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package noop
|
||||
|
||||
import (
|
||||
"go-micro.dev/v6/debug/log"
|
||||
)
|
||||
|
||||
type noop struct{}
|
||||
|
||||
func (n *noop) Read(...log.ReadOption) ([]log.Record, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (n *noop) Write(log.Record) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n *noop) Stream() (log.Stream, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func NewLog(opts ...log.Option) log.Log {
|
||||
return new(noop)
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
package log
|
||||
|
||||
import "time"
|
||||
|
||||
// Option used by the logger.
|
||||
type Option func(*Options)
|
||||
|
||||
// Options are logger options.
|
||||
type Options struct {
|
||||
// Format specifies the output format
|
||||
Format FormatFunc
|
||||
// Name of the log
|
||||
Name string
|
||||
// Size is the size of ring buffer
|
||||
Size int
|
||||
}
|
||||
|
||||
// Name of the log.
|
||||
func Name(n string) Option {
|
||||
return func(o *Options) {
|
||||
o.Name = n
|
||||
}
|
||||
}
|
||||
|
||||
// Size sets the size of the ring buffer.
|
||||
func Size(s int) Option {
|
||||
return func(o *Options) {
|
||||
o.Size = s
|
||||
}
|
||||
}
|
||||
|
||||
func Format(f FormatFunc) Option {
|
||||
return func(o *Options) {
|
||||
o.Format = f
|
||||
}
|
||||
}
|
||||
|
||||
// DefaultOptions returns default options.
|
||||
func DefaultOptions() Options {
|
||||
return Options{
|
||||
Size: DefaultSize,
|
||||
}
|
||||
}
|
||||
|
||||
// ReadOptions for querying the logs.
|
||||
type ReadOptions struct {
|
||||
// Since what time in past to return the logs
|
||||
Since time.Time
|
||||
// Count specifies number of logs to return
|
||||
Count int
|
||||
// Stream requests continuous log stream
|
||||
Stream bool
|
||||
}
|
||||
|
||||
// ReadOption used for reading the logs.
|
||||
type ReadOption func(*ReadOptions)
|
||||
|
||||
// Since sets the time since which to return the log records.
|
||||
func Since(s time.Time) ReadOption {
|
||||
return func(o *ReadOptions) {
|
||||
o.Since = s
|
||||
}
|
||||
}
|
||||
|
||||
// Count sets the number of log records to return.
|
||||
func Count(c int) ReadOption {
|
||||
return func(o *ReadOptions) {
|
||||
o.Count = c
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
package log
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"go-micro.dev/v6/internal/util/ring"
|
||||
)
|
||||
|
||||
// Should stream from OS.
|
||||
type osLog struct {
|
||||
format FormatFunc
|
||||
buffer *ring.Buffer
|
||||
subs map[string]*osStream
|
||||
|
||||
sync.RWMutex
|
||||
}
|
||||
|
||||
type osStream struct {
|
||||
stream chan Record
|
||||
}
|
||||
|
||||
// Read reads log entries from the logger.
|
||||
func (o *osLog) Read(...ReadOption) ([]Record, error) {
|
||||
var records []Record
|
||||
|
||||
// read the last 100 records
|
||||
for _, v := range o.buffer.Get(100) {
|
||||
records = append(records, v.Value.(Record))
|
||||
}
|
||||
|
||||
return records, nil
|
||||
}
|
||||
|
||||
// Write writes records to log.
|
||||
func (o *osLog) Write(r Record) error {
|
||||
o.buffer.Put(r)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Stream log records.
|
||||
func (o *osLog) Stream() (Stream, error) {
|
||||
o.Lock()
|
||||
defer o.Unlock()
|
||||
|
||||
// create stream
|
||||
st := &osStream{
|
||||
stream: make(chan Record, 128),
|
||||
}
|
||||
|
||||
// save stream
|
||||
o.subs[uuid.New().String()] = st
|
||||
|
||||
return st, nil
|
||||
}
|
||||
|
||||
func (o *osStream) Chan() <-chan Record {
|
||||
return o.stream
|
||||
}
|
||||
|
||||
func (o *osStream) Stop() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func NewLog(opts ...Option) Log {
|
||||
options := Options{
|
||||
Format: DefaultFormat,
|
||||
}
|
||||
for _, o := range opts {
|
||||
o(&options)
|
||||
}
|
||||
|
||||
l := &osLog{
|
||||
format: options.Format,
|
||||
buffer: ring.New(1024),
|
||||
subs: make(map[string]*osStream),
|
||||
}
|
||||
|
||||
return l
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
// Package http enables the http profiler
|
||||
package http
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"net/http/pprof"
|
||||
"sync"
|
||||
|
||||
"go-micro.dev/v6/debug/profile"
|
||||
)
|
||||
|
||||
type httpProfile struct {
|
||||
server *http.Server
|
||||
sync.Mutex
|
||||
running bool
|
||||
}
|
||||
|
||||
var (
|
||||
DefaultAddress = ":6060"
|
||||
)
|
||||
|
||||
// Start the profiler.
|
||||
func (h *httpProfile) Start() error {
|
||||
h.Lock()
|
||||
defer h.Unlock()
|
||||
|
||||
if h.running {
|
||||
return nil
|
||||
}
|
||||
|
||||
go func() {
|
||||
if err := h.server.ListenAndServe(); err != nil {
|
||||
h.Lock()
|
||||
h.running = false
|
||||
h.Unlock()
|
||||
}
|
||||
}()
|
||||
|
||||
h.running = true
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Stop the profiler.
|
||||
func (h *httpProfile) Stop() error {
|
||||
h.Lock()
|
||||
defer h.Unlock()
|
||||
|
||||
if !h.running {
|
||||
return nil
|
||||
}
|
||||
|
||||
h.running = false
|
||||
|
||||
return h.server.Shutdown(context.TODO())
|
||||
}
|
||||
|
||||
func (h *httpProfile) String() string {
|
||||
return "http"
|
||||
}
|
||||
|
||||
func NewProfile(opts ...profile.Option) profile.Profile {
|
||||
mux := http.NewServeMux()
|
||||
|
||||
mux.HandleFunc("/debug/pprof/", pprof.Index)
|
||||
mux.HandleFunc("/debug/pprof/cmdline", pprof.Cmdline)
|
||||
mux.HandleFunc("/debug/pprof/profile", pprof.Profile)
|
||||
mux.HandleFunc("/debug/pprof/symbol", pprof.Symbol)
|
||||
mux.HandleFunc("/debug/pprof/trace", pprof.Trace)
|
||||
|
||||
return &httpProfile{
|
||||
server: &http.Server{
|
||||
Addr: DefaultAddress,
|
||||
Handler: mux,
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
// Package pprof provides a pprof profiler
|
||||
package pprof
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"runtime/pprof"
|
||||
"sync"
|
||||
|
||||
"go-micro.dev/v6/debug/profile"
|
||||
)
|
||||
|
||||
type profiler struct {
|
||||
|
||||
// where the cpu profile is written
|
||||
cpuFile *os.File
|
||||
// where the mem profile is written
|
||||
memFile *os.File
|
||||
opts profile.Options
|
||||
|
||||
sync.Mutex
|
||||
running bool
|
||||
}
|
||||
|
||||
func (p *profiler) Start() error {
|
||||
p.Lock()
|
||||
defer p.Unlock()
|
||||
|
||||
if p.running {
|
||||
return nil
|
||||
}
|
||||
|
||||
cpuFile := filepath.Join(os.TempDir(), "cpu.pprof")
|
||||
memFile := filepath.Join(os.TempDir(), "mem.pprof")
|
||||
|
||||
if len(p.opts.Name) > 0 {
|
||||
cpuFile = filepath.Join(os.TempDir(), p.opts.Name+".cpu.pprof")
|
||||
memFile = filepath.Join(os.TempDir(), p.opts.Name+".mem.pprof")
|
||||
}
|
||||
|
||||
f1, err := os.Create(cpuFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
f2, err := os.Create(memFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// start cpu profiling
|
||||
if err := pprof.StartCPUProfile(f1); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// set cpu file
|
||||
p.cpuFile = f1
|
||||
// set mem file
|
||||
p.memFile = f2
|
||||
|
||||
p.running = true
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *profiler) Stop() error {
|
||||
p.Lock()
|
||||
defer p.Unlock()
|
||||
|
||||
if !p.running {
|
||||
return nil
|
||||
}
|
||||
|
||||
pprof.StopCPUProfile()
|
||||
p.cpuFile.Close()
|
||||
runtime.GC()
|
||||
_ = pprof.WriteHeapProfile(p.memFile)
|
||||
p.memFile.Close()
|
||||
p.running = false
|
||||
p.cpuFile = nil
|
||||
p.memFile = nil
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *profiler) String() string {
|
||||
return "pprof"
|
||||
}
|
||||
|
||||
func NewProfile(opts ...profile.Option) profile.Profile {
|
||||
var options profile.Options
|
||||
for _, o := range opts {
|
||||
o(&options)
|
||||
}
|
||||
p := new(profiler)
|
||||
p.opts = options
|
||||
return p
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
// Package profile is for profilers
|
||||
package profile
|
||||
|
||||
type Profile interface {
|
||||
// Start the profiler
|
||||
Start() error
|
||||
// Stop the profiler
|
||||
Stop() error
|
||||
// Name of the profiler
|
||||
String() string
|
||||
}
|
||||
|
||||
var (
|
||||
DefaultProfile Profile = new(noop)
|
||||
)
|
||||
|
||||
type noop struct{}
|
||||
|
||||
func (p *noop) Start() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *noop) Stop() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *noop) String() string {
|
||||
return "noop"
|
||||
}
|
||||
|
||||
type Options struct {
|
||||
// Name to use for the profile
|
||||
Name string
|
||||
}
|
||||
|
||||
type Option func(o *Options)
|
||||
|
||||
// Name of the profile.
|
||||
func Name(n string) Option {
|
||||
return func(o *Options) {
|
||||
o.Name = n
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,314 @@
|
||||
// Code generated by protoc-gen-micro. DO NOT EDIT.
|
||||
// source: proto/debug.proto
|
||||
|
||||
package debug
|
||||
|
||||
import (
|
||||
fmt "fmt"
|
||||
proto "google.golang.org/protobuf/proto"
|
||||
math "math"
|
||||
)
|
||||
|
||||
import (
|
||||
context "context"
|
||||
client "go-micro.dev/v6/client"
|
||||
server "go-micro.dev/v6/server"
|
||||
)
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ = proto.Marshal
|
||||
var _ = fmt.Errorf
|
||||
var _ = math.Inf
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ context.Context
|
||||
var _ client.Option
|
||||
var _ server.Option
|
||||
|
||||
// Client API for Debug service
|
||||
|
||||
type DebugService interface {
|
||||
Log(ctx context.Context, in *LogRequest, opts ...client.CallOption) (Debug_LogService, error)
|
||||
Health(ctx context.Context, in *HealthRequest, opts ...client.CallOption) (*HealthResponse, error)
|
||||
Stats(ctx context.Context, in *StatsRequest, opts ...client.CallOption) (*StatsResponse, error)
|
||||
Trace(ctx context.Context, in *TraceRequest, opts ...client.CallOption) (*TraceResponse, error)
|
||||
MessageBus(ctx context.Context, opts ...client.CallOption) (Debug_MessageBusService, error)
|
||||
}
|
||||
|
||||
type debugService struct {
|
||||
c client.Client
|
||||
name string
|
||||
}
|
||||
|
||||
func NewDebugService(name string, c client.Client) DebugService {
|
||||
return &debugService{
|
||||
c: c,
|
||||
name: name,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *debugService) Log(ctx context.Context, in *LogRequest, opts ...client.CallOption) (Debug_LogService, error) {
|
||||
req := c.c.NewRequest(c.name, "Debug.Log", &LogRequest{})
|
||||
stream, err := c.c.Stream(ctx, req, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := stream.Send(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &debugServiceLog{stream}, nil
|
||||
}
|
||||
|
||||
type Debug_LogService interface {
|
||||
Context() context.Context
|
||||
SendMsg(interface{}) error
|
||||
RecvMsg(interface{}) error
|
||||
CloseSend() error
|
||||
Close() error
|
||||
Recv() (*Record, error)
|
||||
}
|
||||
|
||||
type debugServiceLog struct {
|
||||
stream client.Stream
|
||||
}
|
||||
|
||||
func (x *debugServiceLog) CloseSend() error {
|
||||
return x.stream.CloseSend()
|
||||
}
|
||||
|
||||
func (x *debugServiceLog) Close() error {
|
||||
return x.stream.Close()
|
||||
}
|
||||
|
||||
func (x *debugServiceLog) Context() context.Context {
|
||||
return x.stream.Context()
|
||||
}
|
||||
|
||||
func (x *debugServiceLog) SendMsg(m interface{}) error {
|
||||
return x.stream.Send(m)
|
||||
}
|
||||
|
||||
func (x *debugServiceLog) RecvMsg(m interface{}) error {
|
||||
return x.stream.Recv(m)
|
||||
}
|
||||
|
||||
func (x *debugServiceLog) Recv() (*Record, error) {
|
||||
m := new(Record)
|
||||
err := x.stream.Recv(m)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
func (c *debugService) Health(ctx context.Context, in *HealthRequest, opts ...client.CallOption) (*HealthResponse, error) {
|
||||
req := c.c.NewRequest(c.name, "Debug.Health", in)
|
||||
out := new(HealthResponse)
|
||||
err := c.c.Call(ctx, req, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *debugService) Stats(ctx context.Context, in *StatsRequest, opts ...client.CallOption) (*StatsResponse, error) {
|
||||
req := c.c.NewRequest(c.name, "Debug.Stats", in)
|
||||
out := new(StatsResponse)
|
||||
err := c.c.Call(ctx, req, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *debugService) Trace(ctx context.Context, in *TraceRequest, opts ...client.CallOption) (*TraceResponse, error) {
|
||||
req := c.c.NewRequest(c.name, "Debug.Trace", in)
|
||||
out := new(TraceResponse)
|
||||
err := c.c.Call(ctx, req, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *debugService) MessageBus(ctx context.Context, opts ...client.CallOption) (Debug_MessageBusService, error) {
|
||||
req := c.c.NewRequest(c.name, "Debug.MessageBus", &BusMsg{})
|
||||
stream, err := c.c.Stream(ctx, req, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &debugServiceMessageBus{stream}, nil
|
||||
}
|
||||
|
||||
type Debug_MessageBusService interface {
|
||||
Context() context.Context
|
||||
SendMsg(interface{}) error
|
||||
RecvMsg(interface{}) error
|
||||
CloseSend() error
|
||||
Close() error
|
||||
Send(*BusMsg) error
|
||||
Recv() (*BusMsg, error)
|
||||
}
|
||||
|
||||
type debugServiceMessageBus struct {
|
||||
stream client.Stream
|
||||
}
|
||||
|
||||
func (x *debugServiceMessageBus) CloseSend() error {
|
||||
return x.stream.CloseSend()
|
||||
}
|
||||
|
||||
func (x *debugServiceMessageBus) Close() error {
|
||||
return x.stream.Close()
|
||||
}
|
||||
|
||||
func (x *debugServiceMessageBus) Context() context.Context {
|
||||
return x.stream.Context()
|
||||
}
|
||||
|
||||
func (x *debugServiceMessageBus) SendMsg(m interface{}) error {
|
||||
return x.stream.Send(m)
|
||||
}
|
||||
|
||||
func (x *debugServiceMessageBus) RecvMsg(m interface{}) error {
|
||||
return x.stream.Recv(m)
|
||||
}
|
||||
|
||||
func (x *debugServiceMessageBus) Send(m *BusMsg) error {
|
||||
return x.stream.Send(m)
|
||||
}
|
||||
|
||||
func (x *debugServiceMessageBus) Recv() (*BusMsg, error) {
|
||||
m := new(BusMsg)
|
||||
err := x.stream.Recv(m)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
// Server API for Debug service
|
||||
|
||||
type DebugHandler interface {
|
||||
Log(context.Context, *LogRequest, Debug_LogStream) error
|
||||
Health(context.Context, *HealthRequest, *HealthResponse) error
|
||||
Stats(context.Context, *StatsRequest, *StatsResponse) error
|
||||
Trace(context.Context, *TraceRequest, *TraceResponse) error
|
||||
MessageBus(context.Context, Debug_MessageBusStream) error
|
||||
}
|
||||
|
||||
func RegisterDebugHandler(s server.Server, hdlr DebugHandler, opts ...server.HandlerOption) error {
|
||||
type debug interface {
|
||||
Log(ctx context.Context, stream server.Stream) error
|
||||
Health(ctx context.Context, in *HealthRequest, out *HealthResponse) error
|
||||
Stats(ctx context.Context, in *StatsRequest, out *StatsResponse) error
|
||||
Trace(ctx context.Context, in *TraceRequest, out *TraceResponse) error
|
||||
MessageBus(ctx context.Context, stream server.Stream) error
|
||||
}
|
||||
type Debug struct {
|
||||
debug
|
||||
}
|
||||
h := &debugHandler{hdlr}
|
||||
return s.Handle(s.NewHandler(&Debug{h}, opts...))
|
||||
}
|
||||
|
||||
type debugHandler struct {
|
||||
DebugHandler
|
||||
}
|
||||
|
||||
func (h *debugHandler) Log(ctx context.Context, stream server.Stream) error {
|
||||
m := new(LogRequest)
|
||||
if err := stream.Recv(m); err != nil {
|
||||
return err
|
||||
}
|
||||
return h.DebugHandler.Log(ctx, m, &debugLogStream{stream})
|
||||
}
|
||||
|
||||
type Debug_LogStream interface {
|
||||
Context() context.Context
|
||||
SendMsg(interface{}) error
|
||||
RecvMsg(interface{}) error
|
||||
Close() error
|
||||
Send(*Record) error
|
||||
}
|
||||
|
||||
type debugLogStream struct {
|
||||
stream server.Stream
|
||||
}
|
||||
|
||||
func (x *debugLogStream) Close() error {
|
||||
return x.stream.Close()
|
||||
}
|
||||
|
||||
func (x *debugLogStream) Context() context.Context {
|
||||
return x.stream.Context()
|
||||
}
|
||||
|
||||
func (x *debugLogStream) SendMsg(m interface{}) error {
|
||||
return x.stream.Send(m)
|
||||
}
|
||||
|
||||
func (x *debugLogStream) RecvMsg(m interface{}) error {
|
||||
return x.stream.Recv(m)
|
||||
}
|
||||
|
||||
func (x *debugLogStream) Send(m *Record) error {
|
||||
return x.stream.Send(m)
|
||||
}
|
||||
|
||||
func (h *debugHandler) Health(ctx context.Context, in *HealthRequest, out *HealthResponse) error {
|
||||
return h.DebugHandler.Health(ctx, in, out)
|
||||
}
|
||||
|
||||
func (h *debugHandler) Stats(ctx context.Context, in *StatsRequest, out *StatsResponse) error {
|
||||
return h.DebugHandler.Stats(ctx, in, out)
|
||||
}
|
||||
|
||||
func (h *debugHandler) Trace(ctx context.Context, in *TraceRequest, out *TraceResponse) error {
|
||||
return h.DebugHandler.Trace(ctx, in, out)
|
||||
}
|
||||
|
||||
func (h *debugHandler) MessageBus(ctx context.Context, stream server.Stream) error {
|
||||
return h.DebugHandler.MessageBus(ctx, &debugMessageBusStream{stream})
|
||||
}
|
||||
|
||||
type Debug_MessageBusStream interface {
|
||||
Context() context.Context
|
||||
SendMsg(interface{}) error
|
||||
RecvMsg(interface{}) error
|
||||
Close() error
|
||||
Send(*BusMsg) error
|
||||
Recv() (*BusMsg, error)
|
||||
}
|
||||
|
||||
type debugMessageBusStream struct {
|
||||
stream server.Stream
|
||||
}
|
||||
|
||||
func (x *debugMessageBusStream) Close() error {
|
||||
return x.stream.Close()
|
||||
}
|
||||
|
||||
func (x *debugMessageBusStream) Context() context.Context {
|
||||
return x.stream.Context()
|
||||
}
|
||||
|
||||
func (x *debugMessageBusStream) SendMsg(m interface{}) error {
|
||||
return x.stream.Send(m)
|
||||
}
|
||||
|
||||
func (x *debugMessageBusStream) RecvMsg(m interface{}) error {
|
||||
return x.stream.Recv(m)
|
||||
}
|
||||
|
||||
func (x *debugMessageBusStream) Send(m *BusMsg) error {
|
||||
return x.stream.Send(m)
|
||||
}
|
||||
|
||||
func (x *debugMessageBusStream) Recv() (*BusMsg, error) {
|
||||
m := new(BusMsg)
|
||||
if err := x.stream.Recv(m); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package debug;
|
||||
|
||||
option go_package = "./proto;debug";
|
||||
|
||||
// Compile this proto by running the following command in the debug directory:
|
||||
// protoc --proto_path=. --micro_out=. --go_out=:. proto/debug.proto
|
||||
|
||||
service Debug {
|
||||
rpc Log(LogRequest) returns (stream Record) {};
|
||||
rpc Health(HealthRequest) returns (HealthResponse) {};
|
||||
rpc Stats(StatsRequest) returns (StatsResponse) {};
|
||||
rpc Trace(TraceRequest) returns (TraceResponse) {};
|
||||
rpc MessageBus(stream BusMsg) returns (stream BusMsg) {};
|
||||
}
|
||||
|
||||
message BusMsg { string msg = 1; }
|
||||
|
||||
message HealthRequest {
|
||||
// optional service name
|
||||
string service = 1;
|
||||
}
|
||||
|
||||
message HealthResponse {
|
||||
// default: ok
|
||||
string status = 1;
|
||||
}
|
||||
|
||||
message StatsRequest {
|
||||
// optional service name
|
||||
string service = 1;
|
||||
}
|
||||
|
||||
message StatsResponse {
|
||||
// timestamp of recording
|
||||
uint64 timestamp = 1;
|
||||
// unix timestamp
|
||||
uint64 started = 2;
|
||||
// in seconds
|
||||
uint64 uptime = 3;
|
||||
// in bytes
|
||||
uint64 memory = 4;
|
||||
// num threads
|
||||
uint64 threads = 5;
|
||||
// total gc in nanoseconds
|
||||
uint64 gc = 6;
|
||||
// total number of requests
|
||||
uint64 requests = 7;
|
||||
// total number of errors
|
||||
uint64 errors = 8;
|
||||
}
|
||||
|
||||
// LogRequest requests service logs
|
||||
message LogRequest {
|
||||
// service to request logs for
|
||||
string service = 1;
|
||||
// stream records continuously
|
||||
bool stream = 2;
|
||||
// count of records to request
|
||||
int64 count = 3;
|
||||
// relative time in seconds
|
||||
// before the current time
|
||||
// from which to show logs
|
||||
int64 since = 4;
|
||||
}
|
||||
|
||||
// Record is service log record
|
||||
// Also used as default basic message type to test requests.
|
||||
message Record {
|
||||
// timestamp of log record
|
||||
int64 timestamp = 1;
|
||||
// record metadata
|
||||
map<string, string> metadata = 2;
|
||||
// message
|
||||
string message = 3;
|
||||
}
|
||||
|
||||
message TraceRequest {
|
||||
// trace id to retrieve
|
||||
string id = 1;
|
||||
}
|
||||
|
||||
message TraceResponse { repeated Span spans = 1; }
|
||||
|
||||
enum SpanType {
|
||||
INBOUND = 0;
|
||||
OUTBOUND = 1;
|
||||
}
|
||||
|
||||
message Span {
|
||||
// the trace id
|
||||
string trace = 1;
|
||||
// id of the span
|
||||
string id = 2;
|
||||
// parent span
|
||||
string parent = 3;
|
||||
// name of the resource
|
||||
string name = 4;
|
||||
// time of start in nanoseconds
|
||||
uint64 started = 5;
|
||||
// duration of the execution in nanoseconds
|
||||
uint64 duration = 6;
|
||||
// associated metadata
|
||||
map<string, string> metadata = 7;
|
||||
SpanType type = 8;
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
package stats
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"go-micro.dev/v6/internal/util/ring"
|
||||
)
|
||||
|
||||
type stats struct {
|
||||
// used to store past stats
|
||||
buffer *ring.Buffer
|
||||
|
||||
sync.RWMutex
|
||||
started int64
|
||||
requests uint64
|
||||
errors uint64
|
||||
}
|
||||
|
||||
func (s *stats) snapshot() *Stat {
|
||||
s.RLock()
|
||||
defer s.RUnlock()
|
||||
|
||||
var mstat runtime.MemStats
|
||||
runtime.ReadMemStats(&mstat)
|
||||
|
||||
now := time.Now().Unix()
|
||||
|
||||
return &Stat{
|
||||
Timestamp: now,
|
||||
Started: s.started,
|
||||
Uptime: now - s.started,
|
||||
Memory: mstat.Alloc,
|
||||
GC: mstat.PauseTotalNs,
|
||||
Threads: uint64(runtime.NumGoroutine()),
|
||||
Requests: s.requests,
|
||||
Errors: s.errors,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *stats) Read() ([]*Stat, error) {
|
||||
// TODO adjustable size and optional read values
|
||||
buf := s.buffer.Get(60)
|
||||
var stats []*Stat
|
||||
|
||||
// get a value from the buffer if it exists
|
||||
for _, b := range buf {
|
||||
stat, ok := b.Value.(*Stat)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
stats = append(stats, stat)
|
||||
}
|
||||
|
||||
// get a snapshot
|
||||
stats = append(stats, s.snapshot())
|
||||
|
||||
return stats, nil
|
||||
}
|
||||
|
||||
func (s *stats) Write(stat *Stat) error {
|
||||
s.buffer.Put(stat)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *stats) Record(err error) error {
|
||||
s.Lock()
|
||||
defer s.Unlock()
|
||||
|
||||
// increment the total request count
|
||||
s.requests++
|
||||
|
||||
// increment the error count
|
||||
if err != nil {
|
||||
s.errors++
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewStats returns a new in memory stats buffer
|
||||
// TODO add options.
|
||||
func NewStats() Stats {
|
||||
return &stats{
|
||||
started: time.Now().Unix(),
|
||||
buffer: ring.New(60),
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
// Package stats provides runtime stats
|
||||
package stats
|
||||
|
||||
// Stats provides stats interface.
|
||||
type Stats interface {
|
||||
// Read stat snapshot
|
||||
Read() ([]*Stat, error)
|
||||
// Write a stat snapshot
|
||||
Write(*Stat) error
|
||||
// Record a request
|
||||
Record(error) error
|
||||
}
|
||||
|
||||
// A runtime stat.
|
||||
type Stat struct {
|
||||
// Timestamp of recording
|
||||
Timestamp int64
|
||||
// Start time as unix timestamp
|
||||
Started int64
|
||||
// Uptime in seconds
|
||||
Uptime int64
|
||||
// Memory usage in bytes
|
||||
Memory uint64
|
||||
// Threads aka go routines
|
||||
Threads uint64
|
||||
// Garbage collection in nanoseconds
|
||||
GC uint64
|
||||
// Total requests
|
||||
Requests uint64
|
||||
// Total errors
|
||||
Errors uint64
|
||||
}
|
||||
|
||||
var (
|
||||
DefaultStats = NewStats()
|
||||
)
|
||||
@@ -0,0 +1,89 @@
|
||||
package trace
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"go-micro.dev/v6/internal/util/ring"
|
||||
)
|
||||
|
||||
type memTracer struct {
|
||||
|
||||
// ring buffer of traces
|
||||
buffer *ring.Buffer
|
||||
opts Options
|
||||
}
|
||||
|
||||
func (t *memTracer) Read(opts ...ReadOption) ([]*Span, error) {
|
||||
var options ReadOptions
|
||||
for _, o := range opts {
|
||||
o(&options)
|
||||
}
|
||||
|
||||
sp := t.buffer.Get(t.buffer.Size())
|
||||
|
||||
spans := make([]*Span, 0, len(sp))
|
||||
|
||||
for _, span := range sp {
|
||||
val := span.Value.(*Span)
|
||||
// skip if trace id is specified and doesn't match
|
||||
if len(options.Trace) > 0 && val.Trace != options.Trace {
|
||||
continue
|
||||
}
|
||||
spans = append(spans, val)
|
||||
}
|
||||
|
||||
return spans, nil
|
||||
}
|
||||
|
||||
func (t *memTracer) Start(ctx context.Context, name string) (context.Context, *Span) {
|
||||
span := &Span{
|
||||
Name: name,
|
||||
Trace: uuid.New().String(),
|
||||
Id: uuid.New().String(),
|
||||
Started: time.Now(),
|
||||
Metadata: make(map[string]string),
|
||||
}
|
||||
|
||||
// return span if no context
|
||||
if ctx == nil {
|
||||
return ToContext(context.Background(), span.Trace, span.Id), span
|
||||
}
|
||||
traceID, parentSpanID, ok := FromContext(ctx)
|
||||
// If the trace can not be found in the header,
|
||||
// that means this is where the trace is created.
|
||||
if !ok {
|
||||
return ToContext(ctx, span.Trace, span.Id), span
|
||||
}
|
||||
|
||||
// set trace id
|
||||
span.Trace = traceID
|
||||
// set parent
|
||||
span.Parent = parentSpanID
|
||||
|
||||
// return the span
|
||||
return ToContext(ctx, span.Trace, span.Id), span
|
||||
}
|
||||
|
||||
func (t *memTracer) Finish(s *Span) error {
|
||||
// set finished time
|
||||
s.Duration = time.Since(s.Started)
|
||||
// save the span
|
||||
t.buffer.Put(s)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func NewTracer(opts ...Option) Tracer {
|
||||
var options Options
|
||||
for _, o := range opts {
|
||||
o(&options)
|
||||
}
|
||||
|
||||
return &memTracer{
|
||||
opts: options,
|
||||
// the last 256 requests
|
||||
buffer: ring.New(256),
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
package trace
|
||||
@@ -0,0 +1,34 @@
|
||||
package trace
|
||||
|
||||
type Options struct {
|
||||
// Size is the size of ring buffer
|
||||
Size int
|
||||
}
|
||||
|
||||
type Option func(o *Options)
|
||||
|
||||
type ReadOptions struct {
|
||||
// Trace id
|
||||
Trace string
|
||||
}
|
||||
|
||||
type ReadOption func(o *ReadOptions)
|
||||
|
||||
// Read the given trace.
|
||||
func ReadTrace(t string) ReadOption {
|
||||
return func(o *ReadOptions) {
|
||||
o.Trace = t
|
||||
}
|
||||
}
|
||||
|
||||
const (
|
||||
// DefaultSize of the buffer.
|
||||
DefaultSize = 64
|
||||
)
|
||||
|
||||
// DefaultOptions returns default options.
|
||||
func DefaultOptions() Options {
|
||||
return Options{
|
||||
Size: DefaultSize,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
// Package trace provides an interface for distributed tracing
|
||||
package trace
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"go-micro.dev/v6/metadata"
|
||||
"go-micro.dev/v6/transport/headers"
|
||||
)
|
||||
|
||||
var (
|
||||
// DefaultTracer is the default tracer.
|
||||
DefaultTracer = NewTracer()
|
||||
)
|
||||
|
||||
// Tracer is an interface for distributed tracing.
|
||||
type Tracer interface {
|
||||
// Start a trace
|
||||
Start(ctx context.Context, name string) (context.Context, *Span)
|
||||
// Finish the trace
|
||||
Finish(*Span) error
|
||||
// Read the traces
|
||||
Read(...ReadOption) ([]*Span, error)
|
||||
}
|
||||
|
||||
// SpanType describe the nature of the trace span.
|
||||
type SpanType int
|
||||
|
||||
const (
|
||||
// SpanTypeRequestInbound is a span created when serving a request.
|
||||
SpanTypeRequestInbound SpanType = iota
|
||||
// SpanTypeRequestOutbound is a span created when making a service call.
|
||||
SpanTypeRequestOutbound
|
||||
)
|
||||
|
||||
// Span is used to record an entry.
|
||||
type Span struct {
|
||||
// Start time
|
||||
Started time.Time
|
||||
// associated data
|
||||
Metadata map[string]string
|
||||
// Id of the trace
|
||||
Trace string
|
||||
// name of the span
|
||||
Name string
|
||||
// id of the span
|
||||
Id string
|
||||
// parent span id
|
||||
Parent string
|
||||
// Duration in nano seconds
|
||||
Duration time.Duration
|
||||
// Type
|
||||
Type SpanType
|
||||
}
|
||||
|
||||
// FromContext returns a span from context.
|
||||
func FromContext(ctx context.Context) (traceID string, parentSpanID string, isFound bool) {
|
||||
traceID, traceOk := metadata.Get(ctx, headers.TraceIDKey)
|
||||
microID, microOk := metadata.Get(ctx, headers.ID)
|
||||
|
||||
if !traceOk && !microOk {
|
||||
isFound = false
|
||||
return
|
||||
}
|
||||
|
||||
if !traceOk {
|
||||
traceID = microID
|
||||
}
|
||||
|
||||
parentSpanID, ok := metadata.Get(ctx, headers.SpanID)
|
||||
|
||||
return traceID, parentSpanID, ok
|
||||
}
|
||||
|
||||
// ToContext saves the trace and span ids in the context.
|
||||
func ToContext(ctx context.Context, traceID, parentSpanID string) context.Context {
|
||||
return metadata.MergeContext(ctx, map[string]string{
|
||||
headers.TraceIDKey: traceID,
|
||||
headers.SpanID: parentSpanID,
|
||||
}, true)
|
||||
}
|
||||
Reference in New Issue
Block a user