1// Copyright The OpenTelemetry 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 15package scraperhelper 16 17import ( 18 "context" 19 20 "go.opentelemetry.io/collector/component" 21 "go.opentelemetry.io/collector/component/componenthelper" 22 "go.opentelemetry.io/collector/config" 23 "go.opentelemetry.io/collector/model/pdata" 24 "go.opentelemetry.io/collector/obsreport" 25 "go.opentelemetry.io/collector/receiver/scrapererror" 26) 27 28// ScrapeMetrics scrapes metrics. 29type ScrapeMetrics func(context.Context) (pdata.MetricSlice, error) 30 31// ScrapeResourceMetrics scrapes resource metrics. 32type ScrapeResourceMetrics func(context.Context) (pdata.ResourceMetricsSlice, error) 33 34type baseSettings struct { 35 componentOptions []componenthelper.Option 36} 37 38// ScraperOption apply changes to internal options. 39type ScraperOption func(*baseSettings) 40 41// Scraper is the base interface for scrapers. 42type Scraper interface { 43 component.Component 44 45 // ID returns the scraper id. 46 ID() config.ComponentID 47 Scrape(context.Context, config.ComponentID) (pdata.Metrics, error) 48} 49 50type baseScraper struct { 51 component.Component 52 id config.ComponentID 53} 54 55func (b baseScraper) ID() config.ComponentID { 56 return b.id 57} 58 59// WithStart sets the function that will be called on startup. 60func WithStart(start componenthelper.StartFunc) ScraperOption { 61 return func(o *baseSettings) { 62 o.componentOptions = append(o.componentOptions, componenthelper.WithStart(start)) 63 } 64} 65 66// WithShutdown sets the function that will be called on shutdown. 67func WithShutdown(shutdown componenthelper.ShutdownFunc) ScraperOption { 68 return func(o *baseSettings) { 69 o.componentOptions = append(o.componentOptions, componenthelper.WithShutdown(shutdown)) 70 } 71} 72 73type metricsScraper struct { 74 baseScraper 75 ScrapeMetrics 76} 77 78var _ Scraper = (*metricsScraper)(nil) 79 80// NewMetricsScraper creates a Scraper that calls Scrape at the specified 81// collection interval, reports observability information, and passes the 82// scraped metrics to the next consumer. 83func NewMetricsScraper( 84 name string, 85 scrape ScrapeMetrics, 86 options ...ScraperOption, 87) Scraper { 88 set := &baseSettings{} 89 for _, op := range options { 90 op(set) 91 } 92 93 ms := &metricsScraper{ 94 baseScraper: baseScraper{ 95 Component: componenthelper.New(set.componentOptions...), 96 id: config.NewID(config.Type(name)), 97 }, 98 ScrapeMetrics: scrape, 99 } 100 101 return ms 102} 103 104func (ms metricsScraper) Scrape(ctx context.Context, receiverID config.ComponentID) (pdata.Metrics, error) { 105 ctx = obsreport.ScraperContext(ctx, receiverID, ms.ID()) 106 scrp := obsreport.NewScraper(obsreport.ScraperSettings{ReceiverID: receiverID, Scraper: ms.ID()}) 107 ctx = scrp.StartMetricsOp(ctx) 108 metrics, err := ms.ScrapeMetrics(ctx) 109 count := 0 110 md := pdata.Metrics{} 111 if err == nil || scrapererror.IsPartialScrapeError(err) { 112 md = pdata.NewMetrics() 113 metrics.MoveAndAppendTo(md.ResourceMetrics().AppendEmpty().InstrumentationLibraryMetrics().AppendEmpty().Metrics()) 114 count = md.MetricCount() 115 } 116 scrp.EndMetricsOp(ctx, count, err) 117 return md, err 118} 119 120type resourceMetricsScraper struct { 121 baseScraper 122 ScrapeResourceMetrics 123} 124 125var _ Scraper = (*resourceMetricsScraper)(nil) 126 127// NewResourceMetricsScraper creates a Scraper that calls Scrape at the 128// specified collection interval, reports observability information, and 129// passes the scraped resource metrics to the next consumer. 130func NewResourceMetricsScraper( 131 id config.ComponentID, 132 scrape ScrapeResourceMetrics, 133 options ...ScraperOption, 134) Scraper { 135 set := &baseSettings{} 136 for _, op := range options { 137 op(set) 138 } 139 140 rms := &resourceMetricsScraper{ 141 baseScraper: baseScraper{ 142 Component: componenthelper.New(set.componentOptions...), 143 id: id, 144 }, 145 ScrapeResourceMetrics: scrape, 146 } 147 148 return rms 149} 150 151func (rms resourceMetricsScraper) Scrape(ctx context.Context, receiverID config.ComponentID) (pdata.Metrics, error) { 152 ctx = obsreport.ScraperContext(ctx, receiverID, rms.ID()) 153 scrp := obsreport.NewScraper(obsreport.ScraperSettings{ReceiverID: receiverID, Scraper: rms.ID()}) 154 ctx = scrp.StartMetricsOp(ctx) 155 resourceMetrics, err := rms.ScrapeResourceMetrics(ctx) 156 157 count := 0 158 md := pdata.Metrics{} 159 if err == nil || scrapererror.IsPartialScrapeError(err) { 160 md = pdata.NewMetrics() 161 resourceMetrics.MoveAndAppendTo(md.ResourceMetrics()) 162 count = md.MetricCount() 163 } 164 165 scrp.EndMetricsOp(ctx, count, err) 166 return md, err 167} 168