Golang 微服务系列 go-kit(Log,Metrics,Tracing)

本文涉及的产品
服务治理 MSE Sentinel/OpenSergo,Agent数量 不受限
简介: Log & Metrics & Tracing

go-kit Log & Metrics & Tracing

微服务监控3大核心 Log & Metrics & Tracing

Log

Log 模块源码有待分析(分析完再补上)

Metrics

主要是封装 Metrics 接口,及各个 Metrics(Prometheus,InfluxDB,StatsD,expvar) 中间件的封装。

// Counter describes a metric that accumulates values monotonically.
// An example of a counter is the number of received HTTP requests.
type Counter interface {
    With(labelValues ...string) Counter
    Add(delta float64)
}

// Gauge describes a metric that takes specific values over time.
// An example of a gauge is the current depth of a job queue.
type Gauge interface {
    With(labelValues ...string) Gauge
    Set(value float64)
    Add(delta float64)
}

// Histogram describes a metric that takes repeated observations of the same
// kind of thing, and produces a statistical summary of those observations,
// typically expressed as quantiles or buckets. An example of a histogram is
// HTTP request latencies.
type Histogram interface {
    With(labelValues ...string) Histogram
    Observe(value float64)
}

Tracing

Tracing 主要是对 Zipkin, OpenTracing, OpenCensus 的封装。

Zipkin

func TraceEndpoint(tracer *zipkin.Tracer, name string) endpoint.Middleware {
    return func(next endpoint.Endpoint) endpoint.Endpoint {
        return func(ctx context.Context, request interface{}) (interface{}, error) {
            var sc model.SpanContext
            if parentSpan := zipkin.SpanFromContext(ctx); parentSpan != nil {
                sc = parentSpan.Context()
            }
            sp := tracer.StartSpan(name, zipkin.Parent(sc))
            defer sp.Finish()

            ctx = zipkin.NewContext(ctx, sp)
            return next(ctx, request)
        }
    }
}

OpenTracing

// TraceServer returns a Middleware that wraps the `next` Endpoint in an
// OpenTracing Span called `operationName`.
//
// If `ctx` already has a Span, it is re-used and the operation name is
// overwritten. If `ctx` does not yet have a Span, one is created here.
func TraceServer(tracer opentracing.Tracer, operationName string) endpoint.Middleware {
    return func(next endpoint.Endpoint) endpoint.Endpoint {
        return func(ctx context.Context, request interface{}) (interface{}, error) {
            serverSpan := opentracing.SpanFromContext(ctx)
            if serverSpan == nil {
                // All we can do is create a new root span.
                serverSpan = tracer.StartSpan(operationName)
            } else {
                serverSpan.SetOperationName(operationName)
            }
            defer serverSpan.Finish()
            otext.SpanKindRPCServer.Set(serverSpan)
            ctx = opentracing.ContextWithSpan(ctx, serverSpan)
            return next(ctx, request)
        }
    }
}

// TraceClient returns a Middleware that wraps the `next` Endpoint in an
// OpenTracing Span called `operationName`.
func TraceClient(tracer opentracing.Tracer, operationName string) endpoint.Middleware {
    return func(next endpoint.Endpoint) endpoint.Endpoint {
        return func(ctx context.Context, request interface{}) (interface{}, error) {
            var clientSpan opentracing.Span
            if parentSpan := opentracing.SpanFromContext(ctx); parentSpan != nil {
                clientSpan = tracer.StartSpan(
                    operationName,
                    opentracing.ChildOf(parentSpan.Context()),
                )
            } else {
                clientSpan = tracer.StartSpan(operationName)
            }
            defer clientSpan.Finish()
            otext.SpanKindRPCClient.Set(clientSpan)
            ctx = opentracing.ContextWithSpan(ctx, clientSpan)
            return next(ctx, request)
        }
    }
}

OpenCensus

func TraceEndpoint(name string, options ...EndpointOption) endpoint.Middleware {
    if name == "" {
        name = TraceEndpointDefaultName
    }

    cfg := &EndpointOptions{}

    for _, o := range options {
        o(cfg)
    }

    return func(next endpoint.Endpoint) endpoint.Endpoint {
        return func(ctx context.Context, request interface{}) (response interface{}, err error) {
            ctx, span := trace.StartSpan(ctx, name)
            if len(cfg.Attributes) > 0 {
                span.AddAttributes(cfg.Attributes...)
            }
            defer span.End()

            defer func() {
                if err != nil {
                    if lberr, ok := err.(lb.RetryError); ok {
                        // handle errors originating from lb.Retry
                        attrs := make([]trace.Attribute, 0, len(lberr.RawErrors))
                        for idx, rawErr := range lberr.RawErrors {
                            attrs = append(attrs, trace.StringAttribute(
                                "gokit.retry.error."+strconv.Itoa(idx+1), rawErr.Error(),
                            ))
                        }
                        span.AddAttributes(attrs...)
                        span.SetStatus(trace.Status{
                            Code:    trace.StatusCodeUnknown,
                            Message: lberr.Final.Error(),
                        })
                        return
                    }
                    // generic error
                    span.SetStatus(trace.Status{
                        Code:    trace.StatusCodeUnknown,
                        Message: err.Error(),
                    })
                    return
                }

                // test for business error
                if res, ok := response.(endpoint.Failer); ok && res.Failed() != nil {
                    span.AddAttributes(
                        trace.StringAttribute("gokit.business.error", res.Failed().Error()),
                    )
                    if cfg.IgnoreBusinessError {
                        span.SetStatus(trace.Status{Code: trace.StatusCodeOK})
                        return
                    }
                    // treating business error as real error in span.
                    span.SetStatus(trace.Status{
                        Code:    trace.StatusCodeUnknown,
                        Message: res.Failed().Error(),
                    })
                    return
                }

                // no errors identified
                span.SetStatus(trace.Status{Code: trace.StatusCodeOK})
            }()
            response, err = next(ctx, request)
            return
        }
    }
}

使用

参考 examples/set.go

var concatEndpoint endpoint.Endpoint

concatEndpoint = MakeConcatEndpoint(svc)
concatEndpoint = opentracing.TraceServer(otTracer, "Concat")(concatEndpoint)
concatEndpoint = zipkin.TraceEndpoint(zipkinTracer, "Concat")(concatEndpoint)
concatEndpoint = LoggingMiddleware(log.With(logger, "method", "Concat"))(concatEndpoint)
concatEndpoint = InstrumentingMiddleware(duration.With("method", "Concat"))(concatEndpoint)

小结

通过把第三方中间件封装成 endpoint.Middleware, 可以与其它 go-kit 中间件组合。

相关实践学习
日志服务之使用Nginx模式采集日志
本文介绍如何通过日志服务控制台创建Nginx模式的Logtail配置快速采集Nginx日志并进行多维度分析。
目录
相关文章
|
1月前
|
运维 监控 Go
Go语言微服务实战与最佳实践
【2月更文挑战第14天】本文将深入探讨使用Go语言进行微服务实战中的最佳实践,包括服务拆分、API设计、并发处理、错误处理、服务治理与监控等方面。通过实际案例和详细步骤,我们将分享如何在Go语言环境中构建高效、稳定、可扩展的微服务系统。
|
1月前
|
监控 网络协议 Go
应用监控 eBPF 版:实现 Golang 微服务的无侵入应用监控
应用监控 eBPF 版:实现 Golang 微服务的无侵入应用监控
109642 118
|
3月前
|
存储 Go
Go 浅析主流日志库:从设计层学习如何集成日志轮转与切割功能
本文将探讨几个热门的 go 日志库如 logrus、zap 和官网的 slog,我将分析这些库的的关键设计元素,探讨它们是如何支持日志轮转与切割功能的配置。
99 0
Go 浅析主流日志库:从设计层学习如何集成日志轮转与切割功能
|
1月前
|
SQL 前端开发 Go
编程笔记 GOLANG基础 001 为什么要学习Go语言
编程笔记 GOLANG基础 001 为什么要学习Go语言
|
3月前
|
物联网 Go 网络性能优化
使用Go语言(Golang)可以实现MQTT协议的点对点(P2P)消息发送。MQTT协议本身支持多种消息收发模式
使用Go语言(Golang)可以实现MQTT协议的点对点(P2P)消息发送。MQTT协议本身支持多种消息收发模式【1月更文挑战第21天】【1月更文挑战第104篇】
88 1
|
3天前
|
Kubernetes Cloud Native Go
《Go 简易速速上手小册》第10章:微服务与云原生应用(2024 最新版)(下)
《Go 简易速速上手小册》第10章:微服务与云原生应用(2024 最新版)
38 0
|
3天前
|
Cloud Native 算法 Go
《Go 简易速速上手小册》第10章:微服务与云原生应用(2024 最新版)(上)
《Go 简易速速上手小册》第10章:微服务与云原生应用(2024 最新版)
22 0
|
1月前
|
运维 监控 负载均衡
Go语言中微服务架构设计与原则
【2月更文挑战第14天】本文将深入探讨在Go语言环境下,微服务架构的设计原则和实践。我们将讨论如何根据微服务架构的核心概念,如服务拆分、独立部署、容错处理、服务治理等,来构建一个稳定、可扩展、可维护的Go语言微服务系统。
|
1月前
|
运维 Go 开发者
Go语言基础及其在微服务中的应用
【2月更文挑战第14天】本文旨在探讨Go语言的核心特性及其在构建微服务架构中的实际应用。我们将首先简要介绍Go语言的基本概念与特点,然后详细分析Go语言在构建高性能、高可用微服务中的优势,并通过实例展示如何使用Go语言实现微服务的基础组件和通信机制。
|
1月前
|
Go 开发工具 git
编程笔记 GOLANG基础 003 Go语言开发环境搭建
编程笔记 GOLANG基础 003 Go语言开发环境搭建