From ac39113dc5e0162f7b560954de19829fd69640c4 Mon Sep 17 00:00:00 2001 From: Yang Hu Date: Thu, 30 Jul 2020 12:34:59 -0500 Subject: [PATCH 1/7] Add Cortex exporter design --- exporter/cortexexporter/DESIGN.md | 287 ++++++++++++++++++ .../png;base6470b5ba4b11b18d5c.png | Bin 0 -> 22960 bytes .../png;base64e4e5466cd84608b5 (1).png | Bin 0 -> 73392 bytes 3 files changed, 287 insertions(+) create mode 100644 exporter/cortexexporter/DESIGN.md create mode 100644 exporter/cortexexporter/png;base6470b5ba4b11b18d5c.png create mode 100644 exporter/cortexexporter/png;base64e4e5466cd84608b5 (1).png diff --git a/exporter/cortexexporter/DESIGN.md b/exporter/cortexexporter/DESIGN.md new file mode 100644 index 00000000000..4ba29293a87 --- /dev/null +++ b/exporter/cortexexporter/DESIGN.md @@ -0,0 +1,287 @@ + +# **OpenTelemetry Collector Cortex Exporter Design** + +Authors: @huyan0, @danielbang907 + +Date: July 30, 2020 + +## **1. Introduction** + +Cortex is an open source, horizontally scalable, highly available, multi-tenant, long term storage for Prometheus. Cortex accepts data defined by the Prometheus Remote Write API. Our project is focused on developing an exporter for the OpenTelemetry Collector to a Cortex instance. + +The following diagram is the architecture of Cortex. + +![Cortex Archietecture](https://raw.githubusercontent.com/open-o11y/opentelemetry-collector/design-doc/exporter/cortexexporter/png%3Bbase64e4e5466cd84608b5%20(1).png) + +### **1.1 Remote Write API** + +Cortex has a Remote Write API that accepts incoming metrics. This exporter should write metrics to a remote URL in a snappy-compressed, protocol buffer encoded HTTP request defined by the Remote Write API. Each request encodes multiple Cortex TimeSeries, which are composed of a set of labels and a collection of samples. Each label contains a name-value pair of strings, and each sample contains a timestamp-value number pair. + +![Image of TimeSeries +](https://raw.githubusercontent.com/open-o11y/opentelemetry-collector/design-doc/exporter/cortexexporter/png%3Bbase6470b5ba4b11b18d5c.png) + +TimeSeries stores its metric name in its labels and does not describe metric types or start timestamps. To convert to TimeSeries data, buckets of a Histogram are broken down into individual TimeSeries with a bound label(`le`), and a similar process happens with quantiles in a Summary. + + +More details of Remote Write API can be found in Prometheus [documentation](https://cortexmetrics.io/docs/apis/) and Cortex [documentation](https://cortexmetrics.io/docs/apis/). + +### **1.2 Gaps and Assumptions** + +**Gap 1:** +Currently, metrics from the OpenTelemetry SDKs cannot be exported to Prometheus from the collector correctly ([#1255](https://github.com/open-telemetry/opentelemetry-collector/issues/1255)). This is because the SDKs send metrics to the collector via their OTLP exporter, which exports the delta value of cumulative counters. The same issue will arise for any cumulative backend service, such as Cortex. + +To overcome this gap in the Collector pipeline, we had proposed 2 different solutions: + +1. Add a [metric aggregation processor](https://github.com/open-telemetry/opentelemetry-collector/issues/1422) to the collector pipeline to aggregate delta values into cumulative values for cumulative backends like Cortex. This solution requires users to set up a collector agent next to each SDK to make sure delta values are aggregated correctly. +2. Require the OTLP exporters in SDKs to [send cumulative values for cumulative metric types to the Collector by default](https://github.com/open-telemetry/opentelemetry-specification/issues/731). Therefore, no aggregation of delta metric values is required in the Collector pipeline for Cortex/Prometheus to properly process the data. + +**Gap 2:** +Another gap is that OTLP metric definition is still in development. This exporter will require refactoring as OTLP changes in the future. + +**Assumptions:** +Because of the gaps mentioned above, this project will convert from the current OTLP metrics and work under the assumption one of the above solutions will be implemented, and all incoming monotonic scalars/histogram/summary metrics should be cumulative or otherwise dropped. More details on the behavior of the exporter is in section 2.2. + +## **2. Cortex Exporter** + +The Cortex exporter should receive OTLP metrics, group data points by metric name and label set, convert each group to a TimeSeries, and send all TimeSeries to Cortex via HTTP. + +### **2.1 Receiving Metrics** +The Cortex exporter receives a MetricsData instance in its PushMetrics() function. MetricsData contains a collection of Metric instances. Each Metric instance contains a series of data points, and each data point has a set of labels associated with it. Since Cortex TimeSeries are identified by unique sets of labels, the exporter needs to group data points within each Metric instance by their label set, and convert each group to a TimeSeries. + +To group data points by label set, the Cortex exporter should create a map with each PushMetrics() call. The key of the map should represent a combination of the following information: + +* the metric type +* the metric name +* the set of labels that identify a unique TimeSeries + + +The exporter should create a signature string as map key by concatenating metric type, metric name, and label names and label values at each data point. To ensure correctness, the label set at each data point should be sorted by label key before generating the signature string. + +An alternative key type is in the exiting label.Set implementation from the OpenTelemetry Go API. It provides a Distinct type that guarantees the result will equal the equivalent Distinct value of any label set with the same elements as this, +where sets are made unique by choosing the last value in the input for any given key. If we allocate a Go API's kv.KeyValue for every label of a data point, then a label.Set from the API can be created, and its Distinct value can be used as map key. + + +The value of the map should be Cortex TimeSeries, and each data point’s value and timestamp should be inserted to its corresponding TimeSeries in the map as a Sample, each metric’s label set and metric name should be combined and translated to a Cortex label set; a new TimeSeries should be created if the string signature is not in the map. + + +Pseudocode: + + func PushMetrics(metricsData) { + + // Create a map that stores distinct TimeSeries + map := make(map[String][]TimeSeries) + + for metric in metricsData: + for point in metric: + // Generate signature string + sig := pointSignature(metric, point) + + // Find corresponding TimeSeries in map + // Add to TimeSeries + + // Sends TimeSeries to backend + export(map) + } + +### **2.2 Mapping of OTLP Metrics to TimeSeries** + +Each Cortex TimeSeries represents less semantic information than an OTLP metric. The temporality property of a OTLP metric is ignored in a TimeSeries because it is always considered as cumulative for monotonic types and histogram, and the type property of a OTLP metric is translated by mapping each metric to one or multiple TimeSeries. The following sections explain how to map each OTLP metric type to Cortex TimeSeries. + + +**INT64, MONOTONIC_INT64, DOUBLE, MONOTONIC_DOUBLE** + +Each unique label set within metrics of these types can be converted to exactly one TimeSeries. From the perspective of Prometheus Client types, INT64 and DOUBLE correspond to gauge metrics, and MONOTONIC types correspond to counter metrics. In both cases, data points will be exported directly without aggregation. Any metric of the monotonic types that is not cumulative should be dropped; non-monotonic scalar types are assumed to represent gauge values, thus its temporality is not checked. Monotonic types need to have a `_total` suffix in its metric name when exporting; this is a requirement of [Prometheus](https://www.slideshare.net/brianbrazil/openmetrics-what-does-it-mean-for-you-promcon-2019-munich) (slide 11). + + +**HISTOGRAM** + +Each histogram data point can be converted to 2 + n + 1 Cortex TimeSeries: + +* 1 *TimeSeries* representing metric_name_count contains HistogramDataPoint.count +* 1 *TimeSeries* representing metric_name_sum contains HistogramDataPoint.sum +* n *TimeSeries* each representing metric_name_bucket{le=“upperbound”} contain the count of each bucket defined by the bounds of the data point +* 1 *TimeSeries* representing metric_name_bucket{le=“+Inf”} contains counts for the bucket with infinity as upper bound; its value is equivalent to metric_name_count. + +Cortex bucket values are cumulative, meaning the count of each bucket should contain counts from buckets with lower bounds. In addition, Exemplars from a histogram data point are ignored. When adding a bucket of the histogram data point to the map, the string signature should also contain a `le` label that indicates the bound value. This label should also be exported. Any histogram metric that is not cumulative should be dropped. + + +**SUMMARY** + +Each summary data point can be converted to 2 + n Cortex TimeSeries: + +* 1 *TimeSeries* representing metric_name_count contains SummaryDataPoint.count +* 1 *TimeSeries* representing metric_name_sum contains SummaryDataPoint.sum +* and n *TimeSeries* each representing metric_name{quantile=“quantileValue”} contains the value of each quantile in the data point. + +When adding a quantile of the summary data point to the map, the string signature should also contain a `quantile ` label that indicates the quantile value. This label should also be exported. Any summary metric that is not cumulative should be dropped. + +### **2.3 Exporting Metrics** + +The Cortex exporter should call proto.Marshal() to convert multiple TimeSeries to a byte array. Then, the exporter should send the byte array to Cortex in a HTTP request. + + +Authentication credentials should be added to each request before sending to the backend. Basic auth and bearer token headers can be added using Golang http.Client’s default configuration options. Other authentication headers can be added by implementing a client interceptor. + + +Pseudocode: + + + func export(*map) error { + // Stores timeseries + arr := make([]TimeSeries) + + for timeseries in map: + arr = append(arr, timeseries) + + // Converts arr to WriteRequest + request := proto.Marshal(arr) + + // Sends HTTP request to endpoint + } + +## **3. Other Components** + +### **3.1 Config Struct** + +This struct is based on an inputted YAML file at the beginning of the pipeline and defines the configurations for an Exporter build. Examples of configuration parameters are HTTP endpoint, compression type, backend program, etc. + + +Converting YAML to a Go struct is done by the Collector, using [the Viper package](https://github.com/spf13/viper), which is an open-source library that seamlessly converts inputted YAML files into a usable, appropriate Config struct. + + +An example of the exporter section of the Collector config.yml YAML file can be seen below: + + ... + + exporters: + cortex: + http_endpoint: + # Prefix to metric name + namespace: + # Labels to add to each TimeSeries + const_labels: + [label: ] + # Allow users to add any header; only required headers listed here + headers: + [X-Prometheus-Remote-Write-Version:] + [Tenant-id:] + request_timeout: + + # ************************************************************************ + # below are configurations copied from Prometheus remote write config + # ************************************************************************ + # Sets the `Authorization` header on every remote write request with the + # configured username and password. + # password and password_file are mutually exclusive. + basic_auth: + [ username: ] + [ password: ] + [ password_file: ] + + # Sets the `Authorization` header on every remote write request with + # the configured bearer token. It is mutually exclusive with `bearer_token_file`. + [ bearer_token: ] + + # Sets the `Authorization` header on every remote write request with the bearer token + # read from the configured file. It is mutually exclusive with `bearer_token`. + [ bearer_token_file: /path/to/bearer/token/file ] + + # Configures the remote write request's TLS settings. + tls_config: + # CA certificate to validate API server certificate with. + [ ca_file: ] + + # Certificate and key files for client cert authentication to the server. + [ cert_file: ] + [ key_file: ] + + # ServerName extension to indicate the name of the server. + # https://tools.ietf.org/html/rfc4366#section-3.1 + [ server_name: ] + + # Disable validation of the server certificate. + [ insecure_skip_verify: ] + + ... + +### **3.2 Factory Struct** + +This struct implements the ExporterFactory interface, and is used during collector’s pipeline initialization to create the Exporter instances as defined by the Config struct. The `exporterhelper` package will be used to create the exporter and the factory. + + +Our Factory type will look very similar to other exporters’ factory implementation. For our implementation, our Factory instance will implement three methods + + +**Methods** + + NewFactory +This method will use the NewFactory method within the `exporterhelper` package to create a instance of the factory. + + createDefaultConfig + +This method creates the default configuration for Cortex exporter. + + + createMetricsExporter + +This method constructs a new http.Client with interceptors that add headers to any request it sends. Then, this method initializes a new Cortex exporter with the http.Client. This method constructs a collector Cortex exporter with the created SDK exporter + + + +## **4. Other Considerations** + +### **4.1 Concurrency** + +The Cortex exporter should be thread-safe; In this design, the only resource shared across goroutines is the http.Client from the Golang library. It is thread-safe, thus, our code is thread-safe. + +### **4.2 Shutdown Behavior** + +Once the shutdown() function is called, the exporter should stop accepting incoming calls(return error), and wait for current operations to finish before returning. This can be done by using a stop channel and a wait group. + + func Shutdown () { + close(stopChan) + waitGroup.Wait() + } + + func PushMetrics() { + select: + case <- stopCh + return error + default: + waitGroup.Add(1) + defer waitGroup.Done() + // export metrics + ... + } + +### **4.3 Timeout Behavior** + +Users should be able to pass in a time for the each http request as part of the Configuration. The factory should read the configuration file and set the timeout field of the http.Client + + func (f *Factory) CreateNewExporter (config) { + ... + client := &http.Client{ + Timeout config.requestTimeout + } + ... + } + +### **4.4 Error Behavior** + +The PushMetricsData() function should return the number of dropped metrics. Any MONOTONIC and HISTOGRAM metrics that are not cumulative should be dropped. This can be done by checking the temporality of each received metric. Any error should be returned to the caller, and the error message should be descriptive. + + + +### **4.5 Test Strategy** + +We will follow test-driven development practices while completing this project. We’ll write unit tests before implementing production code. Tests will cover normal and abnormal inputs and test for edge cases. We will provide end-to-end tests using mock backend/client. Our target is to get 90% or more of code coverage. + + + +## **Request for Feedback** +We'd like to get some feedback on whether we made the appropriate assumptions in [this](#12-gaps-and-assumptions) section, and appreciate more comments, updates, and suggestions on the topic. + +Please let us know if there are any revisions, technical or informational, necessary for this document. Thank you! + + + diff --git a/exporter/cortexexporter/png;base6470b5ba4b11b18d5c.png b/exporter/cortexexporter/png;base6470b5ba4b11b18d5c.png new file mode 100644 index 0000000000000000000000000000000000000000..a5c8da0fa76801ad5befc75cd5b2cd0fe433c6ca GIT binary patch literal 22960 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 literal 0 HcmV?d00001 diff --git a/exporter/cortexexporter/png;base64e4e5466cd84608b5 (1).png b/exporter/cortexexporter/png;base64e4e5466cd84608b5 (1).png new file mode 100644 index 0000000000000000000000000000000000000000..75769e61302c18a97c64ebf08785f578c2044390 GIT binary patch literal 73392 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

jml3=ICD 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 literal 0 HcmV?d00001 From 2abf6045dc3dade722b9a4b163cc16c3166272fc Mon Sep 17 00:00:00 2001 From: Yang Hu Date: Fri, 31 Jul 2020 08:30:24 -0500 Subject: [PATCH 2/7] fix typo in image --- exporter/cortexexporter/DESIGN.md | 4 ++-- ...ase64e4e5466cd84608b5 (1).png => cortex.png} | Bin .../png;base6470b5ba4b11b18d5c.png | Bin 22960 -> 0 bytes exporter/cortexexporter/timeseries.png | Bin 0 -> 9001 bytes 4 files changed, 2 insertions(+), 2 deletions(-) rename exporter/cortexexporter/{png;base64e4e5466cd84608b5 (1).png => cortex.png} (100%) delete mode 100644 exporter/cortexexporter/png;base6470b5ba4b11b18d5c.png create mode 100644 exporter/cortexexporter/timeseries.png diff --git a/exporter/cortexexporter/DESIGN.md b/exporter/cortexexporter/DESIGN.md index 4ba29293a87..1ac2f5c586e 100644 --- a/exporter/cortexexporter/DESIGN.md +++ b/exporter/cortexexporter/DESIGN.md @@ -11,14 +11,14 @@ Cortex is an open source, horizontally scalable, highly available, multi-tenant, The following diagram is the architecture of Cortex. -![Cortex Archietecture](https://raw.githubusercontent.com/open-o11y/opentelemetry-collector/design-doc/exporter/cortexexporter/png%3Bbase64e4e5466cd84608b5%20(1).png) +![Cortex Archietecture](https://raw.githubusercontent.com/open-o11y/opentelemetry-collector/design-doc/exporter/cortexexporter/cortex.png) ### **1.1 Remote Write API** Cortex has a Remote Write API that accepts incoming metrics. This exporter should write metrics to a remote URL in a snappy-compressed, protocol buffer encoded HTTP request defined by the Remote Write API. Each request encodes multiple Cortex TimeSeries, which are composed of a set of labels and a collection of samples. Each label contains a name-value pair of strings, and each sample contains a timestamp-value number pair. ![Image of TimeSeries -](https://raw.githubusercontent.com/open-o11y/opentelemetry-collector/design-doc/exporter/cortexexporter/png%3Bbase6470b5ba4b11b18d5c.png) +](https://raw.githubusercontent.com/open-o11y/opentelemetry-collector/design-doc/exporter/cortexexporter/timeseries.png) TimeSeries stores its metric name in its labels and does not describe metric types or start timestamps. To convert to TimeSeries data, buckets of a Histogram are broken down into individual TimeSeries with a bound label(`le`), and a similar process happens with quantiles in a Summary. diff --git a/exporter/cortexexporter/png;base64e4e5466cd84608b5 (1).png b/exporter/cortexexporter/cortex.png similarity index 100% rename from exporter/cortexexporter/png;base64e4e5466cd84608b5 (1).png rename to exporter/cortexexporter/cortex.png diff --git a/exporter/cortexexporter/png;base6470b5ba4b11b18d5c.png b/exporter/cortexexporter/png;base6470b5ba4b11b18d5c.png deleted file mode 100644 index a5c8da0fa76801ad5befc75cd5b2cd0fe433c6ca..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 22960 zcmeEuc{G)8`|nOd=1P*8BuS=(WXO;tA%qZ_hZGqyQ-mZ*l4MGfgpd#!(oRUGWR@W$ zk$E1^XZNn(T4$ZJ)>-HLbN)GJulHTw65F%y=f1D&GhI)Zw&p21YF26jfk1a!O+|-5 z*y4%*n^TeDEA#yP&+wnEu1cr%sHmvA2ekSL1a87<6-7PIgz+C9Cb|ZL3RBT?r$DlIEEQFp2MAR+b*{7;6*yexIC-TtJzK4y>yK7mo1DXs)R#}ycIWZ_w4iyjTZzSd zKY0!3;$pQ|Qt8aL%ZFIA7fLs&rKA>`e-~S0{L=W*+eEtW7QpW)0f$ zKfL8OxDk!Uwe=jWcQAzJC2`S$%K!o;|UT9+45IJWC@Y&T4CG>*;-8 zovhGGZ;8k=BM|6AgF-`C7^t6=>t^Vw@JsCETyIvbVPi-Fcf}jDxjxf&aQU z;YL~6df&l(6XA57jK-cHM`&6)I^G3rZX7arEiEG>e6i&F_wR30Q}-P@H1@M9JS{El z`t|EOcI*&wt7#PCxp{({k!oeX2ES|1yK6UZ-V|c4!v}Y^j!;e27WnN}5+(d%;|dn# z2EKYGRyd>e&&hD+ixvK5B_&VP)9!J~byZh~Dy0wz;zy3Sy0|>o8GU{_T1H03#l@wx ztc*w`9%QMe;3aU&$duqt4-iACgFjYPS$}zWkTrt;=+S~VZyr=$Qc+Ry z^z;;Ep(WhVOuu~j@*yFitgI|YC#S`knQwbkGh5rffA1;xi?6(NmqqMnw^PN@BS&_H zSK*%F_ex4hweLBP?`drv9vWiWy<05qYuK&>-kzQxgzl^H`7BPH7mR6cZuXV0c=yh8 zyd%%w-=CaJQGn^!$jHd>@D8e5=ERWY)z$dNj|l`O>XYZrHLtHO%E`)pBoZ_9Ug3fq z9ZS7tZO)&MF%g!MlJdBIot}=aATLj>&8)RTeKP%URImT)U#TTIvr^Bz=;&RzyUljz z&Yhc{p7xdJBIe$@MZOeyZK9_*Dk{oN#;@wmZr-Thzv|;N4RVsCuRX&NTPtcDcCoay z#9;|EH^c(Cxk(U6n{i`#WXh+?BG0BFVHqEL+qP|$eNnNo20A(q)YCqSI&Q2jPd%}# zeXxIFVZl>2Nj**Vx4*tv>@w^#S#~P58T+L@$IMJeXC}e&`<~BJ zdrcBG(*ve9=SEvtLPnE6`faW+hSKgO6WPVi!BKRjd!g09?AWnmqu~!@V<|<T>{2R;6F zwf^|wH}|_)OJQ9?RP??IZ`|X@cUg}*w7xpOyu2(J^C~059&1Rrf%iaVuw>z9qL#YS zH8ei1!5bybP9LMrA?@7%r!{l`&ak|VSG_zO931`2T2A{!_>LaM^<~)D*q}sWH5zMb zsJ3s=uL`<1bqu93) zUbgk<# zd=MR-bMgK4&5iX8-E>w-2YJsaZUF(iuaCuWnmRi>u~%^*0?ob~@b2Gl{qs{;5G8}A zy85fk%1JSj2@7&o*MUE@iI(s(a_HA6(Il&l8diI){n%w++HW{~<`T4Uy-h1F+ z8ygs;w>lg+`(y}bf|C>L?mzqMY@CLq=hP)iN=hxQ#*Ot=;!u@nR!NEX;O7Y5sG90( z+xb7OM%mX_7yc3moRR4{!#>V^h?F*%2)5kH;)0^wB3__Q{F)Mo&y&!Tc@P&wy zl9GOoY5lOvw5RWd3m5Q{K0fc&)4t_=uoB15Nm_os>%YDt=QTsVu~}45aIwTKBQ~}j zb!yqy-`m^!^y#n@+)YK^jA5UeQ%~hw$mid`e|TknOeS%nHu^wVSlE^=Td;eJuKw_z z>c6Qc7`$1O($u8g@vOPE^*pv;Z?ABangnZv|28S9BCpwB)1IYuu_EoQt=Ldfl9CH{ zT3&lmwVR)voS2xvM^;MF=4LG2+*rMK?V7cCQCnjl;Qdh?YNGaG)1E$kIz(|_Y@KaG!syu8*y!l?GifcOA@m$2j-BGNoXN?_ zB23gyedX)Tr?{|&dpX0EQta&Qu~lEZc(Ij|eyu-X4H;rDV zWQzQ1NTkWxOifKS->Tx}mQ6Ot@`|V1!eHb6H;OP_3MEe-+==M0s;bdh7I-i zi()GiZm6dPUMyyeQO6CBiHo~nXD2|oae{kkc{zgp*n@hTQzuVWx_5PTi3a&6pU>3C zMK#;KJ*fNQ!-J`1-(aN_Gjp74dU|>d{+7PJfTpL4&NO5<`_CudLA|EmEB@*8XMRyp zM*XM1zQ$QzzI@zeNR2guf&f4hrJjaT>Tgj0^{bJwvH5Tfn(m7i{8CZ|sm;#P35$IJ zn>b;&@7=?jU-$5soS5i+?=g9b%kQn{^v_?vY!WpokN01bPAD#3<_cKDmLzA(Jt8iC zY3nZX_=FJJy&o;|l4QNe`rf>K>x;5=^{Q}u{R^E8ERI|Q-`dm2$jIbmGh^fK>A~v7 zzug8{Y(_@W%WYYlXPaxnl?){O%r9T2snEl^Po31&jW!W(&*>i-!9i6Z+)z#3K}CM{ z>{-;36|tMY-DI+CD?R3uKMpwK(4$^$q+=WHQ@wk`X>VH{ZCqSjC7HCe^anxg(`fT1 zXKD6#J)b_27v8v2^%EZ%P{_bVu(Y)F(w4x$z}qU%)YIM+6`}LXt=u*5ArMevoO(+E zeat(h+3zJLCZcDFi48bQM<38ilCXbZC?pJsCnVHUCOxF^PT2F9y`RkW2Zz#I{{Is1vdV4sz2#@)`w*z_o&eEtO_3=_U zMg_0X-K)6*^aPl2ZYl$}B?oLQXUbS^Y6{q`JS#5l{PBY$vf9Jlebh~ca3eT4*xZ95 zP9wz3$uHm)cCCX$;N+`T2Wb_?D7En@?vTVBPuY#el?U^mEX1-Zw+Aa3TE5B670qJK zXmy|^UMkwrb1;;c_vXzuNu8m1@hxD^pWZZs7?K>sF z^aTy%$B!R$MOtY(^9Bz^XZpwb`}?slZtm`WGqnd46%|A3jd6zM0x1Y5_@kATl;}BR z9wsLGVGRN29z1xU97>PvKUuL^-e|jB@qBuD&DXDv4h}oRRM1(1j*@9VzBd@$u%C z&OkG+uC6Gc*sK>Xa&SgU9XXO;SUB4YkCvk7DO#2g$3J}dpcq5}P)T<4l7+>yiVvea($XwRbST1qNw4|==i4g0L6yJ# zBJZm3f>|^+G%$x(;iGNLx0~(SxzkSKiBd{ULxX>T?Pwpx#pbyx6muMLjk9L~1NKL$ z845kh&b~6qX806qkM@Dy`FFG>-MHu~#}OM1ef_p#*AW~6e9ws!ge={;7n35s_pWpo zUNADc?(Y7#r&yOa>Vx)?QX!e^! z>A8feyXq_Gtt-G}nDaig*yMFkkg~u32FmmCKHIcRhM(UC(AeGG9m_U9KQGJa1iYfc+l*@1+iS|rxCUY)!J3_yH$9e9@kott z->G|FYic}g%t67xk678*7RNg+9UNY@j4q6K>YA904-UEx)kJYdR1Oah1LmTj<%EUR z0%$0#{7(J+S(P)gy{!!;WPw_%xq=bw=dnV-2Ac1(uRLdDQGWjFP_&kbi3tiwn2H6k zH3e}h6M&Yl2 zv3i<|G<(NdrA40n#*(Eh=jP@{-{vmrVD$6j*T?VI4TC>@{`|SSo2{_wo88;P#)XbO z@38*>P=Zcyi^ggA_^i&3G>(NoZ*`bI8CbsjD?WlvN?%`J_`)0VM|Y|R@ew;)TJp=v zIC@LB(i{U6q6j)?YMK-s?b`q0CQ4QuI+LA*hsR>0{A!j{fFKnKtDs%Jx2U@Fx4S66 z6U%Q)WHuq(NKWnpZGB%-l8~4%THLDkdg|e%4d$%D^eha?}Cb>tRsPR+F2+ zsYo@xm6er``!zK+XMfdiB{&zG5Fcb+$hYZyW6dUYb-X>N554RA zdp=y!vH1FOmtl4EQa87^<%xmB0Tor%l6UXkm6Whv&Gj(n7LKK%=^q**CtMPbo2n+E zI`(UBZV5+ham3#MOpE_wPS{{`^!V=f!g0V+snJWsAMxckjBn zx&|Ew$HanmehS-_V^(2$?i^2aEvKxfsL#SUx}J}hSEV@6R!2w2@87>c%Sorq&=82n z72AF)K{W0wF8?4CwO`az9AXA8d08-}?!qh5>{r2kvD@FGOYIDkU_C4#pe7uYYM{Hw2^9>mV|8^kG&mRl@M~AsO4^3S#yogx-eQ69H)9>0Z}@Fi;uiq^ zLPM+Be0Msb&j6C@X0`$?H9E2>-EY_8jl%n)tiJye$r`l!3?RqD=RhEL0LA%4Q^GiT zO3SF6(H2t${%HHFS6}OAHZM`s7;o8DPcyZ!&|7dhRyFlzS+-LyU%pK}H3LKYs9Qi= zb~yK@z{>~<#rp{|eNUI` zc=qieS4_f1uXVC?Y|^nxc<#hM1^sVmJ^zQ-@c;C;|Lij>c=k*wTmSg6V`40KM{*9v zs9$z)V4)3(j@IS+qNk&Sk~KB;VQ@7m=KoI%vZ1a$0~aC?ClRm z*J2x24>vYURR_Obc~_vD>D@2u27$JN!L9cBBx)qjCQ z{`@&#fzgkmNM$rRC?&j6c1@d)A3p}D-^0vIMz9i(+n6^~KYhA5V6$95^RKd)`k6B= zEiG)UtSeI&$h~Y!KkTkQVqajCsCkN;aU<(VkojY!l&8&hjlO$cY!6fMuC35sSx-4-KjC?L<+dTh!6jbp@3NEUgTp+_cVbo`r-nH#hh5r$wHj zMw-q=6BE_DEapy5PVY2^@mc15>>=EqkS(IWVko;X)_WGD-%{tO|C!D_1~Rhhh#HX#9JL(uUu@ z4PH(Z&4O5T`0(GZg3AyHRe1A&MAKUUQt(>nXvR$k7}w;D`i~mR%F4<@`}y{ceF9AC zXV2D8{CJPGg0R0A%g z4{FiU(h^B`cq}>qUZvpr>#q4JEgzrb$&F^^zROa5zPJ&OM9loB`V~op5YVdQTiTiP zf~~kx1`Zhs{QcIgTYv?N9pO3|CXfbve0(e{egYZ1&`iX#1uPHm2~!~>fIdu?Eha%c zjnkMI8`x@?1RMbF3Fa^Y&bvGi%0(bRS(^C$yD29cAO$c5+y-qQz3(dYX|yVx3?_o} zRDUI4tGk<xhBaO)(uCB-3elvzwkr5J( zdo;AQT_0=92LBUt%o)Yi*-3D6a<;X#VLL(NgZg^(=us>2y+qM~fX&oXk>x|v$|}6q zAk&329|rG(Sf`WW2ABsu51sLQPCi)t)2H0BoLqbNj*g6Ytgp-m)9lg7_fxo@aBre!ZcwaZh+vNWpO5)=Z^ zbSon7+*_Lca+>Vv=_x6ZdG|&?69~j2kA;|PV5mv%MJuR&bXZ8pTL@50)?6ZM%hbtKslG%S=@c_&CU z75bn|BK>Rr*gEJsPj#lFX4CiKi_39Pi+@<|$f-!egcv((_9EY?F z_6C(7wF57JTI|!s$v!7LyWE0;s=;ZzV`O#3SHD9b)md4zk<~zR8p-mB8vKW1zF^A| zZm97Ec2g2SmEmRq(-Us!WC#fe0QrNjU3>tjEO=2nqFQjDJdBrA4e%9BRET&oH|uKKi0nNTYiH3o%|eG<(C>ue8Kmw9}cH8T@9LJlOluYR}!r+8_~bE=mkc+cqW*&n}~X zV~e3l!A8N3gU?V^wI6%<(4jNjjNjYaA)!TbDaiBiq(E(GB$cACG$zgeIMB`NK@N6y zs5pPd#s+`>gcNl1#MJzJqNuqN3d!lywNH*-MxmwW+6V7r6}8yO+WP#33!{Jkmcfo7 z5W=gvP_mSTYIY0Hu-|F z@u2fN@IN#vO{iWFIm^aO8bgT#;D{hVj8u>%@e>eFU}Q;3N-CvHff|E>2?z+>zI*qU z#{?v|#N!@GGVWh$Ye##_eDnm}0aeg5QRP5rU_7i(S98J5_}1LKCH@vV^WTAiEd=mM zpr7g(*1)yy(i2UP`>^9WqL{e26db<4Ec00ukNXO)5gHomTHJ#R#LoeLT)sTEjfREV<_+zg8zKmP2_f}W;Ffox(#ZYauR<1;fDV}Q3hZ@cYXQ#H3i}8lcRO@^@mYv(EGs6AWX0xb9tVTaWHG|HcHC$ z^mGFQgUy*b5ojam!e|1d)>3NR*x1Mr2@Ub!b#kxvOOpvpOP)BhaER6)K-B;^1-K}$ z)ZwW)PJ6xmP3L&8Q(4^)UX~CbR!=rD0_2$0`bU(C8wI_cPkEx$&_+OjaBG@GZv7Q^ z%GINqnVf5&s7Fhii;(w*dUXEBjUeutzQsN^IZ?NKa1fDG|3#5jd%Y#3kNpoGa>`S$ zgJ?hT687Kv_U3XZZq#cx$wul4%DMjf>Y9{x2NEj-!ztzKyfw2ars*SaK)!+pAD5A7 znscI>iA`!5n|m^m3cVKV4SI`JgU=@?m#D^fk45aG^C#vARb5k4`t%HTdEa+2F){v+ zg8@`&Xu@yYxB*UF9mxe*3v4s5(@~ZacGb7<-(d`0MthbP3Np&RYHwfL*x2q+AS^75 zo;y{mnyqhq@!~~&eP$LGJ6l`Ijnk)BIsPjPAjNmq-Mt__o{c^}O!7oifHD1vmz$UJ%c z7{r-W{=#>&u)vSs)`!Agn3uQr`1LHXh(t|W@i-hd(B_^u*5U9Fqie1546zta_V$&c z8iFzWC?l*#?^IcgdXQ7Hg-+rSv{zR096h?5C>pGkXHKMe#~%)((9ltG6+Q?{Nb{)M z;~j1?>p=|iaJPX?xO}H70e^roUqDQ1Yx^}ZL2q`6MG|lg_7S!^WCfIJiXd|w6#Ewb z3_WU(hjDRxIXLPq@`Mf_9;uIquXGW23Uz~Zb!cDB*mQzzqN#4v_qMi=PUF}G0B4=w zzt7D!D(Gf>N2w+5MqYrDo!(<(%_~ghTT@f6MHRJR4E0FpC(*n0HmE{qsMz+~$pc}L z;5S=X%+yQuK{mm8gS%}x@G%%?izNgdAOgD_!~vcPj9)4{rmUeR$XuH2?Y6M24Y~n4GMa0ZZ060 z-ud$&DrhPYH`?0tX^HTD!&H9W=TU~}2Sy}pR?d#r#m2U}G;K*s#OhZMPRq*4!6whl z%nZP%1R|Tee_%#yio0c^MhoLy2@OqJ-Dl7cPLqTr4o63qT`;QiFOK zT{n!L&i??4k3ft%Es@lgAWVaaK6}OscedFMqVGGCiNY)0qO{#5?wRT7BVaWHUt|)6 zk00-DZoc;YI-QC>-Umzq;9St!*V%tDY_;7~DPfSNg z2d&Iah9ktBBw9N!j9)PP)zHxJ?wvFjS2A|MOTAZb-%6k!Ih8NtT#dPv;6#FyJ$tIy zyMOaxdQpa+1B6z`L+0XfJUl$;Yavd>B_$8ko(~OKk(z#q_xyj<{BI1j)CAa*HzQRL z8n~~)4`RSnIQSu|h*Qq{`;Q-oqiZ3I4!L-_yU!2T#STz{j-#pQ30}E+wXmQ7hR8}G z5Aqp%_7njm;--MOqBGzQDr^d%W6lM=UW6pv1cL?piJ|G!C%~amw-SJsCr?=MgRHF7 z9?+k0i_CtS@YZw>xhv>pOm6>-yT-{NxlTqX1Ovw|N5bJsmjo>l3v(i;T-484B)kd; zJtW^G9!3o9tL$vQl{tGfc?jJ8CdYACbTT|;IomQ_Bh=Gg8yDTH8|GB-_x(}g4!0l3 z2B3hBntG*9`FDL))yZVVki#v|reJgDes$g+rvcS@Wp?%!_vAyV<+;L*Ox$;vL^;|5 z0Cm9r+XHX2@#zRKftCT+Kf_wd z$Q?L9+0gWDm33$t^)6T`Y4#({F9+jh;GXkg3R&b&A*3hDyridSlQ_*ZPOu?%RyoF^IM0L?=~}ZI!4`n zvbPLs#)UMU_4RC2C^IuN&>NIkY_hiYc3Uf}yX?nYu&Wg|mi7@&r0H1O*nHeeFTr{i zXb9~cey$*MID9-4VVEcIwQe3h>a1RhO)Hd2wV_l5`l*|u$e(u;Z~Zn6v7E6Y5VIU$LK@D@7(DsaX%DU z4eNabg+P9(Kd7`+mW?eQABTwmj@{4XhRBcx|LXGc*~g;h{}F4@^Wg0QJHe8a%d+wV zBPHBuRM?c2kYJKzo4;`BQa@U}ub&@K<0{Y?NC$A#A8X0m+)h#Lb5wyk8DHz`)3gOp zznsG~(*b37hF3WYlA(ZhbUcoV61(()uxHO6=s?oDUTdb$&dvr!A0nrq_!we{RF{c~ zBO33OD|G2kcl~o6QTahPSwcX`aMbZPG?hVTX?RUgjy!)nZyCL%WbWrz79LJhdTB>^ z6&)Sj?%lhUQlRo*h0uc2LO~Z|j!hsQM@wL2XQw^nR#{oOV)F}}j3TJHA|QY_%Fx8* zZuKD0_k6zi_tw@h6<#p@cl#n#_w5XWU5oOLig@y5)w^qhd9^dbulX^CjP^r8Er)Iu7! zTw-anymRL9S=NXUHNJ)4dV$9Nnqts>d)^IPUbo#s0F(|T9+i+tkn=%uh{UuJhvM4_ zXefrdB_hJW&mCC$w|6aHvwzcHfrmKBx77FiYU7&PWaEP2H%r|Wx{t<2-XfFJ*RYd~ zqkM84-wluK8C<5M5^=qbfJ#$SKinB;Qb>W_3J#9LdFwqGSZdkZSy{Vj-{H6bWdb({ zCRUYAK8UZg^AuoW^4=(_nR|*vl~Q>SCxFpcuc%4ZA%qB=v-x=yap#u z1D8MF4;uEv!^QqLt?*AI4j_}q%*50`y#$F7aSk}|Fh^l%g(DvH^^)itq-0oG;W)vy zK~bJ(I7CHGCT;cUv6v+^N(4&)DSUl>P3MbG-4D3c&Ln}v&-YQcfq?AxlLDkM|jc6p-W;&Y;!b1I*=wzWllVUr9VLcA_a z1xS(r(zG%0ev+D+8t9XA_wN0SRP>y(*i%bDi;*m1WJJ+y{X@~UwADOqP$p=Yg+7Qn z!a;?UPNL7~BcOq#G76;&YOBQ2qbRwR&CO9KxUFn$iIiV(H}~=KVqcnzfwDs%g|2}L zZ)<6(%o~OD+WnXqEI61pfZBWXetaM>4(w8P8MoW2sc7JJot>TUDk#Xk2JcFX%Y-1+ zmX#$0;&A=1(K{8vm590)#pYX;ZYN0dOQZ$Y$t08-%EGQpErS(5tZwkS2(eSFo#}ol zCjb2TI5_RBQ`8&jh6W=U|95mW4yiNPgV2|-^rTq9Y`xSL_{#1a+z412CP;z z;4OszDCSJm+rGYowJjVAwN=}Y7M3#LS4F^Msu@Y|vr^-EhFNIFq@IrI25N~rhS(G- z3`mGjb%i9`lCD)$Scons&O)1^x00}tsL%rKrnz|agr_A@^D=eeu*>lm)skREaaEPfyRx$aqYUl130swh(~+p!FdwhTDye>*nV6wYK)= zks`Th^L;c4;q>Kg4h1ra4Jgy8k@BpPj*kaU849%}U-bUHBkU6?*O!}%W1B4JvjFIi zfC?1@gVvcdW5lg(NK`iNr9K7R>r`7QIxeEjsMvcw=b#;MGRAA2N}? zfff8x?~g0LWgz4f6X6kOX*3AD5E|_3Q`ol;E~~PFkcxrhiuX$}7mr!w4s)eB441J&3di5UmO?tdm;o#4Q9IcxEbcIq%y_ z%F0kJmwd}rc;VIpSus-wPaPJHz3k-Fha8`_HZnZi%;Bg&BBrJ9T!z2Q&AE{h#AAQ{ z0LePjUQ&kkaOluw2=D*GF&ABm6I6IB_m&T&QO2lO4LBpHF%|bh9-26oMJHnp28~vd zEJ_x-7=A(||1A0Ph>st`c}VeypP(ebq3G)Dtn|N%WPN`>s}D{u>|RLqp>69MiHDcrBVw zB<$hFV}s>B8~XKYp+!rfK3QQCPM_4(o~Jq)DCjqqw-FGB*WlmPJJbE%6I89Xwid>} z0#1vekmS*$q_8qz(!hHtA?UdHxFm*ontWrDyAn7wvYM#Q?weh4bO+&sO+L82xk6cH z=9oyu+Jo3wk6&N+$|U+jw8Q&C8D@JO$vycvph-K)d1qinh4{@AOZl2yO_%Qk@8R7U zmb6UxbSOnX6GZ7KYXtm}U?m9UqoboB!KKavWTjt}?-zY{4VhV^l+#;Qa=o~i)K^F; z%h^}-a0`J&#F{_=Mkd`0Ab0SLDvN7gUdwRTa8dI|OnGYFj~r%M`ol3eI$Dej7O8EC zi|-`dFlPaqfXqoVEEHIJaRZUC00vqjOhDXjC>BF5%itL+i<6r#Xo9&##<;|E_Xl!M z78a$F!UrC^|1(JPKgA3FFI)R1MNX`34Go%!n$;G0Q%eSq9g}yVawt(V;JlC5 zFp_3bYG4+P$sz2Dz3+-d>~`+jRb?>^q6(hB@~SXHPf*ISqsgbZqGA(?8naeY(_)gX zS6qA+HbUj4($P2EHM6CD=hLCh@bU9wrlS1g6~UM<$^I*&2KEUTETOG8eP;AvYs*2I!F6>G&@8 z!GoR5;nt`~ot<&f(U$i1qma6#<~5)^!IMc#3$7ccXA%AI<%@csZ>pXkiWLMwDD8-j ziLwYW(_mYmBRgEV0!1)n6-5c-D#U@cdfn!Z4)Ag$ImZI1$OFq|K@&hLal#AL{EdeNa0*laRoUB+|$TC0hut8Oa4C-inH1)%ZYKX6NSK z=I8rphHD_-;N^wV3rgS$6e1?J93L9QtvXMpsB3JDo%`d}D?zL@mFg-5PAjdTO_3MSXImFgyrl&FDbNcLA zd_>eEU7C8~z z&a!{6Ed-pctc;9N=u4i{ywSC#@V0<4u+8M;2;u4R! zVv-1y23dfg(@P&dK)QO_G76oKKtQTkzGsinH@F!Gn8U?r>%M+{o104o@rF7WqVky_ z5RpX9#lJ;;*eoPxOtb>1H$B<^4}=Q{zr`m0%QM!Pgn;n%FH?=37K_h-q~X8uC2SzT zY`P*uf`RgWH6}ZG)(~#Q9kE@I-))iC(c3F8D{I2d_+dXN3VJ(uaHXgta5Qr4PP9_&ED&7_;ZIeSBB&C5_7!Q42&REnykvGk0mi*B>4d6aZDob%DJ>Bf3%-Hu z?FCcQjo1nkazIpNs1AjTiXuCUiW%=18l#JCM)FkmcF0-_0W88;(_g*2W>gjHD}SOnxlZ1t4wg?%?y zF9lqMSHSEP9h`8)SmA!cMTI!n)zOi4@x6ZEtvL*2)jt+f&eoTbkU-=;O6^wlATASO z06v z4$%{&yR3zSjYjuG`Wi2)&g<79BbA>%@gcKRCsKiQ=orzQx8~5CM^MNKZeL_;n5c(m zhU;)gXBHPB#xT&+!^}cZUXb;*-YU&`wY`H?f(M!rgH%~aby6$77LLXi6 zI2FO_4rQGHRpU7nY9+b0Oc4zjay>U(bRMzy#=z7ex|#t z>*{!Zqh_K^f#Fjm%M*JPz}Ql0%)*ObMh|a02;|UZ0@d4zM&^gGhX^~ z0$>y_>)6DEq@yoXa-9r4Kt=E;M0|uXE4H%WN$!x+H3H>8Gu zRfnv6QbolC#`OEia!h{0r2?TN@%~WaF-SRe4>iEz%z#Fr*{&N<{rUYHiF*mwdaNaK z2*Mb<N}) z6ft6qd0MS#7=eHtVwBwmE)R^Mp2nI)jq(g96$X~52FZ|;=~&Cq7MSw%wfpxnG3h}!Zp$_X{Evez`KKbn@2Q?(fzjU^ zTec#5B)>cqjrQL7?xxo}^gzTjp8&|#J=Jp2qG zB&In_%~dj6QKbRw!5gvLA+&*+7Mqyr>tj}NRYyox9_6U4>>`vg@<6Cel$4>IekDM$ zCc=wo!B{*{E%7)F0VW$OtLJHHpx`Omo+Ay3C?Jm>F@w8WiIeoa2)1^7BV0doLkw}9 z;J&Y(CSp{G)C!=PzJWoVl{gk{WJC(+VArl_ygzVRgzDhrq?3)!i)K5-Zb0G@(m{|= zTi|HM1 z3<@d?TRCqxOkhG^MKMA22)@3&Jm*F;*l$wfNcxKx=!|=I?~Xt*gVY5@dJUtu7#M*| z3M6N4xg|;sc>ajv;E+o(n9>Rtl71y%@i5h1U&s%sytIbpCJmU)bHEz+J5N>^!?%Bb zB1Vt@1;L=c{tOo>r=q4NBS6*M98uWpL{Wv%EJ}-J7zQ8gt^FQA4>5jKL) zmfOIBIZbRczp>qcrCtFQne4rH4>3^>)E^At+X-9rVcoFYBTTt~h(JzU-x;HR-pnk= zsr{2^7LHwjF*V^5KGf93C^d2?+GY%R!)%x`)yQCNpEca&IBk1M(bZAWOs!Uy?*7NL)rfJZ6=Nja$d;r ztTjJa`b*qzpmpeAgw$Lgv4xI!cm1ey=!11c7Tk0cc zAh#38fA^S+iR2EJHQ!hfvq$P_yAEjj$|-N+2@x+|@Wa_b2`n(;*tIJtiBswRp818N z6pLf+k&PpmV*0ov_bz7Ce;*kJXirH=nL0KuPrGhUMHDSI>Fw;S9h^2jDYj!MnG8R3 zQdxN`fg5?cva%m_bz|qxertnxN@P&L^z2}aBb{O|_2%!V0G2sJ1D1)J2V&IC+%{4H zhPDu1XvEDV_*}6T?{vM&|ESgqwNvgY)t~WkUl0PC7;@;RPTY17$AMn(qC1t@Y$w4^ ze`?cN+1GasEd|U95Cx;nP)reF@OEvv`~%#=gNaS7PX3U_;U0O27~_6hU@|4(DKG8br#Mo*^R&CJZ4P;W9k6 zVCrzT{(F3F`!0UzW5<97FaVFio9wr5k$N6!;0OrVL`Oj3=;-PqSu%dIVN^5vURxEF z`J=(@Px78aEYKg1gGj;@{n5+J1a3LGb&!CWxj9*IGDM*f_JGd-!PHxd&mEp{nhx;{ z1#l21A9KVJ(Lg$gG}Dhx0OnI&U5(v-*tC?z2A;6<)o5GO2abK^awKsp7ia^QhKwGI zej-vidpJ1!pj`ruA>RR{<%wA~+=SiXRfHS3;PLJvG(bO$MnGD0zJ9&;tvzPm5`gdA z-Jt{SzgvoMGSJEL?j%F<<#eCKNKXtw!hfd@okV~R0=qDkw)W2;NKZpv+%K9Qu00m&>Zf(r`?-P)Ip8U+*P8Ib`_Sxjs!WbR2M zGcjoE%oAb`Exvm2blNw0t%1jYU3!9G1lCLMQo+2@LpB0@7CBqKuWxKwo1-{f)Z!9# zCOnXO_mJe>I}@MD-IPz(O73~4_)yf1)xDuu=#O@~zvTGcAs z>D_MhoUGhF{7wB#nt@&8Vx(4^Sx zGsxaE1e~hE_f%kH5o6e38&+=YA1b0P1Az~~nLmH}1UW<$W+SHRptC=>*Ecqf*UwC| zU-^%&0hk39L(5)E_xyS5_s7-JbmV-O?#o_xalyDTa^9)UXzdIu2UqQ>8y(=);$bMS zUVU+t>`p56UoSxlU~Ue@4fq3~<+PSo0G7 zFfOefqg2rM|NL=)*9US9>5bl^z$kgXobyI(FO5muh5{6arvmC2^2QGxskx(}rbdQ6H@LmJ{k@pLftJxmNU`wZetC$AOvKu-dW`-$;{r}o~MFT&U& z$bC-PLKpmfA?7pC@YK_ITWCyS4#7fXVq!x1$}%btl#y8hfk*c2z~4~&C#D!TKr=!F z0dnH9?*{NNIHUTn6E4LjkT0<}H=QPRPMkQQu72lAmDI6g7t&jzjvavB4DAnMFq}^s zdV0)-U}h5LAry3WdRxFbpb1`uaNZNB)N-iK2f0UXXk71^7V@1%KwfF~HIy5hsK2wuNLo9R=~j2ht=w zl9-j3L|G&7y9-T+X^Fq*y~|9U3XD!-(2VcRMFadA@3LNyc4pTF4EZ7x;V3dUHJ}_~ z4z1SFViyNkU)Ic2qFCAy%dB~N=*d97$cMw8FD&8F1_MbLY6Y=OCjn_XgqMa=kL9?P{Spf z`7aOeTHV-E`kb9vLNztYt%whD2I%iFfDkiI5*dsbxJM7Xe1+^MaV8S#uS`g;dabPp_)LDWaSqE z4ruxd+1WU8AmeK1@yS|Sk8*S07`H*^gtm#N4-`g$g;=9t$hH8DiHbsJbNsvIFmpI) zot?zj*VQPwsmX@yq!aBxg{A?vy;X;#Sn$gka zQk#ziSu=X7C-`3IW8r&5p49&WCx&KNYxM?F+?{8jhx>SY10wNjCj6e5Xc%x-`-Cy0q8Bd^jeTES43h;V0rv=FKcXyv{YdKJ z;bNn^SQ!~lbhRM*f^>Ul`z1+IlSFXU*vLrW4y)8v6JSI`LzaW9NF?ZFJk-O}j__&0 zFWEUck=BA}@T%DkRv?6Hu)AaI4bU|>q41l3B!}7sx46ud3+H&p4w&+=#+&V+;b&xJ zef{#qTh`8T|LHWHM9o+n2xN~~S-W6O=;;A{0M5$6vV{bPqlh<&QgZ`2zj@-s%B}L6 zP4ycUHCq%d3ycts0Ml^9jyVr%oZ}h-nP@WHaR2+1TErZ9&I| zXd}pUXLD^t0g8Qsxb+p~sK$&CJ&w}5f@jC8Em|s7-y6`jyo5rO2ky(J7 z82D16EQn(ugmfISU7%o5+I++cpw+^9!K9Rt&?EdFtO$lqhet*jXlZeyZ&&@q{NMcs zL#(Nmre;G;4I&nBIAC4q2UT8Lf*8uLm4rdZ&!TrtbIjn^oIHn;{M=paSnP!|Z<4ly zCp_S30KloRtYJoHwT~SV6a?lF7Z=BNV+rv>Ea%{zJ9qCw{K+-S26!{UL@6RYs6Kl` z@a&=Yc=Q*%{#wonMF?FdxDow%YXtQ;d+0|;v8(N=Q=z6geR-ClP{gi}v+V6X(c*?q zp_5@XTS3z=;-#+eB*n$chfzMc!m9QGLM31YNIt;T#C74xP1}P%A`_aTZHPxjp(N*b zniv`V85xOv{1^#i+}5I^A`@XJJcZ4D>`|)&hOVHgVM9aX)X&gEBOzsW(J!x@<#U9V z02_m(79fa$Dh74JBM<0bsP--yRiEe+j%_r409ghaX9UF z_Z|T?cm#bEPp1I|WM!4H`N{&orI=GAs)4XG8X=xVf{89nOCy>raHj-g+t~px{m#Pz zljRhVlzcSz=Gyh^zg&t@IVE$xA;N-(`?QUI#vBF8Fg#oShQ>SL zXwc)~4h#E*V-M{NL#Hs!321womrOgW)%9wt^cP0DC*VPfel?Ul$=uI%GSv8gwc- z^Bi^~c6~P-Ts$HK2ni<&6}uU_4bqON>p>#`hJaH*ZMacm@0(!|t$pf8?=K|`yQB%uns9{LcBKL}Bh#9Bw zs-2jaGu~QglnvwW+t@r`JZFF{x*Xc-{+-(oegNK>%9KJwWi3cm6|8xOeO67D5{jo*U;* z#Y(uufuSSA(EtCX|BEXzU8?}t;Ni$Yn0nLVP;gIp3t@x9?uiX2kxR_tR_cEnH*>>( ySzsa3Z+|jT;@Q#ZObq|EB6!&F$JyO_My6?e*S1PcI08I^nZeW5&t;ucLK6V$5)zOA diff --git a/exporter/cortexexporter/timeseries.png b/exporter/cortexexporter/timeseries.png new file mode 100644 index 0000000000000000000000000000000000000000..54f406ce53fbe7dc6f7c7cbbb8af70b056da3704 GIT binary patch literal 9001 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 nRgvbOOB} Date: Fri, 31 Jul 2020 11:54:31 -0500 Subject: [PATCH 3/7] change name to remote write exporter --- .../DESIGN.md | 61 +++++++++--------- .../cortex.png | Bin .../timeseries.png | Bin 3 files changed, 31 insertions(+), 30 deletions(-) rename exporter/{cortexexporter => remotewriteexporter}/DESIGN.md (64%) rename exporter/{cortexexporter => remotewriteexporter}/cortex.png (100%) rename exporter/{cortexexporter => remotewriteexporter}/timeseries.png (100%) diff --git a/exporter/cortexexporter/DESIGN.md b/exporter/remotewriteexporter/DESIGN.md similarity index 64% rename from exporter/cortexexporter/DESIGN.md rename to exporter/remotewriteexporter/DESIGN.md index 1ac2f5c586e..b55c19df6c0 100644 --- a/exporter/cortexexporter/DESIGN.md +++ b/exporter/remotewriteexporter/DESIGN.md @@ -1,5 +1,6 @@ -# **OpenTelemetry Collector Cortex Exporter Design** + +# **OpenTelemetry Collector Prometheus Remote Write Exporter Design** Authors: @huyan0, @danielbang907 @@ -7,33 +8,34 @@ Date: July 30, 2020 ## **1. Introduction** -Cortex is an open source, horizontally scalable, highly available, multi-tenant, long term storage for Prometheus. Cortex accepts data defined by the Prometheus Remote Write API. Our project is focused on developing an exporter for the OpenTelemetry Collector to a Cortex instance. +Prometheus can be integrated with remote storage systems that supports its remote write API. Existing remote storage integration support is included in [Cortex](https://cortexmetrics.io/docs/apis/), [influxDB](https://docs.influxdata.com/influxdb/v1.8/supported_protocols/prometheus/), and many [others](https://prometheus.io/docs/operating/integrations/#remote-endpoints-and-storage). -The following diagram is the architecture of Cortex. +The following diagram shows an example of Prometheus remote write API usage, with Cortex as a remote storage backend. ![Cortex Archietecture](https://raw.githubusercontent.com/open-o11y/opentelemetry-collector/design-doc/exporter/cortexexporter/cortex.png) +Our project is focused on developing an exporter for the OpenTelemetry Collector to any Prometheus remote storage backend. + ### **1.1 Remote Write API** -Cortex has a Remote Write API that accepts incoming metrics. This exporter should write metrics to a remote URL in a snappy-compressed, protocol buffer encoded HTTP request defined by the Remote Write API. Each request encodes multiple Cortex TimeSeries, which are composed of a set of labels and a collection of samples. Each label contains a name-value pair of strings, and each sample contains a timestamp-value number pair. +The Prometheus remote write exporter should write metrics to a remote URL in a snappy-compressed, protocol buffer encoded HTTP request defined by the Prometheus remote write API. Each request should encode multiple Prometheus remote write TimeSeries, which are composed of a set of labels and a collection of samples. Each label contains a name-value pair of strings, and each sample contains a timestamp-value number pair. -![Image of TimeSeries -](https://raw.githubusercontent.com/open-o11y/opentelemetry-collector/design-doc/exporter/cortexexporter/timeseries.png) +![Image of TimeSeries](https://raw.githubusercontent.com/open-o11y/opentelemetry-collector/design-doc/exporter/cortexexporter/timeseries.png) TimeSeries stores its metric name in its labels and does not describe metric types or start timestamps. To convert to TimeSeries data, buckets of a Histogram are broken down into individual TimeSeries with a bound label(`le`), and a similar process happens with quantiles in a Summary. -More details of Remote Write API can be found in Prometheus [documentation](https://cortexmetrics.io/docs/apis/) and Cortex [documentation](https://cortexmetrics.io/docs/apis/). +More details of Prometheus remote write API can be found in Prometheus [documentation](https://prometheus.io/docs/prometheus/latest/storage/#overview) and Cortex [documentation](https://cortexmetrics.io/docs/apis/). ### **1.2 Gaps and Assumptions** **Gap 1:** -Currently, metrics from the OpenTelemetry SDKs cannot be exported to Prometheus from the collector correctly ([#1255](https://github.com/open-telemetry/opentelemetry-collector/issues/1255)). This is because the SDKs send metrics to the collector via their OTLP exporter, which exports the delta value of cumulative counters. The same issue will arise for any cumulative backend service, such as Cortex. +Currently, metrics from the OpenTelemetry SDKs cannot be exported to Prometheus from the collector correctly ([#1255](https://github.com/open-telemetry/opentelemetry-collector/issues/1255)). This is because the SDKs send metrics to the collector via their OTLP exporter, which exports the delta value of cumulative counters. The same issue will arise for exporting to any Prometheus remote storage backend. To overcome this gap in the Collector pipeline, we had proposed 2 different solutions: -1. Add a [metric aggregation processor](https://github.com/open-telemetry/opentelemetry-collector/issues/1422) to the collector pipeline to aggregate delta values into cumulative values for cumulative backends like Cortex. This solution requires users to set up a collector agent next to each SDK to make sure delta values are aggregated correctly. -2. Require the OTLP exporters in SDKs to [send cumulative values for cumulative metric types to the Collector by default](https://github.com/open-telemetry/opentelemetry-specification/issues/731). Therefore, no aggregation of delta metric values is required in the Collector pipeline for Cortex/Prometheus to properly process the data. +1. Add a [metric aggregation processor](https://github.com/open-telemetry/opentelemetry-collector/issues/1422) to the collector pipeline to aggregate delta values into cumulative values for cumulative backends. This solution requires users to set up a collector agent next to each SDK to make sure delta values are aggregated correctly. +2. Require the OTLP exporters in SDKs to [send cumulative values for cumulative metric types to the Collector by default](https://github.com/open-telemetry/opentelemetry-specification/issues/731). Therefore, no aggregation of delta metric values is required in the Collector pipeline for Prometheus/storage backends to properly process the data. **Gap 2:** Another gap is that OTLP metric definition is still in development. This exporter will require refactoring as OTLP changes in the future. @@ -41,14 +43,14 @@ Another gap is that OTLP metric definition is still in development. This exporte **Assumptions:** Because of the gaps mentioned above, this project will convert from the current OTLP metrics and work under the assumption one of the above solutions will be implemented, and all incoming monotonic scalars/histogram/summary metrics should be cumulative or otherwise dropped. More details on the behavior of the exporter is in section 2.2. -## **2. Cortex Exporter** +## **2. Prometheus Remote Write Exporter** -The Cortex exporter should receive OTLP metrics, group data points by metric name and label set, convert each group to a TimeSeries, and send all TimeSeries to Cortex via HTTP. +The Prometheus remote write exporter should receive OTLP metrics, group data points by metric name and label set, convert each group to a TimeSeries, and send all TimeSeries to a storage backend via HTTP. ### **2.1 Receiving Metrics** -The Cortex exporter receives a MetricsData instance in its PushMetrics() function. MetricsData contains a collection of Metric instances. Each Metric instance contains a series of data points, and each data point has a set of labels associated with it. Since Cortex TimeSeries are identified by unique sets of labels, the exporter needs to group data points within each Metric instance by their label set, and convert each group to a TimeSeries. +The Prometheus remote write exporter receives a MetricsData instance in its PushMetrics() function. MetricsData contains a collection of Metric instances. Each Metric instance contains a series of data points, and each data point has a set of labels associated with it. Since Prometheus remote write TimeSeries are identified by unique sets of labels, the exporter needs to group data points within each Metric instance by their label set, and convert each group to a TimeSeries. -To group data points by label set, the Cortex exporter should create a map with each PushMetrics() call. The key of the map should represent a combination of the following information: +To group data points by label set, the exporter should create a map with each PushMetrics() call. The key of the map should represent a combination of the following information: * the metric type * the metric name @@ -57,11 +59,10 @@ To group data points by label set, the Cortex exporter should create a map with The exporter should create a signature string as map key by concatenating metric type, metric name, and label names and label values at each data point. To ensure correctness, the label set at each data point should be sorted by label key before generating the signature string. -An alternative key type is in the exiting label.Set implementation from the OpenTelemetry Go API. It provides a Distinct type that guarantees the result will equal the equivalent Distinct value of any label set with the same elements as this, -where sets are made unique by choosing the last value in the input for any given key. If we allocate a Go API's kv.KeyValue for every label of a data point, then a label.Set from the API can be created, and its Distinct value can be used as map key. +An alternative key type is in the exiting label.Set implementation from the OpenTelemetry Go API. It provides a Distinct type that guarantees the result will equal the equivalent Distinct value of any label set with the same elements as this, where sets are made unique by choosing the last value in the input for any given key. If we allocate a Go API's kv.KeyValue for every label of a data point, then a label.Set from the API can be created, and its Distinct value can be used as map key. -The value of the map should be Cortex TimeSeries, and each data point’s value and timestamp should be inserted to its corresponding TimeSeries in the map as a Sample, each metric’s label set and metric name should be combined and translated to a Cortex label set; a new TimeSeries should be created if the string signature is not in the map. +The value of the map should be Prometheus TimeSeries, and each data point’s value and timestamp should be inserted to its corresponding TimeSeries in the map as a Sample, each metric’s label set and metric name should be combined and translated to a Prometheus label set; a new TimeSeries should be created if the string signature is not in the map. Pseudocode: @@ -85,29 +86,29 @@ Pseudocode: ### **2.2 Mapping of OTLP Metrics to TimeSeries** -Each Cortex TimeSeries represents less semantic information than an OTLP metric. The temporality property of a OTLP metric is ignored in a TimeSeries because it is always considered as cumulative for monotonic types and histogram, and the type property of a OTLP metric is translated by mapping each metric to one or multiple TimeSeries. The following sections explain how to map each OTLP metric type to Cortex TimeSeries. +Each Prometheus remote write TimeSeries represents less semantic information than an OTLP metric. The temporality property of a OTLP metric is ignored in a TimeSeries because it is always considered as cumulative for monotonic types and histogram, and the type property of a OTLP metric is translated by mapping each metric to one or multiple TimeSeries. The following sections explain how to map each OTLP metric type to Prometheus remote write TimeSeries. **INT64, MONOTONIC_INT64, DOUBLE, MONOTONIC_DOUBLE** -Each unique label set within metrics of these types can be converted to exactly one TimeSeries. From the perspective of Prometheus Client types, INT64 and DOUBLE correspond to gauge metrics, and MONOTONIC types correspond to counter metrics. In both cases, data points will be exported directly without aggregation. Any metric of the monotonic types that is not cumulative should be dropped; non-monotonic scalar types are assumed to represent gauge values, thus its temporality is not checked. Monotonic types need to have a `_total` suffix in its metric name when exporting; this is a requirement of [Prometheus](https://www.slideshare.net/brianbrazil/openmetrics-what-does-it-mean-for-you-promcon-2019-munich) (slide 11). +Each unique label set within metrics of these types can be converted to exactly one TimeSeries. From the perspective of Prometheus client types, INT64 and DOUBLE correspond to gauge metrics, and MONOTONIC types correspond to counter metrics. In both cases, data points will be exported directly without aggregation. Any metric of the monotonic types that is not cumulative should be dropped; non-monotonic scalar types are assumed to represent gauge values, thus its temporality is not checked. Monotonic types need to have a `_total` suffix in its metric name when exporting; this is a requirement of [Prometheus](https://www.slideshare.net/brianbrazil/openmetrics-what-does-it-mean-for-you-promcon-2019-munich) (slide 11). **HISTOGRAM** -Each histogram data point can be converted to 2 + n + 1 Cortex TimeSeries: +Each histogram data point can be converted to 2 + n + 1 Prometheus remote write TimeSeries: * 1 *TimeSeries* representing metric_name_count contains HistogramDataPoint.count * 1 *TimeSeries* representing metric_name_sum contains HistogramDataPoint.sum * n *TimeSeries* each representing metric_name_bucket{le=“upperbound”} contain the count of each bucket defined by the bounds of the data point * 1 *TimeSeries* representing metric_name_bucket{le=“+Inf”} contains counts for the bucket with infinity as upper bound; its value is equivalent to metric_name_count. -Cortex bucket values are cumulative, meaning the count of each bucket should contain counts from buckets with lower bounds. In addition, Exemplars from a histogram data point are ignored. When adding a bucket of the histogram data point to the map, the string signature should also contain a `le` label that indicates the bound value. This label should also be exported. Any histogram metric that is not cumulative should be dropped. +Prometheus bucket values are cumulative, meaning the count of each bucket should contain counts from buckets with lower bounds. In addition, Exemplars from a histogram data point are ignored. When adding a bucket of the histogram data point to the map, the string signature should also contain a `le` label that indicates the bound value. This label should also be exported. Any histogram metric that is not cumulative should be dropped. **SUMMARY** -Each summary data point can be converted to 2 + n Cortex TimeSeries: +Each summary data point can be converted to 2 + n Prometheus remote write TimeSeries: * 1 *TimeSeries* representing metric_name_count contains SummaryDataPoint.count * 1 *TimeSeries* representing metric_name_sum contains SummaryDataPoint.sum @@ -117,7 +118,7 @@ When adding a quantile of the summary data point to the map, the string signatur ### **2.3 Exporting Metrics** -The Cortex exporter should call proto.Marshal() to convert multiple TimeSeries to a byte array. Then, the exporter should send the byte array to Cortex in a HTTP request. +The Prometheus remote write exporter should call proto.Marshal() to convert multiple TimeSeries to a byte array. Then, the exporter should send the byte array to Prometheus remote storage in a HTTP request. Authentication credentials should be added to each request before sending to the backend. Basic auth and bearer token headers can be added using Golang http.Client’s default configuration options. Other authentication headers can be added by implementing a client interceptor. @@ -146,7 +147,7 @@ Pseudocode: This struct is based on an inputted YAML file at the beginning of the pipeline and defines the configurations for an Exporter build. Examples of configuration parameters are HTTP endpoint, compression type, backend program, etc. -Converting YAML to a Go struct is done by the Collector, using [the Viper package](https://github.com/spf13/viper), which is an open-source library that seamlessly converts inputted YAML files into a usable, appropriate Config struct. +Converting YAML to a Go struct is done by the Collector, using [_the Viper package_](https://github.com/spf13/viper), which is an open-source library that seamlessly converts inputted YAML files into a usable, appropriate Config struct. An example of the exporter section of the Collector config.yml YAML file can be seen below: @@ -154,7 +155,7 @@ An example of the exporter section of the Collector config.yml YAML file can be ... exporters: - cortex: + prometheus_remote_write: http_endpoint: # Prefix to metric name namespace: @@ -219,12 +220,12 @@ This method will use the NewFactory method within the `exporterhelper` package t createDefaultConfig -This method creates the default configuration for Cortex exporter. +This method creates the default configuration for Prometheus remote write exporter. createMetricsExporter -This method constructs a new http.Client with interceptors that add headers to any request it sends. Then, this method initializes a new Cortex exporter with the http.Client. This method constructs a collector Cortex exporter with the created SDK exporter +This method constructs a new http.Client with interceptors that add headers to any request it sends. Then, this method initializes a new Prometheus remote write exporter exporter with the http.Client. This method constructs a collector Prometheus remote write exporter exporter with the created SDK exporter @@ -232,7 +233,7 @@ This method constructs a new http.Client with interceptors that add headers to a ### **4.1 Concurrency** -The Cortex exporter should be thread-safe; In this design, the only resource shared across goroutines is the http.Client from the Golang library. It is thread-safe, thus, our code is thread-safe. +The Prometheus remote write exporter should be thread-safe; In this design, the only resource shared across goroutines is the http.Client from the Golang library. It is thread-safe, thus, our code is thread-safe. ### **4.2 Shutdown Behavior** @@ -268,7 +269,7 @@ Users should be able to pass in a time for the each http request as part of the ### **4.4 Error Behavior** -The PushMetricsData() function should return the number of dropped metrics. Any MONOTONIC and HISTOGRAM metrics that are not cumulative should be dropped. This can be done by checking the temporality of each received metric. Any error should be returned to the caller, and the error message should be descriptive. +The PushMetricsData() function should return the number of dropped metrics. Any monotonic and histogram metrics that are not cumulative should be dropped. This can be done by checking the temporality of each received metric. Any error should be returned to the caller, and the error message should be descriptive. @@ -279,7 +280,7 @@ We will follow test-driven development practices while completing this project. ## **Request for Feedback** -We'd like to get some feedback on whether we made the appropriate assumptions in [this](#12-gaps-and-assumptions) section, and appreciate more comments, updates, and suggestions on the topic. +We'd like to get some feedback on whether we made the appropriate assumptions in [this](#1.2-gaps-and-ssumptions) section, and appreciate more comments, updates , and suggestions on the topic. Please let us know if there are any revisions, technical or informational, necessary for this document. Thank you! diff --git a/exporter/cortexexporter/cortex.png b/exporter/remotewriteexporter/cortex.png similarity index 100% rename from exporter/cortexexporter/cortex.png rename to exporter/remotewriteexporter/cortex.png diff --git a/exporter/cortexexporter/timeseries.png b/exporter/remotewriteexporter/timeseries.png similarity index 100% rename from exporter/cortexexporter/timeseries.png rename to exporter/remotewriteexporter/timeseries.png From 7b2ad3f0554e77636305dcb355d8dcf816e14591 Mon Sep 17 00:00:00 2001 From: Yang Hu Date: Wed, 5 Aug 2020 20:10:11 -0500 Subject: [PATCH 4/7] change name to Prometheus Remote Write/Cortex Exporter --- exporter/remotewriteexporter/DESIGN.md | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/exporter/remotewriteexporter/DESIGN.md b/exporter/remotewriteexporter/DESIGN.md index b55c19df6c0..502aa662ff1 100644 --- a/exporter/remotewriteexporter/DESIGN.md +++ b/exporter/remotewriteexporter/DESIGN.md @@ -1,6 +1,6 @@ -# **OpenTelemetry Collector Prometheus Remote Write Exporter Design** +# **OpenTelemetry Collector Prometheus Remote Write/Cortex Exporter Design** Authors: @huyan0, @danielbang907 @@ -10,7 +10,7 @@ Date: July 30, 2020 Prometheus can be integrated with remote storage systems that supports its remote write API. Existing remote storage integration support is included in [Cortex](https://cortexmetrics.io/docs/apis/), [influxDB](https://docs.influxdata.com/influxdb/v1.8/supported_protocols/prometheus/), and many [others](https://prometheus.io/docs/operating/integrations/#remote-endpoints-and-storage). -The following diagram shows an example of Prometheus remote write API usage, with Cortex as a remote storage backend. +The following diagram shows an example of Prometheus remote write API usage, with Cortex,n open source, horizontally scalable, highly available, multi-tenant, long term storage, as a remote storage backend. ![Cortex Archietecture](https://raw.githubusercontent.com/open-o11y/opentelemetry-collector/design-doc/exporter/cortexexporter/cortex.png) @@ -18,7 +18,7 @@ Our project is focused on developing an exporter for the OpenTelemetry Collector ### **1.1 Remote Write API** -The Prometheus remote write exporter should write metrics to a remote URL in a snappy-compressed, protocol buffer encoded HTTP request defined by the Prometheus remote write API. Each request should encode multiple Prometheus remote write TimeSeries, which are composed of a set of labels and a collection of samples. Each label contains a name-value pair of strings, and each sample contains a timestamp-value number pair. +The Prometheus remote write/Cortex exporter should write metrics to a remote URL in a snappy-compressed, [protocol buffer](https://github.com/prometheus/prometheus/blob/master/prompb/remote.proto#L22) encoded HTTP request defined by the Prometheus remote write API. Each request should encode multiple Prometheus remote write TimeSeries, which are composed of a set of labels and a collection of samples. Each label contains a name-value pair of strings, and each sample contains a timestamp-value number pair. ![Image of TimeSeries](https://raw.githubusercontent.com/open-o11y/opentelemetry-collector/design-doc/exporter/cortexexporter/timeseries.png) @@ -43,12 +43,12 @@ Another gap is that OTLP metric definition is still in development. This exporte **Assumptions:** Because of the gaps mentioned above, this project will convert from the current OTLP metrics and work under the assumption one of the above solutions will be implemented, and all incoming monotonic scalars/histogram/summary metrics should be cumulative or otherwise dropped. More details on the behavior of the exporter is in section 2.2. -## **2. Prometheus Remote Write Exporter** +## **2. Prometheus Remote Write/Cortex Exporter** -The Prometheus remote write exporter should receive OTLP metrics, group data points by metric name and label set, convert each group to a TimeSeries, and send all TimeSeries to a storage backend via HTTP. +The Prometheus remote write/Cortex exporter should receive OTLP metrics, group data points by metric name and label set, convert each group to a TimeSeries, and send all TimeSeries to a storage backend via HTTP. ### **2.1 Receiving Metrics** -The Prometheus remote write exporter receives a MetricsData instance in its PushMetrics() function. MetricsData contains a collection of Metric instances. Each Metric instance contains a series of data points, and each data point has a set of labels associated with it. Since Prometheus remote write TimeSeries are identified by unique sets of labels, the exporter needs to group data points within each Metric instance by their label set, and convert each group to a TimeSeries. +The Prometheus remote write/Cortex exporter receives a MetricsData instance in its PushMetrics() function. MetricsData contains a collection of Metric instances. Each Metric instance contains a series of data points, and each data point has a set of labels associated with it. Since Prometheus remote write TimeSeries are identified by unique sets of labels, the exporter needs to group data points within each Metric instance by their label set, and convert each group to a TimeSeries. To group data points by label set, the exporter should create a map with each PushMetrics() call. The key of the map should represent a combination of the following information: @@ -118,7 +118,7 @@ When adding a quantile of the summary data point to the map, the string signatur ### **2.3 Exporting Metrics** -The Prometheus remote write exporter should call proto.Marshal() to convert multiple TimeSeries to a byte array. Then, the exporter should send the byte array to Prometheus remote storage in a HTTP request. +The Prometheus remote write/Cortex exporter should call proto.Marshal() to convert multiple TimeSeries to a byte array. Then, the exporter should send the byte array to Prometheus remote storage in a HTTP request. Authentication credentials should be added to each request before sending to the backend. Basic auth and bearer token headers can be added using Golang http.Client’s default configuration options. Other authentication headers can be added by implementing a client interceptor. @@ -220,12 +220,12 @@ This method will use the NewFactory method within the `exporterhelper` package t createDefaultConfig -This method creates the default configuration for Prometheus remote write exporter. +This method creates the default configuration for Prometheus remote write/Cortex exporter. createMetricsExporter -This method constructs a new http.Client with interceptors that add headers to any request it sends. Then, this method initializes a new Prometheus remote write exporter exporter with the http.Client. This method constructs a collector Prometheus remote write exporter exporter with the created SDK exporter +This method constructs a new http.Client with interceptors that add headers to any request it sends. Then, this method initializes a new Prometheus remote write exporter/Cortex exporter with the http.Client. This method constructs a collector Prometheus remote write/Cortex exporter exporter with the created SDK exporter @@ -233,7 +233,7 @@ This method constructs a new http.Client with interceptors that add headers to a ### **4.1 Concurrency** -The Prometheus remote write exporter should be thread-safe; In this design, the only resource shared across goroutines is the http.Client from the Golang library. It is thread-safe, thus, our code is thread-safe. +The Prometheus remote write/Cortex exporter should be thread-safe; In this design, the only resource shared across goroutines is the http.Client from the Golang library. It is thread-safe, thus, our code is thread-safe. ### **4.2 Shutdown Behavior** From 039bbbdab1bb83db522f291b0735d3af733f3a2f Mon Sep 17 00:00:00 2001 From: Yang Hu Date: Tue, 11 Aug 2020 09:46:16 -0500 Subject: [PATCH 5/7] fix dead links --- .../DESIGN.md | 4 ++-- .../img}/cortex.png | Bin .../img}/timeseries.png | Bin 3 files changed, 2 insertions(+), 2 deletions(-) rename exporter/{remotewriteexporter => prometheusremotewriteexporter}/DESIGN.md (96%) rename exporter/{remotewriteexporter => prometheusremotewriteexporter/img}/cortex.png (100%) rename exporter/{remotewriteexporter => prometheusremotewriteexporter/img}/timeseries.png (100%) diff --git a/exporter/remotewriteexporter/DESIGN.md b/exporter/prometheusremotewriteexporter/DESIGN.md similarity index 96% rename from exporter/remotewriteexporter/DESIGN.md rename to exporter/prometheusremotewriteexporter/DESIGN.md index 502aa662ff1..f2af4aaf930 100644 --- a/exporter/remotewriteexporter/DESIGN.md +++ b/exporter/prometheusremotewriteexporter/DESIGN.md @@ -12,7 +12,7 @@ Prometheus can be integrated with remote storage systems that supports its remot The following diagram shows an example of Prometheus remote write API usage, with Cortex,n open source, horizontally scalable, highly available, multi-tenant, long term storage, as a remote storage backend. -![Cortex Archietecture](https://raw.githubusercontent.com/open-o11y/opentelemetry-collector/design-doc/exporter/cortexexporter/cortex.png) +![Cortex Archietecture](./img/cortex.png) Our project is focused on developing an exporter for the OpenTelemetry Collector to any Prometheus remote storage backend. @@ -20,7 +20,7 @@ Our project is focused on developing an exporter for the OpenTelemetry Collector The Prometheus remote write/Cortex exporter should write metrics to a remote URL in a snappy-compressed, [protocol buffer](https://github.com/prometheus/prometheus/blob/master/prompb/remote.proto#L22) encoded HTTP request defined by the Prometheus remote write API. Each request should encode multiple Prometheus remote write TimeSeries, which are composed of a set of labels and a collection of samples. Each label contains a name-value pair of strings, and each sample contains a timestamp-value number pair. -![Image of TimeSeries](https://raw.githubusercontent.com/open-o11y/opentelemetry-collector/design-doc/exporter/cortexexporter/timeseries.png) +![Image of TimeSeries](./img/timeseries.png) TimeSeries stores its metric name in its labels and does not describe metric types or start timestamps. To convert to TimeSeries data, buckets of a Histogram are broken down into individual TimeSeries with a bound label(`le`), and a similar process happens with quantiles in a Summary. diff --git a/exporter/remotewriteexporter/cortex.png b/exporter/prometheusremotewriteexporter/img/cortex.png similarity index 100% rename from exporter/remotewriteexporter/cortex.png rename to exporter/prometheusremotewriteexporter/img/cortex.png diff --git a/exporter/remotewriteexporter/timeseries.png b/exporter/prometheusremotewriteexporter/img/timeseries.png similarity index 100% rename from exporter/remotewriteexporter/timeseries.png rename to exporter/prometheusremotewriteexporter/img/timeseries.png From dd390eabc852d6dde8f3e3792775bd0388dba87f Mon Sep 17 00:00:00 2001 From: Yang Hu Date: Tue, 11 Aug 2020 09:48:32 -0500 Subject: [PATCH 6/7] Update DESIGN.md --- exporter/prometheusremotewriteexporter/DESIGN.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/exporter/prometheusremotewriteexporter/DESIGN.md b/exporter/prometheusremotewriteexporter/DESIGN.md index f2af4aaf930..8a176ee397f 100644 --- a/exporter/prometheusremotewriteexporter/DESIGN.md +++ b/exporter/prometheusremotewriteexporter/DESIGN.md @@ -12,7 +12,7 @@ Prometheus can be integrated with remote storage systems that supports its remot The following diagram shows an example of Prometheus remote write API usage, with Cortex,n open source, horizontally scalable, highly available, multi-tenant, long term storage, as a remote storage backend. -![Cortex Archietecture](./img/cortex.png) +![Cortex Archietecture](/img/cortex.png) Our project is focused on developing an exporter for the OpenTelemetry Collector to any Prometheus remote storage backend. @@ -20,7 +20,7 @@ Our project is focused on developing an exporter for the OpenTelemetry Collector The Prometheus remote write/Cortex exporter should write metrics to a remote URL in a snappy-compressed, [protocol buffer](https://github.com/prometheus/prometheus/blob/master/prompb/remote.proto#L22) encoded HTTP request defined by the Prometheus remote write API. Each request should encode multiple Prometheus remote write TimeSeries, which are composed of a set of labels and a collection of samples. Each label contains a name-value pair of strings, and each sample contains a timestamp-value number pair. -![Image of TimeSeries](./img/timeseries.png) +![Image of TimeSeries](/img/timeseries.png) TimeSeries stores its metric name in its labels and does not describe metric types or start timestamps. To convert to TimeSeries data, buckets of a Histogram are broken down into individual TimeSeries with a bound label(`le`), and a similar process happens with quantiles in a Summary. From 7f8529affb101c6f73aeb51ae85f8330595cd027 Mon Sep 17 00:00:00 2001 From: Yang Hu Date: Tue, 11 Aug 2020 09:49:19 -0500 Subject: [PATCH 7/7] Update DESIGN.md --- exporter/prometheusremotewriteexporter/DESIGN.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/exporter/prometheusremotewriteexporter/DESIGN.md b/exporter/prometheusremotewriteexporter/DESIGN.md index 8a176ee397f..f2af4aaf930 100644 --- a/exporter/prometheusremotewriteexporter/DESIGN.md +++ b/exporter/prometheusremotewriteexporter/DESIGN.md @@ -12,7 +12,7 @@ Prometheus can be integrated with remote storage systems that supports its remot The following diagram shows an example of Prometheus remote write API usage, with Cortex,n open source, horizontally scalable, highly available, multi-tenant, long term storage, as a remote storage backend. -![Cortex Archietecture](/img/cortex.png) +![Cortex Archietecture](./img/cortex.png) Our project is focused on developing an exporter for the OpenTelemetry Collector to any Prometheus remote storage backend. @@ -20,7 +20,7 @@ Our project is focused on developing an exporter for the OpenTelemetry Collector The Prometheus remote write/Cortex exporter should write metrics to a remote URL in a snappy-compressed, [protocol buffer](https://github.com/prometheus/prometheus/blob/master/prompb/remote.proto#L22) encoded HTTP request defined by the Prometheus remote write API. Each request should encode multiple Prometheus remote write TimeSeries, which are composed of a set of labels and a collection of samples. Each label contains a name-value pair of strings, and each sample contains a timestamp-value number pair. -![Image of TimeSeries](/img/timeseries.png) +![Image of TimeSeries](./img/timeseries.png) TimeSeries stores its metric name in its labels and does not describe metric types or start timestamps. To convert to TimeSeries data, buckets of a Histogram are broken down into individual TimeSeries with a bound label(`le`), and a similar process happens with quantiles in a Summary.