πŸ’« Π˜Π½ΡΡ‚Ρ€ΡƒΠΌΠ΅Π½Ρ‚ΠΈΡ€ΠΎΠ²Π°Π½ΠΈΠ΅ Π² Go

РассказываСм ΠΎ ΠΌΠ΅Ρ‚ΠΎΠ΄Π°Ρ… инструмСнтирования Go-ΠΊΠΎΠ΄Π°, контСкстной трассировкС ΠΈ ΡΠΏΠ΅Ρ†ΠΈΠ°Π»ΡŒΠ½ΠΎΠΌ срСдствС Π»Π°ΠΊΠΎΠ½ΠΈΡ‡Π½ΠΎΠ³ΠΎ ΠΈ Π³ΠΈΠ±ΠΊΠΎΠ³ΠΎ инструмСнтирования gtrace.

Π’Π΅Π΄Π΅Π½ΠΈΠ΅ ΠΆΡƒΡ€Π½Π°Π»Π°, сбор ΠΌΠ΅Ρ‚Ρ€ΠΈΠΊ ΠΈ всё, Ρ‡Ρ‚ΠΎ Π½Π΅ связано с основной Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΎΠ½Π°Π»ΡŒΠ½ΠΎΡΡ‚ΡŒΡŽ ΠΊΠΎΠ΄Π°, Π½Π΅ Π΄ΠΎΠ»ΠΆΠ½ΠΎ Π² Π½Ρ‘ΠΌ ΠΏΠΎΡΠ²Π»ΡΡ‚ΡŒΡΡ. ВмСсто этого Π½ΡƒΠΆΠ½ΠΎ ΠΎΠΏΡ€Π΅Π΄Π΅Π»ΠΈΡ‚ΡŒ Ρ‚ΠΎΡ‡ΠΊΠΈ трассировки ΠΊΠΎΠ΄Π°, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Π΅ ΠΌΠΎΠ³ΡƒΡ‚ Π±Ρ‹Ρ‚ΡŒ инструмСнтованы ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»Π΅ΠΌ.

ΠŸΡ€ΠΎΠ±Π»Π΅ΠΌΠ°

ΠŸΡƒΡΡ‚ΡŒ Ρƒ нас Π΅ΡΡ‚ΡŒ ΠΏΠ°ΠΊΠ΅Ρ‚ ΠΏΠΎΠ΄ Π½Π°Π·Π²Π°Π½ΠΈΠ΅ΠΌ lib ΠΈ структура 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)
}

Π§Ρ‚ΠΎ Ссли Π½Π°ΠΌ Π½ΡƒΠΆΠ½ΠΎ ΡΠΎΠ·Π΄Π°Ρ‚ΡŒ нСсколько Π»ΠΎΠ³ΠΎΠ² Π΄ΠΎ ΠΈ послС ΠΏΠΈΠ½Π³Π°? Один ΠΈΠ· способов состоит Π² Ρ‚ΠΎΠΌ, Ρ‡Ρ‚ΠΎΠ±Ρ‹ Π²ΡΡ‚Ρ€ΠΎΠΈΡ‚ΡŒ Π² 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
}

ΠžΠ±Ρ‹Ρ‡Π½ΠΎ это Π½Π΅ΠΎΠ±Ρ…ΠΎΠ΄ΠΈΠΌΠΎ, ΠΊΠΎΠ³Π΄Π° ΠΌΡ‹ добавляСм расчёт ΠΌΠ΅Ρ‚Ρ€ΠΈΠΊ Π² Client:

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().

ΠšΠΎΠ»ΠΈΡ‡Π΅ΡΡ‚Π²ΠΎ Π½Π΅ΠΊΠΎΠ³Π΅Ρ€Π΅Π½Ρ‚Π½Ρ‹Ρ… строк ΠΊΠΎΠ΄Π°, Ρ‚ΠΎ Π΅ΡΡ‚ΡŒ Π½Π΅ связанных с основной Π΄Π΅ΡΡ‚Π΅Π»ΡŒΠ½ΠΎΡΡ‚ΡŒΡŽ нашСго ΠΊΠ»ΠΈΠ΅Π½Ρ‚Π° – это Ρ‚ΠΎΠ»ΡŒΠΊΠΎ пСрвая ΠΏΡ€ΠΎΠ±Π»Π΅ΠΌΠ° Ρ‚Π°ΠΊΠΎΠ³ΠΎ ΠΏΠΎΠ΄Ρ…ΠΎΠ΄Π°.

Π§Ρ‚ΠΎ Ссли Π²ΠΎ врСмя Ρ€Π°Π±ΠΎΡ‚Ρ‹ ΠΏΡ€ΠΎΠ³Ρ€Π°ΠΌΠΌΡ‹ Π²Ρ‹ ΠΏΠΎΠΉΠΌΡ‘Ρ‚Π΅, Ρ‡Ρ‚ΠΎ ΠΌΠ΅Ρ‚Ρ€ΠΈΠΊΡƒ Π½Π΅ΠΎΠ±Ρ…ΠΎΠ΄ΠΈΠΌΠΎ ΠΏΠ΅Ρ€Π΅ΠΈΠΌΠ΅Π½ΠΎΠ²Π°Ρ‚ΡŒ? Или Π½ΡƒΠΆΠ½ΠΎ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒ Π΄Ρ€ΡƒΠ³ΡƒΡŽ Π±ΠΈΠ±Π»ΠΈΠΎΡ‚Π΅ΠΊΡƒ для логирования? ΠŸΡ€ΠΈ описанном ΠΏΠΎΠ΄Ρ…ΠΎΠ΄Π΅ Π½ΡƒΠΆΠ½ΠΎ Π±ΡƒΠ΄Π΅Ρ‚ ΠΏΠ΅Ρ€Π΅ΠΉΡ‚ΠΈ ΠΊ Ρ€Π΅Π°Π»ΠΈΠ·Π°Ρ†ΠΈΠΈ ΠΊΠ»ΠΈΠ΅Π½Ρ‚Π°, Π° Ρ‚Π°ΠΊΠΆΠ΅ Π΄Ρ€ΡƒΠ³ΠΈΡ… ΠΏΠΎΠ΄ΠΎΠ±Π½Ρ‹Ρ… ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ‚ΠΎΠ² ΠΈ ΠΈΠ·ΠΌΠ΅Π½ΠΈΡ‚ΡŒ Π΅Ρ‘.

Π­Ρ‚ΠΎ ΠΎΠ·Π½Π°Ρ‡Π°Π΅Ρ‚, Ρ‡Ρ‚ΠΎ придСтся ΠΌΠ΅Π½ΡΡ‚ΡŒ ΠΊΠΎΠ΄ ΠΊΠ°ΠΆΠ΄Ρ‹ΠΉ Ρ€Π°Π·, ΠΊΠΎΠ³Π΄Π° измСняСтся Ρ‡Ρ‚ΠΎ-Ρ‚ΠΎ Π½Π΅ связанноС с основной Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΎΠ½Π°Π»ΡŒΠ½ΠΎΡΡ‚ΡŒΡŽ ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ‚Π°, Π° Ρ‚Π°ΠΊΠΈΠ΅ дСйствия Π½Π°Ρ€ΡƒΡˆΠ°ΡŽΡ‚ ΠΏΡ€ΠΈΠ½Ρ†ΠΈΠΏ SRP.

ВсС эти ΠΌΠΎΠΌΠ΅Π½Ρ‚Ρ‹ Π²ΡΠΏΠ»Ρ‹Π²Π°ΡŽΡ‚ ΠΈΠ·-Π·Π° ошибки Π² ΠΏΡ€ΠΎΠ΅ΠΊΡ‚ΠΈΡ€ΠΎΠ²Π°Π½ΠΈΠΈ: ΠΌΡ‹ Π½Π΅ Π΄ΠΎΠ»ΠΆΠ½Ρ‹ ΠΏΠ΅Ρ€Π΅ΠΆΠΈΠ²Π°Ρ‚ΡŒ ΠΎ Ρ‚ΠΎΠΌ, ΠΊΠ°ΠΊΠΈΠ΅ ΠΌΠ΅Ρ‚ΠΎΠ΄Ρ‹ инструмСнтирования ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»ΡŒ Π·Π°Ρ…ΠΎΡ‡Π΅Ρ‚ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒ с нашим ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ‚ΠΎΠΌ.

РСшСниС

ΠŸΡ€Π°Π²ΠΈΠ»ΡŒΠ½Ρ‹ΠΉ способ всё ΠΈΡΠΏΡ€Π°Π²ΠΈΡ‚ΡŒ – ΠΎΠΏΡ€Π΅Π΄Π΅Π»ΠΈΡ‚ΡŒ Ρ‚ΠΎΡ‡ΠΊΠΈ трассировки (hooks), ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Π΅ ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»ΡŒ вашСго ΠΊΠΎΠ΄Π° смоТСт ΠΈΠ½ΠΈΡ†ΠΈΠ°Π»ΠΈΠ·ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ с ΠΏΠΎΠΌΠΎΡ‰ΡŒΡŽ Π½Π΅ΠΊΠΎΡ‚ΠΎΡ€ΠΎΠΉ Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΈ (probe) Π²ΠΎ врСмя Ρ€Π°Π½Ρ‚Π°ΠΉΠΌΠ°.

Π­Ρ‚ΠΎ, ΠΊΠΎΠ½Π΅Ρ‡Π½ΠΎ, Π΄ΠΎΠ±Π°Π²ΠΈΡ‚ Π΄ΠΎΠΏΠΎΠ»Π½ΠΈΡ‚Π΅Π»ΡŒΠ½Ρ‹Π΅ строки ΠΊΠΎΠ΄Π°, Π½ΠΎ даст ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»ΡΠΌ Π³ΠΈΠ±ΠΊΠΎΡΡ‚ΡŒ для измСрСния Π²Ρ€Π΅ΠΌΠ΅Π½ΠΈ выполнСния ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ‚Π° с ΠΏΠΎΠΌΠΎΡ‰ΡŒΡŽ любого подходящСго ΠΌΠ΅Ρ‚ΠΎΠ΄Π°.

Π’Π°ΠΊΠΎΠΉ ΠΏΠΎΠ΄Ρ…ΠΎΠ΄ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅Ρ‚ΡΡ, Π½Π°ΠΏΡ€ΠΈΠΌΠ΅Ρ€, ΠΏΠ°ΠΊΠ΅Ρ‚ΠΎΠΌ httptrace ΠΈΠ· стандартной Π±ΠΈΠ±Π»ΠΈΠΎΡ‚Π΅ΠΊΠΈ.

Π”Π°Π²Π°ΠΉΡ‚Π΅ прСдставим ΠΏΠΎΡ‡Ρ‚ΠΈ Ρ‚Ρƒ ΠΆΠ΅ ΠΌΠ΅Ρ…Π°Π½ΠΈΠΊΡƒ, Π½ΠΎ с ΠΎΠ΄Π½ΠΈΠΌ ΠΈΠ·ΠΌΠ΅Π½Π΅Π½ΠΈΠ΅ΠΌ. ВмСсто Ρ‚ΠΎΠ³ΠΎ Ρ‡Ρ‚ΠΎΠ±Ρ‹ ΠΏΡ€Π΅Π΄ΠΎΡΡ‚Π°Π²Π»ΡΡ‚ΡŒ Ρ…ΡƒΠΊΠΈ OnPingStart() ΠΈ OnPingDone(), Π²Π²Π΅Π΄Π΅ΠΌ ΠΎΠ΄ΠΈΠ½ OnPing(), ΠΊΠΎΡ‚ΠΎΡ€Ρ‹ΠΉ Π±ΡƒΠ΄Π΅Ρ‚ Π²Ρ‹Π·Π²Π°Π½ нСпосрСдствСнно ΠΏΠ΅Ρ€Π΅Π΄ ping ΠΈ Π²Π΅Ρ€Π½Π΅Ρ‚ ΠΎΠ±Ρ€Π°Ρ‚Π½Ρ‹ΠΉ Π²Ρ‹Π·ΠΎΠ², Π²Ρ‹Π·Π²Π°Π½Π½Ρ‹ΠΉ послС ping. Π’Π°ΠΊΠΈΠΌ ΠΎΠ±Ρ€Π°Π·ΠΎΠΌ, ΠΌΡ‹ ΠΌΠΎΠΆΠ΅ΠΌ Ρ…Ρ€Π°Π½ΠΈΡ‚ΡŒ Π½Π΅ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Π΅ ΠΏΠ΅Ρ€Π΅ΠΌΠ΅Π½Π½Ρ‹Π΅ Π² Π·Π°ΠΌΡ‹ΠΊΠ°Π½ΠΈΠΈ, Ρ‡Ρ‚ΠΎΠ±Ρ‹ ΠΏΠΎΠ»ΡƒΡ‡ΠΈΡ‚ΡŒ ΠΊ Π½ΠΈΠΌ доступ послС Π·Π°Π²Π΅Ρ€ΡˆΠ΅Π½ΠΈΡ ping (Π½Π°ΠΏΡ€ΠΈΠΌΠ΅Ρ€, для расчСта Π΅Π³ΠΎ Π·Π°Π΄Π΅Ρ€ΠΆΠΊΠΈ).

Π”Π°Π²Π°ΠΉΡ‚Π΅ взглянСм, ΠΊΠ°ΠΊ измСнится 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, Ρ‚Π°ΠΊ ΠΈ ΠΏΡ€ΠΈ ΠΎΠ±Ρ€Π°Ρ‚Π½ΠΎΠΌ Π²Ρ‹Π·ΠΎΠ²Π΅ ΠΎΠ½ ΠΌΠΎΠΆΠ΅Ρ‚ Π²Π΅Ρ€Π½ΡƒΡ‚ΡŒΡΡ с Π½ΡƒΠ»Π΅Π²Ρ‹ΠΌ Π·Π½Π°Ρ‡Π΅Π½ΠΈΠ΅ΠΌ:

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, Π½ΠΎ ΠΊΠΎΠ΄ пСрСстал Π±Ρ‹Ρ‚ΡŒ простым.

ΠŸΡ€Π΅ΠΆΠ΄Π΅ Ρ‡Π΅ΠΌ Π·Π°Π½ΡΡ‚ΡŒΡΡ ΡƒΠΏΡ€ΠΎΡ‰Π΅Π½ΠΈΠ΅ΠΌ, рассмотрим Π΅Ρ‰Ρ‘ ΠΎΠ΄Π½Ρƒ ΠΏΡ€ΠΎΠ±Π»Π΅ΠΌΡƒ.

Π‘ΠΎΠ·Π΄Π°Π½ΠΈΠ΅ Ρ…ΡƒΠΊΠΎΠ²

Как ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»ΡŒ смоТСт ΡΠΎΠ·Π΄Π°Ρ‚ΡŒ нСсколько prob-ΠΎΠ²? Упомянутый Ρ€Π°Π½Π΅Π΅ ΠΏΠ°ΠΊΠ΅Ρ‚ httptrace Π²ΠΊΠ»ΡŽΡ‡Π°Π΅Ρ‚ Π² сСбя ClientTrace.compose(), ΠΊΠΎΡ‚ΠΎΡ€Ρ‹ΠΉ ΠΎΠ±ΡŠΠ΅Π΄ΠΈΠ½ΡΠ΅Ρ‚ Π΄Π²Π΅ структуры трассировки Π² Ρ‚Ρ€Π΅Ρ‚ΡŒΡŽ. Π’Π°ΠΊΠΈΠΌ ΠΎΠ±Ρ€Π°Π·ΠΎΠΌ, каТдая probe-функция ΠΈΠ· Ρ€Π΅Π·ΡƒΠ»ΡŒΡ‚ΠΈΡ€ΡƒΡŽΡ‰Π΅ΠΉ трассировки Π²Ρ‹Π·ΠΎΠ²Π΅Ρ‚ ΡΠΎΠΎΡ‚Π²Π΅Ρ‚ΡΡ‚Π²ΡƒΡŽΡ‰ΠΈΠ΅ prob-Ρ‹ ΠΏΡ€Π΅Π΄Ρ‹Π΄ΡƒΡ‰ΠΈΡ… трассировок.

ΠŸΠΎΡΡ‚ΠΎΠΌΡƒ сначала ΠΏΠΎΠΏΡ€ΠΎΠ±ΡƒΠ΅ΠΌ ΡΠ΄Π΅Π»Π°Ρ‚ΡŒ Ρ‚ΠΎ ΠΆΠ΅ самоС Π²Ρ€ΡƒΡ‡Π½ΡƒΡŽ ΠΈ Π±Π΅Π· рСфлСксии. Для этого ΠΌΡ‹ ΠΏΠ΅Ρ€Π΅ΠΌΠ΅Ρ‰Π°Π΅ΠΌ OnPing ΠΈΠ· ΠΊΠ»ΠΈΠ΅Π½Ρ‚Π° Π² ΠΎΡ‚Π΄Π΅Π»ΡŒΠ½ΡƒΡŽ структуру 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,
	}
}

ΠšΠΎΠ½Ρ‚Π΅ΠΊΡΡ‚Π½Π°Ρ трассировка

Π•Ρ‰Ρ‘ ΠΎΠ΄Π½Π° ΡˆΡ‚ΡƒΠΊΠ°, ΠΊΠΎΡ‚ΠΎΡ€ΡƒΡŽ ΠΌΡ‹ ΠΌΠΎΠΆΠ΅ΠΌ ΠΏΡ€Π΅Π΄ΠΎΡΡ‚Π°Π²ΠΈΡ‚ΡŒ ΡŽΠ·Π΅Ρ€Π°ΠΌ – контСкстная трассировка. Π­Ρ‚ΠΎ ΠΏΡ€ΠΈΠΌΠ΅Ρ€Π½ΠΎ Ρ‚ΠΎ ΠΆΠ΅ самоС, Ρ‡Ρ‚ΠΎ ΠΈ Π² ΠΏΠ°ΠΊΠ΅Ρ‚Π΅ httptrace – Π²ΠΎΠ·ΠΌΠΎΠΆΠ½ΠΎΡΡ‚ΡŒ ΡΠ²ΡΠ·Ρ‹Π²Π°Ρ‚ΡŒ Ρ…ΡƒΠΊΠΈ с 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-макросы, Π½ΠΎ Π΄Π°Π²Π°ΠΉΡ‚Π΅ рассмотрим Π°Π»ΡŒΡ‚Π΅Ρ€Π½Π°Ρ‚ΠΈΠ²Ρ‹.

ОбъСдинСниС Ρ…ΡƒΠΊΠΎΠ², ΠΏΡ€ΠΎΠ²Π΅Ρ€ΠΊΠ° Π½Π° ноль ΠΈ контСкстно-зависимыС Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΈ – ΡˆΠ°Π±Π»ΠΎΠ½Π½Ρ‹. Π—Π½Π°Ρ‡ΠΈΡ‚, ΠΌΡ‹ ΠΌΠΎΠΆΠ΅ΠΌ Π³Π΅Π½Π΅Ρ€ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ для Π½ΠΈΡ… ΠΊΠΎΠ΄ Go Π±Π΅Π· макросов ΠΈΠ»ΠΈ рСфлСксии.

Π˜Π½ΡΡ‚Ρ€ΡƒΠΌΠ΅Π½Ρ‚ gtrace

Π˜Π½ΡΡ‚Ρ€ΡƒΠΌΠ΅Π½Ρ‚ ΠΊΠΎΠΌΠ°Π½Π΄Π½ΠΎΠΉ строки gtrace Π³Π΅Π½Π΅Ρ€ΠΈΡ€ΡƒΠ΅Ρ‚ ΠΊΠΎΠ΄ Go для описанной Π²Ρ‹ΡˆΠ΅ трассировки. Он ΠΏΡ€Π΅Π΄Π»Π°Π³Π°Π΅Ρ‚ структуры с полями Ρ…ΡƒΠΊΠΎΠ² (ΠΏΠΎΠΌΠ΅Ρ‡Π΅Π½Π½Ρ‹Π΅ Π½ΠΈΠΆΠ΅ / / gtrace:gen), Π° Ρ‚Π°ΠΊΠΆΠ΅ Π³Π΅Π½Π΅Ρ€ΠΈΡ€ΡƒΠ΅Ρ‚ Π²ΡΠΏΠΎΠΌΠΎΠ³Π°Ρ‚Π΅Π»ΡŒΠ½Ρ‹Π΅ Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΈ Π²ΠΎΠΊΡ€ΡƒΠ³ Π½ΠΈΡ…, Ρ‚Π°ΠΊ Ρ‡Ρ‚ΠΎ ΠΌΡ‹ ΠΌΠΎΠΆΠ΅ΠΌ ΠΎΠ±ΡŠΠ΅Π΄ΠΈΠ½ΡΡ‚ΡŒ Π΄Π°Π½Π½Ρ‹Π΅ структуры ΠΈ Π²Ρ‹Π·Ρ‹Π²Π°Ρ‚ΡŒ Ρ…ΡƒΠΊΠΈ Π±Π΅Π· ΠΊΠ°ΠΊΠΈΡ…-Π»ΠΈΠ±ΠΎ ΠΏΡ€ΠΎΠ²Π΅Ρ€ΠΎΠΊ. Он Ρ‚Π°ΠΊΠΆΠ΅ ΡƒΠΌΠ΅Π΅Ρ‚ ΡΠΎΠ·Π΄Π°Π²Π°Ρ‚ΡŒ контСкстно-зависимых Ρ…Π΅Π»ΠΏΠ΅Ρ€Ρ‹ для Π²Ρ‹Π·ΠΎΠ²Π° связанных с контСкстом Ρ…ΡƒΠΊΠΎΠ².

ΠŸΡ€ΠΈΠΌΠ΅Ρ€ сгСнСрированного ΠΊΠΎΠ΄Π° находится Π½Π° ΠΎΡ„ΠΈΡ†ΠΈΠ°Π»ΡŒΠ½ΠΎΠΌ Π³ΠΈΡ‚Ρ…Π°Π±Π΅.

ΠœΡ‹ ΠΌΠΎΠΆΠ΅ΠΌ ΠΎΡ‚Π±Ρ€ΠΎΡΠΈΡ‚ΡŒ вСсь «лишний» ΠΊΠΎΠ΄, написанный Π²Ρ‹ΡˆΠ΅, ΠΈ ΠΎΡΡ‚Π°Π²ΠΈΡ‚ΡŒ Ρ‚ΠΎΠ»ΡŒΠΊΠΎ ΡΠ»Π΅Π΄ΡƒΡŽΡ‰ΠΈΠΉ Π±Π»ΠΎΠΊ:

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 позаботится ΠΎ шаблонном ΠΊΠΎΠ΄Π΅ ΠΈ ΠΏΠΎΠ·Π²ΠΎΠ»ΠΈΡ‚ ΡΠΎΡΡ€Π΅Π΄ΠΎΡ‚ΠΎΡ‡ΠΈΡ‚ΡŒΡΡ Π½Π° Ρ‚ΠΎΡ‡ΠΊΠ°Ρ… трассировки, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Π΅ ΠΌΠΎΠ³ΡƒΡ‚ Π±Ρ‹Ρ‚ΡŒ инструмСнтированы ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»ΡΠΌΠΈ.

Π—Π°ΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈΠ΅

Π’ Π΄Π°Π½Π½ΠΎΠΉ ΡΡ‚Π°Ρ‚ΡŒΠ΅ ΠΌΡ‹ рассмотрСли Π²Π°ΠΆΠ½ΡƒΡŽ ΠΏΡ€ΠΎΠ±Π»Π΅ΠΌΡƒ инструмСнтирования Π² ΠΏΡ€ΠΎΠ³Ρ€Π°ΠΌΠΌΠ°Ρ… Π½Π° языкС программирования Go. Вся ΡΠ»ΠΎΠΆΠ½ΠΎΡΡ‚ΡŒ Π·Π°ΠΊΠ»ΡŽΡ‡Π°Π΅Ρ‚ΡΡ Π² гибкости ΠΈ пСрСносимости ΠΊΠΎΠ΄Π° – ΠΎΠ±Ρ‹Ρ‡Π½Ρ‹Π΅ ΠΏΠΎΠ΄Ρ…ΠΎΠ΄Ρ‹ ΠΊ Ρ€Π΅ΡˆΠ΅Π½ΠΈΡŽ Π·Π°Π΄Π°Ρ‡ΠΈ приводят ΠΊ Π½Π°Π³Ρ€ΠΎΠΌΠΎΠΆΠ΄Π΅Π½ΠΈΡŽ ΠΈ ΡƒΡ…ΡƒΠ΄ΡˆΠ΅Π½ΠΈΡŽ Ρ‡ΠΈΡ‚Π°Π±Π΅Π»ΡŒΠ½ΠΎΡΡ‚ΠΈ. ΠŸΠΎΠ΄Ρ…ΠΎΠ΄, рассмотрСнный здСсь, Π²Ρ‹ ΠΌΠΎΠΆΠ΅Ρ‚Π΅ смСло Π±Ρ€Π°Ρ‚ΡŒ Π½Π° Π²ΠΎΠΎΡ€ΡƒΠΆΠ΅Π½ΠΈΠ΅ Π² вашСм ΡΠ»Π΅Π΄ΡƒΡŽΡ‰Π΅ΠΌ ΠΏΡ€ΠΎΠ΅ΠΊΡ‚Π΅.

Π˜ΡΡ‚ΠΎΡ‡Π½ΠΈΠΊΠΈ

Π›Π£Π§Π¨Π˜Π• БВАВЬИ ПО Π’Π•ΠœΠ•

admin
30 июня 2018

Π¨Π°Π±Π»ΠΎΠ½Ρ‹ проСктирования Π² Python: для ΡΡ‚ΠΈΠ»ΡŒΠ½ΠΎΠ³ΠΎ ΠΊΠΎΠ΄Π°

МногиС ΡˆΠ°Π±Π»ΠΎΠ½Ρ‹ проСктирования встроСны Π² Python ΠΈΠ· ΠΊΠΎΡ€ΠΎΠ±ΠΊΠΈ, Π° Π΄Ρ€ΡƒΠ³ΠΈΠ΅ ΠΎΡ‡Π΅Π½ΡŒ ...
Π‘ΠΈΠ±Π»ΠΈΠΎΡ‚Π΅ΠΊΠ° программиста
27 октября 2017

30 Π»ΡƒΡ‡ΡˆΠΈΡ… ΠΊΠ½ΠΈΠ³ для освоСния языка программирования Go

Π˜Ρ‰Π΅Ρ‚Π΅ ΠΊΠ½ΠΈΠ³ΠΈ ΠΏΠΎ Go? ΠŸΡ€Π΅Π΄ΡΡ‚Π°Π²Π»ΡΠ΅ΠΌ 30 книг для освоСния языка программирования...
Π‘ΠΈΠ±Π»ΠΈΠΎΡ‚Π΅ΠΊΠ° программиста
27 ΠΌΠ°Ρ€Ρ‚Π° 2017

4 Π»ΡƒΡ‡ΡˆΠΈΡ… ΠΊΠ½ΠΈΠ³ ΠΎ ΡˆΠ°Π±Π»ΠΎΠ½Π°Ρ… проСктирования

Π›ΡƒΡ‡ΡˆΠΈΠ΅ ΠΊΠ½ΠΈΠ³ΠΈ ΠΎ ΡˆΠ°Π±Π»ΠΎΠ½Π°Ρ… проСктирования, рассчитанныС ΠΊΠ°ΠΊ для Π½ΠΎΠ²ΠΈΡ‡ΠΊΠΎΠ², Ρ‚Π°ΠΊ ...