اذهب أدوات قياس البرمجيات

جوفر بمكبر


في هذه المقالة ، أود أن أشارك طريقة لتوصيف البرامج وتتبعها على Go. سأوضح لك كيفية القيام بذلك مع الحفاظ على المرونة والنظافة.


TL ؛ د


لا يجب أن يكون التسجيل وجمع المقاييس وكل ما لا يرتبط بالوظيفة الرئيسية لأي رمز داخل هذا الرمز. بدلاً من ذلك ، تحتاج إلى تحديد نقاط التتبع التي يمكن استخدامها لقياس التعليمات البرمجية بواسطة المستخدم.


بمعنى آخر ، يعد تسجيل المقاييس وجمعها مجموعات فرعية من التتبع .


يمكن إنشاء رمز تتبع القالب باستخدام gtrace .


مشكلة


لنفترض أن لدينا حزمة libوبعض البنية lib.Client. قبل تنفيذ أي طلب ، lib.Clientيتحقق من الاتصال:


package lib

type Client struct {
    conn net.Conn
}

func (c *Client) Request(ctx context.Context) error {
    if err := c.ping(ctx); err != nil {
        return err
    }
    // Some logic here.
}

func (c *Client) ping(ctx context.Context) error {
    return doPing(ctx, c.conn)
}

ماذا لو أردنا التسجيل في السجل قبل وبعد إرسال رسالة ping مباشرة؟ الخيار الأول هو تضمين المسجل (أو واجهته) في Client:


package lib

type Client struct {
    Logger Logger

    conn net.Conn
}

func (c *Client) ping(ctx context.Context) (err error) {
    c.Logger.Info("ping started")
    err = doPing(ctx, c.conn)
    c.Logger.Info("ping done (err is %v)", err)
    return
}

إذا أردنا جمع أي مقاييس ، يمكننا أن نفعل الشيء نفسه:


package lib

type Client struct {
    Logger  Logger
    Metrics Registry

    conn net.Conn
}

func (c *Client) ping(ctx context.Context) (err error) {
    start := time.Now()
    c.Logger.Info("ping started")

    err = doPing(ctx, c.conn)

    c.Logger.Info("ping done (err is %v)", err)
    metric := c.Metrics.Get("ping_latency")
    metric.Send(time.Since(start))

    return err
}

, – . Client, , ó , ( doPing()).


( Client) .


, , , ? , , ?


, Client.


, , - . , (SRP).


, Client ? , , ? , Go, , .


, :


,
.


, ( ), ( ).


, , .


, , httptrace Go.


, : OnPingStart() OnPingDone(), OnPing(), callback. OnPing() ping-, callback – . (, doPing()).


Client :


package lib

type Client struct {
    OnPing func() func(error)
    conn net.Conn
}

func (c *Client) ping(ctx context.Context) (err error) {
    done := c.OnPing()
    err = doPing(ctx, c.conn)
    done(err)
    return
}

, OnPing nil. :


func (c *Client) ping(ctx context.Context) (err error) {
    var done func(error)
    if fn := c.OnPing; fn != nil {
        done = fn()
    }
    err = doPing(ctx, c.conn)
    if done != nil {
        done(err)
    }
    return
}

SRP , .


, .



?
httptrace ClientTrace.compose(), n . n ( ).


( reflect). OnPing Client ClientTrace:


package lib

type Client struct {
    Trace ClientTrace
    conn net.Conn
}

type ClientTrace struct {
    OnPing func() func(error)
}

:


func (a ClientTrace) Compose(b ClientTrace) (c ClientTrace) {
    switch {
    case a.OnPing == nil:
        c.OnPing = b.OnPing
    case b.OnPing == nil:
        c.OnPing = a.OnPing
    default:
        c.OnPing = func() func(error) {
            doneA := a.OnPing()
            doneB := b.OnPing() 
            switch {
            case doneA == nil:
                return doneB
            case doneB == nil:
                return doneA
            default:
                return func(err error) {
                    doneA(err)
                    doneB(err)
                }
            }
        }
    }
    return c
}

, ? , .


:


package main

import (
    "log"

    "some/path/to/lib"
)

func main() {
    var trace lib.ClientTrace

    // Logging hooks.
    trace = trace.Compose(lib.ClientTrace{
        OnPing: func() func(error) {
            log.Println("ping start")
            return func(err error) {
                log.Println("ping done", err)
            }
        },
    })

    // Some metrics hooks.
    trace = trace.Compose(lib.ClientTrace{
        OnPing: func() func(error) {
            start := time.Now()
            return func(err error) {
                metric := stats.Get("ping_latency")
                metric.Send(time.Since(start))
            }
        },
    })

    c := lib.Client{
        Trace: trace,
    }
}


. ClientTrace context.Context, Client.Request():


package lib

type clientTraceContextKey struct{}

func ClientTrace(ctx context.Context) ClientTrace {
    t, _ := ctx.Value(clientTraceContextKey{})
    return t
}

func WithClientTrace(ctx context.Context, t ClientTrace) context.Context {
    prev := ContextClientTrace(ctx)
    return context.WithValue(ctx,
        clientTraceContextKey{},
        prev.Compose(t),
    )
}

. , !


?


, Vim ( - ), .


, , nil , reflection.


github.com/gobwas/gtrace


gtrace . , //gtrace:gen. - nil, .


.


:


package lib

//go:generate gtrace

//gtrace:gen
//gtrace:set context
type ClientTrace struct {
    OnPing func() func(error)
}

type Client struct {
    Trace ClientTrace
    conn net.Conn
}

func (c *Client) ping(ctx context.Context) (err error) {
    done := c.Trace.onPing(ctx)
    err = doPing(ctx, c.conn)
    done(err)
    return
}

go generate ClientTrace.


! gtrace , .


!


References



All Articles