├── go.mod ├── .gitignore ├── .github └── workflows │ ├── pr-check.yml │ ├── reviewdog.yml │ ├── push-check.yml │ ├── release-check.yml │ └── codeql-analysis.yml ├── .licenserc.yaml ├── metrics.go ├── tracer_test.go ├── options.go ├── README_ZH.md ├── README.md ├── tracer.go ├── LICENSE ├── licenses └── LICENSE-prometheus └── go.sum /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/kitex-contrib/monitor-prometheus 2 | 3 | go 1.13 4 | 5 | require ( 6 | github.com/cloudwego/kitex v0.8.0 7 | github.com/prometheus/client_golang v1.17.0 8 | github.com/stretchr/testify v1.8.2 9 | ) 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.exe~ 4 | *.dll 5 | *.so 6 | *.dylib 7 | 8 | # Test binary, built with `go test -c` 9 | *.test 10 | 11 | # Output of the go coverage tool, specifically when used with LiteIDE 12 | *.out 13 | 14 | # Dependency directories (remove the comment below to include it) 15 | # vendor/ 16 | 17 | # IDE config 18 | .vscode 19 | .idea -------------------------------------------------------------------------------- /.github/workflows/pr-check.yml: -------------------------------------------------------------------------------- 1 | name: Pull Request Check 2 | 3 | on: pull_request 4 | 5 | jobs: 6 | build: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/checkout@v3 10 | 11 | - name: Set up Go 12 | uses: actions/setup-go@v3 13 | with: 14 | go-version: 1.18 15 | 16 | - uses: actions/cache@v3 17 | with: 18 | path: ~/go/pkg/mod 19 | key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} 20 | restore-keys: | 21 | ${{ runner.os }}-go- 22 | 23 | - name: Benchmark 24 | run: go test -bench=. -benchmem -run=none ./... 25 | -------------------------------------------------------------------------------- /.github/workflows/reviewdog.yml: -------------------------------------------------------------------------------- 1 | name: reviewdog 2 | on: [pull_request] 3 | jobs: 4 | staticcheck: 5 | name: runner / staticcheck 6 | runs-on: ubuntu-latest 7 | steps: 8 | - uses: actions/checkout@v3 9 | - uses: reviewdog/action-staticcheck@v1 10 | with: 11 | github_token: ${{ secrets.github_token }} 12 | # Change reviewdog reporter if you need [github-pr-check,github-check,github-pr-review]. 13 | reporter: github-pr-review 14 | # Report all results. 15 | filter_mode: nofilter 16 | # Exit with 1 when it find at least one finding. 17 | fail_on_error: true 18 | # Set staticcheck flags 19 | staticcheck_flags: -checks=inherit,-SA1029 20 | -------------------------------------------------------------------------------- /.licenserc.yaml: -------------------------------------------------------------------------------- 1 | # Copyright 2021 CloudWeGo Authors. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | # 15 | 16 | header: 17 | license: 18 | spdx-id: Apache-2.0 19 | copyright-owner: CloudWeGo Authors 20 | copyright-year: 2021 21 | paths: 22 | - "**/*.go" 23 | 24 | comment: on-failure 25 | -------------------------------------------------------------------------------- /.github/workflows/push-check.yml: -------------------------------------------------------------------------------- 1 | name: Push Check 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | build: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/checkout@v3 10 | 11 | - name: Set up Go 12 | uses: actions/setup-go@v3 13 | with: 14 | go-version: 1.18 15 | 16 | - uses: actions/cache@v3 17 | with: 18 | path: ~/go/pkg/mod 19 | key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} 20 | restore-keys: | 21 | ${{ runner.os }}-go- 22 | 23 | - name: Check License Header 24 | uses: apache/skywalking-eyes@main 25 | env: 26 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 27 | 28 | - name: Lint 29 | run: | 30 | test -z "$(gofmt -s -l .)" 31 | go vet -stdmethods=false $(go list ./...) 32 | 33 | - name: Unit Test 34 | run: go test -v -race -covermode=atomic -coverprofile=coverage.out ./... 35 | -------------------------------------------------------------------------------- /.github/workflows/release-check.yml: -------------------------------------------------------------------------------- 1 | name: Release Check 2 | 3 | on: 4 | pull_request: 5 | branches: 6 | - main 7 | 8 | jobs: 9 | build: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v3 13 | 14 | - name: Check Source Branch 15 | run: python2 -c "exit(0 if '${{ github.head_ref }}'.startswith('release') or '${{ github.head_ref }}'.startswith('hotfix') else 1)" 16 | 17 | - name: Check Version 18 | run: | 19 | # get version code, runner not support grep -E here 20 | SOURCE_VERSION=`grep 'Version\s*=\s*\"v[0-9]\{1,\}\.[0-9]\{1,\}\.[0-9]\{1,\}\"' *.go | awk -F '\"' '{print $(NF-1)}'` 21 | git checkout main 22 | MASTER_VERSION=`grep 'Version\s*=\s*\"v[0-9]\{1,\}\.[0-9]\{1,\}\.[0-9]\{1,\}\"' *.go | awk -F '\"' '{print $(NF-1)}'` 23 | git checkout ${{Head.SHA}} 24 | # check version update 25 | python2 -c "exit(0 if list(map(int,'${SOURCE_VERSION#v}'.split('.')[:3])) > list(map(int,'${MASTER_VERSION#v}'.split('.')[:3])) else 1)" 26 | -------------------------------------------------------------------------------- /metrics.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 CloudWeGo Authors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package prometheus 18 | 19 | import ( 20 | "time" 21 | 22 | prom "github.com/prometheus/client_golang/prometheus" 23 | ) 24 | 25 | // counterAdd wraps Add of prom.Counter. 26 | func counterAdd(counterVec *prom.CounterVec, value int, labels prom.Labels) error { 27 | counter, err := counterVec.GetMetricWith(labels) 28 | if err != nil { 29 | return err 30 | } 31 | counter.Add(float64(value)) 32 | return nil 33 | } 34 | 35 | // histogramObserve wraps Observe of prom.Observer. 36 | func histogramObserve(histogramVec *prom.HistogramVec, value time.Duration, labels prom.Labels) error { 37 | histogram, err := histogramVec.GetMetricWith(labels) 38 | if err != nil { 39 | return err 40 | } 41 | histogram.Observe(float64(value.Microseconds())) 42 | return nil 43 | } 44 | -------------------------------------------------------------------------------- /tracer_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 CloudWeGo Authors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package prometheus 18 | 19 | import ( 20 | "io/ioutil" 21 | "net/http" 22 | "strings" 23 | "testing" 24 | "time" 25 | 26 | prom "github.com/prometheus/client_golang/prometheus" 27 | "github.com/prometheus/client_golang/prometheus/promhttp" 28 | "github.com/stretchr/testify/assert" 29 | ) 30 | 31 | func TestPrometheusReporter(t *testing.T) { 32 | registry := prom.NewRegistry() 33 | http.Handle("/prometheus", promhttp.HandlerFor(registry, promhttp.HandlerOpts{ErrorHandling: promhttp.ContinueOnError})) 34 | go func() { 35 | if err := http.ListenAndServe(":9090", nil); err != nil { 36 | t.Error("Unable to start a promhttp server, err: " + err.Error()) 37 | return 38 | } 39 | }() 40 | 41 | counter := prom.NewCounterVec( 42 | prom.CounterOpts{ 43 | Name: "test_counter", 44 | ConstLabels: prom.Labels{"service": "prometheus-test"}, 45 | }, 46 | []string{"test1", "test2"}, 47 | ) 48 | registry.MustRegister(counter) 49 | 50 | histogram := prom.NewHistogramVec( 51 | prom.HistogramOpts{ 52 | Name: "test_histogram", 53 | ConstLabels: prom.Labels{"service": "prometheus-test"}, 54 | Buckets: prom.DefBuckets, 55 | }, 56 | []string{"test1", "test2"}, 57 | ) 58 | registry.MustRegister(histogram) 59 | 60 | labels := prom.Labels{ 61 | "test1": "abc", 62 | "test2": "def", 63 | } 64 | 65 | assert.True(t, counterAdd(counter, 6, labels) == nil) 66 | assert.True(t, histogramObserve(histogram, time.Second, labels) == nil) 67 | 68 | promServerResp, err := http.Get("http://localhost:9090/prometheus") 69 | if err != nil { 70 | t.Fatal(err) 71 | } 72 | assert.True(t, promServerResp.StatusCode == http.StatusOK) 73 | 74 | bodyBytes, err := ioutil.ReadAll(promServerResp.Body) 75 | assert.True(t, err == nil) 76 | respStr := string(bodyBytes) 77 | assert.True(t, strings.Contains(respStr, `test_counter{service="prometheus-test",test1="abc",test2="def"} 6`)) 78 | assert.True(t, strings.Contains(respStr, `test_histogram_sum{service="prometheus-test",test1="abc",test2="def"} 1e+06`)) 79 | } 80 | -------------------------------------------------------------------------------- /.github/workflows/codeql-analysis.yml: -------------------------------------------------------------------------------- 1 | # For most projects, this workflow file will not need changing; you simply need 2 | # to commit it to your repository. 3 | # 4 | # You may wish to alter this file to override the set of languages analyzed, 5 | # or to provide custom queries or build logic. 6 | # 7 | # ******** NOTE ******** 8 | # We have attempted to detect the languages in your repository. Please check 9 | # the `language` matrix defined below to confirm you have the correct set of 10 | # supported CodeQL languages. 11 | # 12 | name: "CodeQL" 13 | 14 | on: 15 | push: 16 | branches: [develop, main] 17 | pull_request: 18 | # The branches below must be a subset of the branches above 19 | branches: [develop] 20 | schedule: 21 | - cron: "35 21 * * 4" 22 | 23 | jobs: 24 | analyze: 25 | name: Analyze 26 | runs-on: ubuntu-latest 27 | permissions: 28 | actions: read 29 | contents: read 30 | security-events: write 31 | 32 | strategy: 33 | fail-fast: false 34 | matrix: 35 | language: ["go"] 36 | # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ] 37 | # Learn more: 38 | # https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed 39 | 40 | steps: 41 | - name: Checkout repository 42 | uses: actions/checkout@v3 43 | 44 | - name: Set up Go 45 | uses: actions/setup-go@v3 46 | with: 47 | go-version: 1.18 48 | 49 | - uses: actions/cache@v2 50 | with: 51 | path: ~/go/pkg/mod 52 | key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} 53 | restore-keys: | 54 | ${{ runner.os }}-go- 55 | 56 | # Initializes the CodeQL tools for scanning. 57 | - name: Initialize CodeQL 58 | uses: github/codeql-action/init@v1 59 | with: 60 | languages: ${{ matrix.language }} 61 | # If you wish to specify custom queries, you can do so here or in a config file. 62 | # By default, queries listed here will override any specified in a config file. 63 | # Prefix the list here with "+" to use these queries and those in the config file. 64 | # queries: ./path/to/local/query, your-org/your-repo/queries@main 65 | 66 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). 67 | # If this step fails, then you should remove it and run the build manually (see below) 68 | - name: Autobuild 69 | uses: github/codeql-action/autobuild@v1 70 | 71 | # ℹ️ Command-line programs to run using the OS shell. 72 | # 📚 https://git.io/JvXDl 73 | 74 | # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines 75 | # and modify them (or add more) to build your code if your project 76 | # uses a compiled language 77 | 78 | #- run: | 79 | # make bootstrap 80 | # make release 81 | 82 | - name: Perform CodeQL Analysis 83 | uses: github/codeql-action/analyze@v1 84 | -------------------------------------------------------------------------------- /options.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 CloudWeGo Authors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package prometheus 18 | 19 | import ( 20 | "net/http" 21 | 22 | prom "github.com/prometheus/client_golang/prometheus" 23 | "github.com/prometheus/client_golang/prometheus/collectors" 24 | ) 25 | 26 | var defaultBuckets = []float64{5000, 10000, 25000, 50000, 100000, 250000, 500000, 1000000} 27 | 28 | // Option opts for monitor prometheus 29 | type Option interface { 30 | apply(cfg *config) 31 | } 32 | 33 | type option func(cfg *config) 34 | 35 | func (fn option) apply(cfg *config) { 36 | fn(cfg) 37 | } 38 | 39 | type config struct { 40 | buckets []float64 41 | enableGoCollector bool 42 | runtimeMetricRules []collectors.GoRuntimeMetricsRule 43 | registry *prom.Registry 44 | serveMux *http.ServeMux 45 | disableServer bool 46 | } 47 | 48 | func defaultConfig() *config { 49 | return &config{ 50 | buckets: defaultBuckets, 51 | enableGoCollector: false, 52 | runtimeMetricRules: []collectors.GoRuntimeMetricsRule{}, 53 | registry: prom.NewRegistry(), 54 | serveMux: http.DefaultServeMux, 55 | disableServer: false, 56 | } 57 | } 58 | 59 | // WithEnableGoCollector enable go collector 60 | func WithEnableGoCollector(enable bool) Option { 61 | return option(func(cfg *config) { 62 | cfg.enableGoCollector = enable 63 | }) 64 | } 65 | 66 | // WithGoCollectorRule define your custom go collector rule 67 | func WithGoCollectorRule(rules ...collectors.GoRuntimeMetricsRule) Option { 68 | return option(func(cfg *config) { 69 | cfg.runtimeMetricRules = rules 70 | }) 71 | } 72 | 73 | // WithHistogramBuckets define your custom histogram buckets base on your biz 74 | func WithHistogramBuckets(buckets []float64) Option { 75 | return option(func(cfg *config) { 76 | if len(buckets) > 0 { 77 | cfg.buckets = buckets 78 | } 79 | }) 80 | } 81 | 82 | // WithRegistry define your custom registry 83 | func WithRegistry(registry *prom.Registry) Option { 84 | return option(func(cfg *config) { 85 | if registry != nil { 86 | cfg.registry = registry 87 | } 88 | }) 89 | } 90 | 91 | // WithServeMux define your custom serve mux 92 | func WithServeMux(serveMux *http.ServeMux) Option { 93 | return option(func(cfg *config) { 94 | if serveMux != nil { 95 | cfg.serveMux = serveMux 96 | } 97 | }) 98 | } 99 | 100 | // WithDisableServer disable prometheus server 101 | func WithDisableServer(disable bool) Option { 102 | return option(func(cfg *config) { 103 | cfg.disableServer = disable 104 | }) 105 | } 106 | -------------------------------------------------------------------------------- /README_ZH.md: -------------------------------------------------------------------------------- 1 | # Prometheus monitoring for Kitex 2 | [English](README.md) | 中文 3 | 4 | ## Abstract 5 | Prometheus 大概的工作流程: 6 | 1. Prometheus server 定期从配置好的 jobs 或者 exporters 中拉(pull模式) metrics,或者接收来自 Pushgateway 发过来(push模式)的 metrics,或者从其他的 Prometheus server 中拉 metrics; 7 | 2. Prometheus server 在本地存储收集到的 metrics,并运行已定义好的 `alert.rules`,记录新的时间序列或者向 Alertmanager 推送警报; 8 | 3. Alertmanager 根据配置文件,对接收到的警报进行处理,发出告警; 9 | 4. 在图形界面中,可视化采集数据,例如对接Grafana。 10 | 11 | ### 数据模型 12 | Prometheus 中存储的数据为时间序列,是由 metric 的名字和一系列的标签(键值对)唯一标识的,不同的标签则代表不同的时间序列。 13 | - name:一般用于表示 metric 的功能;注意,metric 名字由 ASCII 字符,数字,下划线,以及冒号组成,必须满足正则表达式 [a-zA-Z_:][a-zA-Z0-9_:]*; 14 | - tag:标识了特征维度,便于过滤和聚合。例如 PSM 和 method 等信息。tag 中的 key 由 ASCII 字符,数字,以及下划线组成,必须满足正则表达式 [a-zA-Z_:][a-zA-Z0-9_:]*; 15 | - sample:实际的时间序列,每个序列包括一个 float64 的值和一个毫秒级的时间戳; 16 | - metric:通过如下格式表示:{