Skip to content

Commit

Permalink
[cmd/mdatagen] Enhance mdatagen to support string parsing in RecordXD…
Browse files Browse the repository at this point in the history
…ataPoint functions (open-telemetry#8986)

Add support to mdatagen that will parse a string to integer/float before creating the data point in the RecordXDataPoint function if the input_type is set in metadatayaml.
  • Loading branch information
dehaansa committed Apr 20, 2022
1 parent f49b291 commit 7a1626b
Show file tree
Hide file tree
Showing 10 changed files with 313 additions and 391 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
- `pkg/translator/prometheusremotewrite`: Allow to disable sanitize metric labels (#8270)
- `basicauthextension`: Implement `configauth.ClientAuthenticator` so that the extension can also be used as HTTP client basic authenticator.(#8847)

- `cmd/mdatagen`: Update generated functions to have simple parse function to handle string parsing consistently and limit code duplication across receivers (#7574)

### 🧰 Bug fixes 🧰

- `fluentforwardreceiver`: Release port on shutdown (#9111)
Expand Down
8 changes: 8 additions & 0 deletions cmd/mdatagen/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,14 @@ func generateMetrics(ymlDir string, thisDir string, md metadata, useExpGen bool)
"publicVar": func(s string) (string, error) {
return formatIdentifier(s, true)
},
"parseImportsRequired": func(metrics map[metricName]metric) bool {
for _, m := range metrics {
if m.Data().HasMetricInputType() {
return true
}
}
return false
},
}).ParseFiles(path.Join(thisDir, tmplFile)))
buf := bytes.Buffer{}

Expand Down
26 changes: 26 additions & 0 deletions cmd/mdatagen/metricdata.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ type MetricData interface {
HasMonotonic() bool
HasAggregated() bool
HasMetricValueType() bool
HasMetricInputType() bool
}

// Aggregated defines a metric aggregation type.
Expand Down Expand Up @@ -59,6 +60,17 @@ type Mono struct {
Monotonic bool `mapstructure:"monotonic"`
}

// MetricInputType defines the metric input value type
type MetricInputType struct {
// InputType is the type the metric needs to be parsed from, options are "string"
InputType string `mapstructure:"input_type" validate:"omitempty,oneof=string"`
}

// Type returns name of the datapoint type.
func (mit MetricInputType) String() string {
return mit.InputType
}

// MetricValueType defines the metric number type.
type MetricValueType struct {
// ValueType is type of the metric number, options are "double", "int".
Expand Down Expand Up @@ -97,6 +109,7 @@ func (mvt MetricValueType) BasicType() string {

type gauge struct {
MetricValueType `mapstructure:"value_type"`
MetricInputType `mapstructure:",squash"`
}

func (d gauge) Type() string {
Expand All @@ -115,10 +128,15 @@ func (d gauge) HasMetricValueType() bool {
return true
}

func (d gauge) HasMetricInputType() bool {
return d.InputType != ""
}

type sum struct {
Aggregated `mapstructure:",squash"`
Mono `mapstructure:",squash"`
MetricValueType `mapstructure:"value_type"`
MetricInputType `mapstructure:",squash"`
}

func (d sum) Type() string {
Expand All @@ -137,6 +155,10 @@ func (d sum) HasMetricValueType() bool {
return true
}

func (d sum) HasMetricInputType() bool {
return d.InputType != ""
}

type histogram struct {
Aggregated `mapstructure:",squash"`
}
Expand All @@ -156,3 +178,7 @@ func (d histogram) HasAggregated() bool {
func (d histogram) HasMetricValueType() bool {
return false
}

func (d histogram) HasMetricInputType() bool {
return false
}
36 changes: 34 additions & 2 deletions cmd/mdatagen/metrics_v2.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@
package {{ .Package }}

import (
{{- if .Metrics | parseImportsRequired }}
"strconv"
"fmt"
{{- end }}
"time"

"go.opentelemetry.io/collector/pdata/pcommon"
Expand Down Expand Up @@ -198,11 +202,39 @@ func (mb *MetricsBuilder) Emit(ro ...ResourceOption) pmetric.Metrics {
{{ range $name, $metric := .Metrics -}}
// Record{{ $name.Render }}DataPoint adds a data point to {{ $name }} metric.
func (mb *MetricsBuilder) Record{{ $name.Render }}DataPoint(ts pcommon.Timestamp
{{- if $metric.Data.HasMetricValueType }}, val {{ $metric.Data.MetricValueType.BasicType }}{{ end }}
{{- range $metric.Attributes -}} , {{ .RenderUnexported }}AttributeValue string{{ end }}) {
{{- if $metric.Data.HasMetricInputType }}, val {{ $metric.Data.MetricInputType.String }}
{{- else }}
{{- if $metric.Data.HasMetricValueType }}, val {{ $metric.Data.MetricValueType.BasicType }}{{- end }}
{{- end -}}
{{- range $metric.Attributes -}} , {{ .RenderUnexported }}AttributeValue string{{ end }})
{{- if $metric.Data.HasMetricInputType }} error{{ end }} {
{{- if $metric.Data.HasMetricInputType }}
{{- if $metric.Data.HasMetricValueType }}
{{- if eq $metric.Data.MetricValueType.BasicType "float64" }}
if f, err := strconv.ParseFloat(val, 64); err != nil {
return fmt.Errorf("failed to parse float for {{ $name.Render }}, value was %s: %w", val, err)
} else {
mb.metric{{ $name.Render }}.recordDataPoint(mb.startTime, ts
{{- if $metric.Data.HasMetricValueType }}, f {{ end }}
{{- range $metric.Attributes -}} , {{ .RenderUnexported }}AttributeValue{{ end }})
}
{{- end }}
{{- if eq $metric.Data.MetricValueType.BasicType "int64" }}
if i, err := strconv.ParseInt(val, 10, 64); err != nil {
return fmt.Errorf("failed to parse int for {{ $name.Render }}, value was %s: %w", val, err)
} else {
mb.metric{{ $name.Render }}.recordDataPoint(mb.startTime, ts
{{- if $metric.Data.HasMetricValueType }}, i {{ end }}
{{- range $metric.Attributes -}} , {{ .RenderUnexported }}AttributeValue{{ end }})
}
{{- end }}
return nil
{{- end }}
{{- else }}
mb.metric{{ $name.Render }}.recordDataPoint(mb.startTime, ts
{{- if $metric.Data.HasMetricValueType }}, val {{ end }}
{{- range $metric.Attributes -}} , {{ .RenderUnexported }}AttributeValue{{ end }})
{{- end }}
}
{{ end }}

Expand Down
38 changes: 30 additions & 8 deletions receiver/apachereceiver/internal/metadata/generated_metrics_v2.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions receiver/apachereceiver/metadata.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ metrics:
unit: s
sum:
value_type: int
input_type: string
monotonic: true
aggregation: cumulative
attributes: [ server_name ]
Expand All @@ -41,6 +42,7 @@ metrics:
unit: connections
sum:
value_type: int
input_type: string
monotonic: false
aggregation: cumulative
attributes: [ server_name ]
Expand All @@ -50,6 +52,7 @@ metrics:
unit: connections
sum:
value_type: int
input_type: string
monotonic: false
aggregation: cumulative
attributes: [ server_name, workers_state]
Expand All @@ -59,6 +62,7 @@ metrics:
unit: 1
sum:
value_type: int
input_type: string
monotonic: true
aggregation: cumulative
attributes: [ server_name ]
Expand Down
54 changes: 18 additions & 36 deletions receiver/apachereceiver/scraper.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import (
"go.opentelemetry.io/collector/component"
"go.opentelemetry.io/collector/pdata/pcommon"
"go.opentelemetry.io/collector/pdata/pmetric"
"go.opentelemetry.io/collector/receiver/scrapererror"
"go.uber.org/zap"

"github.com/open-telemetry/opentelemetry-collector-contrib/receiver/apachereceiver/internal/metadata"
Expand Down Expand Up @@ -69,31 +70,25 @@ func (r *apacheScraper) scrape(context.Context) (pmetric.Metrics, error) {
return pmetric.Metrics{}, err
}

var errors scrapererror.ScrapeErrors
now := pcommon.NewTimestampFromTime(time.Now())
for metricKey, metricValue := range parseStats(stats) {
switch metricKey {
case "ServerUptimeSeconds":
if i, ok := r.parseInt(metricKey, metricValue); ok {
r.mb.RecordApacheUptimeDataPoint(now, i, r.cfg.serverName)
}
addPartialIfError(errors, r.mb.RecordApacheUptimeDataPoint(now, metricValue, r.cfg.serverName))
case "ConnsTotal":
if i, ok := r.parseInt(metricKey, metricValue); ok {
r.mb.RecordApacheCurrentConnectionsDataPoint(now, i, r.cfg.serverName)
}
addPartialIfError(errors, r.mb.RecordApacheCurrentConnectionsDataPoint(now, metricValue, r.cfg.serverName))
case "BusyWorkers":
if i, ok := r.parseInt(metricKey, metricValue); ok {
r.mb.RecordApacheWorkersDataPoint(now, i, r.cfg.serverName, "busy")
}
addPartialIfError(errors, r.mb.RecordApacheWorkersDataPoint(now, metricValue, r.cfg.serverName, "busy"))
case "IdleWorkers":
if i, ok := r.parseInt(metricKey, metricValue); ok {
r.mb.RecordApacheWorkersDataPoint(now, i, r.cfg.serverName, "idle")
}
addPartialIfError(errors, r.mb.RecordApacheWorkersDataPoint(now, metricValue, r.cfg.serverName, "idle"))
case "Total Accesses":
if i, ok := r.parseInt(metricKey, metricValue); ok {
r.mb.RecordApacheRequestsDataPoint(now, i, r.cfg.serverName)
}
addPartialIfError(errors, r.mb.RecordApacheRequestsDataPoint(now, metricValue, r.cfg.serverName))
case "Total kBytes":
if i, ok := r.parseInt(metricKey, metricValue); ok {
i, err := strconv.ParseInt(metricValue, 10, 64)
if err != nil {
errors.AddPartial(1, err)
} else {
r.mb.RecordApacheTrafficDataPoint(now, kbytesToBytes(i), r.cfg.serverName)
}
case "Scoreboard":
Expand All @@ -104,7 +99,13 @@ func (r *apacheScraper) scrape(context.Context) (pmetric.Metrics, error) {
}
}

return r.mb.Emit(), nil
return r.mb.Emit(), errors.Combine()
}

func addPartialIfError(errors scrapererror.ScrapeErrors, err error) {
if err != nil {
errors.AddPartial(1, err)
}
}

// GetStats collects metric stats by making a get request at an endpoint.
Expand Down Expand Up @@ -138,25 +139,6 @@ func parseStats(resp string) map[string]string {
return metrics
}

// parseInt converts string to int64.
func (r *apacheScraper) parseInt(key, value string) (int64, bool) {
i, err := strconv.ParseInt(value, 10, 64)
if err != nil {
r.logInvalid("int", key, value)
return 0, false
}
return i, true
}

func (r *apacheScraper) logInvalid(expectedType, key, value string) {
r.settings.Logger.Info(
"invalid value",
zap.String("expectedType", expectedType),
zap.String("key", key),
zap.String("value", value),
)
}

type scoreboardCountsByLabel map[string]int64

// parseScoreboard quantifies the symbolic mapping of the scoreboard.
Expand Down
Loading

0 comments on commit 7a1626b

Please sign in to comment.