23

Elastic APM-Go Agent介绍(中文翻译)

 4 years ago
source link: https://studygolang.com/articles/27094
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

是什么

Elastic APM是一款应用程序性能检测工具(Application Performance Management),可以借助Elasticsearch进行存储性能数据,利用Kibana进行UI可视化。

其中收集各个应用的性能数据是利用Agent进行收集,Agent根据不同语言提供了不同的实现,针对Go的应用程序也同样提供了GoAgent,结合下图APM的架构图更容易理解。

54d5b00ad428de02c11857b4da30f69d.html

概念介绍

Agent对所支持的 WEB框架 提供了中间件支持,进入的HTTP请求都会进入Agent的中间件进行收集,记录。

  • 对于HTTP进入请求 可以使用 apmhttp 包进行记录。
  • 对与数据库操作请求 可以使用 apmsql 包进行记录。

Agent的上下文传参是通过context包来进行传输

初始化

安装agent

go get -u go.elastic.co/apm

依赖

GO 1.8+, Linux, Windows, MacOS

集成进你的程序

有两种方案

官方集成包

Web框架

数据库框架

RPC框架

服务框架

日志框架

自定义集成

在集成之前得明白三个概念

  • Transaction 表示一次顶级会话(HTTP请求或RPC调用)
  • Span 在会话中的一次调用(数据库访问,或者对其他服务进行RPC调用))
  • Error 错误

开启一个会话

tx := apm.DefaultTracer.StartTransaction("GET /api/v1", "request")
defer tx.End()
...
tx.Result = "HTTP 2xx"
tx.Context.SetLabel("region", "us-east-1")

只需要在会话入口处执行StartTransaction 给予调用地址,以及类型 则会返回一个Transaction 我们还可以对结果进行赋值,并且打上自定义的标签。

并且你可以把这个会话集成到现有的context中

ctx = apm.ContextWithTransaction(ctx, tx)

执行一次调用

我们可以通过Transaction

span := tx.StartSpan("SELECT FROM foo", "db.mysql.query", nil)
defer span.End()

也可以从包含Transaction的Context中初始化

span, ctx := apm.StartSpan(ctx, "SELECT FROM foo", "db.mysql.query")
defer span.End()

错误记录

对于Panic错误可以用此方法进行拦截记录:

defer func() {
  if v:= recover(); v != nil {
    e := apm.DefaultTracer.Recovered()
    e.SetTransaction(tx)
    e.Send()
  }
}()

对于非Panic错误可以利用如下方式记录:

API介绍

Tracer API

为了更加方便的进行调用,Go agent引入了Tracer的概念,Tracer包含了一些公共初始化配置,官方提供了一个默认的实现

import (
    "go.elastic.co/apm"
)

func main() {
    tracer := apm.DefaultTracer
    ...
}

Transactions

func (*Tracer) StartTransaction(name, type string) *Transaction

开启一个会话 返回一个Transaction实例, 此方法应该在HTTP或gRPC入口处调用

transaction := apm.DefaultTracer.StartTransaction("GET /", "request")

在会话完成时候要对会话的状态进行记录

transaction.Result = "Success"
transaction.Context.SetLabel("region", "us-east-1")

func (*Tracer) StartTransactionOptions(name, type string, opts TransactionOptions) *Transaction

可以根据自定义配置开启一个自定义的会话

func (*Transaction) End()

结束一个会话

func (*Transaction) TraceContext() TraceContext

获取会话上下文

func (*Transaction) EnsureParent() SpanID

这个不懂?

func ContextWithTransaction(context.Context, *Transaction) context.Context

把当前会话附加到上下文中

func TransactionFromContext(context.Context) *Transaction

从上下文中获取会话,如果上下文中不存在则返回nil

func DetachedContext(context.Context) context.Context

把上下文拷贝一份出来防止上下文被关闭

func TraceFormatter(context.Context) fmt.Formatter

根据上下文产生一个格式化Formatter格式,来对上下文的会话等信息进行格式化。

这个格式化有如下格式:

  • %v: 包含了TraceID Transaction ID, SpanID(如果是在Span中) 空格分割
  • %t: trace ID only
  • %x: transaction ID only
  • %s: span ID only

SPANS

func (*Transaction) StartSpan(name, spanType string, parent *Span) *Span

开启一个调用

其中注意spanType 它的格式是:类型.子类型.动作

如果是db.mysql 则会在elastic search 上记录成 db类型下的mysql子类型

如果是db.mysql.query 则会在elastic search上记录成db类型下的mysql子类型的query动作

func (*Transaction) StartSpanOptions(name, spanType string, opts SpanOptions) *Span

以自定义的配置开启一个SPAN

opts := apm.SpanOptions{
    Start: time.Now(),
    Parent: parentSpan.TraceContext(),
}
span := tx.StartSpanOptions("SELECT FROM foo", "db.mysql.query", opts)

func StartSpan(ctx context.Context, name, spanType string) (*Span, context.Context)

从上下文中获取一个SPAN 前提是上下文中包含了一个SPAN

func (*Span) End()

结束一个调用

func (*Span) Dropped() bool

丢弃一个调用 ,不上传到服务器

func (*Span) TraceContext() TraceContext

返回一个包含SPAN的Trace上下文

func ContextWithSpan(context.Context, *Span) context.Context

把Span加入到上下文中

func SpanFromContext(context.Context) *Span

从上下文中获取Span

Context

你可以通过设置上下来对收集的信息增加自定义的标签

func (*Context) SetLabel(key string, value interface{})

设置上下文标签 以及内容,会对key进行索引

func (*Context) SetCustom(key string, value interface{})

设置自定义的上下文内容,不会对key进行索引

func (*Context) SetUsername(username string)

设置用户名,方便调试

func (*Context) SetUserID(id string)

设置用户ID,方便调试

func (*Context) SetUserEmail(email string)

设置用户邮箱,方便调试

Errors

Go Agent提供了两种记录错误的方式:通过打印错误日志或者直接汇报一个异常(panic)

func (*Tracer) NewError(error) *Error

通过错误实例化一个Error类型 相关参数将会被设置

其中error 可以实例化以下接口来提供更多的详细信息

// Errors implementing ErrorsStacktracer will have their stacktrace
// set based on the result of the StackTrace method.
type ErrorsStacktracer interface {
    StackTrace() github.com/pkg/errors.StackTrace
}

// Errors implementing Stacktracer will have their stacktrace
// set based on the result of the StackTrace method.
type Stacktracer interface {
    StackTrace() []go.elastic.co/apm/stacktrace.Frame
}

// Errors implementing Typer will have a "type" field set to the
// result of the Type method.
type Typer interface {
    Type() string
}

// Errors implementing StringCoder will have a "code" field set to the
// result of the Code method.
type StringCoder interface {
    Code() string
}

// Errors implementing NumberCoder will have a "code" field set to the
// result of the Code method.
type NumberCoder interface {
    Code() float64
}

NewError 返回的Error将会带有唯一的错误ID 可以方便你进行调试

func (*Tracer) NewErrorLog(ErrorLogRecord) *Error

通过一个ErrorLogRecord来实例化一个自定义的Error

ErrorLogRecord结构如下:

type ErrorLogRecord struct {
    // Message holds the message for the log record,
    // e.g. "failed to connect to %s".
    //
    // If this is empty, "[EMPTY]" will be used.
    Message string

    // MessageFormat holds the non-interpolated format
    // of the log record, e.g. "failed to connect to %s".
    //
    // This is optional.
    MessageFormat string

    // Level holds the severity level of the log record.
    //
    // This is optional.
    Level string

    // LoggerName holds the name of the logger used.
    //
    // This is optional.
    LoggerName string

    // Error is an error associated with the log record.
    //
    // This is optional.
    Error error
}

func (*Error) SetTransaction(*Transaction)

把此错误关联到会话当中

func (*Error) SetSpan(*Span)

把此错误关联到调用当中

func (*Error) Send()

把当前错误信息发送到服务器上

func (*Tracer) Recovered(interface{}) *Error

从recoverd value中实例化出Error

tx := apm.DefaultTracer.StartTransaction(...)
defer tx.End()
defer func() {
    if v := recover(); v != nil {
        e := apm.DefaultTracer.Recovered(v)
        e.SetTransaction(tx)
        e.Send()
    }
}()

func CaptureError(context.Context, error) *Error

从上下文中获取会话信息以及调用信息与错误进行关联实例化出一个Error

if err != nil {
        e := apm.CaptureError(ctx, err)
        e.Send()
}

Trace Context

Trace Context包含了会话ID或者调用ID,这个ID可以标志着这个Context属于哪一个调用或者会话,Trace context可以通过字符串化进行HTTP传输来进行链路追踪

Error Context

错误可以与上下文进行关联

并且提供了SetTransaction和SetSpan将错误与具体调用或会话进行关联。

Go性能指标记录

Go Agent除了对会话进行记录,还会对Go的一些性能指标进行记录

通过设置环境来决定 采集时间 ,以及 是否开启


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK