是什么
Elastic APM是一款应用程序性能检测工具(Application Performance Management),可以借助Elasticsearch进行存储性能数据,利用Kibana进行UI可视化。
其中收集各个应用的性能数据是利用Agent进行收集,Agent根据不同语言提供了不同的实现,针对Go的应用程序也同样提供了GoAgent,结合下图APM的架构图更容易理解。
概念介绍
Agent对所支持的WEB框架提供了中间件支持,进入的HTTP请求都会进入Agent的中间件进行收集,记录。
Agent的上下文传参是通过context包来进行传输
初始化
安装agent
go get -u go.elastic.co/apm
依赖
GO 1.8+, Linux, Windows, MacOS
集成进你的程序
有两种方案
官方集成包
Web框架
数据库框架
- database/sql
- GORM
- go-pg/pg
- Cassandra(gocql)
- Redis(gomodule/redigo)
- Redis(go-redis/redis)
- Elasticsearch
- MongoDB
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的一些性能指标进行记录