1// Copyright 2019 Google LLC 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// Package vault provides helper functions to improve the go-metrics to stackdriver metric 16// conversions specific to HashiCorp Vault. 17package vault 18 19import "github.com/armon/go-metrics" 20 21// Extractor extracts known patterns from the key into metrics.Label for better metric grouping 22// and to help avoid the limit of 500 custom metric descriptors per project 23// (https://cloud.google.com/monitoring/quotas). 24func Extractor(key []string, kind string) ([]string, []metrics.Label, error) { 25 // Metrics documented at https://www.vaultproject.io/docs/internals/telemetry.html should be 26 // extracted here into a base metric name with appropriate labels extracted from the 'key'. 27 switch len(key) { 28 case 3: // metrics of format: *.*.* 29 // vault.database.<method> 30 if key[0] == "vault" && key[1] == "database" { 31 return typeWrap(key[:2], kind), []metrics.Label{ 32 { 33 Name: "method", 34 Value: key[2], 35 }, 36 }, nil 37 } 38 // vault.token.create_root 39 if key[0] == "vault" && key[1] == "token" && key[2] == "create_root" { 40 return typeWrap(key, kind), nil, nil 41 } 42 43 // vault.barrier.<method> 44 // vault.token.<method> 45 // vault.policy.<method> 46 if key[0] == "vault" && (key[1] == "barrier" || key[1] == "token" || key[1] == "policy") { 47 return typeWrap(key[:2], kind), []metrics.Label{ 48 { 49 Name: "method", 50 Value: key[2], 51 }, 52 }, nil 53 } 54 // vault.<backend>.<method> 55 if key[0] == "vault" && (key[2] == "put" || key[2] == "get" || key[2] == "delete" || key[2] == "list") { 56 return typeWrap(key[:2], kind), []metrics.Label{ 57 { 58 Name: "method", 59 Value: key[2], 60 }, 61 }, nil 62 } 63 case 4: // metrics of format: *.*.*.* 64 // vault.route.<method>.<mount> 65 if key[0] == "vault" && key[1] == "route" { 66 return typeWrap(key[:2], kind), []metrics.Label{ 67 { 68 Name: "method", 69 Value: key[2], 70 }, 71 { 72 Name: "mount", 73 Value: key[3], 74 }, 75 }, nil 76 } 77 // vault.audit.<type>.* 78 if key[0] == "vault" && key[1] == "audit" { 79 return typeWrap([]string{"vault", "audit", key[3]}, kind), []metrics.Label{ 80 { 81 Name: "type", 82 Value: key[2], 83 }, 84 }, nil 85 } 86 // vault.rollback.attempt.<mount> 87 if key[0] == "vault" && key[1] == "rollback" && key[2] == "attempt" { 88 return typeWrap(key[:3], kind), []metrics.Label{ 89 { 90 Name: "mount", 91 Value: key[3], 92 }, 93 }, nil 94 } 95 // vault.<backend>.lock.<method> 96 if key[0] == "vault" && key[2] == "lock" { 97 return typeWrap(key[:3], kind), []metrics.Label{ 98 { 99 Name: "method", 100 Value: key[3], 101 }, 102 }, nil 103 } 104 // vault.database.<name>.<method> 105 // note: there are vault.database.<method>.error counters. Those are handled separately. 106 if key[0] == "vault" && key[1] == "database" && key[3] != "error" { 107 return typeWrap(key[:2], kind), []metrics.Label{ 108 { 109 Name: "name", 110 Value: key[2], 111 }, 112 { 113 Name: "method", 114 Value: key[3], 115 }, 116 }, nil 117 } 118 //ivault.database.<method>.error 119 if key[0] == "vault" && key[1] == "database" && key[3] == "error" { 120 return typeWrap([]string{"vault", "database", "error"}, kind), []metrics.Label{ 121 { 122 Name: "method", 123 Value: key[2], 124 }, 125 }, nil 126 } 127 case 5: 128 // vault.database.<name>.<method>.error 129 if key[0] == "vault" && key[1] == "database" && key[4] == "error" { 130 return typeWrap([]string{key[0], key[1], key[4]}, kind), []metrics.Label{ 131 { 132 Name: "name", 133 Value: key[2], 134 }, 135 { 136 Name: "method", 137 Value: key[3], 138 }, 139 }, nil 140 } 141 default: 142 // unknown key pattern, keep it as-is. 143 } 144 return typeWrap(key, kind), nil, nil 145} 146 147func typeWrap(key []string, kind string) []string { 148 out := []string{} 149 for _, a := range key { 150 out = append(out, a) 151 } 152 switch kind { 153 case "counter": 154 return append(out, kind) 155 case "gauge": 156 return append(out, kind) 157 default: 158 return out 159 } 160} 161 162// Bucketer specifies the bucket boundaries that should be used for the given metric key. 163func Bucketer(key []string) []float64 { 164 // These were chosen to give some reasonable boundaires for RPC times in the 10-100ms range and 165 // then rough values for 1-5 seconds. 166 // TODO: investigate better boundaires for different metrics. 167 return []float64{10.0, 25.0, 50.0, 100.0, 150.0, 200.0, 250.0, 300.0, 500.0, 1000.0, 1500.0, 2000.0, 3000.0, 4000.0, 5000.0} 168} 169