
इस लेख में, मैं गो पर प्रोफाइल और ट्रेस प्रोग्राम का एक तरीका साझा करना चाहूंगा। मैं आपको दिखाता हूं कि कोड को लचीला और साफ रखते हुए यह कैसे करना है।
टीएल, डॉ
लॉगिंग, मैट्रिक्स का संग्रह और सब कुछ जो किसी भी कोड की मुख्य कार्यक्षमता से जुड़ा नहीं है, इस कोड के अंदर नहीं होना चाहिए। इसके बजाय, आपको उन ट्रेस बिंदुओं की पहचान करने की आवश्यकता है जिनका उपयोग उपयोगकर्ता द्वारा कोड को मापने के लिए किया जा सकता है।
दूसरे शब्दों में, मैट्रिक्स का लॉगिंग और संग्रह ट्रेस का सबसेट है ।
टेम्पलेट ट्रेस कोड ग्रेस्रेस का उपयोग करके उत्पन्न किया जा सकता है ।
मुसीबत
मान लीजिए कि हमारे पास एक पैकेज 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
}
}
func (c *Client) ping(ctx context.Context) error {
return doPing(ctx, c.conn)
}
क्या होगा यदि हम पिंग संदेश भेजे जाने के ठीक पहले और सही लॉग में रिकॉर्ड करना चाहते हैं? पहला विकल्प लकड़हारा (या इसके इंटरफ़ेस) को एम्बेड करना है 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
trace = trace.Compose(lib.ClientTrace{
OnPing: func() func(error) {
log.Println("ping start")
return func(err error) {
log.Println("ping done", err)
}
},
})
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
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