diff --git a/.chloggen/nginx-connections-sum.yaml b/.chloggen/nginx-connections-sum.yaml new file mode 100755 index 000000000000..e60e8be31a82 --- /dev/null +++ b/.chloggen/nginx-connections-sum.yaml @@ -0,0 +1,16 @@ +# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix' +change_type: breaking + +# The name of the component, or a single word describing the area of concern, (e.g. filelogreceiver) +component: nginxreceiver + +# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`). +note: Add featuregate to emit 'nginx.connections_current' as a non-monotonic sum + +# One or more tracking issues related to the change +issues: [4326] + +# (Optional) One or more lines of additional information to render under the primary note. +# These lines will be padded with 2 spaces and then inserted directly into the document. +# Use pipe (|) for multiline entries. +subtext: diff --git a/receiver/nginxreceiver/README.md b/receiver/nginxreceiver/README.md index 28d7e4be069a..c4ea35db8e0f 100644 --- a/receiver/nginxreceiver/README.md +++ b/receiver/nginxreceiver/README.md @@ -51,3 +51,15 @@ receivers: The full list of settings exposed for this receiver are documented [here](./config.go) with detailed sample configurations [here](./testdata/config.yaml). +## Feature gate configurations + +See the [Collector feature gates](https://github.com/open-telemetry/opentelemetry-collector/blob/main/featuregate/README.md#collector-feature-gates) for an overview of feature gates in the collector. + +**ALPHA**: `receiver.nginx.emitCurrentConnectionsAsSum` + +The feature gate `receiver.nginx.emitConnectionsCurrentAsSum` once enabled will change the data type of the +`nginx.connections_current` metric from a gauge to a non-monotonic sum. + +This feature gate will eventually be enabled by default, and eventually the old implementation will be removed. It aims +to give users time to migrate to the new implementation. The target release for this featuregate to be enabled by default +is 0.80.0. diff --git a/receiver/nginxreceiver/documentation.md b/receiver/nginxreceiver/documentation.md index ee8402ad581b..f11f149f2fd5 100644 --- a/receiver/nginxreceiver/documentation.md +++ b/receiver/nginxreceiver/documentation.md @@ -49,3 +49,17 @@ Total number of requests made to the server since it started | Unit | Metric Type | Value Type | Aggregation Temporality | Monotonic | | ---- | ----------- | ---------- | ----------------------- | --------- | | requests | Sum | Int | Cumulative | true | + +### temp.connections_current + +Temporary placeholder for new version of nginx.connections_current. See featuregate 'nginx.connections_as_sum'. + +| Unit | Metric Type | Value Type | Aggregation Temporality | Monotonic | +| ---- | ----------- | ---------- | ----------------------- | --------- | +| connections | Sum | Int | Cumulative | false | + +#### Attributes + +| Name | Description | Values | +| ---- | ----------- | ------ | +| state | The state of a connection | Str: ``active``, ``reading``, ``writing``, ``waiting`` | diff --git a/receiver/nginxreceiver/factory.go b/receiver/nginxreceiver/factory.go index abfc931d1ec8..6a56cb82e5a1 100644 --- a/receiver/nginxreceiver/factory.go +++ b/receiver/nginxreceiver/factory.go @@ -21,6 +21,7 @@ import ( "go.opentelemetry.io/collector/component" "go.opentelemetry.io/collector/config/confighttp" "go.opentelemetry.io/collector/consumer" + "go.opentelemetry.io/collector/featuregate" "go.opentelemetry.io/collector/receiver" "go.opentelemetry.io/collector/receiver/scraperhelper" @@ -28,7 +29,16 @@ import ( ) const ( - typeStr = "nginx" + typeStr = "nginx" + connectionsAsSum = "receiver.nginx.emitConnectionsCurrentAsSum" +) + +var connectorsAsSumGate = featuregate.GlobalRegistry().MustRegister( + connectionsAsSum, + featuregate.StageAlpha, + featuregate.WithRegisterDescription("When enabled, the receiver will emit the 'nginx.connections_current' metric as a nonmonotonic sum, rather than a gauge."), + featuregate.WithRegisterReferenceURL("https://github.com/open-telemetry/opentelemetry-collector-contrib/issues/4326"), + featuregate.WithRegisterFromVersion("v0.78.0"), ) // NewFactory creates a factory for nginx receiver. diff --git a/receiver/nginxreceiver/go.mod b/receiver/nginxreceiver/go.mod index 858903869cd6..b9e11474db59 100644 --- a/receiver/nginxreceiver/go.mod +++ b/receiver/nginxreceiver/go.mod @@ -13,6 +13,7 @@ require ( go.opentelemetry.io/collector/component v0.77.0 go.opentelemetry.io/collector/confmap v0.77.0 go.opentelemetry.io/collector/consumer v0.77.0 + go.opentelemetry.io/collector/featuregate v0.77.0 go.opentelemetry.io/collector/pdata v1.0.0-rcv0011 go.opentelemetry.io/collector/receiver v0.77.0 go.uber.org/zap v1.24.0 @@ -61,7 +62,6 @@ require ( github.com/sirupsen/logrus v1.9.0 // indirect go.opencensus.io v0.24.0 // indirect go.opentelemetry.io/collector/exporter v0.77.0 // indirect - go.opentelemetry.io/collector/featuregate v0.77.0 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.41.1 // indirect go.opentelemetry.io/otel v1.15.1 // indirect go.opentelemetry.io/otel/metric v0.38.1 // indirect diff --git a/receiver/nginxreceiver/internal/metadata/generated_config.go b/receiver/nginxreceiver/internal/metadata/generated_config.go index 2adefae5d073..984a3f5e6737 100644 --- a/receiver/nginxreceiver/internal/metadata/generated_config.go +++ b/receiver/nginxreceiver/internal/metadata/generated_config.go @@ -29,6 +29,7 @@ type MetricsConfig struct { NginxConnectionsCurrent MetricConfig `mapstructure:"nginx.connections_current"` NginxConnectionsHandled MetricConfig `mapstructure:"nginx.connections_handled"` NginxRequests MetricConfig `mapstructure:"nginx.requests"` + TempConnectionsCurrent MetricConfig `mapstructure:"temp.connections_current"` } func DefaultMetricsConfig() MetricsConfig { @@ -45,6 +46,9 @@ func DefaultMetricsConfig() MetricsConfig { NginxRequests: MetricConfig{ Enabled: true, }, + TempConnectionsCurrent: MetricConfig{ + Enabled: true, + }, } } diff --git a/receiver/nginxreceiver/internal/metadata/generated_config_test.go b/receiver/nginxreceiver/internal/metadata/generated_config_test.go index e406f9b2191c..e6d63d80c191 100644 --- a/receiver/nginxreceiver/internal/metadata/generated_config_test.go +++ b/receiver/nginxreceiver/internal/metadata/generated_config_test.go @@ -30,6 +30,7 @@ func TestMetricsBuilderConfig(t *testing.T) { NginxConnectionsCurrent: MetricConfig{Enabled: true}, NginxConnectionsHandled: MetricConfig{Enabled: true}, NginxRequests: MetricConfig{Enabled: true}, + TempConnectionsCurrent: MetricConfig{Enabled: true}, }, }, }, @@ -41,6 +42,7 @@ func TestMetricsBuilderConfig(t *testing.T) { NginxConnectionsCurrent: MetricConfig{Enabled: false}, NginxConnectionsHandled: MetricConfig{Enabled: false}, NginxRequests: MetricConfig{Enabled: false}, + TempConnectionsCurrent: MetricConfig{Enabled: false}, }, }, }, diff --git a/receiver/nginxreceiver/internal/metadata/generated_metrics.go b/receiver/nginxreceiver/internal/metadata/generated_metrics.go index 5b22a4fffa9a..6053be37ffeb 100644 --- a/receiver/nginxreceiver/internal/metadata/generated_metrics.go +++ b/receiver/nginxreceiver/internal/metadata/generated_metrics.go @@ -249,6 +249,59 @@ func newMetricNginxRequests(cfg MetricConfig) metricNginxRequests { return m } +type metricTempConnectionsCurrent struct { + data pmetric.Metric // data buffer for generated metric. + config MetricConfig // metric config provided by user. + capacity int // max observed number of data points added to the metric. +} + +// init fills temp.connections_current metric with initial data. +func (m *metricTempConnectionsCurrent) init() { + m.data.SetName("temp.connections_current") + m.data.SetDescription("Temporary placeholder for new version of nginx.connections_current. See featuregate 'nginx.connections_as_sum'.") + m.data.SetUnit("connections") + m.data.SetEmptySum() + m.data.Sum().SetIsMonotonic(false) + m.data.Sum().SetAggregationTemporality(pmetric.AggregationTemporalityCumulative) + m.data.Sum().DataPoints().EnsureCapacity(m.capacity) +} + +func (m *metricTempConnectionsCurrent) recordDataPoint(start pcommon.Timestamp, ts pcommon.Timestamp, val int64, stateAttributeValue string) { + if !m.config.Enabled { + return + } + dp := m.data.Sum().DataPoints().AppendEmpty() + dp.SetStartTimestamp(start) + dp.SetTimestamp(ts) + dp.SetIntValue(val) + dp.Attributes().PutStr("state", stateAttributeValue) +} + +// updateCapacity saves max length of data point slices that will be used for the slice capacity. +func (m *metricTempConnectionsCurrent) updateCapacity() { + if m.data.Sum().DataPoints().Len() > m.capacity { + m.capacity = m.data.Sum().DataPoints().Len() + } +} + +// emit appends recorded metric data to a metrics slice and prepares it for recording another set of data points. +func (m *metricTempConnectionsCurrent) emit(metrics pmetric.MetricSlice) { + if m.config.Enabled && m.data.Sum().DataPoints().Len() > 0 { + m.updateCapacity() + m.data.MoveTo(metrics.AppendEmpty()) + m.init() + } +} + +func newMetricTempConnectionsCurrent(cfg MetricConfig) metricTempConnectionsCurrent { + m := metricTempConnectionsCurrent{config: cfg} + if cfg.Enabled { + m.data = pmetric.NewMetric() + m.init() + } + return m +} + // MetricsBuilder provides an interface for scrapers to report metrics while taking care of all the transformations // required to produce metric representation defined in metadata and user config. type MetricsBuilder struct { @@ -261,6 +314,7 @@ type MetricsBuilder struct { metricNginxConnectionsCurrent metricNginxConnectionsCurrent metricNginxConnectionsHandled metricNginxConnectionsHandled metricNginxRequests metricNginxRequests + metricTempConnectionsCurrent metricTempConnectionsCurrent } // metricBuilderOption applies changes to default metrics builder. @@ -282,6 +336,7 @@ func NewMetricsBuilder(mbc MetricsBuilderConfig, settings receiver.CreateSetting metricNginxConnectionsCurrent: newMetricNginxConnectionsCurrent(mbc.Metrics.NginxConnectionsCurrent), metricNginxConnectionsHandled: newMetricNginxConnectionsHandled(mbc.Metrics.NginxConnectionsHandled), metricNginxRequests: newMetricNginxRequests(mbc.Metrics.NginxRequests), + metricTempConnectionsCurrent: newMetricTempConnectionsCurrent(mbc.Metrics.TempConnectionsCurrent), } for _, op := range options { op(mb) @@ -338,6 +393,7 @@ func (mb *MetricsBuilder) EmitForResource(rmo ...ResourceMetricsOption) { mb.metricNginxConnectionsCurrent.emit(ils.Metrics()) mb.metricNginxConnectionsHandled.emit(ils.Metrics()) mb.metricNginxRequests.emit(ils.Metrics()) + mb.metricTempConnectionsCurrent.emit(ils.Metrics()) for _, op := range rmo { op(rm) @@ -378,6 +434,11 @@ func (mb *MetricsBuilder) RecordNginxRequestsDataPoint(ts pcommon.Timestamp, val mb.metricNginxRequests.recordDataPoint(mb.startTime, ts, val) } +// RecordTempConnectionsCurrentDataPoint adds a data point to temp.connections_current metric. +func (mb *MetricsBuilder) RecordTempConnectionsCurrentDataPoint(ts pcommon.Timestamp, val int64, stateAttributeValue AttributeState) { + mb.metricTempConnectionsCurrent.recordDataPoint(mb.startTime, ts, val, stateAttributeValue.String()) +} + // Reset resets metrics builder to its initial state. It should be used when external metrics source is restarted, // and metrics builder should update its startTime and reset it's internal state accordingly. func (mb *MetricsBuilder) Reset(options ...metricBuilderOption) { diff --git a/receiver/nginxreceiver/internal/metadata/generated_metrics_test.go b/receiver/nginxreceiver/internal/metadata/generated_metrics_test.go index 82bf3dafe2db..5ecaf1639429 100644 --- a/receiver/nginxreceiver/internal/metadata/generated_metrics_test.go +++ b/receiver/nginxreceiver/internal/metadata/generated_metrics_test.go @@ -70,6 +70,10 @@ func TestMetricsBuilder(t *testing.T) { allMetricsCount++ mb.RecordNginxRequestsDataPoint(ts, 1) + defaultMetricsCount++ + allMetricsCount++ + mb.RecordTempConnectionsCurrentDataPoint(ts, 1, AttributeState(1)) + metrics := mb.Emit() if test.configSet == testSetNone { @@ -152,6 +156,23 @@ func TestMetricsBuilder(t *testing.T) { assert.Equal(t, ts, dp.Timestamp()) assert.Equal(t, pmetric.NumberDataPointValueTypeInt, dp.ValueType()) assert.Equal(t, int64(1), dp.IntValue()) + case "temp.connections_current": + assert.False(t, validatedMetrics["temp.connections_current"], "Found a duplicate in the metrics slice: temp.connections_current") + validatedMetrics["temp.connections_current"] = true + assert.Equal(t, pmetric.MetricTypeSum, ms.At(i).Type()) + assert.Equal(t, 1, ms.At(i).Sum().DataPoints().Len()) + assert.Equal(t, "Temporary placeholder for new version of nginx.connections_current. See featuregate 'nginx.connections_as_sum'.", ms.At(i).Description()) + assert.Equal(t, "connections", ms.At(i).Unit()) + assert.Equal(t, false, ms.At(i).Sum().IsMonotonic()) + assert.Equal(t, pmetric.AggregationTemporalityCumulative, ms.At(i).Sum().AggregationTemporality()) + dp := ms.At(i).Sum().DataPoints().At(0) + assert.Equal(t, start, dp.StartTimestamp()) + assert.Equal(t, ts, dp.Timestamp()) + assert.Equal(t, pmetric.NumberDataPointValueTypeInt, dp.ValueType()) + assert.Equal(t, int64(1), dp.IntValue()) + attrVal, ok := dp.Attributes().Get("state") + assert.True(t, ok) + assert.Equal(t, "active", attrVal.Str()) } } }) diff --git a/receiver/nginxreceiver/internal/metadata/temporary_metrics.go b/receiver/nginxreceiver/internal/metadata/temporary_metrics.go new file mode 100644 index 000000000000..0ea684cd3501 --- /dev/null +++ b/receiver/nginxreceiver/internal/metadata/temporary_metrics.go @@ -0,0 +1,23 @@ +// Code generated by mdatagen. DO NOT EDIT. + +package metadata + +// WithCurrentConnectionsAsSum sets the current connections metric as a sum. +func WithCurrentConnectionsAsSum() metricBuilderOption { + return func(mb *MetricsBuilder) { + if mb.metricNginxConnectionsCurrent.config.Enabled { + mb.metricNginxConnectionsCurrent.config.Enabled = false + mb.metricTempConnectionsCurrent.config.Enabled = true + mb.metricTempConnectionsCurrent.data.SetName(mb.metricNginxConnectionsCurrent.data.Name()) + mb.metricTempConnectionsCurrent.data.SetDescription(mb.metricNginxConnectionsCurrent.data.Description()) + } + } +} + +// WithCurrentConnectionsAsSumDisabled disables the current connections metric as a sum. +// This is necessary because the metric must be enabled by default in order to be able to apply the other option. +func WithCurrentConnectionsAsSumDisabled() metricBuilderOption { + return func(mb *MetricsBuilder) { + mb.metricTempConnectionsCurrent.config.Enabled = false + } +} diff --git a/receiver/nginxreceiver/internal/metadata/testdata/config.yaml b/receiver/nginxreceiver/internal/metadata/testdata/config.yaml index 05f6368506a2..7612db967b5d 100644 --- a/receiver/nginxreceiver/internal/metadata/testdata/config.yaml +++ b/receiver/nginxreceiver/internal/metadata/testdata/config.yaml @@ -9,6 +9,8 @@ all_set: enabled: true nginx.requests: enabled: true + temp.connections_current: + enabled: true none_set: metrics: nginx.connections_accepted: @@ -19,3 +21,5 @@ none_set: enabled: false nginx.requests: enabled: false + temp.connections_current: + enabled: false diff --git a/receiver/nginxreceiver/metadata.yaml b/receiver/nginxreceiver/metadata.yaml index 6426cd0e885a..59fa4fbc8a14 100644 --- a/receiver/nginxreceiver/metadata.yaml +++ b/receiver/nginxreceiver/metadata.yaml @@ -44,6 +44,8 @@ metrics: monotonic: true aggregation: cumulative attributes: [] + +# Old version of metric, to be enabled when featuregate is stable nginx.connections_current: enabled: true description: The current number of nginx connections by state @@ -51,3 +53,14 @@ metrics: gauge: value_type: int attributes: [state] + +# New version of metric, to be enabled when featuregate is stable + temp.connections_current: + enabled: true # must be enabled by default in order to apply necessary MetricBuilder option + description: Temporary placeholder for new version of nginx.connections_current. See featuregate 'nginx.connections_as_sum'. + unit: connections + sum: + value_type: int + monotonic: false + aggregation: cumulative + attributes: [state] diff --git a/receiver/nginxreceiver/scraper.go b/receiver/nginxreceiver/scraper.go index 9f3071580b6d..9edaae1d7763 100644 --- a/receiver/nginxreceiver/scraper.go +++ b/receiver/nginxreceiver/scraper.go @@ -42,10 +42,16 @@ func newNginxScraper( settings receiver.CreateSettings, cfg *Config, ) *nginxScraper { + var mb *metadata.MetricsBuilder + if connectorsAsSumGate.IsEnabled() { + mb = metadata.NewMetricsBuilder(cfg.MetricsBuilderConfig, settings, metadata.WithCurrentConnectionsAsSum()) + } else { + mb = metadata.NewMetricsBuilder(cfg.MetricsBuilderConfig, settings, metadata.WithCurrentConnectionsAsSumDisabled()) + } return &nginxScraper{ settings: settings.TelemetrySettings, cfg: cfg, - mb: metadata.NewMetricsBuilder(cfg.MetricsBuilderConfig, settings), + mb: mb, } } @@ -81,10 +87,18 @@ func (r *nginxScraper) scrape(context.Context) (pmetric.Metrics, error) { r.mb.RecordNginxRequestsDataPoint(now, stats.Requests) r.mb.RecordNginxConnectionsAcceptedDataPoint(now, stats.Connections.Accepted) r.mb.RecordNginxConnectionsHandledDataPoint(now, stats.Connections.Handled) - r.mb.RecordNginxConnectionsCurrentDataPoint(now, stats.Connections.Active, metadata.AttributeStateActive) - r.mb.RecordNginxConnectionsCurrentDataPoint(now, stats.Connections.Reading, metadata.AttributeStateReading) - r.mb.RecordNginxConnectionsCurrentDataPoint(now, stats.Connections.Writing, metadata.AttributeStateWriting) - r.mb.RecordNginxConnectionsCurrentDataPoint(now, stats.Connections.Waiting, metadata.AttributeStateWaiting) + + if connectorsAsSumGate.IsEnabled() { + r.mb.RecordTempConnectionsCurrentDataPoint(now, stats.Connections.Active, metadata.AttributeStateActive) + r.mb.RecordTempConnectionsCurrentDataPoint(now, stats.Connections.Reading, metadata.AttributeStateReading) + r.mb.RecordTempConnectionsCurrentDataPoint(now, stats.Connections.Writing, metadata.AttributeStateWriting) + r.mb.RecordTempConnectionsCurrentDataPoint(now, stats.Connections.Waiting, metadata.AttributeStateWaiting) + } else { + r.mb.RecordNginxConnectionsCurrentDataPoint(now, stats.Connections.Active, metadata.AttributeStateActive) + r.mb.RecordNginxConnectionsCurrentDataPoint(now, stats.Connections.Reading, metadata.AttributeStateReading) + r.mb.RecordNginxConnectionsCurrentDataPoint(now, stats.Connections.Writing, metadata.AttributeStateWriting) + r.mb.RecordNginxConnectionsCurrentDataPoint(now, stats.Connections.Waiting, metadata.AttributeStateWaiting) + } return r.mb.Emit(), nil } diff --git a/receiver/nginxreceiver/scraper_test.go b/receiver/nginxreceiver/scraper_test.go index ad45409b941f..7e0c7abd75f7 100644 --- a/receiver/nginxreceiver/scraper_test.go +++ b/receiver/nginxreceiver/scraper_test.go @@ -27,6 +27,7 @@ import ( "go.opentelemetry.io/collector/component/componenttest" "go.opentelemetry.io/collector/config/confighttp" "go.opentelemetry.io/collector/config/configtls" + "go.opentelemetry.io/collector/featuregate" "go.opentelemetry.io/collector/receiver/receivertest" "github.com/open-telemetry/opentelemetry-collector-contrib/internal/coreinternal/golden" @@ -55,6 +56,33 @@ func TestScraper(t *testing.T) { pmetrictest.IgnoreTimestamp())) } +func TestScraperWithConnectionsAsSum(t *testing.T) { + nginxMock := newMockServer(t) + cfg := createDefaultConfig().(*Config) + cfg.Endpoint = nginxMock.URL + "/status" + require.NoError(t, component.ValidateConfig(cfg)) + + require.NoError(t, featuregate.GlobalRegistry().Set(connectionsAsSum, true)) + defer func() { + require.NoError(t, featuregate.GlobalRegistry().Set(connectionsAsSum, false)) + }() + + scraper := newNginxScraper(receivertest.NewNopCreateSettings(), cfg) + + err := scraper.start(context.Background(), componenttest.NewNopHost()) + require.NoError(t, err) + + actualMetrics, err := scraper.scrape(context.Background()) + require.NoError(t, err) + + expectedFile := filepath.Join("testdata", "scraper", "expected_with_connections_as_sum.yaml") + expectedMetrics, err := golden.ReadMetrics(expectedFile) + require.NoError(t, err) + + require.NoError(t, pmetrictest.CompareMetrics(expectedMetrics, actualMetrics, pmetrictest.IgnoreStartTimestamp(), + pmetrictest.IgnoreTimestamp(), pmetrictest.IgnoreMetricsOrder())) +} + func TestScraperError(t *testing.T) { nginxMock := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { if req.URL.Path == "/status" { diff --git a/receiver/nginxreceiver/testdata/scraper/expected_with_connections_as_sum.yaml b/receiver/nginxreceiver/testdata/scraper/expected_with_connections_as_sum.yaml new file mode 100644 index 000000000000..4bb98b5adf94 --- /dev/null +++ b/receiver/nginxreceiver/testdata/scraper/expected_with_connections_as_sum.yaml @@ -0,0 +1,65 @@ +resourceMetrics: + - resource: {} + scopeMetrics: + - metrics: + - description: The total number of accepted client connections + name: nginx.connections_accepted + sum: + aggregationTemporality: 2 + dataPoints: + - asInt: "16630948" + timeUnixNano: "1638471548185885000" + isMonotonic: true + unit: connections + - description: The current number of nginx connections by state + sum: + aggregationTemporality: 2 + dataPoints: + - asInt: "291" + attributes: + - key: state + value: + stringValue: active + timeUnixNano: "1638471548185885000" + - asInt: "6" + attributes: + - key: state + value: + stringValue: reading + timeUnixNano: "1638471548185885000" + - asInt: "179" + attributes: + - key: state + value: + stringValue: writing + timeUnixNano: "1638471548185885000" + - asInt: "106" + attributes: + - key: state + value: + stringValue: waiting + timeUnixNano: "1638471548185885000" + isMonotonic: false + name: nginx.connections_current + unit: connections + - description: The total number of handled connections. Generally, the parameter value is the same as nginx.connections_accepted unless some resource limits have been reached (for example, the worker_connections limit). + name: nginx.connections_handled + sum: + aggregationTemporality: 2 + dataPoints: + - asInt: "16630946" + timeUnixNano: "1638471548185885000" + isMonotonic: true + unit: connections + - description: Total number of requests made to the server since it started + name: nginx.requests + sum: + aggregationTemporality: 2 + dataPoints: + - asInt: "31070465" + timeUnixNano: "1638471548185885000" + isMonotonic: true + unit: requests + scope: + name: otelcol/nginxreceiver + version: latest