OpenCensus 使用的简单教程 - 文档翻译

  1. 安装 - go get

    OpenCensus: go get -u -v go.opencensus.io/...
    Prometheus exporter: go get -u -v contrib.go.opencensus.io/exporter/prometheus
    
  2. 概览

    1. 创建我们将要记录的量化 Metrics(numerical)
    2. 创建我们将要和Metrics关联的tags
    3. 组织metrics到一个View
    4. views出口到后端:(Prometheus)
  3. 代码开始

    • 写一个REPL程序,然后通过这个程序收集metrics观察以下动作
      • 循环中每次处理的延迟时间
      • 命令行中读取的行数
      • 错误次数
      • 每次命令的长度
  4. 让Metrics可用

    1. import Packages

      import (
        "bufio"
        "bytes"
        "context"
        "fmt"
        "io"
        "log"
        "os"
        "time"
      
        "go.opencensus.io/stats"
        "go.opencensus.io/tag"
      
      )
      
    2. 创建Metrics

      实质为变量:待会将要记录的metrics

      var (
        // The latency in milliseconds
        MLatencyMs = stats.Float64("repl/latency", "The latency in milliseconds per REPL loop", "ms")
      
        // Counts/groups the lengths of lines read in.
        MLineLengths = stats.Int64("repl/line_lengths", "The distribution of line lengths", "By")
      )
      
    3. 创建Tags

      同样也是变量,稍后将用这些变量记录那些方法被调用

      var (
        KeyMethod, _ = tag.NewKey("method")
        KeyStatus, _ = tag.NewKey("status")
        KeyError, _  = tag.NewKey("error")
      )
      
    4. Inserting Tags - 嵌入的Tags

      插入一个特殊的tag,它会返回一个ctx.Context 当我们记录metrics的时候需要用到ctx,这个ctx有所有预先插入的tags,允许在上下文中传播

      ctx, _ := tag.New(context.Background(), tag.Insert(KeyMethod, "repl"), tag.Insert(KeyStatus, "OK"))
      
      

      当记录metrics的时候,我们需要tags.New()返回的ctx

    5. 记录Metrics

      现在我们将要记录期望的Metrics,为了实现这个,我们需要用到stats.Record方法和ctx与预先实例化Metrics变量

      func processLine(ctx context.Context, in []byte) (out []byte, err error) {
        defer func() {
            stats.Record(ctx, MLatencyMs.M(sinceInMilliseconds(startTime)),
                MLineLengths.M(int64(len(in))))
        }()
      }
      
  5. 使Views可用 - Enable Views

    1. import "go.opencensus.io/stats/view"
      
    2. 创建 Views

      现在创建让metrics以何种方式组织的Views

      var (
        LatencyView = &view.View{
            Name:        "demo/latency",
            Measure:     MLatencyMs,
            Description: "The distribution of the latencies",
      
            // Latency in buckets:
            // [>=0ms, >=25ms, >=50ms, >=75ms, >=100ms, >=200ms, >=400ms, >=600ms, >=800ms, >=1s, >=2s, >=4s, >=6s]
            Aggregation: view.Distribution(0, 25, 50, 75, 100, 200, 400, 600, 800, 1000, 2000, 4000, 6000),
            TagKeys:     []tag.Key{KeyMethod}}
      
        LineCountView = &view.View{
            Name:        "demo/lines_in",
            Measure:     MLineLengths,
            Description: "The number of lines from standard input",
            Aggregation: view.Count(),
        }
      
        LineLengthView = &view.View{
            Name:        "demo/line_lengths",
            Description: "Groups the lengths of keys in buckets",
            Measure:     MLineLengths,
            // Lengths: [>=0B, >=5B, >=10B, >=15B, >=20B, >=40B, >=60B, >=80, >=100B, >=200B, >=400, >=600, >=800, >=1000]
            Aggregation: view.Distribution(0, 5, 10, 15, 20, 40, 60, 80, 100, 200, 400, 600, 800, 1000),
        }
      )
      
    3. 注册Views

      if err := view.Register(LatencyView, LineCountView, LineLengthView); err != nil {
            log.Fatalf("Failed to register views: %v", err)
      }
      
  1. 导出stats - Exporting stats

    1. 导包:import Packages

      在import中加入Prometheus GO exporter

      import "contrib.go.opencensus.io/exporter/prometheus"
      
    2. 创建出口者 - Create the exporter

    3. 为了保证metrics出口到Prometheus,应用需要暴露一个端口,OpenCensus Go Prometheus exporter是一个http.Handler,并且监听必须通过HTTP路径/metrics

    4. go func() {
                mux := http.NewServeMux()
                mux.Handle("/metrics", pe)
                if err := http.ListenAndServe(":8888", mux); err != nil {
                    log.Fatalf("Failed to run Prometheus scrape endpoint: %v", err)
                }
            }()
      
    5. Running the App

      go run repl.go

    6. Prometheus configuration file

    scrape_configs:
      - job_name: 'ocmetricstutorial'
    
        scrape_interval: 10s
    
        static_configs:
          - targets: ['localhost:8888']
    
    1. Running Prometheus

    prometheus --config.file=promconfig.yaml
    
  2. 代码汇总

package main

import (
    "bufio"
    "bytes"
    "context"
    "fmt"
    "io"
    "log"
    "net/http"
    "os"
    "time"

    "contrib.go.opencensus.io/exporter/prometheus"
    "go.opencensus.io/stats"
    "go.opencensus.io/stats/view"
    "go.opencensus.io/tag"
)

var (
    // The latency in milliseconds
    MLatencyMs = stats.Float64("repl/latency", "The latency in milliseconds per REPL loop", "ms")

    // Counts/groups the lengths of lines read in.
    MLineLengths = stats.Int64("repl/line_lengths", "The distribution of line lengths", "By")
)

var (
    KeyMethod, _ = tag.NewKey("method")
    KeyStatus, _ = tag.NewKey("status")
    KeyError, _  = tag.NewKey("error")
)

var (
    LatencyView = &view.View{
        Name:        "demo/latency",
        Measure:     MLatencyMs,
        Description: "The distribution of the latencies",

        // Latency in buckets:
        // [>=0ms, >=25ms, >=50ms, >=75ms, >=100ms, >=200ms, >=400ms, >=600ms, >=800ms, >=1s, >=2s, >=4s, >=6s]
        Aggregation: view.Distribution(0, 25, 50, 75, 100, 200, 400, 600, 800, 1000, 2000, 4000, 6000),
        TagKeys:     []tag.Key{KeyMethod}}

    LineCountView = &view.View{
        Name:        "demo/lines_in",
        Measure:     MLineLengths,
        Description: "The number of lines from standard input",
        Aggregation: view.Count(),
    }

    LineLengthView = &view.View{
        Name:        "demo/line_lengths",
        Description: "Groups the lengths of keys in buckets",
        Measure:     MLineLengths,
        // Lengths: [>=0B, >=5B, >=10B, >=15B, >=20B, >=40B, >=60B, >=80, >=100B, >=200B, >=400, >=600, >=800, >=1000]
        Aggregation: view.Distribution(0, 5, 10, 15, 20, 40, 60, 80, 100, 200, 400, 600, 800, 1000),
    }
)

func main() {
    // Register the views, it is imperative that this step exists
    // lest recorded metrics will be dropped and never exported.
    if err := view.Register(LatencyView, LineCountView, LineLengthView); err != nil {
        log.Fatalf("Failed to register the views: %v", err)
    }

    // Create the Prometheus exporter.
    pe, err := prometheus.NewExporter(prometheus.Options{
        Namespace: "ocmetricstutorial",
    })
    if err != nil {
        log.Fatalf("Failed to create the Prometheus stats exporter: %v", err)
    }

    // Now finally run the Prometheus exporter as a scrape endpoint.
    // We'll run the server on port 8888.
    go func() {
        mux := http.NewServeMux()
        mux.Handle("/metrics", pe)
        if err := http.ListenAndServe(":8888", mux); err != nil {
            log.Fatalf("Failed to run Prometheus scrape endpoint: %v", err)
        }
    }()

    // In a REPL:
    //   1. Read input
    //   2. process input
    br := bufio.NewReader(os.Stdin)

    // Register the views
    if err := view.Register(LatencyView, LineCountView, LineLengthView); err != nil {
        log.Fatalf("Failed to register views: %v", err)
    }

    // repl is the read, evaluate, print, loop
    for {
        if err := readEvaluateProcess(br); err != nil {
            if err == io.EOF {
                return
            }
            log.Fatal(err)
        }
    }
}

// readEvaluateProcess reads a line from the input reader and
// then processes it. It returns an error if any was encountered.
func readEvaluateProcess(br *bufio.Reader) (terr error) {
//注意这里有一行,在官网上边没有,如果复制官网代码执行会报错
//加上这一行代码即可 startTime := time.New() 
startTime := time.New() 
ctx, err := tag.New(context.Background(), tag.Insert(KeyMethod, "repl"), tag.Insert(KeyStatus, "OK"))
    if err != nil {
        return err
    }

    defer func() {
        if terr != nil {
            ctx, _ = tag.New(ctx, tag.Upsert(KeyStatus, "ERROR"),
                tag.Upsert(KeyError, terr.Error()))
        }

        stats.Record(ctx, MLatencyMs.M(sinceInMilliseconds(startTime)))
    }()

    fmt.Printf("> ")
    line, _, err := br.ReadLine()
    if err != nil {
        if err != io.EOF {
            return err
        }
        log.Fatal(err)
    }

    out, err := processLine(ctx, line)
    if err != nil {
        return err
    }
    fmt.Printf("< %s\n\n", out)
    return nil
}

// processLine takes in a line of text and
// transforms it. Currently it just capitalizes it.
func processLine(ctx context.Context, in []byte) (out []byte, err error) {
    startTime := time.Now()
    defer func() {
        stats.Record(ctx, MLatencyMs.M(sinceInMilliseconds(startTime)),
            MLineLengths.M(int64(len(in))))
    }()

    return bytes.ToUpper(in), nil
}

func sinceInMilliseconds(startTime time.Time) float64 {
    return float64(time.Since(startTime).Nanoseconds()) / 1e6
}

opencensus参考网址:https://opencensus.io/quickstart/go/metrics/

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容