dev.azure.com/aidainnovazione0090/DeviceManager/_git/go-mod-core-contracts@v1.0.2/dtos/metric.go (about) 1 /******************************************************************************* 2 * Copyright 2022 Intel Corp. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 * in compliance with the License. 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 distributed under the License 10 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 * or implied. See the License for the specific language governing permissions and limitations under 12 * the License. 13 *******************************************************************************/ 14 15 package dtos 16 17 import ( 18 "errors" 19 "fmt" 20 "strings" 21 "time" 22 23 "dev.azure.com/aidainnovazione0090/DeviceManager/_git/go-mod-core-contracts/dtos/common" 24 ) 25 26 // Metric defines the metric data for a specific named metric 27 type Metric struct { 28 common.Versionable `json:",inline"` 29 // Name is the identifier of the collected Metric 30 Name string `json:"name" validate:"edgex-dto-none-empty-string"` 31 // Fields are the Key/Value measurements associated with the metric 32 Fields []MetricField `json:"fields,omitempty" validate:"required"` 33 // Tags and the Key/Value tags associated with the metric 34 Tags []MetricTag `json:"tags,omitempty"` 35 // Timestamp is the time and date the metric was collected. 36 Timestamp int64 `json:"timestamp" validate:"required"` 37 } 38 39 // MetricField defines a metric field associated with a metric 40 type MetricField struct { 41 // Name is the identifier of the metric field 42 Name string `json:"name" validate:"edgex-dto-none-empty-string"` 43 // Value is measurement for the metric field 44 Value interface{} `json:"value" validate:"required"` 45 } 46 47 // MetricTag defines a metric tag associated with a metric 48 type MetricTag struct { 49 // Name is the identifier of the metric tag 50 Name string `json:"name" validate:"edgex-dto-none-empty-string"` 51 // Value is tag vale for the metric tag 52 Value string `json:"value" validate:"required"` 53 } 54 55 // NewMetric creates a new metric for the specified data 56 func NewMetric(name string, fields []MetricField, tags []MetricTag) (Metric, error) { 57 if err := ValidateMetricName(name, "metric"); err != nil { 58 return Metric{}, err 59 } 60 61 if len(fields) == 0 { 62 return Metric{}, errors.New("one or more metric fields are required") 63 } 64 65 for _, field := range fields { 66 if err := ValidateMetricName(field.Name, "field"); err != nil { 67 return Metric{}, err 68 } 69 } 70 71 if len(tags) > 0 { 72 for _, tag := range tags { 73 if err := ValidateMetricName(tag.Name, "tag"); err != nil { 74 return Metric{}, err 75 } 76 } 77 } 78 79 metric := Metric{ 80 Versionable: common.NewVersionable(), 81 Name: name, 82 Fields: fields, 83 Timestamp: time.Now().UnixNano(), 84 Tags: tags, 85 } 86 87 return metric, nil 88 } 89 90 func ValidateMetricName(name string, nameType string) error { 91 if len(strings.TrimSpace(name)) == 0 { 92 return fmt.Errorf("%s name can not be empty or blank", nameType) 93 } 94 95 // TODO: Use regex to validate Name characters 96 return nil 97 } 98 99 // ToLineProtocol transforms the Metric to Line Protocol syntax which is most commonly used with InfluxDB 100 // For more information on Line Protocol see: https://docs.influxdata.com/influxdb/v2.0/reference/syntax/line-protocol/ 101 // Line Protocol Syntax: 102 // 103 // <measurement>[,<tag_key>=<tag_value>[,<tag_key>=<tag_value>]] <field_key>=<field_value>[,<field_key>=<field_value>] [<timestamp>] 104 // 105 // Examples: 106 // 107 // measurementName fieldKey="field string value" 1556813561098000000 108 // myMeasurement,tag1=value1,tag2=value2 fieldKey="fieldValue" 1556813561098000000 109 // 110 // Note that this is a simple helper function for those receiving this DTO that are pushing metrics to an endpoint 111 // that receives LineProtocol such as InfluxDb or Telegraf 112 func (m *Metric) ToLineProtocol() string { 113 var fields strings.Builder 114 isFirst := true 115 for _, field := range m.Fields { 116 // Fields section doesn't have a leading comma per syntax above so need to skip the comma on the first field 117 if isFirst { 118 isFirst = false 119 } else { 120 fields.WriteString(",") 121 } 122 fields.WriteString(field.Name + "=" + formatLineProtocolValue(field.Value)) 123 } 124 125 // Tags section does have a leading comma per syntax above 126 var tags strings.Builder 127 for _, tag := range m.Tags { 128 tags.WriteString("," + tag.Name + "=" + tag.Value) 129 } 130 131 result := fmt.Sprintf("%s%s %s %d", m.Name, tags.String(), fields.String(), m.Timestamp) 132 133 return result 134 } 135 136 func formatLineProtocolValue(value interface{}) string { 137 switch value.(type) { 138 case string: 139 return fmt.Sprintf("\"%s\"", value) 140 case int, int8, int16, int32, int64: 141 return fmt.Sprintf("%di", value) 142 case uint, uint8, uint16, uint32, uint64: 143 return fmt.Sprintf("%du", value) 144 default: 145 return fmt.Sprintf("%v", value) 146 } 147 }