From a18f5205e6320bf3d84f4752ec53427268b38760 Mon Sep 17 00:00:00 2001 From: sairaj18 Date: Mon, 8 Dec 2025 21:28:55 +0530 Subject: [PATCH 01/32] feat(nginx): document steps to configure and monitor nginx using otel collector --- src/install/config/nginx-otel.yaml | 224 +++ src/install/nginx-otel/appInfo.mdx | 7 + .../nginx-otel/collector/environment.mdx | 44 + .../nginx-otel/host/choose-install.mdx | 7 + .../host/configure-collector-opensource.mdx | 66 + .../host/configure-collector-plus.mdx | 72 + .../nginx-otel/host/configure-collector.mdx | 6 + .../host/enable-status-opensource.mdx | 39 + .../nginx-otel/host/enable-status-plus.mdx | 30 + .../nginx-otel/host/enable-status-select.mdx | 5 + src/install/nginx-otel/host/enable-status.mdx | 6 + .../nginx-otel/host/install-collector-apt.mdx | 18 + .../nginx-otel/host/install-collector-yum.mdx | 18 + .../nginx-otel/host/install-collector.mdx | 6 + src/install/nginx-otel/host/license.mdx | 9 + .../nginx-otel/host/metrics-opensource.mdx | 108 ++ src/install/nginx-otel/host/metrics-plus.mdx | 386 +++++ src/install/nginx-otel/host/prerequisites.mdx | 11 + src/install/nginx-otel/host/verify.mdx | 13 + src/install/nginx-otel/intro.mdx | 8 + src/install/nginx-otel/kubernetes/deploy.mdx | 27 + src/install/nginx-otel/kubernetes/license.mdx | 6 + .../nginx-otel/kubernetes/prepare-values.mdx | 139 ++ .../nginx-otel/kubernetes/prerequisites.mdx | 11 + src/install/nginx-otel/kubernetes/secret.mdx | 37 + .../nginx-otel/kubernetes/status-endpoint.mdx | 22 + src/install/nginx-otel/kubernetes/verify.mdx | 29 + .../nginx-otel/logs/forwarding-opensource.mdx | 60 + .../nginx-otel/logs/forwarding-plus.mdx | 62 + src/install/nginx-otel/logs/forwarding.mdx | 6 + src/install/nginx-otel/whatsNext.mdx | 1426 +++++++++++++++++ src/nav/infrastructure.yml | 8 +- 32 files changed, 2914 insertions(+), 2 deletions(-) create mode 100644 src/install/config/nginx-otel.yaml create mode 100644 src/install/nginx-otel/appInfo.mdx create mode 100644 src/install/nginx-otel/collector/environment.mdx create mode 100644 src/install/nginx-otel/host/choose-install.mdx create mode 100644 src/install/nginx-otel/host/configure-collector-opensource.mdx create mode 100644 src/install/nginx-otel/host/configure-collector-plus.mdx create mode 100644 src/install/nginx-otel/host/configure-collector.mdx create mode 100644 src/install/nginx-otel/host/enable-status-opensource.mdx create mode 100644 src/install/nginx-otel/host/enable-status-plus.mdx create mode 100644 src/install/nginx-otel/host/enable-status-select.mdx create mode 100644 src/install/nginx-otel/host/enable-status.mdx create mode 100644 src/install/nginx-otel/host/install-collector-apt.mdx create mode 100644 src/install/nginx-otel/host/install-collector-yum.mdx create mode 100644 src/install/nginx-otel/host/install-collector.mdx create mode 100644 src/install/nginx-otel/host/license.mdx create mode 100644 src/install/nginx-otel/host/metrics-opensource.mdx create mode 100644 src/install/nginx-otel/host/metrics-plus.mdx create mode 100644 src/install/nginx-otel/host/prerequisites.mdx create mode 100644 src/install/nginx-otel/host/verify.mdx create mode 100644 src/install/nginx-otel/intro.mdx create mode 100644 src/install/nginx-otel/kubernetes/deploy.mdx create mode 100644 src/install/nginx-otel/kubernetes/license.mdx create mode 100644 src/install/nginx-otel/kubernetes/prepare-values.mdx create mode 100644 src/install/nginx-otel/kubernetes/prerequisites.mdx create mode 100644 src/install/nginx-otel/kubernetes/secret.mdx create mode 100644 src/install/nginx-otel/kubernetes/status-endpoint.mdx create mode 100644 src/install/nginx-otel/kubernetes/verify.mdx create mode 100644 src/install/nginx-otel/logs/forwarding-opensource.mdx create mode 100644 src/install/nginx-otel/logs/forwarding-plus.mdx create mode 100644 src/install/nginx-otel/logs/forwarding.mdx create mode 100644 src/install/nginx-otel/whatsNext.mdx diff --git a/src/install/config/nginx-otel.yaml b/src/install/config/nginx-otel.yaml new file mode 100644 index 00000000000..5c952057ec8 --- /dev/null +++ b/src/install/config/nginx-otel.yaml @@ -0,0 +1,224 @@ +agentName: nginx-otel +agentType: integration +title: 'NGINX OpenTelemetry collector' +metaDescription: 'Install the NGINX OpenTelemetry collector' +introFilePath: 'src/install/nginx-otel/intro.mdx' +appInfo: + - optionType: dropdown1 + label: '' + placeholder: 'Select your environment' + options: + - value: 'host' + displayName: 'Linux' + logo: 'linux' + - value: 'kubernetes' + displayName: 'Kubernetes' + logo: 'k8' + - optionType: deployment + label: '' + placeholder: "Choose one:" + options: + - value: "apt" + displayName: "Install using Apt (Debian, Ubuntu)" + recommendedGuided: true + - value: "yum" + displayName: "Install using Yum (Amazon Linux, CentOS, RHEL)" + logo: 'linux' + recommendedGuided: true + - optionType: nginx_type + label: '' + placeholder: "Choose one:" + options: + - value: "opensource" + displayName: "NGINX Open Source" + recommendedGuided: true + - value: "plus" + displayName: "NGINX Plus" + recommendedGuided: true + +steps: + - filePath: 'src/install/nginx-otel/appInfo.mdx' + - filePath: 'src/install/nginx-otel/host/choose-install.mdx' + overrides: + - isConditionalStep: true + selectedOptions: + - optionType: dropdown1 + options: + - value: 'host' + - filePath: 'src/install/nginx-otel/host/prerequisites.mdx' + overrides: + - isConditionalStep: true + selectedOptions: + - optionType: dropdown1 + options: + - value: 'host' + - filePath: 'src/install/nginx-otel/host/enable-status-select.mdx' + overrides: + - isConditionalStep: true + selectedOptions: + - optionType: dropdown1 + options: + - value: 'host' + - filePath: 'src/install/nginx-otel/host/enable-status.mdx' + overrides: + - filePath: 'src/install/nginx-otel/host/enable-status-opensource.mdx' + selectedOptions: + - optionType: dropdown1 + options: + - value: 'host' + - optionType: nginx_type + options: + - value: 'opensource' + - filePath: 'src/install/nginx-otel/host/enable-status-plus.mdx' + selectedOptions: + - optionType: dropdown1 + options: + - value: 'host' + - optionType: nginx_type + options: + - value: 'plus' + - isConditionalStep: true + selectedOptions: + - optionType: dropdown1 + options: + - value: 'host' + - filePath: 'src/install/nginx-otel/host/license.mdx' + overrides: + - isConditionalStep: true + selectedOptions: + - optionType: dropdown1 + options: + - value: 'host' + - filePath: 'src/install/nginx-otel/host/install-collector.mdx' + overrides: + - filePath: 'src/install/nginx-otel/host/install-collector-apt.mdx' + selectedOptions: + - optionType: dropdown1 + options: + - value: 'host' + - optionType: deployment + options: + - value: 'apt' + - filePath: 'src/install/nginx-otel/host/install-collector-yum.mdx' + selectedOptions: + - optionType: dropdown1 + options: + - value: 'host' + - optionType: deployment + options: + - value: 'yum' + - isConditionalStep: true + selectedOptions: + - optionType: dropdown1 + options: + - value: 'host' + - filePath: 'src/install/nginx-otel/host/configure-collector.mdx' + overrides: + - filePath: 'src/install/nginx-otel/host/configure-collector-opensource.mdx' + selectedOptions: + - optionType: dropdown1 + options: + - value: 'host' + - optionType: nginx_type + options: + - value: 'opensource' + - filePath: 'src/install/nginx-otel/host/configure-collector-plus.mdx' + selectedOptions: + - optionType: dropdown1 + options: + - value: 'host' + - optionType: nginx_type + options: + - value: 'plus' + - isConditionalStep: true + selectedOptions: + - optionType: dropdown1 + options: + - value: 'host' + - filePath: 'src/install/nginx-otel/collector/environment.mdx' + overrides: + - isConditionalStep: true + selectedOptions: + - optionType: dropdown1 + options: + - value: 'host' + - filePath: 'src/install/nginx-otel/logs/forwarding.mdx' + overrides: + - filePath: 'src/install/nginx-otel/logs/forwarding-opensource.mdx' + selectedOptions: + - optionType: dropdown1 + options: + - value: 'host' + - optionType: nginx_type + options: + - value: 'opensource' + - filePath: 'src/install/nginx-otel/logs/forwarding-plus.mdx' + selectedOptions: + - optionType: dropdown1 + options: + - value: 'host' + - optionType: nginx_type + options: + - value: 'plus' + - isConditionalStep: true + selectedOptions: + - optionType: dropdown1 + options: + - value: 'host' + - filePath: 'src/install/nginx-otel/host/verify.mdx' + overrides: + - isConditionalStep: true + selectedOptions: + - optionType: dropdown1 + options: + - value: 'host' + - filePath: 'src/install/nginx-otel/kubernetes/prerequisites.mdx' + overrides: + - isConditionalStep: true + selectedOptions: + - optionType: dropdown1 + options: + - value: 'kubernetes' + - filePath: 'src/install/nginx-otel/kubernetes/status-endpoint.mdx' + overrides: + - isConditionalStep: true + selectedOptions: + - optionType: dropdown1 + options: + - value: 'kubernetes' + - filePath: 'src/install/nginx-otel/kubernetes/license.mdx' + overrides: + - isConditionalStep: true + selectedOptions: + - optionType: dropdown1 + options: + - value: 'kubernetes' + - filePath: 'src/install/nginx-otel/kubernetes/prepare-values.mdx' + overrides: + - isConditionalStep: true + selectedOptions: + - optionType: dropdown1 + options: + - value: 'kubernetes' + - filePath: 'src/install/nginx-otel/kubernetes/secret.mdx' + overrides: + - isConditionalStep: true + selectedOptions: + - optionType: dropdown1 + options: + - value: 'kubernetes' + - filePath: 'src/install/nginx-otel/kubernetes/deploy.mdx' + overrides: + - isConditionalStep: true + selectedOptions: + - optionType: dropdown1 + options: + - value: 'kubernetes' + - filePath: 'src/install/nginx-otel/kubernetes/verify.mdx' + overrides: + - isConditionalStep: true + selectedOptions: + - optionType: dropdown1 + options: + - value: 'kubernetes' +whatsNextFilePath: 'src/install/nginx-otel/whatsNext.mdx' diff --git a/src/install/nginx-otel/appInfo.mdx b/src/install/nginx-otel/appInfo.mdx new file mode 100644 index 00000000000..7ee435867be --- /dev/null +++ b/src/install/nginx-otel/appInfo.mdx @@ -0,0 +1,7 @@ +--- +componentType: appInfoConfigOption +optionType: dropdown1 +headingText: Choose your deployment +--- + +Use this page to set up the OpenTelemetry Collector for either a single NGINX host or a Kubernetes deployment. Pick the option that matches your environment to see the right steps. diff --git a/src/install/nginx-otel/collector/environment.mdx b/src/install/nginx-otel/collector/environment.mdx new file mode 100644 index 00000000000..0b1aefc44b1 --- /dev/null +++ b/src/install/nginx-otel/collector/environment.mdx @@ -0,0 +1,44 @@ +--- +headingText: Set environment variables for the collector +componentType: default +--- + +Inject your New Relic license key and OTLP endpoint into the collector service so the exporter can authenticate. + +1. Create a systemd override directory: + ```bash + sudo mkdir -p /etc/systemd/system/otelcol-contrib.service.d + ``` +2. Write `environment.conf` with the region-specific endpoint. Replace `YOUR_LICENSE_KEY` with the key you collected earlier. + + **US region** + ```bash + cat < + collection_interval: 30s +processors: + resourcedetection: + detectors: [system] + system: + resource_attributes: + host.name: + enabled: true + host.id: + enabled: true + resource: + attributes: + - action: upsert + key: nginx.server.endpoint + value: + - action: upsert + key: nginx.deployment.name + value: + batch: + timeout: 30s + send_batch_size: 512 + transform/nginx_metrics: + metric_statements: + - context: resource + statements: + - set(attributes["nginx.display.name"], Concat(["server", attributes["nginx.deployment.name"]], ":")) +exporters: + otlphttp/newrelic: + endpoint: ${env:NEWRELIC_OTLP_ENDPOINT} + headers: + api-key: ${env:NEWRELIC_LICENSE_KEY} + compression: gzip +extensions: + health_check: +service: + pipelines: + metrics: + receivers: [nginx] + processors: [resourcedetection, resource, batch, transform/nginx_metrics] + exporters: [otlphttp/newrelic] +``` + +Save the file and ensure the `otelcol-contrib` system user can read it. diff --git a/src/install/nginx-otel/host/configure-collector-plus.mdx b/src/install/nginx-otel/host/configure-collector-plus.mdx new file mode 100644 index 00000000000..9b588d414ae --- /dev/null +++ b/src/install/nginx-otel/host/configure-collector-plus.mdx @@ -0,0 +1,72 @@ +--- +headingText: Configure the collector for NGINX metrics +componentType: default +--- + +Create or replace `/etc/otelcol-contrib/config.yaml` with the configuration below. This scrapes metrics from the NGINX Prometheus exporter and sends them to New Relic over OTLP. + +**Note:** You'll need to install and run the [NGINX Prometheus exporter](https://github.com/nginx/nginx-prometheus-exporter) alongside your NGINX Plus instance to expose HTTP API metrics in Prometheus format. + +- Update the `targets` value to match your Prometheus exporter host and port (default is `127.0.0.1:9113`). +- Update the `nginx.server.endpoint` value to match your API status path and port. +- Update the `nginx.deployment.name` value to with a unique name to identify this NGINX server in New Relic. + +```yaml +receivers: + prometheus: + config: + scrape_configs: + - job_name: nginx-plus + scrape_interval: 30s + static_configs: + - targets: + - 127.0.0.1:9113 +processors: + batch: + send_batch_size: 1024 + timeout: 30s + filter/nginx_metrics: + metrics: + include: + match_type: regexp + metric_names: + - nginxplus_.* + resource/nginx: + attributes: + - key: nginx.server.endpoint + value: + action: insert + - key: nginx.deployment.name + value: + action: insert + resourcedetection: + detectors: [system] + system: + resource_attributes: + host.name: + enabled: true + host.id: + enabled: true + transform/nginx: + metric_statements: + - context: resource + statements: + - set(attributes["nginx.display.name"], Concat(["server", attributes["nginx.deployment.name"]], ":")) + - context: resource + statements: + - delete_key(attributes, "service.name") +exporters: + otlphttp/newrelic: + endpoint: ${env:NEWRELIC_OTLP_ENDPOINT} + headers: + api-key: ${env:NEWRELIC_LICENSE_KEY} + compression: gzip +service: + pipelines: + metrics/nginx: + receivers: [prometheus] + processors: [batch, filter/nginx_metrics, resourcedetection, resource/nginx, transform/nginx] + exporters: [otlphttp/newrelic] +``` + +Save the file and ensure the `otelcol-contrib` system user can read it. diff --git a/src/install/nginx-otel/host/configure-collector.mdx b/src/install/nginx-otel/host/configure-collector.mdx new file mode 100644 index 00000000000..6caf8fe1b21 --- /dev/null +++ b/src/install/nginx-otel/host/configure-collector.mdx @@ -0,0 +1,6 @@ +--- +headingText: Configure the collector for NGINX metrics +componentType: default +--- + +The OpenTelemetry Collector configuration will be shown based on your NGINX version selection. diff --git a/src/install/nginx-otel/host/enable-status-opensource.mdx b/src/install/nginx-otel/host/enable-status-opensource.mdx new file mode 100644 index 00000000000..7b650f54ea6 --- /dev/null +++ b/src/install/nginx-otel/host/enable-status-opensource.mdx @@ -0,0 +1,39 @@ +--- +headingText: Configure NGINX Open Source +componentType: default +--- + +Configure and enable the [stub_status](https://nginx.org/en/docs/http/ngx_http_stub_status_module.html) module to expose metrics from your NGINX Open Source server. + +After updating `nginx.conf`, test and reload the service: + +```bash +sudo nginx -t && sudo nginx -s reload +``` + +List the configuration block that exposes `stub_status`: + +```bash +sudo nginx -T 2>&1 | grep -ozP "(?s:.*\s)\Klocation(?s).*stub_status" | grep -aoP "\/([^\s]+)" +``` + +Example output: + +``` +/status +``` +If you do not see output, inspect your nginx.conf manually for a stub_status block. + +Use `curl` to confirm your status endpoint is reachable: + +```bash +curl -I http://127.0.0.1:8080/status 2>/dev/null | head -n 1 | cut -d$' ' -f1,2 +``` + +Expected output: + +``` +HTTP/1.1 200 +``` + +If you see a different response, troubleshoot your NGINX configuration. diff --git a/src/install/nginx-otel/host/enable-status-plus.mdx b/src/install/nginx-otel/host/enable-status-plus.mdx new file mode 100644 index 00000000000..e99608ea153 --- /dev/null +++ b/src/install/nginx-otel/host/enable-status-plus.mdx @@ -0,0 +1,30 @@ +--- +headingText: Configure NGINX Plus +componentType: default +--- + +Configure and enable the [HTTP API module](https://nginx.org/en/docs/http/ngx_http_api_module.html) to expose metrics from your NGINX Plus server. + +After updating `nginx.conf`, test and reload the service: + +```bash +sudo nginx -t && sudo nginx -s reload +``` + +Confirm the API endpoint path (including version) exposed in your configuration. + +Record the full API endpoint path (without the leading slash) and the port that serves it. Common defaults are `api/9` on port `8080`. + +Use `curl` to confirm your API endpoint is reachable: + +```bash +curl -I http://127.0.0.1:8080/api/9 2>/dev/null | head -n 1 | cut -d$' ' -f1,2 +``` + +Expected output: + +``` +HTTP/1.1 200 +``` + +If you see a different response, verify your NGINX Plus configuration and ensure the API module is properly enabled. diff --git a/src/install/nginx-otel/host/enable-status-select.mdx b/src/install/nginx-otel/host/enable-status-select.mdx new file mode 100644 index 00000000000..7c0b9eb7694 --- /dev/null +++ b/src/install/nginx-otel/host/enable-status-select.mdx @@ -0,0 +1,5 @@ +--- +componentType: appInfoConfigOption +optionType: nginx_type +headingText: Select how your NGINX server is set up +--- diff --git a/src/install/nginx-otel/host/enable-status.mdx b/src/install/nginx-otel/host/enable-status.mdx new file mode 100644 index 00000000000..7b5c4a44643 --- /dev/null +++ b/src/install/nginx-otel/host/enable-status.mdx @@ -0,0 +1,6 @@ +--- +headingText: Prepare and validate your NGINX status endpoint +componentType: default +--- + +Select how your NGINX server is set up below to see the appropriate configuration and verification steps. diff --git a/src/install/nginx-otel/host/install-collector-apt.mdx b/src/install/nginx-otel/host/install-collector-apt.mdx new file mode 100644 index 00000000000..7d1d96bdd29 --- /dev/null +++ b/src/install/nginx-otel/host/install-collector-apt.mdx @@ -0,0 +1,18 @@ +--- +headingText: Install the OpenTelemetry Collector Contrib +componentType: default +--- + +Download and install the [OpenTelemetry Collector Contrib](https://opentelemetry.io/docs/collector/install/binary/linux/#deb-installation) package for Debian or Ubuntu systems. + +```bash +VERSION=0.140.0 +ARCH=amd64 +sudo apt-get update +sudo apt-get -y install wget +wget https://github.com/open-telemetry/opentelemetry-collector-releases/releases/download/v${VERSION}/otelcol-contrib_${VERSION}_linux_${ARCH}.deb +sudo dpkg -i otelcol-contrib_${VERSION}_linux_${ARCH}.deb +sudo systemctl enable --now otelcol-contrib +``` + +The package installs an `otelcol-contrib` systemd service and places configuration files under `/etc/otelcol-contrib/`. diff --git a/src/install/nginx-otel/host/install-collector-yum.mdx b/src/install/nginx-otel/host/install-collector-yum.mdx new file mode 100644 index 00000000000..2994e99117e --- /dev/null +++ b/src/install/nginx-otel/host/install-collector-yum.mdx @@ -0,0 +1,18 @@ +--- +headingText: Install the OpenTelemetry Collector Contrib +componentType: default +--- + +Download and install the OpenTelemetry Collector Contrib package for RHEL, CentOS, or Amazon Linux systems. + +```bash +VERSION=0.140.0 +ARCH=amd64 +sudo yum update -y +sudo yum -y install wget +wget https://github.com/open-telemetry/opentelemetry-collector-releases/releases/download/v${VERSION}/otelcol-contrib_${VERSION}_linux_${ARCH}.rpm +sudo rpm -ivh otelcol-contrib_${VERSION}_linux_${ARCH}.rpm +sudo systemctl enable --now otelcol-contrib +``` + +The package installs an `otelcol-contrib` systemd service and places configuration files under `/etc/otelcol-contrib/`. diff --git a/src/install/nginx-otel/host/install-collector.mdx b/src/install/nginx-otel/host/install-collector.mdx new file mode 100644 index 00000000000..c50d7e609b7 --- /dev/null +++ b/src/install/nginx-otel/host/install-collector.mdx @@ -0,0 +1,6 @@ +--- +headingText: Install the OpenTelemetry Collector Contrib +componentType: default +--- + +Select your package manager below to see the appropriate installation commands. diff --git a/src/install/nginx-otel/host/license.mdx b/src/install/nginx-otel/host/license.mdx new file mode 100644 index 00000000000..c4cc8759274 --- /dev/null +++ b/src/install/nginx-otel/host/license.mdx @@ -0,0 +1,9 @@ +--- +headingText: Gather your New Relic license key +componentType: default +--- + +The OpenTelemetry Collector sends data to New Relic using OTLP. You'll need a New Relic [license key](https://docs.newrelic.com/docs/apis/intro-apis/new-relic-api-keys/#license-key) so the collector can authenticate. + +- If you manage multiple accounts, choose the account where you want the NGINX data to appear. +- Store the key securely; you'll reference it in the collector environment configuration and when creating secrets for other environments. diff --git a/src/install/nginx-otel/host/metrics-opensource.mdx b/src/install/nginx-otel/host/metrics-opensource.mdx new file mode 100644 index 00000000000..139edc3427c --- /dev/null +++ b/src/install/nginx-otel/host/metrics-opensource.mdx @@ -0,0 +1,108 @@ +--- +componentType: default +headingText: Metrics collected +--- + +The NGINX OpenTelemetry integration collects the following metrics from the HTTP stub status module. + +### Metric data [#metrics] + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Metric + + Description +
+ `nginx.connections_accepted` + + Total number of accepted client connections (Sum) +
+ `nginx.connections_handled` + + Total number of handled client connections (Sum) +
+ `nginx.connections_current` + + Current number of active client connections. Includes `waiting`, `reading`, and `writing` states (Gauge) +
+ `nginx.requests` + + Total number of client requests (Sum) +
+ +### Connection state metrics + +These metrics break down `nginx.connections_current` by connection state: + + + + + + + + + + + + + + + + + + + + + + +
+ Metric + + Description +
+ `nginx_connections_reading` + + Current connections where NGINX is reading the request header (Gauge) +
+ `nginx_connections_writing` + + Current connections where NGINX is writing the response back to the client (Gauge) +
+ `nginx_connections_waiting` + + Current idle client connections waiting for a request (Keep-Alive) (Gauge) +
+ +### Resource attributes + +All metrics include the following resource attributes: + +- `nginx.server.endpoint` - The stub status endpoint URL +- `nginx.deployment.name` - Your configured deployment name +- `nginx.display.name` - Formatted display name combining server and deployment +- `host.name` - The hostname where NGINX is running +- `host.id` - The host identifier diff --git a/src/install/nginx-otel/host/metrics-plus.mdx b/src/install/nginx-otel/host/metrics-plus.mdx new file mode 100644 index 00000000000..af0a7a5153c --- /dev/null +++ b/src/install/nginx-otel/host/metrics-plus.mdx @@ -0,0 +1,386 @@ +--- +componentType: default +headingText: Metrics collected +--- + +The NGINX Plus OpenTelemetry integration collects metrics through the NGINX Prometheus exporter. The integration retrieves data from the [NGINX Plus HTTP API module](http://nginx.org/en/docs/http/ngx_http_api_module.html) via the Prometheus exporter. + +### Metric data [#metrics] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Metric + + Description +
+ `nginxplus_server_zone_requests` + + Total number of client requests received from clients (Counter) +
+ `nginxplus_server_zone_responses` + + Total number of responses sent to clients by status code: 1xx, 2xx, 3xx, 4xx, 5xx (Counter) +
+ `nginxplus_server_zone_discarded` + + Total number of requests completed without sending a response (Counter) +
+ `nginxplus_server_zone_received` + + Total number of bytes received from clients (Counter) +
+ `nginxplus_server_zone_sent` + + Total number of bytes sent to clients (Counter) +
+ `nginxplus_server_zone_processing` + + Current number of client requests being processed (Gauge) +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Metric + + Description +
+ `nginxplus_upstream_server_requests` + + Total number of client requests forwarded to upstream servers (Counter) +
+ `nginxplus_upstream_server_responses` + + Total number of responses obtained from upstream servers by status code (Counter) +
+ `nginxplus_upstream_server_active` + + Current number of active connections (Gauge) +
+ `nginxplus_upstream_server_sent` + + Total number of bytes sent to upstream servers (Counter) +
+ `nginxplus_upstream_server_received` + + Total number of bytes received from upstream servers (Counter) +
+ `nginxplus_upstream_server_fails` + + Total number of unsuccessful attempts to communicate with upstream servers (Counter) +
+ `nginxplus_upstream_server_unavail` + + Number of times the server became unavailable for client requests (Counter) +
+ `nginxplus_upstream_server_health_checks_checks` + + Total health check requests (Counter) +
+ `nginxplus_upstream_server_health_checks_fails` + + Failed health checks (Counter) +
+ `nginxplus_upstream_server_health_checks_unhealthy` + + Number of times server became unhealthy (Counter) +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Metric + + Description +
+ `nginxplus_cache_size` + + Current size of the cache (Gauge) +
+ `nginxplus_cache_max_size` + + Maximum cache size limit (Gauge) +
+ `nginxplus_cache_hit_responses` + + Total responses read from the cache by status code (Counter) +
+ `nginxplus_cache_miss_responses` + + Total responses not found in the cache (Counter) +
+ `nginxplus_cache_stale_responses` + + Total stale cache responses (Counter) +
+ `nginxplus_cache_bypass_responses` + + Total responses bypassing the cache (Counter) +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Metric + + Description +
+ `nginxplus_connections_accepted` + + Total accepted client connections (Counter) +
+ `nginxplus_connections_dropped` + + Total dropped client connections (Counter) +
+ `nginxplus_connections_active` + + Current active client connections (Gauge) +
+ `nginxplus_connections_idle` + + Current idle client connections (Gauge) +
+ `nginxplus_ssl_handshakes` + + Total successful SSL handshakes (Counter) +
+ `nginxplus_ssl_handshakes_failed` + + Total failed SSL handshakes (Counter) +
+ `nginxplus_ssl_session_reuses` + + Total SSL session reuses during handshake (Counter) +
+
+ + + All metrics include the following resource attributes: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Name + + Description +
+ `nginx.server.endpoint` + + The HTTP API endpoint URL +
+ `nginx.deployment.name` + + Your configured deployment name +
+ `nginx.display.name` + + Formatted display name +
+ `host.name` + + The hostname where NGINX Plus is running +
+ `host.id` + + The host identifier +
+
+
+ +For a complete list of all available metrics, see the [NGINX Prometheus exporter documentation](https://github.com/nginx/nginx-prometheus-exporter?tab=readme-ov-file#metrics-for-nginx-plus). diff --git a/src/install/nginx-otel/host/prerequisites.mdx b/src/install/nginx-otel/host/prerequisites.mdx new file mode 100644 index 00000000000..a0d882a78d1 --- /dev/null +++ b/src/install/nginx-otel/host/prerequisites.mdx @@ -0,0 +1,11 @@ +--- +headingText: Check the compatibility and requirements +componentType: default +--- + +Make sure your host meets the following requirements before you begin: + +- You run either NGINX Open Source with the HTTP stub status module or NGINX Plus with the HTTP API module enabled. +- You can connect to the host with `sudo` privileges to install packages and restart services. +- The host can reach New Relic over outbound HTTPS on port 443 and the OTLP ingest endpoint you plan to use. +- You can install and run the OpenTelemetry Collector Contrib distribution on the host. diff --git a/src/install/nginx-otel/host/verify.mdx b/src/install/nginx-otel/host/verify.mdx new file mode 100644 index 00000000000..bc326529081 --- /dev/null +++ b/src/install/nginx-otel/host/verify.mdx @@ -0,0 +1,13 @@ +--- +headingText: Find and use data +componentType: default +--- + +1. Go to **[one.newrelic.com](https://one.newrelic.com) > Integrations & Agents**. +2. Select **Dashboards**, and click **NGINX overview dashboard**. +3. In the popup window, select your account. +4. Click View dashboard, and see your NGINX data in New Relic. + +The NGINX metrics are attached to the `Metric` [event type](/docs/using-new-relic/data/understand-data/new-relic-data-types#events-new-relic). You can [query this data](/docs/using-new-relic/data/understand-data/query-new-relic-data) for troubleshooting purposes or to create custom charts and dashboards. + +You can find a full list of metrics and their attributes in the "Metrics collected" section at the end of this guide. diff --git a/src/install/nginx-otel/intro.mdx b/src/install/nginx-otel/intro.mdx new file mode 100644 index 00000000000..a44e694f335 --- /dev/null +++ b/src/install/nginx-otel/intro.mdx @@ -0,0 +1,8 @@ +--- +headingText: Before you start +componentType: default +--- + +Our NGINX OpenTelemetry integration helps you stream metrics and logs from NGINX (open source or NGINX Plus) through the OpenTelemetry Collector Contrib distribution. Once the collector forwards data via OTLP, you can explore it in New Relic dashboards, charts, and alerts. + +To get the most from this guide, decide where your NGINX workloads run and select the matching install path. You need a [New Relic account](https://newrelic.com/signup) and a license key to complete the setup. diff --git a/src/install/nginx-otel/kubernetes/deploy.mdx b/src/install/nginx-otel/kubernetes/deploy.mdx new file mode 100644 index 00000000000..0231c8a233f --- /dev/null +++ b/src/install/nginx-otel/kubernetes/deploy.mdx @@ -0,0 +1,27 @@ +--- +headingText: Deploy the collector with Helm +componentType: default +--- + +Use the OpenTelemetry Helm chart to deploy (or upgrade) the collector with your custom values file. + +```bash +helm repo add open-telemetry https://open-telemetry.github.io/opentelemetry-helm-charts +helm repo update +helm upgrade --install nginx-otel-collector open-telemetry/opentelemetry-collector \ + --namespace newrelic \ + --create-namespace \ + -f otel-collector-values.yaml +``` + +Successful output looks similar to: + +``` +Release "nginx-otel-collector" has been upgraded. Happy Helming! +NAME: nginx-otel-collector +LAST DEPLOYED: 2025-01-01 12:00:00 -0500 CST +NAMESPACE: newrelic +STATUS: deployed +REVISION: 1 +TEST SUITE: None +``` diff --git a/src/install/nginx-otel/kubernetes/license.mdx b/src/install/nginx-otel/kubernetes/license.mdx new file mode 100644 index 00000000000..33e412db4a0 --- /dev/null +++ b/src/install/nginx-otel/kubernetes/license.mdx @@ -0,0 +1,6 @@ +--- +headingText: Collect your New Relic license key +componentType: default +--- + +The Helm chart expects a Kubernetes secret that supplies your New Relic license key and OTLP endpoint. Retrieve the license key for the account where you want to store NGINX data. You'll add it to a secret in a later step. diff --git a/src/install/nginx-otel/kubernetes/prepare-values.mdx b/src/install/nginx-otel/kubernetes/prepare-values.mdx new file mode 100644 index 00000000000..3550c741fcd --- /dev/null +++ b/src/install/nginx-otel/kubernetes/prepare-values.mdx @@ -0,0 +1,139 @@ +--- +headingText: Build your Helm values file +componentType: default +--- + +Populate a values file that configures the OpenTelemetry Collector deployment and the receiver creator that scrapes your NGINX pods. + +1. Choose identifiers for your cluster and NGINX pod labels. Example values: + - Cluster name: `nginx-cluster` + - `labels["app"]`: `nginx` + - `labels["role"]`: `reverse-proxy` +2. Create `otel-collector-values.yaml` with content similar to the following. Update the placeholders for your cluster, labels, and stub status endpoint if they differ. + +```yaml +opentelemetry-collector: + mode: deployment + + image: + repository: otel/opentelemetry-collector-contrib + pullPolicy: IfNotPresent + + command: + name: otelcol-contrib + + resources: + limits: + cpu: 500m + memory: 300Mi + requests: + cpu: 100m + memory: 100Mi + + extraEnvs: + - name: NEWRELIC_LICENSE_KEY + valueFrom: + secretKeyRef: + name: newrelic-licenses + key: NEWRELIC_LICENSE_KEY + - name: NEWRELIC_OTLP_ENDPOINT + valueFrom: + secretKeyRef: + name: newrelic-licenses + key: NEWRELIC_OTLP_ENDPOINT + - name: K8S_NODE_NAME + valueFrom: + fieldRef: + fieldPath: spec.nodeName + - name: K8S_CLUSTER_NAME + value: nginx-cluster + + clusterRole: + create: true + rules: + - apiGroups: [""] + resources: ["pods", "nodes", "nodes/stats", "nodes/proxy"] + verbs: ["get", "list", "watch"] + - apiGroups: ["apps"] + resources: ["replicasets"] + verbs: ["get", "list", "watch"] + clusterRoleBinding: + name: "" + + config: + extensions: + health_check: + endpoint: 0.0.0.0:13133 + k8s_observer: + auth_type: serviceAccount + observe_pods: true + observe_nodes: true + + receivers: + receiver_creator/nginx: + watch_observers: [k8s_observer] + receivers: + nginx: + rule: type == "pod" && labels["app"] == "nginx" && labels["role"] == "reverse-proxy" + config: + endpoint: 'http://`endpoint`:8080/status' + metrics: + nginx.requests: + enabled: true + nginx.connections_accepted: + enabled: true + nginx.connections_handled: + enabled: true + nginx.connections_current: + enabled: true + collection_interval: 30s + resource_attributes: + nginx.server.endpoint: 'http://`endpoint`:8080/status' + nginx.port: '8080' + + processors: + batch: + send_batch_size: 1024 + timeout: 30s + + resource/cluster: + attributes: + - key: k8s.cluster.name + value: "nginx-cluster" + action: insert + + transform/nginx: + metric_statements: + - context: resource + statements: + - set(attributes["nginx.display.name"], Concat([ + "server", + "k8s", + attributes["k8s.cluster.name"], + attributes["k8s.namespace.name"], + "pod", + attributes["k8s.pod.name"], + "nginx", + attributes["nginx.port"] + ], ":")) + - set(attributes["nginx.deployment.name"], attributes["k8s.pod.name"]) + + exporters: + otlphttp: + endpoint: "${NEWRELIC_OTLP_ENDPOINT}" + headers: + api-key: "${NEWRELIC_LICENSE_KEY}" + debug: + verbosity: detailed + + service: + telemetry: + logs: + level: debug + extensions: [health_check, k8s_observer] + pipelines: + metrics/nginx: + receivers: [receiver_creator/nginx] + processors: [batch, resource/cluster, transform/nginx] + exporters: [otlphttp, debug] +``` diff --git a/src/install/nginx-otel/kubernetes/prerequisites.mdx b/src/install/nginx-otel/kubernetes/prerequisites.mdx new file mode 100644 index 00000000000..5b41fce881c --- /dev/null +++ b/src/install/nginx-otel/kubernetes/prerequisites.mdx @@ -0,0 +1,11 @@ +--- +headingText: Confirm Kubernetes prerequisites +componentType: default +--- + +Before deploying the collector, ensure you have: + +- `kubectl` access to the target cluster and the namespace where you'll deploy the collector. +- Helm installed locally and pointed at the same kube context. +- NGINX pods that expose the stub status endpoint through a ClusterIP Service reachable by the collector. +- A New Relic license key stored securely for later steps. diff --git a/src/install/nginx-otel/kubernetes/secret.mdx b/src/install/nginx-otel/kubernetes/secret.mdx new file mode 100644 index 00000000000..1391c45c9f5 --- /dev/null +++ b/src/install/nginx-otel/kubernetes/secret.mdx @@ -0,0 +1,37 @@ +--- +headingText: Create the New Relic secret +componentType: default +--- + +Store your license key and OTLP endpoint in a Kubernetes secret so the collector can reference them as environment variables. + +Replace `YOUR_LICENSE_KEY` with your value and run the command that matches your ingest region. + +#### US region + +```bash +kubectl create secret generic newrelic-licenses \ + --from-literal=NEWRELIC_LICENSE_KEY=YOUR_LICENSE_KEY \ + --from-literal=NEWRELIC_OTLP_ENDPOINT=https://otlp.nr-data.net:4318 \ + -n newrelic +``` + +#### EU region + +```bash +kubectl create secret generic newrelic-licenses \ + --from-literal=NEWRELIC_LICENSE_KEY=YOUR_LICENSE_KEY \ + --from-literal=NEWRELIC_OTLP_ENDPOINT=https://eu-otlp.nr-data.net:4318 \ + -n newrelic +``` + +#### Staging + +```bash +kubectl create secret generic newrelic-licenses \ + --from-literal=NEWRELIC_LICENSE_KEY=YOUR_LICENSE_KEY \ + --from-literal=NEWRELIC_OTLP_ENDPOINT=https://staging-otlp.nr-data.net:443 \ + -n newrelic +``` + +If the namespace does not exist, add `--create-namespace` or create it beforehand. diff --git a/src/install/nginx-otel/kubernetes/status-endpoint.mdx b/src/install/nginx-otel/kubernetes/status-endpoint.mdx new file mode 100644 index 00000000000..e4a4964a2b7 --- /dev/null +++ b/src/install/nginx-otel/kubernetes/status-endpoint.mdx @@ -0,0 +1,22 @@ +--- +headingText: Expose and verify the stub status endpoint +componentType: default +--- + +Each NGINX pod must expose the HTTP stub status module so the collector can scrape metrics. + +1. Add a location block similar to the following to your NGINX configuration, then reload the deployment: + + ```nginx + location /status { + stub_status on; + } + ``` +2. Record the path (without the leading slash) and the port serving the stub status endpoint. Defaults are `status` on port `80` or `8080`. +3. Verify the endpoint from within one of your pods: + + ```bash + kubectl exec deploy/ -- curl -I http://127.0.0.1:8080/status 2>/dev/null | head -n 1 | cut -d$' ' -f1,2 + ``` + +Expect an `HTTP/1.1 200` response. Adjust the command to match your deployment name, port, and path. diff --git a/src/install/nginx-otel/kubernetes/verify.mdx b/src/install/nginx-otel/kubernetes/verify.mdx new file mode 100644 index 00000000000..cf68479ad4a --- /dev/null +++ b/src/install/nginx-otel/kubernetes/verify.mdx @@ -0,0 +1,29 @@ +--- +headingText: Verify the Kubernetes deployment +componentType: default +--- + +1. Check that the collector pods are running: + ```bash + kubectl get pods -n newrelic -l app.kubernetes.io/name=opentelemetry-collector + ``` +2. Inspect the Helm release status for additional diagnostics: + ```bash + helm status nginx-otel-collector -n newrelic + ``` +3. Review the collector logs if you need to troubleshoot scrape or export issues: + ```bash + kubectl logs deploy/nginx-otel-collector -n newrelic --tail=50 + ``` +4. Run an NRQL query in New Relic to confirm data is arriving. Replace the cluster name with the value you set in the values file: + + ```sql + SELECT entity.guid + FROM Metric + WHERE metricName LIKE 'nginx.%' + AND instrumentation.provider = 'opentelemetry' + AND k8s.cluster.name = 'nginx-cluster' + SINCE 10 minutes ago + ``` + +If the query returns results, New Relic is receiving NGINX metrics from your cluster. Otherwise, review the collector logs for connection errors or missing permissions. diff --git a/src/install/nginx-otel/logs/forwarding-opensource.mdx b/src/install/nginx-otel/logs/forwarding-opensource.mdx new file mode 100644 index 00000000000..8ad663d107f --- /dev/null +++ b/src/install/nginx-otel/logs/forwarding-opensource.mdx @@ -0,0 +1,60 @@ +--- +headingText: (Optional) Forward NGINX logs +componentType: default +--- + +Extend your collector configuration to include access and error logs if you want log events alongside metrics. + +1. Gather the full paths to your NGINX access and error log files. Defaults are usually `/var/log/nginx/access.log` and `/var/log/nginx/error.log`. + +2. Update `/etc/otelcol-contrib/config.yaml` to add a `filelog` receiver and log pipeline: + + ```yaml + receivers: + nginx: + # existing stub status receiver configuration + filelog: + include: + - /var/log/nginx/access.log + - /var/log/nginx/error.log + + processors: + resourcedetection: + # existing settings + resource: + # existing settings + batch: + # existing settings + transform/nginx_metrics: + # existing settings + transform/nginx_logs: + log_statements: + - context: resource + statements: + - set(attributes["nginx.display.name"], Concat(["server", attributes["nginx.deployment.name"]], ":")) + + service: + pipelines: + metrics: + receivers: [nginx] + processors: [resourcedetection, resource, batch, transform/nginx_metrics] + exporters: [otlphttp/newrelic] + logs/nginx: + receivers: [filelog] + processors: [batch, resource, transform/nginx_logs] + exporters: [otlphttp/newrelic] + ``` + +3. Grant the `otelcol-contrib` user read access to the log files: + + ```bash + sudo usermod -a -G adm otelcol-contrib + sudo chmod 644 /var/log/nginx/access.log + sudo chmod 644 /var/log/nginx/error.log + ``` + +4. Restart the collector to apply the changes: + + ```bash + sudo systemctl restart otelcol-contrib + ``` diff --git a/src/install/nginx-otel/logs/forwarding-plus.mdx b/src/install/nginx-otel/logs/forwarding-plus.mdx new file mode 100644 index 00000000000..dfc2f8d40c7 --- /dev/null +++ b/src/install/nginx-otel/logs/forwarding-plus.mdx @@ -0,0 +1,62 @@ +--- +headingText: (Optional) Forward NGINX logs +componentType: default +--- + +Extend your collector configuration to include access and error logs if you want log events alongside metrics. + +1. Gather the full paths to your NGINX access and error log files. Defaults are usually `/var/log/nginx/access.log` and `/var/log/nginx/error.log`. + +2. Update `/etc/otelcol-contrib/config.yaml` to add a `filelog` receiver and log pipeline: + + ```yaml + receivers: + prometheus: + # existing Prometheus receiver configuration + filelog: + include: + - /var/log/nginx/access.log + - /var/log/nginx/error.log + + processors: + batch: + # existing settings + filter/nginx_metrics: + # existing settings + resourcedetection: + # existing settings + resource/nginx: + # existing settings + transform/nginx_metrics: + # existing settings + transform/nginx_logs: + log_statements: + - context: resource + statements: + - set(attributes["nginx.display.name"], Concat(["server", attributes["nginx.deployment.name"]], ":")) + + service: + pipelines: + metrics/nginx: + receivers: [prometheus] + processors: [batch, filter/nginx_metrics, resourcedetection, resource/nginx, transform/nginx_metrics] + exporters: [otlphttp/newrelic] + logs/nginx: + receivers: [filelog] + processors: [batch, resource/nginx, transform/nginx_logs] + exporters: [otlphttp/newrelic] + ``` + +3. Grant the `otelcol-contrib` user read access to the log files: + + ```bash + sudo usermod -a -G adm otelcol-contrib + sudo chmod 644 /var/log/nginx/access.log + sudo chmod 644 /var/log/nginx/error.log + ``` + +4. Restart the collector to apply the changes: + + ```bash + sudo systemctl restart otelcol-contrib + ``` diff --git a/src/install/nginx-otel/logs/forwarding.mdx b/src/install/nginx-otel/logs/forwarding.mdx new file mode 100644 index 00000000000..e28d678c430 --- /dev/null +++ b/src/install/nginx-otel/logs/forwarding.mdx @@ -0,0 +1,6 @@ +--- +headingText: (Optional) Forward NGINX logs +componentType: default +--- + +The log forwarding configuration will be shown based on your NGINX version selection. diff --git a/src/install/nginx-otel/whatsNext.mdx b/src/install/nginx-otel/whatsNext.mdx new file mode 100644 index 00000000000..3e6422e8993 --- /dev/null +++ b/src/install/nginx-otel/whatsNext.mdx @@ -0,0 +1,1426 @@ +--- +headingText: Metrics collected +componentType: default +--- + +## NGINX Open Source metrics [#nginx-opensource] + +The OpenTelemetry Collector's NGINX receiver collects metrics from the HTTP stub status module. Below are the key metrics available: + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Metric + + Description +
+ `nginx.connections_accepted` + + Total number of accepted client connections +
+ `nginx.connections_handled` + + Total number of handled connections. Generally, the same as accepted unless some resource limit has been reached +
+ `nginx.connections_current` + + Current number of nginx connections by state. States include: `active`, `reading`, `writing`, `waiting` +
+ `nginx.requests` + + Total number of requests made to NGINX since start +
+
+ +
+ +For more details, see the [NGINX receiver documentation](https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/main/receiver/nginxreceiver/documentation.md). + +## NGINX Plus metrics [#nginx-plus] + +NGINX Plus metrics are collected via the Prometheus exporter using the HTTP API module. Below are all available metrics: + + + + + + + + + + + + + + + + +
+ Metric + + Description +
+ `nginxplus_up` + + Shows the status of the last metric scrape: 1 for a successful scrape and 0 for a failed one +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Metric + + Description +
+ `nginxplus_connections_accepted` + + Accepted client connections +
+ `nginxplus_connections_active` + + Active client connections +
+ `nginxplus_connections_dropped` + + Dropped client connections +
+ `nginxplus_connections_idle` + + Idle client connections +
+
+ + + + + + + + + + + + + + + + + + + +
+ Metric + + Description +
+ `nginxplus_http_requests_total` + + Total http requests +
+ `nginxplus_http_requests_current` + + Current http requests +
+
+ + + + + + + + + + + + + + + + + + + + + + + +
+ Metric + + Description +
+ `nginxplus_ssl_handshakes` + + Successful SSL handshakes +
+ `nginxplus_ssl_handshakes_failed` + + Failed SSL handshakes +
+ `nginxplus_ssl_session_reuses` + + Session reuses during SSL handshake +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Metric + + Description +
+ `nginxplus_server_zone_processing` + + Client requests that are currently being processed +
+ `nginxplus_server_zone_requests` + + Total client requests +
+ `nginxplus_server_zone_responses` + + Total responses sent to clients +
+ `nginxplus_server_zone_responses_codes` + + Total responses sent to clients by code +
+ `nginxplus_server_zone_discarded` + + Requests completed without sending a response +
+ `nginxplus_server_zone_received` + + Bytes received from clients +
+ `nginxplus_server_zone_sent` + + Bytes sent to clients +
+ `nginxplus_server_ssl_handshakes` + + Successful SSL handshakes +
+ `nginxplus_server_ssl_handshakes_failed` + + Failed SSL handshakes +
+ `nginxplus_server_ssl_session_reuses` + + Session reuses during SSL handshake +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Metric + + Description +
+ `nginxplus_stream_server_zone_processing` + + Client connections that are currently being processed +
+ `nginxplus_stream_server_zone_connections` + + Total connections +
+ `nginxplus_stream_server_zone_sessions` + + Total sessions completed +
+ `nginxplus_stream_server_zone_discarded` + + Connections completed without creating a session +
+ `nginxplus_stream_server_zone_received` + + Bytes received from clients +
+ `nginxplus_stream_server_zone_sent` + + Bytes sent to clients +
+ `nginxplus_stream_server_ssl_handshakes` + + Successful SSL handshakes +
+ `nginxplus_stream_server_ssl_handshakes_failed` + + Failed SSL handshakes +
+ `nginxplus_stream_server_ssl_session_reuses` + + Session reuses during SSL handshake +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Metric + + Description +
+ `nginxplus_upstream_server_state` + + Current state +
+ `nginxplus_upstream_server_active` + + Active connections +
+ `nginxplus_upstream_server_limit` + + Limit for connections which corresponds to the max_conns parameter of the upstream server. Zero value means there is no limit +
+ `nginxplus_upstream_server_requests` + + Total client requests +
+ `nginxplus_upstream_server_responses` + + Total responses sent to clients +
+ `nginxplus_upstream_server_responses_codes` + + Total responses sent to clients by code +
+ `nginxplus_upstream_server_sent` + + Bytes sent to this server +
+ `nginxplus_upstream_server_received` + + Bytes received from this server +
+ `nginxplus_upstream_server_fails` + + Number of unsuccessful attempts to communicate with the server +
+ `nginxplus_upstream_server_unavail` + + How many times the server became unavailable for client requests (state 'unavail') due to the number of unsuccessful attempts reaching the max_fails threshold +
+ `nginxplus_upstream_server_header_time` + + Average time to get the response header from the server +
+ `nginxplus_upstream_server_response_time` + + Average time to get the full response from the server +
+ `nginxplus_upstream_server_health_checks_checks` + + Total health check requests +
+ `nginxplus_upstream_server_health_checks_fails` + + Failed health checks +
+ `nginxplus_upstream_server_health_checks_unhealthy` + + How many times the server became unhealthy (state 'unhealthy') +
+ `nginxplus_upstream_server_ssl_handshakes` + + Successful SSL handshakes +
+ `nginxplus_upstream_server_ssl_handshakes_failed` + + Failed SSL handshakes +
+ `nginxplus_upstream_server_ssl_session_reuses` + + Session reuses during SSL handshake +
+ `nginxplus_upstream_keepalive` + + Idle keepalive connections +
+ `nginxplus_upstream_zombies` + + Servers removed from the group but still processing active client requests +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Metric + + Description +
+ `nginxplus_stream_upstream_server_state` + + Current state +
+ `nginxplus_stream_upstream_server_active` + + Active connections +
+ `nginxplus_stream_upstream_server_limit` + + Limit for connections which corresponds to the max_conns parameter of the upstream server. Zero value means there is no limit +
+ `nginxplus_stream_upstream_server_connections` + + Total number of client connections forwarded to this server +
+ `nginxplus_stream_upstream_server_connect_time` + + Average time to connect to the upstream server +
+ `nginxplus_stream_upstream_server_first_byte_time` + + Average time to receive the first byte of data +
+ `nginxplus_stream_upstream_server_response_time` + + Average time to receive the last byte of data +
+ `nginxplus_stream_upstream_server_sent` + + Bytes sent to this server +
+ `nginxplus_stream_upstream_server_received` + + Bytes received from this server +
+ `nginxplus_stream_upstream_server_fails` + + Number of unsuccessful attempts to communicate with the server +
+ `nginxplus_stream_upstream_server_unavail` + + How many times the server became unavailable for client connections (state 'unavail') due to the number of unsuccessful attempts reaching the max_fails threshold +
+ `nginxplus_stream_upstream_server_health_checks_checks` + + Total health check requests +
+ `nginxplus_stream_upstream_server_health_checks_fails` + + Failed health checks +
+ `nginxplus_stream_upstream_server_health_checks_unhealthy` + + How many times the server became unhealthy (state 'unhealthy') +
+ `nginxplus_stream_upstream_server_ssl_handshakes` + + Successful SSL handshakes +
+ `nginxplus_stream_upstream_server_ssl_handshakes_failed` + + Failed SSL handshakes +
+ `nginxplus_stream_upstream_server_ssl_session_reuses` + + Session reuses during SSL handshake +
+ `nginxplus_stream_upstream_zombies` + + Servers removed from the group but still processing active client connections +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Metric + + Description +
+ `nginxplus_stream_zone_sync_zone_records_pending` + + The number of records that need to be sent to the cluster +
+ `nginxplus_stream_zone_sync_zone_records_total` + + The total number of records stored in the shared memory zone +
+ `nginxplus_stream_zone_sync_zone_bytes_in` + + Bytes received by this node +
+ `nginxplus_stream_zone_sync_zone_bytes_out` + + Bytes sent by this node +
+ `nginxplus_stream_zone_sync_zone_msgs_in` + + Total messages received by this node +
+ `nginxplus_stream_zone_sync_zone_msgs_out` + + Total messages sent by this node +
+ `nginxplus_stream_zone_sync_zone_nodes_online` + + Number of peers this node is connected to +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Metric + + Description +
+ `nginxplus_location_zone_requests` + + Total client requests +
+ `nginxplus_location_zone_responses` + + Total responses sent to clients +
+ `nginxplus_location_zone_responses_codes` + + Total responses sent to clients by code +
+ `nginxplus_location_zone_discarded` + + Requests completed without sending a response +
+ `nginxplus_location_zone_received` + + Bytes received from clients +
+ `nginxplus_location_zone_sent` + + Bytes sent to clients +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Metric + + Description +
+ `nginxplus_resolver_name` + + Total requests to resolve names to addresses +
+ `nginxplus_resolver_srv` + + Total requests to resolve SRV records +
+ `nginxplus_resolver_addr` + + Total requests to resolve addresses to names +
+ `nginxplus_resolver_noerror` + + Total number of successful responses +
+ `nginxplus_resolver_formerr` + + Total number of FORMERR responses +
+ `nginxplus_resolver_servfail` + + Total number of SERVFAIL responses +
+ `nginxplus_resolver_nxdomain` + + Total number of NXDOMAIN responses +
+ `nginxplus_resolver_notimp` + + Total number of NOTIMP responses +
+ `nginxplus_resolver_refused` + + Total number of REFUSED responses +
+ `nginxplus_resolver_timedout` + + Total number of timed out requests +
+ `nginxplus_resolver_unknown` + + Total requests completed with an unknown error +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Metric + + Description +
+ `nginxplus_limit_request_passed` + + Total number of requests that were neither limited nor accounted as limited +
+ `nginxplus_limit_request_rejected` + + Total number of requests that were rejected +
+ `nginxplus_limit_request_delayed` + + Total number of requests that were delayed +
+ `nginxplus_limit_request_rejected_dry_run` + + Total number of requests accounted as rejected in the dry run mode +
+ `nginxplus_limit_request_delayed_dry_run` + + Total number of requests accounted as delayed in the dry run mode +
+
+ + + + + + + + + + + + + + + + + + + + + + + +
+ Metric + + Description +
+ `nginxplus_limit_connection_passed` + + Total number of connections that were neither limited nor accounted as limited +
+ `nginxplus_limit_connection_rejected` + + Total number of connections that were rejected +
+ `nginxplus_limit_connection_rejected_dry_run` + + Total number of connections accounted as rejected in the dry run mode +
+
+ + + + + + + + + + + + + + + + + + + + + + + +
+ Metric + + Description +
+ `nginxplus_stream_limit_connection_passed` + + Total number of connections that were neither limited nor accounted as limited +
+ `nginxplus_stream_limit_connection_rejected` + + Total number of connections that were rejected +
+ `nginxplus_stream_limit_connection_rejected_dry_run` + + Total number of connections accounted as rejected in the dry run mode +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Metric + + Description +
+ `nginxplus_cache_size` + + Total size of the cache +
+ `nginxplus_cache_max_size` + + Maximum size of the cache +
+ `nginxplus_cache_cold` + + Is the cache considered cold +
+ `nginxplus_cache_hit_responses` + + Total number of cache hits +
+ `nginxplus_cache_hit_bytes` + + Total number of bytes returned from cache hits +
+ `nginxplus_cache_stale_responses` + + Total number of stale cache hits +
+ `nginxplus_cache_stale_bytes` + + Total number of bytes returned from stale cache hits +
+ `nginxplus_cache_updating_responses` + + Total number of cache hits while cache is updating +
+ `nginxplus_cache_updating_bytes` + + Total number of bytes returned from cache while cache is updating +
+ `nginxplus_cache_revalidated_responses` + + Total number of cache revalidations +
+ `nginxplus_cache_revalidated_bytes` + + Total number of bytes returned from cache revalidations +
+ `nginxplus_cache_miss_responses` + + Total number of cache misses +
+ `nginxplus_cache_miss_bytes` + + Total number of bytes returned from cache misses +
+ `nginxplus_cache_expired_responses` + + Total number of cache hits with expired TTL +
+ `nginxplus_cache_expired_bytes` + + Total number of bytes returned from cache hits with expired TTL +
+ `nginxplus_cache_expired_responses_written` + + Total number of cache hits with expired TTL written to cache +
+ `nginxplus_cache_expired_bytes_written` + + Total number of bytes written to cache from cache hits with expired TTL +
+ `nginxplus_cache_bypass_responses` + + Total number of cache bypasses +
+ `nginxplus_cache_bypass_bytes` + + Total number of bytes returned from cache bypasses +
+ `nginxplus_cache_bypass_responses_written` + + Total number of cache bypasses written to cache +
+ `nginxplus_cache_bypass_bytes_written` + + Total number of bytes written to cache from cache bypasses +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Metric + + Description +
+ `nginxplus_worker_connection_accepted` + + The total number of accepted client connections +
+ `nginxplus_worker_connection_dropped` + + The total number of dropped client connections +
+ `nginxplus_worker_connection_active` + + The current number of active client connections +
+ `nginxplus_worker_connection_idle` + + The current number of idle client connections +
+ `nginxplus_worker_http_requests_total` + + The total number of client requests received +
+ `nginxplus_worker_http_requests_current` + + The current number of client requests that are currently being processed +
+
+
+ +For more details, see the [NGINX Prometheus exporter documentation](https://github.com/nginx/nginx-prometheus-exporter?tab=readme-ov-file#metrics-for-nginx-plus). + diff --git a/src/nav/infrastructure.yml b/src/nav/infrastructure.yml index 87788ddc398..ea467351934 100644 --- a/src/nav/infrastructure.yml +++ b/src/nav/infrastructure.yml @@ -209,8 +209,12 @@ pages: path: /docs/infrastructure/host-integrations/host-integrations-list/nextcloud-integration - title: NFS integration path: /docs/infrastructure/host-integrations/host-integrations-list/nfs-monitoring-integration - - title: NGINX integration - path: /install/nginx + - title: NGINX integrations + pages: + - title: NGINX on-host integration + path: /install/nginx + - title: NGINX OpenTelemetry collector + path: /install/nginx-otel - title: NVIDIA GPU integration path: /docs/infrastructure/host-integrations/host-integrations-list/nvidia-gpu-integration - title: NVIDIA Jetson integration From b04c166dcb930edaa0e26384ffce6699a59fcb04 Mon Sep 17 00:00:00 2001 From: sairaj18 Date: Mon, 22 Dec 2025 14:50:47 +0530 Subject: [PATCH 02/32] feat(nginx): Add NGINX OTel docs --- src/install/config/nginx-otel-host.yaml | 16 + src/install/config/nginx-otel-kubernetes.yaml | 13 + src/install/config/nginx-otel.yaml | 224 --- src/install/config/nginx-plus-otel.yaml | 16 + .../nginx-otel-host/collector/environment.mdx | 25 + .../host/configure-collector-opensource.mdx | 11 +- .../host/enable-status-opensource.mdx | 2 +- .../host/prerequisites-opensource.mdx | 12 + .../host/verify.mdx | 4 +- src/install/nginx-otel-host/intro.mdx | 7 + .../logs/forwarding-opensource.mdx | 36 +- src/install/nginx-otel-host/whatsNext.mdx | 180 +++ src/install/nginx-otel-kubernetes/intro.mdx | 7 + .../kubernetes/install-collector.mdx | 163 ++ .../kubernetes/prerequisites.mdx | 10 + .../kubernetes/status-endpoint.mdx | 0 .../kubernetes/verify.mdx | 11 + .../nginx-otel-kubernetes/whatsNext.mdx | 191 +++ src/install/nginx-otel/appInfo.mdx | 7 - .../nginx-otel/collector/environment.mdx | 44 - .../nginx-otel/host/choose-install.mdx | 7 - .../nginx-otel/host/configure-collector.mdx | 6 - .../nginx-otel/host/enable-status-select.mdx | 5 - src/install/nginx-otel/host/enable-status.mdx | 6 - .../nginx-otel/host/install-collector-apt.mdx | 18 - .../nginx-otel/host/install-collector-yum.mdx | 18 - .../nginx-otel/host/install-collector.mdx | 6 - src/install/nginx-otel/host/license.mdx | 9 - .../nginx-otel/host/metrics-opensource.mdx | 108 -- src/install/nginx-otel/host/metrics-plus.mdx | 386 ----- src/install/nginx-otel/host/prerequisites.mdx | 11 - src/install/nginx-otel/intro.mdx | 8 - src/install/nginx-otel/kubernetes/deploy.mdx | 27 - src/install/nginx-otel/kubernetes/license.mdx | 6 - .../nginx-otel/kubernetes/prepare-values.mdx | 139 -- .../nginx-otel/kubernetes/prerequisites.mdx | 11 - src/install/nginx-otel/kubernetes/secret.mdx | 37 - src/install/nginx-otel/kubernetes/verify.mdx | 29 - src/install/nginx-otel/logs/forwarding.mdx | 6 - src/install/nginx-otel/whatsNext.mdx | 1426 ----------------- .../nginx-plus-otel/collector/environment.mdx | 25 + .../host/configure-collector-plus.mdx | 33 +- .../host/enable-status-plus.mdx | 0 .../host/prerequisites-plus.mdx | 14 + src/install/nginx-plus-otel/host/verify.mdx | 11 + src/install/nginx-plus-otel/intro.mdx | 8 + .../logs/forwarding-plus.mdx | 36 +- src/install/nginx-plus-otel/whatsNext.mdx | 793 +++++++++ src/nav/infrastructure.yml | 14 +- 49 files changed, 1604 insertions(+), 2578 deletions(-) create mode 100644 src/install/config/nginx-otel-host.yaml create mode 100644 src/install/config/nginx-otel-kubernetes.yaml delete mode 100644 src/install/config/nginx-otel.yaml create mode 100644 src/install/config/nginx-plus-otel.yaml create mode 100644 src/install/nginx-otel-host/collector/environment.mdx rename src/install/{nginx-otel => nginx-otel-host}/host/configure-collector-opensource.mdx (81%) rename src/install/{nginx-otel => nginx-otel-host}/host/enable-status-opensource.mdx (95%) create mode 100644 src/install/nginx-otel-host/host/prerequisites-opensource.mdx rename src/install/{nginx-otel => nginx-otel-host}/host/verify.mdx (75%) create mode 100644 src/install/nginx-otel-host/intro.mdx rename src/install/{nginx-otel => nginx-otel-host}/logs/forwarding-opensource.mdx (51%) create mode 100644 src/install/nginx-otel-host/whatsNext.mdx create mode 100644 src/install/nginx-otel-kubernetes/intro.mdx create mode 100644 src/install/nginx-otel-kubernetes/kubernetes/install-collector.mdx create mode 100644 src/install/nginx-otel-kubernetes/kubernetes/prerequisites.mdx rename src/install/{nginx-otel => nginx-otel-kubernetes}/kubernetes/status-endpoint.mdx (100%) create mode 100644 src/install/nginx-otel-kubernetes/kubernetes/verify.mdx create mode 100644 src/install/nginx-otel-kubernetes/whatsNext.mdx delete mode 100644 src/install/nginx-otel/appInfo.mdx delete mode 100644 src/install/nginx-otel/collector/environment.mdx delete mode 100644 src/install/nginx-otel/host/choose-install.mdx delete mode 100644 src/install/nginx-otel/host/configure-collector.mdx delete mode 100644 src/install/nginx-otel/host/enable-status-select.mdx delete mode 100644 src/install/nginx-otel/host/enable-status.mdx delete mode 100644 src/install/nginx-otel/host/install-collector-apt.mdx delete mode 100644 src/install/nginx-otel/host/install-collector-yum.mdx delete mode 100644 src/install/nginx-otel/host/install-collector.mdx delete mode 100644 src/install/nginx-otel/host/license.mdx delete mode 100644 src/install/nginx-otel/host/metrics-opensource.mdx delete mode 100644 src/install/nginx-otel/host/metrics-plus.mdx delete mode 100644 src/install/nginx-otel/host/prerequisites.mdx delete mode 100644 src/install/nginx-otel/intro.mdx delete mode 100644 src/install/nginx-otel/kubernetes/deploy.mdx delete mode 100644 src/install/nginx-otel/kubernetes/license.mdx delete mode 100644 src/install/nginx-otel/kubernetes/prepare-values.mdx delete mode 100644 src/install/nginx-otel/kubernetes/prerequisites.mdx delete mode 100644 src/install/nginx-otel/kubernetes/secret.mdx delete mode 100644 src/install/nginx-otel/kubernetes/verify.mdx delete mode 100644 src/install/nginx-otel/logs/forwarding.mdx delete mode 100644 src/install/nginx-otel/whatsNext.mdx create mode 100644 src/install/nginx-plus-otel/collector/environment.mdx rename src/install/{nginx-otel => nginx-plus-otel}/host/configure-collector-plus.mdx (60%) rename src/install/{nginx-otel => nginx-plus-otel}/host/enable-status-plus.mdx (100%) create mode 100644 src/install/nginx-plus-otel/host/prerequisites-plus.mdx create mode 100644 src/install/nginx-plus-otel/host/verify.mdx create mode 100644 src/install/nginx-plus-otel/intro.mdx rename src/install/{nginx-otel => nginx-plus-otel}/logs/forwarding-plus.mdx (52%) create mode 100644 src/install/nginx-plus-otel/whatsNext.mdx diff --git a/src/install/config/nginx-otel-host.yaml b/src/install/config/nginx-otel-host.yaml new file mode 100644 index 00000000000..8b221e9aa93 --- /dev/null +++ b/src/install/config/nginx-otel-host.yaml @@ -0,0 +1,16 @@ +agentName: nginx-otel-host +agentType: integration +title: 'Monitor self-hosted NGINX with OpenTelemetry' +metaDescription: 'Send your NGINX metrics and logs to New Relic using the OpenTelemetry Collector.' +introFilePath: 'src/install/nginx-otel-host/intro.mdx' +appInfo: [] +redirects: + - /docs/infrastructure/host-integrations/host-integrations-list/nginx/nginx-otel-host +steps: + - filePath: 'src/install/nginx-otel-host/host/prerequisites-opensource.mdx' + - filePath: 'src/install/nginx-otel-host/host/enable-status-opensource.mdx' + - filePath: 'src/install/nginx-otel-host/host/configure-collector-opensource.mdx' + - filePath: 'src/install/nginx-otel-host/collector/environment.mdx' + - filePath: 'src/install/nginx-otel-host/logs/forwarding-opensource.mdx' + - filePath: 'src/install/nginx-otel-host/host/verify.mdx' +whatsNextFilePath: 'src/install/nginx-otel-host/whatsNext.mdx' diff --git a/src/install/config/nginx-otel-kubernetes.yaml b/src/install/config/nginx-otel-kubernetes.yaml new file mode 100644 index 00000000000..bb71907cdfb --- /dev/null +++ b/src/install/config/nginx-otel-kubernetes.yaml @@ -0,0 +1,13 @@ +agentName: nginx-otel-kubernetes +agentType: integration +title: 'Monitor NGINX on Kubernetes with OpenTelemetry' +metaDescription: 'Send your NGINX metrics and logs from Kubernetes to New Relic using the OpenTelemetry Collector.' +introFilePath: 'src/install/nginx-otel-kubernetes/intro.mdx' +appInfo: [] +redirects: + - /docs/infrastructure/host-integrations/host-integrations-list/nginx/nginx-otel-kubernetes +steps: + - filePath: 'src/install/nginx-otel-kubernetes/kubernetes/prerequisites.mdx' + - filePath: 'src/install/nginx-otel-kubernetes/kubernetes/install-collector.mdx' + - filePath: 'src/install/nginx-otel-kubernetes/kubernetes/verify.mdx' +whatsNextFilePath: 'src/install/nginx-otel-kubernetes/whatsNext.mdx' diff --git a/src/install/config/nginx-otel.yaml b/src/install/config/nginx-otel.yaml deleted file mode 100644 index 5c952057ec8..00000000000 --- a/src/install/config/nginx-otel.yaml +++ /dev/null @@ -1,224 +0,0 @@ -agentName: nginx-otel -agentType: integration -title: 'NGINX OpenTelemetry collector' -metaDescription: 'Install the NGINX OpenTelemetry collector' -introFilePath: 'src/install/nginx-otel/intro.mdx' -appInfo: - - optionType: dropdown1 - label: '' - placeholder: 'Select your environment' - options: - - value: 'host' - displayName: 'Linux' - logo: 'linux' - - value: 'kubernetes' - displayName: 'Kubernetes' - logo: 'k8' - - optionType: deployment - label: '' - placeholder: "Choose one:" - options: - - value: "apt" - displayName: "Install using Apt (Debian, Ubuntu)" - recommendedGuided: true - - value: "yum" - displayName: "Install using Yum (Amazon Linux, CentOS, RHEL)" - logo: 'linux' - recommendedGuided: true - - optionType: nginx_type - label: '' - placeholder: "Choose one:" - options: - - value: "opensource" - displayName: "NGINX Open Source" - recommendedGuided: true - - value: "plus" - displayName: "NGINX Plus" - recommendedGuided: true - -steps: - - filePath: 'src/install/nginx-otel/appInfo.mdx' - - filePath: 'src/install/nginx-otel/host/choose-install.mdx' - overrides: - - isConditionalStep: true - selectedOptions: - - optionType: dropdown1 - options: - - value: 'host' - - filePath: 'src/install/nginx-otel/host/prerequisites.mdx' - overrides: - - isConditionalStep: true - selectedOptions: - - optionType: dropdown1 - options: - - value: 'host' - - filePath: 'src/install/nginx-otel/host/enable-status-select.mdx' - overrides: - - isConditionalStep: true - selectedOptions: - - optionType: dropdown1 - options: - - value: 'host' - - filePath: 'src/install/nginx-otel/host/enable-status.mdx' - overrides: - - filePath: 'src/install/nginx-otel/host/enable-status-opensource.mdx' - selectedOptions: - - optionType: dropdown1 - options: - - value: 'host' - - optionType: nginx_type - options: - - value: 'opensource' - - filePath: 'src/install/nginx-otel/host/enable-status-plus.mdx' - selectedOptions: - - optionType: dropdown1 - options: - - value: 'host' - - optionType: nginx_type - options: - - value: 'plus' - - isConditionalStep: true - selectedOptions: - - optionType: dropdown1 - options: - - value: 'host' - - filePath: 'src/install/nginx-otel/host/license.mdx' - overrides: - - isConditionalStep: true - selectedOptions: - - optionType: dropdown1 - options: - - value: 'host' - - filePath: 'src/install/nginx-otel/host/install-collector.mdx' - overrides: - - filePath: 'src/install/nginx-otel/host/install-collector-apt.mdx' - selectedOptions: - - optionType: dropdown1 - options: - - value: 'host' - - optionType: deployment - options: - - value: 'apt' - - filePath: 'src/install/nginx-otel/host/install-collector-yum.mdx' - selectedOptions: - - optionType: dropdown1 - options: - - value: 'host' - - optionType: deployment - options: - - value: 'yum' - - isConditionalStep: true - selectedOptions: - - optionType: dropdown1 - options: - - value: 'host' - - filePath: 'src/install/nginx-otel/host/configure-collector.mdx' - overrides: - - filePath: 'src/install/nginx-otel/host/configure-collector-opensource.mdx' - selectedOptions: - - optionType: dropdown1 - options: - - value: 'host' - - optionType: nginx_type - options: - - value: 'opensource' - - filePath: 'src/install/nginx-otel/host/configure-collector-plus.mdx' - selectedOptions: - - optionType: dropdown1 - options: - - value: 'host' - - optionType: nginx_type - options: - - value: 'plus' - - isConditionalStep: true - selectedOptions: - - optionType: dropdown1 - options: - - value: 'host' - - filePath: 'src/install/nginx-otel/collector/environment.mdx' - overrides: - - isConditionalStep: true - selectedOptions: - - optionType: dropdown1 - options: - - value: 'host' - - filePath: 'src/install/nginx-otel/logs/forwarding.mdx' - overrides: - - filePath: 'src/install/nginx-otel/logs/forwarding-opensource.mdx' - selectedOptions: - - optionType: dropdown1 - options: - - value: 'host' - - optionType: nginx_type - options: - - value: 'opensource' - - filePath: 'src/install/nginx-otel/logs/forwarding-plus.mdx' - selectedOptions: - - optionType: dropdown1 - options: - - value: 'host' - - optionType: nginx_type - options: - - value: 'plus' - - isConditionalStep: true - selectedOptions: - - optionType: dropdown1 - options: - - value: 'host' - - filePath: 'src/install/nginx-otel/host/verify.mdx' - overrides: - - isConditionalStep: true - selectedOptions: - - optionType: dropdown1 - options: - - value: 'host' - - filePath: 'src/install/nginx-otel/kubernetes/prerequisites.mdx' - overrides: - - isConditionalStep: true - selectedOptions: - - optionType: dropdown1 - options: - - value: 'kubernetes' - - filePath: 'src/install/nginx-otel/kubernetes/status-endpoint.mdx' - overrides: - - isConditionalStep: true - selectedOptions: - - optionType: dropdown1 - options: - - value: 'kubernetes' - - filePath: 'src/install/nginx-otel/kubernetes/license.mdx' - overrides: - - isConditionalStep: true - selectedOptions: - - optionType: dropdown1 - options: - - value: 'kubernetes' - - filePath: 'src/install/nginx-otel/kubernetes/prepare-values.mdx' - overrides: - - isConditionalStep: true - selectedOptions: - - optionType: dropdown1 - options: - - value: 'kubernetes' - - filePath: 'src/install/nginx-otel/kubernetes/secret.mdx' - overrides: - - isConditionalStep: true - selectedOptions: - - optionType: dropdown1 - options: - - value: 'kubernetes' - - filePath: 'src/install/nginx-otel/kubernetes/deploy.mdx' - overrides: - - isConditionalStep: true - selectedOptions: - - optionType: dropdown1 - options: - - value: 'kubernetes' - - filePath: 'src/install/nginx-otel/kubernetes/verify.mdx' - overrides: - - isConditionalStep: true - selectedOptions: - - optionType: dropdown1 - options: - - value: 'kubernetes' -whatsNextFilePath: 'src/install/nginx-otel/whatsNext.mdx' diff --git a/src/install/config/nginx-plus-otel.yaml b/src/install/config/nginx-plus-otel.yaml new file mode 100644 index 00000000000..e57c354d418 --- /dev/null +++ b/src/install/config/nginx-plus-otel.yaml @@ -0,0 +1,16 @@ +agentName: nginx-plus-otel +agentType: integration +title: 'Monitor self-hosted NGINX Plus with OpenTelemetry' +metaDescription: 'Send your NGINX Plus metrics to New Relic using the OpenTelemetry contirb collector.' +introFilePath: 'src/install/nginx-plus-otel/intro.mdx' +appInfo: [] +redirects: + - /docs/infrastructure/host-integrations/host-integrations-list/nginx/nginx-plus-otel +steps: + - filePath: 'src/install/nginx-plus-otel/host/prerequisites-plus.mdx' + - filePath: 'src/install/nginx-plus-otel/host/enable-status-plus.mdx' + - filePath: 'src/install/nginx-plus-otel/host/configure-collector-plus.mdx' + - filePath: 'src/install/nginx-plus-otel/collector/environment.mdx' + - filePath: 'src/install/nginx-plus-otel/logs/forwarding-plus.mdx' + - filePath: 'src/install/nginx-plus-otel/host/verify.mdx' +whatsNextFilePath: 'src/install/nginx-plus-otel/whatsNext.mdx' diff --git a/src/install/nginx-otel-host/collector/environment.mdx b/src/install/nginx-otel-host/collector/environment.mdx new file mode 100644 index 00000000000..836a314b9da --- /dev/null +++ b/src/install/nginx-otel-host/collector/environment.mdx @@ -0,0 +1,25 @@ +--- +headingText: Set environment variables for the collector +componentType: default +--- + +Inject your New Relic and OTLP endpoint into the collector service so the exporter can authenticate. + +1. Create a systemd override directory: + ```bash + sudo mkdir -p /etc/systemd/system/otelcol-contrib.service.d + ``` +2. Write `environment.conf` with your OTLP endpoint. Replace `YOUR_LICENSE_KEY` with the New Relic license key and `YOUR_OTLP_ENDPOINT` with the appropriate endpoint for your region. Refer to the OTLP endpoint configuration [documentation](https://docs.newrelic.com/docs/opentelemetry/best-practices/opentelemetry-otlp/#configure-endpoint-port-protocol) to select the right endpoint. + + ```bash + cat < +- NGINX with the [HTTP stub status](https://nginx.org/en/docs/http/ngx_http_stub_status_module.html) module enabled +- [OpenTelemetry Collector Contrib](https://github.com/open-telemetry/opentelemetry-collector-contrib/releases/latest) installed and running on a linux host +- Network access from the linux host to: + - NGINX HTTP stub status endpoint + - Anyone of the [New Relic OTLP](https://docs.newrelic.com/docs/opentelemetry/best-practices/opentelemetry-otlp/#configure-endpoint-port-protocol:~:text=HTTP-,Endpoint,-Supported%20ports) enpoint diff --git a/src/install/nginx-otel/host/verify.mdx b/src/install/nginx-otel-host/host/verify.mdx similarity index 75% rename from src/install/nginx-otel/host/verify.mdx rename to src/install/nginx-otel-host/host/verify.mdx index bc326529081..6a0e7ee56cc 100644 --- a/src/install/nginx-otel/host/verify.mdx +++ b/src/install/nginx-otel-host/host/verify.mdx @@ -4,10 +4,8 @@ componentType: default --- 1. Go to **[one.newrelic.com](https://one.newrelic.com) > Integrations & Agents**. -2. Select **Dashboards**, and click **NGINX overview dashboard**. +2. Select **Dashboards**, and click **NGINX OTel overview dashboard**. 3. In the popup window, select your account. 4. Click View dashboard, and see your NGINX data in New Relic. The NGINX metrics are attached to the `Metric` [event type](/docs/using-new-relic/data/understand-data/new-relic-data-types#events-new-relic). You can [query this data](/docs/using-new-relic/data/understand-data/query-new-relic-data) for troubleshooting purposes or to create custom charts and dashboards. - -You can find a full list of metrics and their attributes in the "Metrics collected" section at the end of this guide. diff --git a/src/install/nginx-otel-host/intro.mdx b/src/install/nginx-otel-host/intro.mdx new file mode 100644 index 00000000000..88159369454 --- /dev/null +++ b/src/install/nginx-otel-host/intro.mdx @@ -0,0 +1,7 @@ +--- +headingText: Before you start +componentType: default +--- + +Monitor your NGINX servers using the [OpenTelemetry Collector](https://github.com/open-telemetry/opentelemetry-collector-contrib) to send metrics and telemetry data to New Relic. +This integration leverages the OpenTelemetry [nginxreceiver](https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/receiver/nginxreceiver) to monitor your NGINX performance metrics, connection statistics, and server health. diff --git a/src/install/nginx-otel/logs/forwarding-opensource.mdx b/src/install/nginx-otel-host/logs/forwarding-opensource.mdx similarity index 51% rename from src/install/nginx-otel/logs/forwarding-opensource.mdx rename to src/install/nginx-otel-host/logs/forwarding-opensource.mdx index 8ad663d107f..595031ca1fe 100644 --- a/src/install/nginx-otel/logs/forwarding-opensource.mdx +++ b/src/install/nginx-otel-host/logs/forwarding-opensource.mdx @@ -1,11 +1,17 @@ --- -headingText: (Optional) Forward NGINX logs +headingText: Forward NGINX logs componentType: default --- Extend your collector configuration to include access and error logs if you want log events alongside metrics. -1. Gather the full paths to your NGINX access and error log files. Defaults are usually `/var/log/nginx/access.log` and `/var/log/nginx/error.log`. +### Configure NGINX log format + +Before forwarding logs, configure NGINX to use a structured log format. Refer to the [NGINX log module documentation](https://nginx.org/en/docs/http/ngx_http_log_module.html) for guidance on configuring access and error logs. + +### Configure the OpenTelemetry Collector for log forwarding + +1. Note the full paths to your NGINX access and error log files. Defaults are usually `/var/log/nginx/access.log` and `/var/log/nginx/error.log`. 2. Update `/etc/otelcol-contrib/config.yaml` to add a `filelog` receiver and log pipeline: @@ -13,9 +19,11 @@ Extend your collector configuration to include access and error logs if you want receivers: nginx: # existing stub status receiver configuration - filelog: + filelog/nginx_access_logs: include: - /var/log/nginx/access.log + filelog/nginx_error_logs: + include: - /var/log/nginx/error.log processors: @@ -27,11 +35,21 @@ Extend your collector configuration to include access and error logs if you want # existing settings transform/nginx_metrics: # existing settings - transform/nginx_logs: + transform/nginx_access_logs: + log_statements: + - context: resource + statements: + - set(attributes["nginx.display.name"], Concat(["server", attributes["nginx.deployment.name"]], ":")) + - set(attributes["logtype"], "nginx") + transform/nginx_error_logs: log_statements: - context: resource statements: - set(attributes["nginx.display.name"], Concat(["server", attributes["nginx.deployment.name"]], ":")) + - set(attributes["logtype"], "nginx-error") + + exporters: + # existing exporter setup service: pipelines: @@ -39,9 +57,13 @@ Extend your collector configuration to include access and error logs if you want receivers: [nginx] processors: [resourcedetection, resource, batch, transform/nginx_metrics] exporters: [otlphttp/newrelic] - logs/nginx: - receivers: [filelog] - processors: [batch, resource, transform/nginx_logs] + logs/nginx-access: + receivers: [filelog/nginx_access_logs] + processors: [batch, resource/nginx, transform/nginx_access_logs] + exporters: [otlphttp/newrelic] + logs/nginx-error: + receivers: [filelog/nginx_error_logs] + processors: [batch, resource/nginx, transform/nginx_error_logs] exporters: [otlphttp/newrelic] ``` diff --git a/src/install/nginx-otel-host/whatsNext.mdx b/src/install/nginx-otel-host/whatsNext.mdx new file mode 100644 index 00000000000..71dcea5e1e5 --- /dev/null +++ b/src/install/nginx-otel-host/whatsNext.mdx @@ -0,0 +1,180 @@ +--- +headingText: Metrics collected +componentType: default +--- + +## Metric data [#nginx-opensource-metrics] + +The OpenTelemetry Collector Contrib's [nginxreceiver](https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/receiver/nginxreceiver) collects the following metrics from the NGINX stub status module: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Metric + + Description + + Type +
+ `nginx.connections_accepted` + + The total number of accepted client connections + + Sum +
+ `nginx.connections_handled` + + 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) + + Sum +
+ `nginx.connections_current` + + The current number of nginx connections by state + + Sum +
+ `nginx.requests` + + Total number of requests made to the server since it started + + Sum +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Attribute + + Description + + Example Values +
+ `state` + + The state of a connection (applicable to `nginx.connections_current` metric) + + `active`, `reading`, `writing`, `waiting` +
+ `nginx.server.endpoint` + + The NGINX stub status endpoint URL + + `http://localhost:8080/stub_status` +
+ `nginx.deployment.name` + + A unique name to identify this NGINX deployment + + `production-web-01`, `staging-api` +
+ `nginx.display.name` + + A display-friendly name combining "server" prefix with deployment name + + `server:production-web-01` +
+ `host.name` + + The hostname of the system where NGINX is running + + `web-server-01.example.com` +
+ `host.id` + + The unique identifier of the host system + + `i-1234567890abcdef0` +
+ `logtype` + + The type of log being collected (applicable to logs only). Used by New Relic's built-in [parsing rules](https://docs.newrelic.com/docs/logs/ui-data/built-log-parsing-rules/#nginx) + + `nginx` (for access logs), `nginx-error` (for error logs) +
+
+ +
+ +For more details, see the [NGINX receiver documentation](https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/main/receiver/nginxreceiver/documentation.md). diff --git a/src/install/nginx-otel-kubernetes/intro.mdx b/src/install/nginx-otel-kubernetes/intro.mdx new file mode 100644 index 00000000000..126334b4322 --- /dev/null +++ b/src/install/nginx-otel-kubernetes/intro.mdx @@ -0,0 +1,7 @@ +--- +headingText: Before you start +componentType: default +--- + +Monitor your NGINX servers using the [OpenTelemetry Collector](https://github.com/open-telemetry/opentelemetry-collector-contrib) to send metrics and telemetry data to New Relic. +This integration leverages the OpenTelemetry [nginxreceiver](https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/receiver/nginxreceiver), [receivercreator](https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/receiver/receivercreator) to monitor your NGINX performance metrics, connection statistics, and server health. diff --git a/src/install/nginx-otel-kubernetes/kubernetes/install-collector.mdx b/src/install/nginx-otel-kubernetes/kubernetes/install-collector.mdx new file mode 100644 index 00000000000..b79ac2f2131 --- /dev/null +++ b/src/install/nginx-otel-kubernetes/kubernetes/install-collector.mdx @@ -0,0 +1,163 @@ +--- +headingText: Install and configure the OpenTelemetry Collector +componentType: default +--- + +Deploy the OpenTelemetry Collector to your Kubernetes cluster using Helm. The collector will automatically discover and scrape metrics from your NGINX pods. + +1. Create a Kubernetes secret to store your New Relic credentials. Replace `YOUR_LICENSE_KEY` and `YOUR_OTLP_ENDPOINT` with your actual values. Refer to the OTLP endpoint configuration [documentation](https://docs.newrelic.com/docs/opentelemetry/best-practices/opentelemetry-otlp/#configure-endpoint-port-protocol) to select the appropriate endpoint for your region. + + ```bash + kubectl create secret generic newrelic-licenses --from-literal=NEWRELIC_LICENSE_KEY=YOUR_LICENSE_KEY --from-literal=NEWRELIC_OTLP_ENDPOINT=YOUR_OTLP_ENDPOINT -n newrelic --create-namespace + ``` + +2. Create a `values.yaml` file to configure the OpenTelemetry Collector. Update the placeholders for your cluster name, NGINX pod labels, and stub status endpoint. + + ```yaml + opentelemetry-collector: + mode: deployment + + image: + repository: otel/opentelemetry-collector-contrib + pullPolicy: IfNotPresent + + command: + name: otelcol-contrib + + resources: + limits: + cpu: 500m + memory: 300Mi + requests: + cpu: 100m + memory: 100Mi + + extraEnvs: + - name: NEWRELIC_LICENSE_KEY + valueFrom: + secretKeyRef: + name: newrelic-licenses + key: NEWRELIC_LICENSE_KEY + - name: NEWRELIC_OTLP_ENDPOINT + valueFrom: + secretKeyRef: + name: newrelic-licenses + key: NEWRELIC_OTLP_ENDPOINT + - name: K8S_NODE_NAME + valueFrom: + fieldRef: + fieldPath: spec.nodeName + - name: K8S_CLUSTER_NAME + value: nginx-cluster # Update with your cluster name + + clusterRole: + create: true + rules: + - apiGroups: [""] + resources: ["pods", "nodes", "nodes/stats", "nodes/proxy"] + verbs: ["get", "list", "watch"] + - apiGroups: ["apps"] + resources: ["replicasets"] + verbs: ["get", "list", "watch"] + clusterRoleBinding: + name: "" + + config: + extensions: + health_check: + endpoint: 0.0.0.0:13133 + k8s_observer: + auth_type: serviceAccount + observe_pods: true + observe_nodes: true + + receivers: + receiver_creator/nginx: + watch_observers: [k8s_observer] + receivers: + nginx: + rule: type == "pod" && labels["app"] == "nginx" && labels["role"] == "reverse-proxy" # Update with your labels + config: + endpoint: 'http://`endpoint`:/status' + metrics: + nginx.requests: + enabled: true + nginx.connections_accepted: + enabled: true + nginx.connections_handled: + enabled: true + nginx.connections_current: + enabled: true + collection_interval: 30s + resource_attributes: + nginx.server.endpoint: 'http://`endpoint`:/status' + nginx.port: '' + + processors: + batch: + send_batch_size: 1024 + timeout: 30s + + resource/cluster: + attributes: + - key: k8s.cluster.name + value: "${env:K8S_CLUSTER_NAME}" + action: insert + + transform/nginx: + metric_statements: + - context: resource + statements: + - set(attributes["nginx.display.name"], Concat([ + "server", + "k8s", + attributes["k8s.cluster.name"], + attributes["k8s.namespace.name"], + "pod", + attributes["k8s.pod.name"], + "nginx", + attributes["nginx.port"] + ], ":")) + - set(attributes["nginx.deployment.name"], attributes["k8s.pod.name"]) + + exporters: + otlphttp: + endpoint: "${env:NEWRELIC_OTLP_ENDPOINT}" + headers: + api-key: "${env:NEWRELIC_LICENSE_KEY}" + + service: + extensions: [health_check, k8s_observer] + pipelines: + metrics/nginx: + receivers: [receiver_creator/nginx] + processors: [batch, resource/cluster, transform/nginx] + exporters: [otlphttp] + ``` + +3. Install the [Helm chart](https://github.com/open-telemetry/opentelemetry-helm-charts) using your values.yaml file. + + ```bash + helm repo add open-telemetry https://open-telemetry.github.io/opentelemetry-helm-charts + helm repo update + helm upgrade --install nginx-otel-collector open-telemetry/opentelemetry-collector --namespace newrelic --create-namespace -f values.yaml + ``` + +4. Ensure the pods have successfully spun up. + + ```bash + kubectl get pods -n newrelic --watch + ``` + + You should see pods with names like `nginx-otel-collector-` in a `Running` state in the `newrelic` namespace. + +5. Run an NRQL query in New Relic to confirm data is arriving. Replace the cluster name with the value you set in the values file: + + ```sql + FROM Metric + SELECT * + WHERE metricName LIKE 'nginx.%' + AND instrumentation.provider = 'opentelemetry' + AND k8s.cluster.name = 'nginx-cluster' + SINCE 10 minutes ago + ``` diff --git a/src/install/nginx-otel-kubernetes/kubernetes/prerequisites.mdx b/src/install/nginx-otel-kubernetes/kubernetes/prerequisites.mdx new file mode 100644 index 00000000000..5f511a48bb2 --- /dev/null +++ b/src/install/nginx-otel-kubernetes/kubernetes/prerequisites.mdx @@ -0,0 +1,10 @@ +--- +headingText: Prerequisites +componentType: default +--- + +Before you begin, ensure you have: + +- A [New Relic account](https://newrelic.com/signup) with a +- Enable the [HTTP stub status](https://nginx.org/en/docs/http/ngx_http_stub_status_module.html) module on NGINX pod that needs to be monitored +- Add labels `app` and `role` to each NGINX pod that needs to be monitored diff --git a/src/install/nginx-otel/kubernetes/status-endpoint.mdx b/src/install/nginx-otel-kubernetes/kubernetes/status-endpoint.mdx similarity index 100% rename from src/install/nginx-otel/kubernetes/status-endpoint.mdx rename to src/install/nginx-otel-kubernetes/kubernetes/status-endpoint.mdx diff --git a/src/install/nginx-otel-kubernetes/kubernetes/verify.mdx b/src/install/nginx-otel-kubernetes/kubernetes/verify.mdx new file mode 100644 index 00000000000..6a0e7ee56cc --- /dev/null +++ b/src/install/nginx-otel-kubernetes/kubernetes/verify.mdx @@ -0,0 +1,11 @@ +--- +headingText: Find and use data +componentType: default +--- + +1. Go to **[one.newrelic.com](https://one.newrelic.com) > Integrations & Agents**. +2. Select **Dashboards**, and click **NGINX OTel overview dashboard**. +3. In the popup window, select your account. +4. Click View dashboard, and see your NGINX data in New Relic. + +The NGINX metrics are attached to the `Metric` [event type](/docs/using-new-relic/data/understand-data/new-relic-data-types#events-new-relic). You can [query this data](/docs/using-new-relic/data/understand-data/query-new-relic-data) for troubleshooting purposes or to create custom charts and dashboards. diff --git a/src/install/nginx-otel-kubernetes/whatsNext.mdx b/src/install/nginx-otel-kubernetes/whatsNext.mdx new file mode 100644 index 00000000000..58ac2c40cf9 --- /dev/null +++ b/src/install/nginx-otel-kubernetes/whatsNext.mdx @@ -0,0 +1,191 @@ +--- +headingText: Metrics collected +componentType: default +--- + +## Metric data [#nginx-opensource-metrics] + +The OpenTelemetry Collector Contrib's [nginxreceiver](https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/receiver/nginxreceiver) collects the following metrics from the NGINX stub status module: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Metric + + Description + + Type +
+ `nginx.connections_accepted` + + The total number of accepted client connections + + Sum +
+ `nginx.connections_handled` + + 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) + + Sum +
+ `nginx.connections_current` + + The current number of nginx connections by state + + Sum +
+ `nginx.requests` + + Total number of requests made to the server since it started + + Sum +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Attribute + + Description + + Example Values +
+ `state` + + The state of a connection (applicable to `nginx.connections_current` metric) + + `active`, `reading`, `writing`, `waiting` +
+ `nginx.server.endpoint` + + The NGINX stub status endpoint URL + + `http://10.244.1.5:8080/status` +
+ `nginx.port` + + The port number where stub status is exposed + + `8080` +
+ `k8s.cluster.name` + + The name of the Kubernetes cluster + + `nginx-cluster`, `production-k8s` +
+ `k8s.namespace.name` + + The Kubernetes namespace where the NGINX pod is running + + `default`, `nginx-ingress` +
+ `k8s.pod.name` + + The name of the Kubernetes pod running NGINX + + `nginx-deployment-5d7b9c8f4d-abc12` +
+ `nginx.deployment.name` + + The deployment name (set to the pod name in Kubernetes) + + `nginx-deployment-5d7b9c8f4d-abc12` +
+ `nginx.display.name` + + A display-friendly name combining cluster, namespace, and pod information + + `server:k8s:nginx-cluster:default:pod:nginx-deployment-5d7b9c8f4d-abc12:nginx:8080` +
+
+ +
+ +For more details, see the [NGINX receiver documentation](https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/main/receiver/nginxreceiver/documentation.md). diff --git a/src/install/nginx-otel/appInfo.mdx b/src/install/nginx-otel/appInfo.mdx deleted file mode 100644 index 7ee435867be..00000000000 --- a/src/install/nginx-otel/appInfo.mdx +++ /dev/null @@ -1,7 +0,0 @@ ---- -componentType: appInfoConfigOption -optionType: dropdown1 -headingText: Choose your deployment ---- - -Use this page to set up the OpenTelemetry Collector for either a single NGINX host or a Kubernetes deployment. Pick the option that matches your environment to see the right steps. diff --git a/src/install/nginx-otel/collector/environment.mdx b/src/install/nginx-otel/collector/environment.mdx deleted file mode 100644 index 0b1aefc44b1..00000000000 --- a/src/install/nginx-otel/collector/environment.mdx +++ /dev/null @@ -1,44 +0,0 @@ ---- -headingText: Set environment variables for the collector -componentType: default ---- - -Inject your New Relic license key and OTLP endpoint into the collector service so the exporter can authenticate. - -1. Create a systemd override directory: - ```bash - sudo mkdir -p /etc/systemd/system/otelcol-contrib.service.d - ``` -2. Write `environment.conf` with the region-specific endpoint. Replace `YOUR_LICENSE_KEY` with the key you collected earlier. - - **US region** - ```bash - cat < - - - - Metric - - - Description - - - - - - - `nginx.connections_accepted` - - - Total number of accepted client connections (Sum) - - - - - `nginx.connections_handled` - - - Total number of handled client connections (Sum) - - - - - `nginx.connections_current` - - - Current number of active client connections. Includes `waiting`, `reading`, and `writing` states (Gauge) - - - - - `nginx.requests` - - - Total number of client requests (Sum) - - - - - -### Connection state metrics - -These metrics break down `nginx.connections_current` by connection state: - - - - - - - - - - - - - - - - - - - - - - -
- Metric - - Description -
- `nginx_connections_reading` - - Current connections where NGINX is reading the request header (Gauge) -
- `nginx_connections_writing` - - Current connections where NGINX is writing the response back to the client (Gauge) -
- `nginx_connections_waiting` - - Current idle client connections waiting for a request (Keep-Alive) (Gauge) -
- -### Resource attributes - -All metrics include the following resource attributes: - -- `nginx.server.endpoint` - The stub status endpoint URL -- `nginx.deployment.name` - Your configured deployment name -- `nginx.display.name` - Formatted display name combining server and deployment -- `host.name` - The hostname where NGINX is running -- `host.id` - The host identifier diff --git a/src/install/nginx-otel/host/metrics-plus.mdx b/src/install/nginx-otel/host/metrics-plus.mdx deleted file mode 100644 index af0a7a5153c..00000000000 --- a/src/install/nginx-otel/host/metrics-plus.mdx +++ /dev/null @@ -1,386 +0,0 @@ ---- -componentType: default -headingText: Metrics collected ---- - -The NGINX Plus OpenTelemetry integration collects metrics through the NGINX Prometheus exporter. The integration retrieves data from the [NGINX Plus HTTP API module](http://nginx.org/en/docs/http/ngx_http_api_module.html) via the Prometheus exporter. - -### Metric data [#metrics] - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- Metric - - Description -
- `nginxplus_server_zone_requests` - - Total number of client requests received from clients (Counter) -
- `nginxplus_server_zone_responses` - - Total number of responses sent to clients by status code: 1xx, 2xx, 3xx, 4xx, 5xx (Counter) -
- `nginxplus_server_zone_discarded` - - Total number of requests completed without sending a response (Counter) -
- `nginxplus_server_zone_received` - - Total number of bytes received from clients (Counter) -
- `nginxplus_server_zone_sent` - - Total number of bytes sent to clients (Counter) -
- `nginxplus_server_zone_processing` - - Current number of client requests being processed (Gauge) -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- Metric - - Description -
- `nginxplus_upstream_server_requests` - - Total number of client requests forwarded to upstream servers (Counter) -
- `nginxplus_upstream_server_responses` - - Total number of responses obtained from upstream servers by status code (Counter) -
- `nginxplus_upstream_server_active` - - Current number of active connections (Gauge) -
- `nginxplus_upstream_server_sent` - - Total number of bytes sent to upstream servers (Counter) -
- `nginxplus_upstream_server_received` - - Total number of bytes received from upstream servers (Counter) -
- `nginxplus_upstream_server_fails` - - Total number of unsuccessful attempts to communicate with upstream servers (Counter) -
- `nginxplus_upstream_server_unavail` - - Number of times the server became unavailable for client requests (Counter) -
- `nginxplus_upstream_server_health_checks_checks` - - Total health check requests (Counter) -
- `nginxplus_upstream_server_health_checks_fails` - - Failed health checks (Counter) -
- `nginxplus_upstream_server_health_checks_unhealthy` - - Number of times server became unhealthy (Counter) -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- Metric - - Description -
- `nginxplus_cache_size` - - Current size of the cache (Gauge) -
- `nginxplus_cache_max_size` - - Maximum cache size limit (Gauge) -
- `nginxplus_cache_hit_responses` - - Total responses read from the cache by status code (Counter) -
- `nginxplus_cache_miss_responses` - - Total responses not found in the cache (Counter) -
- `nginxplus_cache_stale_responses` - - Total stale cache responses (Counter) -
- `nginxplus_cache_bypass_responses` - - Total responses bypassing the cache (Counter) -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- Metric - - Description -
- `nginxplus_connections_accepted` - - Total accepted client connections (Counter) -
- `nginxplus_connections_dropped` - - Total dropped client connections (Counter) -
- `nginxplus_connections_active` - - Current active client connections (Gauge) -
- `nginxplus_connections_idle` - - Current idle client connections (Gauge) -
- `nginxplus_ssl_handshakes` - - Total successful SSL handshakes (Counter) -
- `nginxplus_ssl_handshakes_failed` - - Total failed SSL handshakes (Counter) -
- `nginxplus_ssl_session_reuses` - - Total SSL session reuses during handshake (Counter) -
-
- - - All metrics include the following resource attributes: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- Name - - Description -
- `nginx.server.endpoint` - - The HTTP API endpoint URL -
- `nginx.deployment.name` - - Your configured deployment name -
- `nginx.display.name` - - Formatted display name -
- `host.name` - - The hostname where NGINX Plus is running -
- `host.id` - - The host identifier -
-
-
- -For a complete list of all available metrics, see the [NGINX Prometheus exporter documentation](https://github.com/nginx/nginx-prometheus-exporter?tab=readme-ov-file#metrics-for-nginx-plus). diff --git a/src/install/nginx-otel/host/prerequisites.mdx b/src/install/nginx-otel/host/prerequisites.mdx deleted file mode 100644 index a0d882a78d1..00000000000 --- a/src/install/nginx-otel/host/prerequisites.mdx +++ /dev/null @@ -1,11 +0,0 @@ ---- -headingText: Check the compatibility and requirements -componentType: default ---- - -Make sure your host meets the following requirements before you begin: - -- You run either NGINX Open Source with the HTTP stub status module or NGINX Plus with the HTTP API module enabled. -- You can connect to the host with `sudo` privileges to install packages and restart services. -- The host can reach New Relic over outbound HTTPS on port 443 and the OTLP ingest endpoint you plan to use. -- You can install and run the OpenTelemetry Collector Contrib distribution on the host. diff --git a/src/install/nginx-otel/intro.mdx b/src/install/nginx-otel/intro.mdx deleted file mode 100644 index a44e694f335..00000000000 --- a/src/install/nginx-otel/intro.mdx +++ /dev/null @@ -1,8 +0,0 @@ ---- -headingText: Before you start -componentType: default ---- - -Our NGINX OpenTelemetry integration helps you stream metrics and logs from NGINX (open source or NGINX Plus) through the OpenTelemetry Collector Contrib distribution. Once the collector forwards data via OTLP, you can explore it in New Relic dashboards, charts, and alerts. - -To get the most from this guide, decide where your NGINX workloads run and select the matching install path. You need a [New Relic account](https://newrelic.com/signup) and a license key to complete the setup. diff --git a/src/install/nginx-otel/kubernetes/deploy.mdx b/src/install/nginx-otel/kubernetes/deploy.mdx deleted file mode 100644 index 0231c8a233f..00000000000 --- a/src/install/nginx-otel/kubernetes/deploy.mdx +++ /dev/null @@ -1,27 +0,0 @@ ---- -headingText: Deploy the collector with Helm -componentType: default ---- - -Use the OpenTelemetry Helm chart to deploy (or upgrade) the collector with your custom values file. - -```bash -helm repo add open-telemetry https://open-telemetry.github.io/opentelemetry-helm-charts -helm repo update -helm upgrade --install nginx-otel-collector open-telemetry/opentelemetry-collector \ - --namespace newrelic \ - --create-namespace \ - -f otel-collector-values.yaml -``` - -Successful output looks similar to: - -``` -Release "nginx-otel-collector" has been upgraded. Happy Helming! -NAME: nginx-otel-collector -LAST DEPLOYED: 2025-01-01 12:00:00 -0500 CST -NAMESPACE: newrelic -STATUS: deployed -REVISION: 1 -TEST SUITE: None -``` diff --git a/src/install/nginx-otel/kubernetes/license.mdx b/src/install/nginx-otel/kubernetes/license.mdx deleted file mode 100644 index 33e412db4a0..00000000000 --- a/src/install/nginx-otel/kubernetes/license.mdx +++ /dev/null @@ -1,6 +0,0 @@ ---- -headingText: Collect your New Relic license key -componentType: default ---- - -The Helm chart expects a Kubernetes secret that supplies your New Relic license key and OTLP endpoint. Retrieve the license key for the account where you want to store NGINX data. You'll add it to a secret in a later step. diff --git a/src/install/nginx-otel/kubernetes/prepare-values.mdx b/src/install/nginx-otel/kubernetes/prepare-values.mdx deleted file mode 100644 index 3550c741fcd..00000000000 --- a/src/install/nginx-otel/kubernetes/prepare-values.mdx +++ /dev/null @@ -1,139 +0,0 @@ ---- -headingText: Build your Helm values file -componentType: default ---- - -Populate a values file that configures the OpenTelemetry Collector deployment and the receiver creator that scrapes your NGINX pods. - -1. Choose identifiers for your cluster and NGINX pod labels. Example values: - - Cluster name: `nginx-cluster` - - `labels["app"]`: `nginx` - - `labels["role"]`: `reverse-proxy` -2. Create `otel-collector-values.yaml` with content similar to the following. Update the placeholders for your cluster, labels, and stub status endpoint if they differ. - -```yaml -opentelemetry-collector: - mode: deployment - - image: - repository: otel/opentelemetry-collector-contrib - pullPolicy: IfNotPresent - - command: - name: otelcol-contrib - - resources: - limits: - cpu: 500m - memory: 300Mi - requests: - cpu: 100m - memory: 100Mi - - extraEnvs: - - name: NEWRELIC_LICENSE_KEY - valueFrom: - secretKeyRef: - name: newrelic-licenses - key: NEWRELIC_LICENSE_KEY - - name: NEWRELIC_OTLP_ENDPOINT - valueFrom: - secretKeyRef: - name: newrelic-licenses - key: NEWRELIC_OTLP_ENDPOINT - - name: K8S_NODE_NAME - valueFrom: - fieldRef: - fieldPath: spec.nodeName - - name: K8S_CLUSTER_NAME - value: nginx-cluster - - clusterRole: - create: true - rules: - - apiGroups: [""] - resources: ["pods", "nodes", "nodes/stats", "nodes/proxy"] - verbs: ["get", "list", "watch"] - - apiGroups: ["apps"] - resources: ["replicasets"] - verbs: ["get", "list", "watch"] - clusterRoleBinding: - name: "" - - config: - extensions: - health_check: - endpoint: 0.0.0.0:13133 - k8s_observer: - auth_type: serviceAccount - observe_pods: true - observe_nodes: true - - receivers: - receiver_creator/nginx: - watch_observers: [k8s_observer] - receivers: - nginx: - rule: type == "pod" && labels["app"] == "nginx" && labels["role"] == "reverse-proxy" - config: - endpoint: 'http://`endpoint`:8080/status' - metrics: - nginx.requests: - enabled: true - nginx.connections_accepted: - enabled: true - nginx.connections_handled: - enabled: true - nginx.connections_current: - enabled: true - collection_interval: 30s - resource_attributes: - nginx.server.endpoint: 'http://`endpoint`:8080/status' - nginx.port: '8080' - - processors: - batch: - send_batch_size: 1024 - timeout: 30s - - resource/cluster: - attributes: - - key: k8s.cluster.name - value: "nginx-cluster" - action: insert - - transform/nginx: - metric_statements: - - context: resource - statements: - - set(attributes["nginx.display.name"], Concat([ - "server", - "k8s", - attributes["k8s.cluster.name"], - attributes["k8s.namespace.name"], - "pod", - attributes["k8s.pod.name"], - "nginx", - attributes["nginx.port"] - ], ":")) - - set(attributes["nginx.deployment.name"], attributes["k8s.pod.name"]) - - exporters: - otlphttp: - endpoint: "${NEWRELIC_OTLP_ENDPOINT}" - headers: - api-key: "${NEWRELIC_LICENSE_KEY}" - debug: - verbosity: detailed - - service: - telemetry: - logs: - level: debug - extensions: [health_check, k8s_observer] - pipelines: - metrics/nginx: - receivers: [receiver_creator/nginx] - processors: [batch, resource/cluster, transform/nginx] - exporters: [otlphttp, debug] -``` diff --git a/src/install/nginx-otel/kubernetes/prerequisites.mdx b/src/install/nginx-otel/kubernetes/prerequisites.mdx deleted file mode 100644 index 5b41fce881c..00000000000 --- a/src/install/nginx-otel/kubernetes/prerequisites.mdx +++ /dev/null @@ -1,11 +0,0 @@ ---- -headingText: Confirm Kubernetes prerequisites -componentType: default ---- - -Before deploying the collector, ensure you have: - -- `kubectl` access to the target cluster and the namespace where you'll deploy the collector. -- Helm installed locally and pointed at the same kube context. -- NGINX pods that expose the stub status endpoint through a ClusterIP Service reachable by the collector. -- A New Relic license key stored securely for later steps. diff --git a/src/install/nginx-otel/kubernetes/secret.mdx b/src/install/nginx-otel/kubernetes/secret.mdx deleted file mode 100644 index 1391c45c9f5..00000000000 --- a/src/install/nginx-otel/kubernetes/secret.mdx +++ /dev/null @@ -1,37 +0,0 @@ ---- -headingText: Create the New Relic secret -componentType: default ---- - -Store your license key and OTLP endpoint in a Kubernetes secret so the collector can reference them as environment variables. - -Replace `YOUR_LICENSE_KEY` with your value and run the command that matches your ingest region. - -#### US region - -```bash -kubectl create secret generic newrelic-licenses \ - --from-literal=NEWRELIC_LICENSE_KEY=YOUR_LICENSE_KEY \ - --from-literal=NEWRELIC_OTLP_ENDPOINT=https://otlp.nr-data.net:4318 \ - -n newrelic -``` - -#### EU region - -```bash -kubectl create secret generic newrelic-licenses \ - --from-literal=NEWRELIC_LICENSE_KEY=YOUR_LICENSE_KEY \ - --from-literal=NEWRELIC_OTLP_ENDPOINT=https://eu-otlp.nr-data.net:4318 \ - -n newrelic -``` - -#### Staging - -```bash -kubectl create secret generic newrelic-licenses \ - --from-literal=NEWRELIC_LICENSE_KEY=YOUR_LICENSE_KEY \ - --from-literal=NEWRELIC_OTLP_ENDPOINT=https://staging-otlp.nr-data.net:443 \ - -n newrelic -``` - -If the namespace does not exist, add `--create-namespace` or create it beforehand. diff --git a/src/install/nginx-otel/kubernetes/verify.mdx b/src/install/nginx-otel/kubernetes/verify.mdx deleted file mode 100644 index cf68479ad4a..00000000000 --- a/src/install/nginx-otel/kubernetes/verify.mdx +++ /dev/null @@ -1,29 +0,0 @@ ---- -headingText: Verify the Kubernetes deployment -componentType: default ---- - -1. Check that the collector pods are running: - ```bash - kubectl get pods -n newrelic -l app.kubernetes.io/name=opentelemetry-collector - ``` -2. Inspect the Helm release status for additional diagnostics: - ```bash - helm status nginx-otel-collector -n newrelic - ``` -3. Review the collector logs if you need to troubleshoot scrape or export issues: - ```bash - kubectl logs deploy/nginx-otel-collector -n newrelic --tail=50 - ``` -4. Run an NRQL query in New Relic to confirm data is arriving. Replace the cluster name with the value you set in the values file: - - ```sql - SELECT entity.guid - FROM Metric - WHERE metricName LIKE 'nginx.%' - AND instrumentation.provider = 'opentelemetry' - AND k8s.cluster.name = 'nginx-cluster' - SINCE 10 minutes ago - ``` - -If the query returns results, New Relic is receiving NGINX metrics from your cluster. Otherwise, review the collector logs for connection errors or missing permissions. diff --git a/src/install/nginx-otel/logs/forwarding.mdx b/src/install/nginx-otel/logs/forwarding.mdx deleted file mode 100644 index e28d678c430..00000000000 --- a/src/install/nginx-otel/logs/forwarding.mdx +++ /dev/null @@ -1,6 +0,0 @@ ---- -headingText: (Optional) Forward NGINX logs -componentType: default ---- - -The log forwarding configuration will be shown based on your NGINX version selection. diff --git a/src/install/nginx-otel/whatsNext.mdx b/src/install/nginx-otel/whatsNext.mdx deleted file mode 100644 index 3e6422e8993..00000000000 --- a/src/install/nginx-otel/whatsNext.mdx +++ /dev/null @@ -1,1426 +0,0 @@ ---- -headingText: Metrics collected -componentType: default ---- - -## NGINX Open Source metrics [#nginx-opensource] - -The OpenTelemetry Collector's NGINX receiver collects metrics from the HTTP stub status module. Below are the key metrics available: - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- Metric - - Description -
- `nginx.connections_accepted` - - Total number of accepted client connections -
- `nginx.connections_handled` - - Total number of handled connections. Generally, the same as accepted unless some resource limit has been reached -
- `nginx.connections_current` - - Current number of nginx connections by state. States include: `active`, `reading`, `writing`, `waiting` -
- `nginx.requests` - - Total number of requests made to NGINX since start -
-
- -
- -For more details, see the [NGINX receiver documentation](https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/main/receiver/nginxreceiver/documentation.md). - -## NGINX Plus metrics [#nginx-plus] - -NGINX Plus metrics are collected via the Prometheus exporter using the HTTP API module. Below are all available metrics: - - - - - - - - - - - - - - - - -
- Metric - - Description -
- `nginxplus_up` - - Shows the status of the last metric scrape: 1 for a successful scrape and 0 for a failed one -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - -
- Metric - - Description -
- `nginxplus_connections_accepted` - - Accepted client connections -
- `nginxplus_connections_active` - - Active client connections -
- `nginxplus_connections_dropped` - - Dropped client connections -
- `nginxplus_connections_idle` - - Idle client connections -
-
- - - - - - - - - - - - - - - - - - - -
- Metric - - Description -
- `nginxplus_http_requests_total` - - Total http requests -
- `nginxplus_http_requests_current` - - Current http requests -
-
- - - - - - - - - - - - - - - - - - - - - - - -
- Metric - - Description -
- `nginxplus_ssl_handshakes` - - Successful SSL handshakes -
- `nginxplus_ssl_handshakes_failed` - - Failed SSL handshakes -
- `nginxplus_ssl_session_reuses` - - Session reuses during SSL handshake -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- Metric - - Description -
- `nginxplus_server_zone_processing` - - Client requests that are currently being processed -
- `nginxplus_server_zone_requests` - - Total client requests -
- `nginxplus_server_zone_responses` - - Total responses sent to clients -
- `nginxplus_server_zone_responses_codes` - - Total responses sent to clients by code -
- `nginxplus_server_zone_discarded` - - Requests completed without sending a response -
- `nginxplus_server_zone_received` - - Bytes received from clients -
- `nginxplus_server_zone_sent` - - Bytes sent to clients -
- `nginxplus_server_ssl_handshakes` - - Successful SSL handshakes -
- `nginxplus_server_ssl_handshakes_failed` - - Failed SSL handshakes -
- `nginxplus_server_ssl_session_reuses` - - Session reuses during SSL handshake -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- Metric - - Description -
- `nginxplus_stream_server_zone_processing` - - Client connections that are currently being processed -
- `nginxplus_stream_server_zone_connections` - - Total connections -
- `nginxplus_stream_server_zone_sessions` - - Total sessions completed -
- `nginxplus_stream_server_zone_discarded` - - Connections completed without creating a session -
- `nginxplus_stream_server_zone_received` - - Bytes received from clients -
- `nginxplus_stream_server_zone_sent` - - Bytes sent to clients -
- `nginxplus_stream_server_ssl_handshakes` - - Successful SSL handshakes -
- `nginxplus_stream_server_ssl_handshakes_failed` - - Failed SSL handshakes -
- `nginxplus_stream_server_ssl_session_reuses` - - Session reuses during SSL handshake -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- Metric - - Description -
- `nginxplus_upstream_server_state` - - Current state -
- `nginxplus_upstream_server_active` - - Active connections -
- `nginxplus_upstream_server_limit` - - Limit for connections which corresponds to the max_conns parameter of the upstream server. Zero value means there is no limit -
- `nginxplus_upstream_server_requests` - - Total client requests -
- `nginxplus_upstream_server_responses` - - Total responses sent to clients -
- `nginxplus_upstream_server_responses_codes` - - Total responses sent to clients by code -
- `nginxplus_upstream_server_sent` - - Bytes sent to this server -
- `nginxplus_upstream_server_received` - - Bytes received from this server -
- `nginxplus_upstream_server_fails` - - Number of unsuccessful attempts to communicate with the server -
- `nginxplus_upstream_server_unavail` - - How many times the server became unavailable for client requests (state 'unavail') due to the number of unsuccessful attempts reaching the max_fails threshold -
- `nginxplus_upstream_server_header_time` - - Average time to get the response header from the server -
- `nginxplus_upstream_server_response_time` - - Average time to get the full response from the server -
- `nginxplus_upstream_server_health_checks_checks` - - Total health check requests -
- `nginxplus_upstream_server_health_checks_fails` - - Failed health checks -
- `nginxplus_upstream_server_health_checks_unhealthy` - - How many times the server became unhealthy (state 'unhealthy') -
- `nginxplus_upstream_server_ssl_handshakes` - - Successful SSL handshakes -
- `nginxplus_upstream_server_ssl_handshakes_failed` - - Failed SSL handshakes -
- `nginxplus_upstream_server_ssl_session_reuses` - - Session reuses during SSL handshake -
- `nginxplus_upstream_keepalive` - - Idle keepalive connections -
- `nginxplus_upstream_zombies` - - Servers removed from the group but still processing active client requests -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- Metric - - Description -
- `nginxplus_stream_upstream_server_state` - - Current state -
- `nginxplus_stream_upstream_server_active` - - Active connections -
- `nginxplus_stream_upstream_server_limit` - - Limit for connections which corresponds to the max_conns parameter of the upstream server. Zero value means there is no limit -
- `nginxplus_stream_upstream_server_connections` - - Total number of client connections forwarded to this server -
- `nginxplus_stream_upstream_server_connect_time` - - Average time to connect to the upstream server -
- `nginxplus_stream_upstream_server_first_byte_time` - - Average time to receive the first byte of data -
- `nginxplus_stream_upstream_server_response_time` - - Average time to receive the last byte of data -
- `nginxplus_stream_upstream_server_sent` - - Bytes sent to this server -
- `nginxplus_stream_upstream_server_received` - - Bytes received from this server -
- `nginxplus_stream_upstream_server_fails` - - Number of unsuccessful attempts to communicate with the server -
- `nginxplus_stream_upstream_server_unavail` - - How many times the server became unavailable for client connections (state 'unavail') due to the number of unsuccessful attempts reaching the max_fails threshold -
- `nginxplus_stream_upstream_server_health_checks_checks` - - Total health check requests -
- `nginxplus_stream_upstream_server_health_checks_fails` - - Failed health checks -
- `nginxplus_stream_upstream_server_health_checks_unhealthy` - - How many times the server became unhealthy (state 'unhealthy') -
- `nginxplus_stream_upstream_server_ssl_handshakes` - - Successful SSL handshakes -
- `nginxplus_stream_upstream_server_ssl_handshakes_failed` - - Failed SSL handshakes -
- `nginxplus_stream_upstream_server_ssl_session_reuses` - - Session reuses during SSL handshake -
- `nginxplus_stream_upstream_zombies` - - Servers removed from the group but still processing active client connections -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- Metric - - Description -
- `nginxplus_stream_zone_sync_zone_records_pending` - - The number of records that need to be sent to the cluster -
- `nginxplus_stream_zone_sync_zone_records_total` - - The total number of records stored in the shared memory zone -
- `nginxplus_stream_zone_sync_zone_bytes_in` - - Bytes received by this node -
- `nginxplus_stream_zone_sync_zone_bytes_out` - - Bytes sent by this node -
- `nginxplus_stream_zone_sync_zone_msgs_in` - - Total messages received by this node -
- `nginxplus_stream_zone_sync_zone_msgs_out` - - Total messages sent by this node -
- `nginxplus_stream_zone_sync_zone_nodes_online` - - Number of peers this node is connected to -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- Metric - - Description -
- `nginxplus_location_zone_requests` - - Total client requests -
- `nginxplus_location_zone_responses` - - Total responses sent to clients -
- `nginxplus_location_zone_responses_codes` - - Total responses sent to clients by code -
- `nginxplus_location_zone_discarded` - - Requests completed without sending a response -
- `nginxplus_location_zone_received` - - Bytes received from clients -
- `nginxplus_location_zone_sent` - - Bytes sent to clients -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- Metric - - Description -
- `nginxplus_resolver_name` - - Total requests to resolve names to addresses -
- `nginxplus_resolver_srv` - - Total requests to resolve SRV records -
- `nginxplus_resolver_addr` - - Total requests to resolve addresses to names -
- `nginxplus_resolver_noerror` - - Total number of successful responses -
- `nginxplus_resolver_formerr` - - Total number of FORMERR responses -
- `nginxplus_resolver_servfail` - - Total number of SERVFAIL responses -
- `nginxplus_resolver_nxdomain` - - Total number of NXDOMAIN responses -
- `nginxplus_resolver_notimp` - - Total number of NOTIMP responses -
- `nginxplus_resolver_refused` - - Total number of REFUSED responses -
- `nginxplus_resolver_timedout` - - Total number of timed out requests -
- `nginxplus_resolver_unknown` - - Total requests completed with an unknown error -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- Metric - - Description -
- `nginxplus_limit_request_passed` - - Total number of requests that were neither limited nor accounted as limited -
- `nginxplus_limit_request_rejected` - - Total number of requests that were rejected -
- `nginxplus_limit_request_delayed` - - Total number of requests that were delayed -
- `nginxplus_limit_request_rejected_dry_run` - - Total number of requests accounted as rejected in the dry run mode -
- `nginxplus_limit_request_delayed_dry_run` - - Total number of requests accounted as delayed in the dry run mode -
-
- - - - - - - - - - - - - - - - - - - - - - - -
- Metric - - Description -
- `nginxplus_limit_connection_passed` - - Total number of connections that were neither limited nor accounted as limited -
- `nginxplus_limit_connection_rejected` - - Total number of connections that were rejected -
- `nginxplus_limit_connection_rejected_dry_run` - - Total number of connections accounted as rejected in the dry run mode -
-
- - - - - - - - - - - - - - - - - - - - - - - -
- Metric - - Description -
- `nginxplus_stream_limit_connection_passed` - - Total number of connections that were neither limited nor accounted as limited -
- `nginxplus_stream_limit_connection_rejected` - - Total number of connections that were rejected -
- `nginxplus_stream_limit_connection_rejected_dry_run` - - Total number of connections accounted as rejected in the dry run mode -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- Metric - - Description -
- `nginxplus_cache_size` - - Total size of the cache -
- `nginxplus_cache_max_size` - - Maximum size of the cache -
- `nginxplus_cache_cold` - - Is the cache considered cold -
- `nginxplus_cache_hit_responses` - - Total number of cache hits -
- `nginxplus_cache_hit_bytes` - - Total number of bytes returned from cache hits -
- `nginxplus_cache_stale_responses` - - Total number of stale cache hits -
- `nginxplus_cache_stale_bytes` - - Total number of bytes returned from stale cache hits -
- `nginxplus_cache_updating_responses` - - Total number of cache hits while cache is updating -
- `nginxplus_cache_updating_bytes` - - Total number of bytes returned from cache while cache is updating -
- `nginxplus_cache_revalidated_responses` - - Total number of cache revalidations -
- `nginxplus_cache_revalidated_bytes` - - Total number of bytes returned from cache revalidations -
- `nginxplus_cache_miss_responses` - - Total number of cache misses -
- `nginxplus_cache_miss_bytes` - - Total number of bytes returned from cache misses -
- `nginxplus_cache_expired_responses` - - Total number of cache hits with expired TTL -
- `nginxplus_cache_expired_bytes` - - Total number of bytes returned from cache hits with expired TTL -
- `nginxplus_cache_expired_responses_written` - - Total number of cache hits with expired TTL written to cache -
- `nginxplus_cache_expired_bytes_written` - - Total number of bytes written to cache from cache hits with expired TTL -
- `nginxplus_cache_bypass_responses` - - Total number of cache bypasses -
- `nginxplus_cache_bypass_bytes` - - Total number of bytes returned from cache bypasses -
- `nginxplus_cache_bypass_responses_written` - - Total number of cache bypasses written to cache -
- `nginxplus_cache_bypass_bytes_written` - - Total number of bytes written to cache from cache bypasses -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- Metric - - Description -
- `nginxplus_worker_connection_accepted` - - The total number of accepted client connections -
- `nginxplus_worker_connection_dropped` - - The total number of dropped client connections -
- `nginxplus_worker_connection_active` - - The current number of active client connections -
- `nginxplus_worker_connection_idle` - - The current number of idle client connections -
- `nginxplus_worker_http_requests_total` - - The total number of client requests received -
- `nginxplus_worker_http_requests_current` - - The current number of client requests that are currently being processed -
-
-
- -For more details, see the [NGINX Prometheus exporter documentation](https://github.com/nginx/nginx-prometheus-exporter?tab=readme-ov-file#metrics-for-nginx-plus). - diff --git a/src/install/nginx-plus-otel/collector/environment.mdx b/src/install/nginx-plus-otel/collector/environment.mdx new file mode 100644 index 00000000000..836a314b9da --- /dev/null +++ b/src/install/nginx-plus-otel/collector/environment.mdx @@ -0,0 +1,25 @@ +--- +headingText: Set environment variables for the collector +componentType: default +--- + +Inject your New Relic and OTLP endpoint into the collector service so the exporter can authenticate. + +1. Create a systemd override directory: + ```bash + sudo mkdir -p /etc/systemd/system/otelcol-contrib.service.d + ``` +2. Write `environment.conf` with your OTLP endpoint. Replace `YOUR_LICENSE_KEY` with the New Relic license key and `YOUR_OTLP_ENDPOINT` with the appropriate endpoint for your region. Refer to the OTLP endpoint configuration [documentation](https://docs.newrelic.com/docs/opentelemetry/best-practices/opentelemetry-otlp/#configure-endpoint-port-protocol) to select the right endpoint. + + ```bash + cat < +- NGINX Plus version [R13](https://docs.nginx.com/nginx/releases/#r13) or higher +- NGINX Plus with the [HTTP API](https://nginx.org/en/docs/http/ngx_http_api_module.html) module enabled +- [NGINX Prometheus exporter](https://github.com/nginx/nginx-prometheus-exporter) installed and running alongside your NGINX Plus instance to expose HTTP API metrics in Prometheus format +- [OpenTelemetry Collector Contrib](https://github.com/open-telemetry/opentelemetry-collector-contrib/releases/latest) installed and running on a linux host +- Network access from the linux host to: + - NGINX Plus HTTP API endpoint + - Anyone of the [New Relic OTLP](https://docs.newrelic.com/docs/opentelemetry/best-practices/opentelemetry-otlp/#configure-endpoint-port-protocol:~:text=HTTP-,Endpoint,-Supported%20ports) enpoint diff --git a/src/install/nginx-plus-otel/host/verify.mdx b/src/install/nginx-plus-otel/host/verify.mdx new file mode 100644 index 00000000000..3a3b74c5552 --- /dev/null +++ b/src/install/nginx-plus-otel/host/verify.mdx @@ -0,0 +1,11 @@ +--- +headingText: Find and use data +componentType: default +--- + +1. Go to **[one.newrelic.com](https://one.newrelic.com) > Integrations & Agents**. +2. Select **Dashboards**, and click **NGINX OTel overview dashboard**. +3. In the popup window, select your account. +4. Click View dashboard, and see your NGINX Plus data in New Relic. + +The NGINX Plus metrics are attached to the `Metric` [event type](/docs/using-new-relic/data/understand-data/new-relic-data-types#events-new-relic). You can [query this data](/docs/using-new-relic/data/understand-data/query-new-relic-data) for troubleshooting purposes or to create custom charts and dashboards. diff --git a/src/install/nginx-plus-otel/intro.mdx b/src/install/nginx-plus-otel/intro.mdx new file mode 100644 index 00000000000..eddf0805a76 --- /dev/null +++ b/src/install/nginx-plus-otel/intro.mdx @@ -0,0 +1,8 @@ +--- +headingText: Before you start +componentType: default +--- + + +Monitor your NGINX Plus servers using the [OpenTelemetry Collector](https://github.com/open-telemetry/opentelemetry-collector-contrib) to send metrics and telemetry data to New Relic. +This integration leverages the OpenTelemetry [prometheusreceiver](https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/receiver/prometheusreceiver) and [nginx-prometheus-exporter](https://github.com/nginx/nginx-prometheus-exporter) to monitor your NGINX Plus performance metrics, connection statistics, and server health. diff --git a/src/install/nginx-otel/logs/forwarding-plus.mdx b/src/install/nginx-plus-otel/logs/forwarding-plus.mdx similarity index 52% rename from src/install/nginx-otel/logs/forwarding-plus.mdx rename to src/install/nginx-plus-otel/logs/forwarding-plus.mdx index dfc2f8d40c7..1a5afab77d7 100644 --- a/src/install/nginx-otel/logs/forwarding-plus.mdx +++ b/src/install/nginx-plus-otel/logs/forwarding-plus.mdx @@ -1,11 +1,17 @@ --- -headingText: (Optional) Forward NGINX logs +headingText: Forward NGINX logs and create log parsing rules componentType: default --- Extend your collector configuration to include access and error logs if you want log events alongside metrics. -1. Gather the full paths to your NGINX access and error log files. Defaults are usually `/var/log/nginx/access.log` and `/var/log/nginx/error.log`. +### Configure NGINX Plus log format + +Before forwarding logs, configure NGINX Plus to use a structured log format. Refer to the [NGINX logging documentation](https://docs.nginx.com/nginx/admin-guide/monitoring/logging/) for guidance on configuring access and error logs. + +### Configure the OpenTelemetry Collector for log forwarding + +1. Note the full paths to your NGINX access and error log files. Defaults are usually `/var/log/nginx/access.log` and `/var/log/nginx/error.log`. 2. Update `/etc/otelcol-contrib/config.yaml` to add a `filelog` receiver and log pipeline: @@ -13,9 +19,11 @@ Extend your collector configuration to include access and error logs if you want receivers: prometheus: # existing Prometheus receiver configuration - filelog: + filelog/nginx_access_logs: include: - /var/log/nginx/access.log + filelog/nginx_error_logs: + include: - /var/log/nginx/error.log processors: @@ -29,11 +37,21 @@ Extend your collector configuration to include access and error logs if you want # existing settings transform/nginx_metrics: # existing settings - transform/nginx_logs: + transform/nginx_access_logs: + log_statements: + - context: resource + statements: + - set(attributes["nginx.display.name"], Concat(["server", attributes["nginx.deployment.name"]], ":")) + - set(attributes["logtype"], "nginx") + transform/nginx_error_logs: log_statements: - context: resource statements: - set(attributes["nginx.display.name"], Concat(["server", attributes["nginx.deployment.name"]], ":")) + - set(attributes["logtype"], "nginx-error") + + exporters: + # existing exporter setup service: pipelines: @@ -41,9 +59,13 @@ Extend your collector configuration to include access and error logs if you want receivers: [prometheus] processors: [batch, filter/nginx_metrics, resourcedetection, resource/nginx, transform/nginx_metrics] exporters: [otlphttp/newrelic] - logs/nginx: - receivers: [filelog] - processors: [batch, resource/nginx, transform/nginx_logs] + logs/nginx-access: + receivers: [filelog/nginx_access_logs] + processors: [batch, resource/nginx, transform/nginx_access_logs] + exporters: [otlphttp/newrelic] + logs/nginx-error: + receivers: [filelog/nginx_error_logs] + processors: [batch, resource/nginx, transform/nginx_error_logs] exporters: [otlphttp/newrelic] ``` diff --git a/src/install/nginx-plus-otel/whatsNext.mdx b/src/install/nginx-plus-otel/whatsNext.mdx new file mode 100644 index 00000000000..a6c60b48c49 --- /dev/null +++ b/src/install/nginx-plus-otel/whatsNext.mdx @@ -0,0 +1,793 @@ +--- +headingText: Metrics collected +componentType: default +--- + +## Metric data [#nginx-plus-metrics] + +The OpenTelemetry Collector scrapes metrics from the [NGINX Prometheus exporter](https://github.com/nginx/nginx-prometheus-exporter), which exposes NGINX Plus HTTP API metrics in Prometheus format. + +Below are the available NGINX Plus metrics: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Metric + + Description + + Type +
+ `nginxplus_connections_accepted` + + Accepted client connections + + Sum +
+ `nginxplus_connections_active` + + Active client connections + + Gauge +
+ `nginxplus_connections_dropped` + + Dropped client connections + + Sum +
+ `nginxplus_connections_idle` + + Idle client connections + + Gauge +
+
+ + + + + + + + + + + + + + + + + + + + + + +
+ Metric + + Description + + Type +
+ `nginxplus_http_requests_total` + + Total http requests + + Sum +
+ `nginxplus_http_requests_current` + + Current http requests + + Gauge +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Metric + + Description + + Type +
+ `nginxplus_ssl_handshakes` + + Successful SSL handshakes + + Sum +
+ `nginxplus_ssl_handshakes_failed` + + Failed SSL handshakes + + Sum +
+ `nginxplus_ssl_session_reuses` + + Session reuses during SSL handshake + + Sum +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Metric + + Description + + Type +
+ `nginxplus_server_zone_processing` + + Client requests that are currently being processed + + Gauge +
+ `nginxplus_server_zone_requests` + + Total client requests + + Sum +
+ `nginxplus_server_zone_responses` + + Total responses sent to clients + + Sum +
+ `nginxplus_server_zone_responses_codes` + + Total responses sent to clients by code + + Sum +
+ `nginxplus_server_zone_discarded` + + Requests completed without sending a response + + Sum +
+ `nginxplus_server_zone_received` + + Bytes received from clients + + Sum +
+ `nginxplus_server_zone_sent` + + Bytes sent to clients + + Sum +
+ `nginxplus_server_ssl_handshakes` + + Successful SSL handshakes + + Sum +
+ `nginxplus_server_ssl_handshakes_failed` + + Failed SSL handshakes + + Sum +
+ `nginxplus_server_ssl_session_reuses` + + Session reuses during SSL handshake + + Sum +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Metric + + Description + + Type +
+ `nginxplus_location_zone_requests` + + Total client requests + + Sum +
+ `nginxplus_location_zone_responses` + + Total responses sent to clients + + Sum +
+ `nginxplus_location_zone_responses_codes` + + Total responses sent to clients by code + + Sum +
+ `nginxplus_location_zone_discarded` + + Requests completed without sending a response + + Sum +
+ `nginxplus_location_zone_received` + + Bytes received from clients + + Sum +
+ `nginxplus_location_zone_sent` + + Bytes sent to clients + + Sum +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Metric + + Description + + Type +
+ `nginxplus_cache_size` + + Total size of the cache + + Gauge +
+ `nginxplus_cache_max_size` + + Maximum size of the cache + + Gauge +
+ `nginxplus_cache_cold` + + Is the cache considered cold + + Gauge +
+ `nginxplus_cache_hit_responses` + + Total number of cache hits + + Sum +
+ `nginxplus_cache_hit_bytes` + + Total number of bytes returned from cache hits + + Sum +
+ `nginxplus_cache_stale_responses` + + Total number of stale cache hits + + Sum +
+ `nginxplus_cache_stale_bytes` + + Total number of bytes returned from stale cache hits + + Sum +
+ `nginxplus_cache_updating_responses` + + Total number of cache hits while cache is updating + + Sum +
+ `nginxplus_cache_updating_bytes` + + Total number of bytes returned from cache while cache is updating + + Sum +
+ `nginxplus_cache_revalidated_responses` + + Total number of cache revalidations + + Sum +
+ `nginxplus_cache_revalidated_bytes` + + Total number of bytes returned from cache revalidations + + Sum +
+ `nginxplus_cache_miss_responses` + + Total number of cache misses + + Sum +
+ `nginxplus_cache_miss_bytes` + + Total number of bytes returned from cache misses + + Sum +
+ `nginxplus_cache_expired_responses` + + Total number of cache hits with expired TTL + + Sum +
+ `nginxplus_cache_expired_bytes` + + Total number of bytes returned from cache hits with expired TTL + + Sum +
+ `nginxplus_cache_expired_responses_written` + + Total number of cache hits with expired TTL written to cache + + Sum +
+ `nginxplus_cache_expired_bytes_written` + + Total number of bytes written to cache from cache hits with expired TTL + + Sum +
+ `nginxplus_cache_bypass_responses` + + Total number of cache bypasses + + Sum +
+ `nginxplus_cache_bypass_bytes` + + Total number of bytes returned from cache bypasses + + Sum +
+ `nginxplus_cache_bypass_responses_written` + + Total number of cache bypasses written to cache + + Sum +
+ `nginxplus_cache_bypass_bytes_written` + + Total number of bytes written to cache from cache bypasses + + Sum +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Attribute + + Description + + Example Values +
+ `server_zone` + + The name of the server zone (applies to HTTP Server Zones metrics) + + `example.com`, `api.example.com` +
+ `code` + + HTTP response status code (applies to response metrics) + + `1xx`, `2xx`, `3xx`, `4xx`, `5xx`, `200`, `404`, `500` +
+ `location_zone` + + The name of the location zone + + `/api`, `/images`, `/static` +
+ `cache` + + The name of the cache + + `my_cache`, `static_cache` +
+ `nginx.server.endpoint` + + The NGINX Plus API endpoint URL + + `http://localhost:8080/api` +
+ `nginx.deployment.name` + + A unique name to identify this NGINX Plus deployment + + `production-web-01`, `staging-api` +
+ `nginx.display.name` + + A display-friendly name combining "server" prefix with deployment name + + `server:production-web-01` +
+ `host.name` + + The hostname of the system where NGINX Plus is running + + `web-server-01.example.com` +
+ `host.id` + + The unique identifier of the host system + + `i-1234567890abcdef0` +
+ `logtype` + + The type of log being collected (applicable to logs only). Used by New Relic's built-in [parsing rules](https://docs.newrelic.com/docs/logs/ui-data/built-log-parsing-rules/#nginx) + + `nginx` (for access logs), `nginx-error` (for error logs) +
+
+ +
+ +For more details, see the [NGINX Prometheus exporter documentation](https://github.com/nginx/nginx-prometheus-exporter?tab=readme-ov-file#metrics-for-nginx-plus). diff --git a/src/nav/infrastructure.yml b/src/nav/infrastructure.yml index ea467351934..9fedca4fb0b 100644 --- a/src/nav/infrastructure.yml +++ b/src/nav/infrastructure.yml @@ -211,10 +211,18 @@ pages: path: /docs/infrastructure/host-integrations/host-integrations-list/nfs-monitoring-integration - title: NGINX integrations pages: - - title: NGINX on-host integration + - title: NR Native NGINX integration path: /install/nginx - - title: NGINX OpenTelemetry collector - path: /install/nginx-otel + - title: OpenTelemetry NGINX + pages: + - title: Self-hosted + path: /install/nginx-otel-host + - title: Kubernetes + path: /install/nginx-otel-kubernetes + - title: OpenTelemetry NGINX Plus + pages: + - title: Self-hosted + path: /install/nginx-plus-otel - title: NVIDIA GPU integration path: /docs/infrastructure/host-integrations/host-integrations-list/nvidia-gpu-integration - title: NVIDIA Jetson integration From 0b52dcdf6c9c16677527c74f4e637895d434cfdf Mon Sep 17 00:00:00 2001 From: sairaj18 Date: Tue, 23 Dec 2025 13:50:41 +0530 Subject: [PATCH 03/32] feat(nginx): move nginx otel to opentelemetry nav and combine all mdx files, create a single file for each deployment type --- .../nginx-plus/nginx-plus-otel.mdx} | 256 ++++++++++- .../opentelemetry/nginx/nginx-otel-host.mdx | 409 ++++++++++++++++++ .../nginx/nginx-otel-kubernetes.mdx | 375 ++++++++++++++++ src/install/config/nginx-otel-host.yaml | 16 - src/install/config/nginx-otel-kubernetes.yaml | 13 - src/install/config/nginx-plus-otel.yaml | 16 - .../nginx-otel-host/collector/environment.mdx | 25 -- .../host/configure-collector-opensource.mdx | 65 --- .../host/enable-status-opensource.mdx | 39 -- .../host/prerequisites-opensource.mdx | 12 - src/install/nginx-otel-host/host/verify.mdx | 11 - src/install/nginx-otel-host/intro.mdx | 7 - .../logs/forwarding-opensource.mdx | 82 ---- src/install/nginx-otel-host/whatsNext.mdx | 180 -------- src/install/nginx-otel-kubernetes/intro.mdx | 7 - .../kubernetes/install-collector.mdx | 163 ------- .../kubernetes/prerequisites.mdx | 10 - .../kubernetes/status-endpoint.mdx | 22 - .../kubernetes/verify.mdx | 11 - .../nginx-otel-kubernetes/whatsNext.mdx | 191 -------- .../nginx-plus-otel/collector/environment.mdx | 25 -- .../host/configure-collector-plus.mdx | 91 ---- .../host/enable-status-plus.mdx | 30 -- .../host/prerequisites-plus.mdx | 14 - src/install/nginx-plus-otel/host/verify.mdx | 11 - src/install/nginx-plus-otel/intro.mdx | 8 - .../nginx-plus-otel/logs/forwarding-plus.mdx | 84 ---- src/nav/infrastructure.yml | 16 +- src/nav/opentelemetry.yml | 12 + 29 files changed, 1051 insertions(+), 1150 deletions(-) rename src/{install/nginx-plus-otel/whatsNext.mdx => content/docs/opentelemetry/nginx-plus/nginx-plus-otel.mdx} (61%) create mode 100644 src/content/docs/opentelemetry/nginx/nginx-otel-host.mdx create mode 100644 src/content/docs/opentelemetry/nginx/nginx-otel-kubernetes.mdx delete mode 100644 src/install/config/nginx-otel-host.yaml delete mode 100644 src/install/config/nginx-otel-kubernetes.yaml delete mode 100644 src/install/config/nginx-plus-otel.yaml delete mode 100644 src/install/nginx-otel-host/collector/environment.mdx delete mode 100644 src/install/nginx-otel-host/host/configure-collector-opensource.mdx delete mode 100644 src/install/nginx-otel-host/host/enable-status-opensource.mdx delete mode 100644 src/install/nginx-otel-host/host/prerequisites-opensource.mdx delete mode 100644 src/install/nginx-otel-host/host/verify.mdx delete mode 100644 src/install/nginx-otel-host/intro.mdx delete mode 100644 src/install/nginx-otel-host/logs/forwarding-opensource.mdx delete mode 100644 src/install/nginx-otel-host/whatsNext.mdx delete mode 100644 src/install/nginx-otel-kubernetes/intro.mdx delete mode 100644 src/install/nginx-otel-kubernetes/kubernetes/install-collector.mdx delete mode 100644 src/install/nginx-otel-kubernetes/kubernetes/prerequisites.mdx delete mode 100644 src/install/nginx-otel-kubernetes/kubernetes/status-endpoint.mdx delete mode 100644 src/install/nginx-otel-kubernetes/kubernetes/verify.mdx delete mode 100644 src/install/nginx-otel-kubernetes/whatsNext.mdx delete mode 100644 src/install/nginx-plus-otel/collector/environment.mdx delete mode 100644 src/install/nginx-plus-otel/host/configure-collector-plus.mdx delete mode 100644 src/install/nginx-plus-otel/host/enable-status-plus.mdx delete mode 100644 src/install/nginx-plus-otel/host/prerequisites-plus.mdx delete mode 100644 src/install/nginx-plus-otel/host/verify.mdx delete mode 100644 src/install/nginx-plus-otel/intro.mdx delete mode 100644 src/install/nginx-plus-otel/logs/forwarding-plus.mdx diff --git a/src/install/nginx-plus-otel/whatsNext.mdx b/src/content/docs/opentelemetry/nginx-plus/nginx-plus-otel.mdx similarity index 61% rename from src/install/nginx-plus-otel/whatsNext.mdx rename to src/content/docs/opentelemetry/nginx-plus/nginx-plus-otel.mdx index a6c60b48c49..40e643147dc 100644 --- a/src/install/nginx-plus-otel/whatsNext.mdx +++ b/src/content/docs/opentelemetry/nginx-plus/nginx-plus-otel.mdx @@ -1,9 +1,259 @@ --- -headingText: Metrics collected -componentType: default +title: 'Monitor self-hosted NGINX Plus with OpenTelemetry' +metaDescription: 'Send your NGINX Plus metrics to New Relic using the OpenTelemetry contrib collector.' +redirects: + - /docs/infrastructure/host-integrations/host-integrations-list/nginx/nginx-plus-otel +freshnessValidatedDate: never --- -## Metric data [#nginx-plus-metrics] +Monitor your NGINX Plus servers using the [OpenTelemetry Collector](https://github.com/open-telemetry/opentelemetry-collector-contrib) to send metrics and telemetry data to New Relic. +This integration leverages the OpenTelemetry [prometheusreceiver](https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/receiver/prometheusreceiver) and [nginx-prometheus-exporter](https://github.com/nginx/nginx-prometheus-exporter) to monitor your NGINX Plus performance metrics, connection statistics, and server health. + +## Step 1: Check the compatibility and requirements [#prerequisites] + +Before you begin, ensure you have: + +- A [New Relic account](https://newrelic.com/signup) with a +- NGINX Plus version [R13](https://docs.nginx.com/nginx/releases/#r13) or higher +- NGINX Plus with the [HTTP API](https://nginx.org/en/docs/http/ngx_http_api_module.html) module enabled +- [NGINX Prometheus exporter](https://github.com/nginx/nginx-prometheus-exporter) installed and running alongside your NGINX Plus instance to expose HTTP API metrics in Prometheus format +- [OpenTelemetry Collector Contrib](https://github.com/open-telemetry/opentelemetry-collector-contrib/releases/latest) installed and running on a linux host +- Network access from the linux host to: + - NGINX Plus HTTP API endpoint + - Anyone of the [New Relic OTLP](https://docs.newrelic.com/docs/opentelemetry/best-practices/opentelemetry-otlp/#configure-endpoint-port-protocol:~:text=HTTP-,Endpoint,-Supported%20ports) enpoint + +## Step 2: Configure NGINX Plus [#configure-nginx] + +Configure and enable the [HTTP API module](https://nginx.org/en/docs/http/ngx_http_api_module.html) to expose metrics from your NGINX Plus server. + +After updating `nginx.conf`, test and reload the service: + +```bash +sudo nginx -t && sudo nginx -s reload +``` + +Confirm the API endpoint path (including version) exposed in your configuration. + +Record the full API endpoint path (without the leading slash) and the port that serves it. Common defaults are `api/9` on port `8080`. + +Use `curl` to confirm your API endpoint is reachable: + +```bash +curl -I http://127.0.0.1:8080/api/9 2>/dev/null | head -n 1 | cut -d$' ' -f1,2 +``` + +Expected output: + +``` +HTTP/1.1 200 +``` + +If you see a different response, verify your NGINX Plus configuration and ensure the API module is properly enabled. + +## Step 3: Configure the OpenTelemetry Collector [#configure-collector] + +In the below OpenTelemetry Collector config snippet: + +- Update the `nginx.deployment.name` value to with a unique name to identify this NGINX Plus server in a New Relic account. +- Update the `targets` value to match your Prometheus exporter host and port (default is `127.0.0.1:9113`). +- Update the `nginx.server.endpoint` value to match your API status path and port. + +Merge the receivers, processors, exporters, and service pipelines from the snippet below into your current configuration (typically located at `/etc/otelcol-contrib/config.yaml`). + +Note: Update the `filter/nginx_metrics` as per your requirement under the processor section in order to ingest additional metrics. + +```yaml +receivers: + prometheus: + config: + scrape_configs: + - job_name: nginx-plus + scrape_interval: 30s + static_configs: + - targets: + - 127.0.0.1:9113 +processors: + batch: + send_batch_size: 1024 + timeout: 30s + filter/nginx_metrics: + metrics: + include: + match_type: regexp + metric_names: + - "nginxplus_connections_.*" + - "nginxplus_http_requests_.*" + - "nginxplus_ssl_.*" + - "nginxplus_server_.*" + - "nginxplus_location_zone_.*" + - "nginxplus_cache_.*" + + exclude: + match_type: regexp + metric_names: + - "nginxplus_stream_server_.*" + - "nginxplus_upstream_.*" + - "nginxplus_stream_upstream_.*" + - "nginxplus_stream_zone_sync_zone_.*" + - "nginxplus_resolver_.*" + - "nginxplus_limit_request_.*" + - "nginxplus_limit_connection_.*" + - "nginxplus_stream_limit_connection_.*" + - "nginxplus_worker_.*" + resource/nginx: + attributes: + - key: nginx.server.endpoint + value: + action: insert + - key: nginx.deployment.name + value: + action: insert + resourcedetection: + detectors: [system] + system: + resource_attributes: + host.name: + enabled: true + host.id: + enabled: true + transform/nginx: + metric_statements: + - context: resource + statements: + - set(attributes["nginx.display.name"], Concat(["server", attributes["nginx.deployment.name"]], ":")) + - context: resource + statements: + - delete_key(attributes, "service.name") +exporters: + otlphttp/newrelic: + endpoint: ${env:NEWRELIC_OTLP_ENDPOINT} + headers: + api-key: ${env:NEWRELIC_LICENSE_KEY} +service: + pipelines: + metrics/nginx: + receivers: [prometheus] + processors: [batch, filter/nginx_metrics, resourcedetection, resource/nginx, transform/nginx] + exporters: [otlphttp/newrelic] +``` + +Save the file and ensure the `otelcol-contrib` system user can read it. + +## Step 4: Set environment variables for the collector [#set-environment] + +Inject your New Relic and OTLP endpoint into the collector service so the exporter can authenticate. + +1. Create a systemd override directory: + ```bash + sudo mkdir -p /etc/systemd/system/otelcol-contrib.service.d + ``` +2. Write `environment.conf` with your OTLP endpoint. Replace `YOUR_LICENSE_KEY` with the New Relic license key and `YOUR_OTLP_ENDPOINT` with the appropriate endpoint for your region. Refer to the OTLP endpoint configuration [documentation](https://docs.newrelic.com/docs/opentelemetry/best-practices/opentelemetry-otlp/#configure-endpoint-port-protocol) to select the right endpoint. + + ```bash + cat < Integrations & Agents**. +2. Select **Dashboards**, and click **NGINX OTel overview dashboard**. +3. In the popup window, select your account. +4. Click View dashboard, and see your NGINX Plus data in New Relic. + +The NGINX Plus metrics are attached to the `Metric` [event type](/docs/using-new-relic/data/understand-data/new-relic-data-types#events-new-relic). You can [query this data](/docs/using-new-relic/data/understand-data/query-new-relic-data) for troubleshooting purposes or to create custom charts and dashboards. + +## Metrics collected [#metrics] The OpenTelemetry Collector scrapes metrics from the [NGINX Prometheus exporter](https://github.com/nginx/nginx-prometheus-exporter), which exposes NGINX Plus HTTP API metrics in Prometheus format. diff --git a/src/content/docs/opentelemetry/nginx/nginx-otel-host.mdx b/src/content/docs/opentelemetry/nginx/nginx-otel-host.mdx new file mode 100644 index 00000000000..1be40274544 --- /dev/null +++ b/src/content/docs/opentelemetry/nginx/nginx-otel-host.mdx @@ -0,0 +1,409 @@ +--- +title: 'Monitor self-hosted NGINX with OpenTelemetry' +metaDescription: 'Send your NGINX metrics and logs to New Relic using the OpenTelemetry Collector.' +redirects: + - /docs/infrastructure/host-integrations/host-integrations-list/nginx/nginx-otel-host +freshnessValidatedDate: never +--- + +Monitor your NGINX servers using the [OpenTelemetry Collector](https://github.com/open-telemetry/opentelemetry-collector-contrib) to send metrics and telemetry data to New Relic. +This integration leverages the OpenTelemetry [nginxreceiver](https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/receiver/nginxreceiver) to monitor your NGINX performance metrics, connection statistics, and server health. + +## Step 1: Check the compatibility and requirements [#prerequisites] + +Before you begin, ensure you have: + +- A [New Relic account](https://newrelic.com/signup) with a +- NGINX with the [HTTP stub status](https://nginx.org/en/docs/http/ngx_http_stub_status_module.html) module enabled +- [OpenTelemetry Collector Contrib](https://github.com/open-telemetry/opentelemetry-collector-contrib/releases/latest) installed and running on a linux host +- Network access from the linux host to: + - NGINX HTTP stub status endpoint + - Anyone of the [New Relic OTLP](https://docs.newrelic.com/docs/opentelemetry/best-practices/opentelemetry-otlp/#configure-endpoint-port-protocol:~:text=HTTP-,Endpoint,-Supported%20ports) enpoint + +## Step 2: Configure NGINX [#configure-nginx] + +Configure and enable the [stub_status](https://nginx.org/en/docs/http/ngx_http_stub_status_module.html) module to expose metrics from your NGINX Open Source server. + +After updating `nginx.conf`, test and reload the service: + +```bash +sudo nginx -t && sudo nginx -s reload +``` + +List the configuration block that exposes `stub_status`: + +```bash +sudo nginx -T 2>&1 | grep -ozP "(?s:.*\s)\Klocation(?s).*stub_status" | grep -aoP "\/([^\s]+)" +``` + +Example output: + +``` +/status +``` +If you do not see output, inspect your nginx.conf manually for a stub_status block. + +Use `curl` to confirm your status endpoint is reachable: + +```bash +curl -I http://127.0.0.1:8080/status 2>/dev/null | head -n 1 | cut -d$' ' -f1,2 +``` + +Expected output: + +``` +HTTP/1.1 200 +``` + +If you see a different response, troubleshoot your NGINX configuration. + +## Step 3: Configure the OpenTelemetry Collector [#configure-collector] + +In the below OpenTelemetry Collector config snippet: + +- Update the `nginx.deployment.name` value to with a unique name to identify this NGINX server in a New Relic account. +- Update the `endpoint` and `nginx.server.endpoint` value to match your stub status path and port. + +Merge the receivers, processors, exporters, and service pipelines from the snippet below into your current configuration (typically located at `/etc/otelcol-contrib/config.yaml`). + +```yaml +receivers: + nginx: + metrics: + nginx.requests: + enabled: true + nginx.connections_accepted: + enabled: true + nginx.connections_handled: + enabled: true + nginx.connections_current: + enabled: true + endpoint: + collection_interval: 30s +processors: + resourcedetection: + detectors: [system] + system: + resource_attributes: + host.name: + enabled: true + host.id: + enabled: true + resource: + attributes: + - action: upsert + key: nginx.server.endpoint + value: + - action: upsert + key: nginx.deployment.name + value: + batch: + timeout: 30s + send_batch_size: 512 + transform/nginx_metrics: + metric_statements: + - context: resource + statements: + - set(attributes["nginx.display.name"], Concat(["server", attributes["nginx.deployment.name"]], ":")) +exporters: + otlphttp/newrelic: + endpoint: ${env:NEWRELIC_OTLP_ENDPOINT} + headers: + api-key: ${env:NEWRELIC_LICENSE_KEY} +service: + pipelines: + metrics: + receivers: [nginx] + processors: [resourcedetection, resource, batch, transform/nginx_metrics] + exporters: [otlphttp/newrelic] +``` + +Save the file and ensure the `otelcol-contrib` system user can read it. + +## Step 4: Set environment variables for the collector [#set-environment] + +Inject your New Relic and OTLP endpoint into the collector service so the exporter can authenticate. + +1. Create a systemd override directory: + ```bash + sudo mkdir -p /etc/systemd/system/otelcol-contrib.service.d + ``` +2. Write `environment.conf` with your OTLP endpoint. Replace `YOUR_LICENSE_KEY` with the New Relic license key and `YOUR_OTLP_ENDPOINT` with the appropriate endpoint for your region. Refer to the OTLP endpoint configuration [documentation](https://docs.newrelic.com/docs/opentelemetry/best-practices/opentelemetry-otlp/#configure-endpoint-port-protocol) to select the right endpoint. + + ```bash + cat < Integrations & Agents**. +2. Select **Dashboards**, and click **NGINX OTel overview dashboard**. +3. In the popup window, select your account. +4. Click View dashboard, and see your NGINX data in New Relic. + +The NGINX metrics are attached to the `Metric` [event type](/docs/using-new-relic/data/understand-data/new-relic-data-types#events-new-relic). You can [query this data](/docs/using-new-relic/data/understand-data/query-new-relic-data) for troubleshooting purposes or to create custom charts and dashboards. + +## Metrics collected [#metrics] + +The OpenTelemetry Collector Contrib's [nginxreceiver](https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/receiver/nginxreceiver) collects the following metrics from the NGINX stub status module: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Metric + + Description + + Type +
+ `nginx.connections_accepted` + + The total number of accepted client connections + + Sum +
+ `nginx.connections_handled` + + 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) + + Sum +
+ `nginx.connections_current` + + The current number of nginx connections by state + + Sum +
+ `nginx.requests` + + Total number of requests made to the server since it started + + Sum +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Attribute + + Description + + Example Values +
+ `state` + + The state of a connection (applicable to `nginx.connections_current` metric) + + `active`, `reading`, `writing`, `waiting` +
+ `nginx.server.endpoint` + + The NGINX stub status endpoint URL + + `http://localhost:8080/stub_status` +
+ `nginx.deployment.name` + + A unique name to identify this NGINX deployment + + `production-web-01`, `staging-api` +
+ `nginx.display.name` + + A display-friendly name combining "server" prefix with deployment name + + `server:production-web-01` +
+ `host.name` + + The hostname of the system where NGINX is running + + `web-server-01.example.com` +
+ `host.id` + + The unique identifier of the host system + + `i-1234567890abcdef0` +
+ `logtype` + + The type of log being collected (applicable to logs only). Used by New Relic's built-in [parsing rules](https://docs.newrelic.com/docs/logs/ui-data/built-log-parsing-rules/#nginx) + + `nginx` (for access logs), `nginx-error` (for error logs) +
+
+ +
+ +For more details, see the [NGINX receiver documentation](https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/main/receiver/nginxreceiver/documentation.md). diff --git a/src/content/docs/opentelemetry/nginx/nginx-otel-kubernetes.mdx b/src/content/docs/opentelemetry/nginx/nginx-otel-kubernetes.mdx new file mode 100644 index 00000000000..8026f30823e --- /dev/null +++ b/src/content/docs/opentelemetry/nginx/nginx-otel-kubernetes.mdx @@ -0,0 +1,375 @@ +--- +title: 'Monitor NGINX on Kubernetes with OpenTelemetry' +metaDescription: 'Send your NGINX metrics from Kubernetes to New Relic using the OpenTelemetry Collector.' +redirects: + - /docs/infrastructure/host-integrations/host-integrations-list/nginx/nginx-otel-kubernetes +freshnessValidatedDate: never +--- + +Monitor your NGINX servers using the [OpenTelemetry Collector](https://github.com/open-telemetry/opentelemetry-collector-contrib) to send metrics and telemetry data to New Relic. +This integration leverages the OpenTelemetry [nginxreceiver](https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/receiver/nginxreceiver), [receivercreator](https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/receiver/receivercreator) to monitor your NGINX performance metrics, connection statistics, and server health. + +## Step 1: Prerequisites [#prerequisites] + +Before you begin, ensure you have: + +- A [New Relic account](https://newrelic.com/signup) with a +- Enable the [HTTP stub status](https://nginx.org/en/docs/http/ngx_http_stub_status_module.html) module on NGINX pod that needs to be monitored +- Add labels `app` and `role` to each NGINX pod that needs to be monitored + +## Step 2: Install and configure the OpenTelemetry Collector [#install-collector] + +Deploy the OpenTelemetry Collector to your Kubernetes cluster using Helm. The collector will automatically discover and scrape metrics from your NGINX pods. + +1. Create a Kubernetes secret to store your New Relic credentials. Replace `YOUR_LICENSE_KEY` and `YOUR_OTLP_ENDPOINT` with your actual values. Refer to the OTLP endpoint configuration [documentation](https://docs.newrelic.com/docs/opentelemetry/best-practices/opentelemetry-otlp/#configure-endpoint-port-protocol) to select the appropriate endpoint for your region. + + ```bash + kubectl create secret generic newrelic-licenses --from-literal=NEWRELIC_LICENSE_KEY=YOUR_LICENSE_KEY --from-literal=NEWRELIC_OTLP_ENDPOINT=YOUR_OTLP_ENDPOINT -n newrelic --create-namespace + ``` + +2. Create a `values.yaml` file to configure the OpenTelemetry Collector. Update the placeholders for your cluster name, NGINX pod labels, and stub status endpoint. + + ```yaml + opentelemetry-collector: + mode: deployment + + image: + repository: otel/opentelemetry-collector-contrib + pullPolicy: IfNotPresent + + command: + name: otelcol-contrib + + resources: + limits: + cpu: 500m + memory: 300Mi + requests: + cpu: 100m + memory: 100Mi + + extraEnvs: + - name: NEWRELIC_LICENSE_KEY + valueFrom: + secretKeyRef: + name: newrelic-licenses + key: NEWRELIC_LICENSE_KEY + - name: NEWRELIC_OTLP_ENDPOINT + valueFrom: + secretKeyRef: + name: newrelic-licenses + key: NEWRELIC_OTLP_ENDPOINT + - name: K8S_NODE_NAME + valueFrom: + fieldRef: + fieldPath: spec.nodeName + - name: K8S_CLUSTER_NAME + value: nginx-cluster # Update with your cluster name + + clusterRole: + create: true + rules: + - apiGroups: [""] + resources: ["pods", "nodes", "nodes/stats", "nodes/proxy"] + verbs: ["get", "list", "watch"] + - apiGroups: ["apps"] + resources: ["replicasets"] + verbs: ["get", "list", "watch"] + clusterRoleBinding: + name: "" + + config: + extensions: + health_check: + endpoint: 0.0.0.0:13133 + k8s_observer: + auth_type: serviceAccount + observe_pods: true + observe_nodes: true + + receivers: + receiver_creator/nginx: + watch_observers: [k8s_observer] + receivers: + nginx: + rule: type == "pod" && labels["app"] == "nginx" && labels["role"] == "reverse-proxy" # Update with your labels + config: + endpoint: 'http://`endpoint`:/status' + metrics: + nginx.requests: + enabled: true + nginx.connections_accepted: + enabled: true + nginx.connections_handled: + enabled: true + nginx.connections_current: + enabled: true + collection_interval: 30s + resource_attributes: + nginx.server.endpoint: 'http://`endpoint`:/status' + nginx.port: '' + + processors: + batch: + send_batch_size: 1024 + timeout: 30s + + resource/cluster: + attributes: + - key: k8s.cluster.name + value: "${env:K8S_CLUSTER_NAME}" + action: insert + + transform/nginx: + metric_statements: + - context: resource + statements: + - set(attributes["nginx.display.name"], Concat([ + "server", + "k8s", + attributes["k8s.cluster.name"], + attributes["k8s.namespace.name"], + "pod", + attributes["k8s.pod.name"], + "nginx", + attributes["nginx.port"] + ], ":")) + - set(attributes["nginx.deployment.name"], attributes["k8s.pod.name"]) + + exporters: + otlphttp: + endpoint: "${env:NEWRELIC_OTLP_ENDPOINT}" + headers: + api-key: "${env:NEWRELIC_LICENSE_KEY}" + + service: + extensions: [health_check, k8s_observer] + pipelines: + metrics/nginx: + receivers: [receiver_creator/nginx] + processors: [batch, resource/cluster, transform/nginx] + exporters: [otlphttp] + ``` + +3. Install the [Helm chart](https://github.com/open-telemetry/opentelemetry-helm-charts) using your values.yaml file. + + ```bash + helm repo add open-telemetry https://open-telemetry.github.io/opentelemetry-helm-charts + helm repo update + helm upgrade --install nginx-otel-collector open-telemetry/opentelemetry-collector --namespace newrelic --create-namespace -f values.yaml + ``` + +4. Ensure the pods have successfully spun up. + + ```bash + kubectl get pods -n newrelic --watch + ``` + + You should see pods with names like `nginx-otel-collector-` in a `Running` state in the `newrelic` namespace. + +5. Run an NRQL query in New Relic to confirm data is arriving. Replace the cluster name with the value you set in the values file: + + ```sql + FROM Metric + SELECT * + WHERE metricName LIKE 'nginx.%' + AND instrumentation.provider = 'opentelemetry' + AND k8s.cluster.name = 'nginx-cluster' + SINCE 10 minutes ago + ``` + +## Step 3: Find and use data [#find-data] + +1. Go to **[one.newrelic.com](https://one.newrelic.com) > Integrations & Agents**. +2. Select **Dashboards**, and click **NGINX OTel overview dashboard**. +3. In the popup window, select your account. +4. Click View dashboard, and see your NGINX data in New Relic. + +The NGINX metrics are attached to the `Metric` [event type](/docs/using-new-relic/data/understand-data/new-relic-data-types#events-new-relic). You can [query this data](/docs/using-new-relic/data/understand-data/query-new-relic-data) for troubleshooting purposes or to create custom charts and dashboards. + +## Metrics collected [#metrics] + +The OpenTelemetry Collector Contrib's [nginxreceiver](https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/receiver/nginxreceiver) collects the following metrics from the NGINX stub status module: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Metric + + Description + + Type +
+ `nginx.connections_accepted` + + The total number of accepted client connections + + Sum +
+ `nginx.connections_handled` + + 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) + + Sum +
+ `nginx.connections_current` + + The current number of nginx connections by state + + Sum +
+ `nginx.requests` + + Total number of requests made to the server since it started + + Sum +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Attribute + + Description + + Example Values +
+ `state` + + The state of a connection (applicable to `nginx.connections_current` metric) + + `active`, `reading`, `writing`, `waiting` +
+ `nginx.server.endpoint` + + The NGINX stub status endpoint URL + + `http://10.244.1.5:8080/status` +
+ `nginx.port` + + The port number where stub status is exposed + + `8080` +
+ `k8s.cluster.name` + + The name of the Kubernetes cluster + + `nginx-cluster`, `production-k8s` +
+ `k8s.namespace.name` + + The Kubernetes namespace where the NGINX pod is running + + `default`, `nginx-ingress` +
+ `k8s.pod.name` + + The name of the Kubernetes pod running NGINX + + `nginx-deployment-5d7b9c8f4d-abc12` +
+ `nginx.deployment.name` + + The deployment name (set to the pod name in Kubernetes) + + `nginx-deployment-5d7b9c8f4d-abc12` +
+ `nginx.display.name` + + A display-friendly name combining cluster, namespace, and pod information + + `server:k8s:nginx-cluster:default:pod:nginx-deployment-5d7b9c8f4d-abc12:nginx:8080` +
+
+ +
+ +For more details, see the [NGINX receiver documentation](https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/main/receiver/nginxreceiver/documentation.md). diff --git a/src/install/config/nginx-otel-host.yaml b/src/install/config/nginx-otel-host.yaml deleted file mode 100644 index 8b221e9aa93..00000000000 --- a/src/install/config/nginx-otel-host.yaml +++ /dev/null @@ -1,16 +0,0 @@ -agentName: nginx-otel-host -agentType: integration -title: 'Monitor self-hosted NGINX with OpenTelemetry' -metaDescription: 'Send your NGINX metrics and logs to New Relic using the OpenTelemetry Collector.' -introFilePath: 'src/install/nginx-otel-host/intro.mdx' -appInfo: [] -redirects: - - /docs/infrastructure/host-integrations/host-integrations-list/nginx/nginx-otel-host -steps: - - filePath: 'src/install/nginx-otel-host/host/prerequisites-opensource.mdx' - - filePath: 'src/install/nginx-otel-host/host/enable-status-opensource.mdx' - - filePath: 'src/install/nginx-otel-host/host/configure-collector-opensource.mdx' - - filePath: 'src/install/nginx-otel-host/collector/environment.mdx' - - filePath: 'src/install/nginx-otel-host/logs/forwarding-opensource.mdx' - - filePath: 'src/install/nginx-otel-host/host/verify.mdx' -whatsNextFilePath: 'src/install/nginx-otel-host/whatsNext.mdx' diff --git a/src/install/config/nginx-otel-kubernetes.yaml b/src/install/config/nginx-otel-kubernetes.yaml deleted file mode 100644 index bb71907cdfb..00000000000 --- a/src/install/config/nginx-otel-kubernetes.yaml +++ /dev/null @@ -1,13 +0,0 @@ -agentName: nginx-otel-kubernetes -agentType: integration -title: 'Monitor NGINX on Kubernetes with OpenTelemetry' -metaDescription: 'Send your NGINX metrics and logs from Kubernetes to New Relic using the OpenTelemetry Collector.' -introFilePath: 'src/install/nginx-otel-kubernetes/intro.mdx' -appInfo: [] -redirects: - - /docs/infrastructure/host-integrations/host-integrations-list/nginx/nginx-otel-kubernetes -steps: - - filePath: 'src/install/nginx-otel-kubernetes/kubernetes/prerequisites.mdx' - - filePath: 'src/install/nginx-otel-kubernetes/kubernetes/install-collector.mdx' - - filePath: 'src/install/nginx-otel-kubernetes/kubernetes/verify.mdx' -whatsNextFilePath: 'src/install/nginx-otel-kubernetes/whatsNext.mdx' diff --git a/src/install/config/nginx-plus-otel.yaml b/src/install/config/nginx-plus-otel.yaml deleted file mode 100644 index e57c354d418..00000000000 --- a/src/install/config/nginx-plus-otel.yaml +++ /dev/null @@ -1,16 +0,0 @@ -agentName: nginx-plus-otel -agentType: integration -title: 'Monitor self-hosted NGINX Plus with OpenTelemetry' -metaDescription: 'Send your NGINX Plus metrics to New Relic using the OpenTelemetry contirb collector.' -introFilePath: 'src/install/nginx-plus-otel/intro.mdx' -appInfo: [] -redirects: - - /docs/infrastructure/host-integrations/host-integrations-list/nginx/nginx-plus-otel -steps: - - filePath: 'src/install/nginx-plus-otel/host/prerequisites-plus.mdx' - - filePath: 'src/install/nginx-plus-otel/host/enable-status-plus.mdx' - - filePath: 'src/install/nginx-plus-otel/host/configure-collector-plus.mdx' - - filePath: 'src/install/nginx-plus-otel/collector/environment.mdx' - - filePath: 'src/install/nginx-plus-otel/logs/forwarding-plus.mdx' - - filePath: 'src/install/nginx-plus-otel/host/verify.mdx' -whatsNextFilePath: 'src/install/nginx-plus-otel/whatsNext.mdx' diff --git a/src/install/nginx-otel-host/collector/environment.mdx b/src/install/nginx-otel-host/collector/environment.mdx deleted file mode 100644 index 836a314b9da..00000000000 --- a/src/install/nginx-otel-host/collector/environment.mdx +++ /dev/null @@ -1,25 +0,0 @@ ---- -headingText: Set environment variables for the collector -componentType: default ---- - -Inject your New Relic and OTLP endpoint into the collector service so the exporter can authenticate. - -1. Create a systemd override directory: - ```bash - sudo mkdir -p /etc/systemd/system/otelcol-contrib.service.d - ``` -2. Write `environment.conf` with your OTLP endpoint. Replace `YOUR_LICENSE_KEY` with the New Relic license key and `YOUR_OTLP_ENDPOINT` with the appropriate endpoint for your region. Refer to the OTLP endpoint configuration [documentation](https://docs.newrelic.com/docs/opentelemetry/best-practices/opentelemetry-otlp/#configure-endpoint-port-protocol) to select the right endpoint. - - ```bash - cat < - collection_interval: 30s -processors: - resourcedetection: - detectors: [system] - system: - resource_attributes: - host.name: - enabled: true - host.id: - enabled: true - resource: - attributes: - - action: upsert - key: nginx.server.endpoint - value: - - action: upsert - key: nginx.deployment.name - value: - batch: - timeout: 30s - send_batch_size: 512 - transform/nginx_metrics: - metric_statements: - - context: resource - statements: - - set(attributes["nginx.display.name"], Concat(["server", attributes["nginx.deployment.name"]], ":")) -exporters: - otlphttp/newrelic: - endpoint: ${env:NEWRELIC_OTLP_ENDPOINT} - headers: - api-key: ${env:NEWRELIC_LICENSE_KEY} -service: - pipelines: - metrics: - receivers: [nginx] - processors: [resourcedetection, resource, batch, transform/nginx_metrics] - exporters: [otlphttp/newrelic] -``` - -Save the file and ensure the `otelcol-contrib` system user can read it. diff --git a/src/install/nginx-otel-host/host/enable-status-opensource.mdx b/src/install/nginx-otel-host/host/enable-status-opensource.mdx deleted file mode 100644 index af455a70c38..00000000000 --- a/src/install/nginx-otel-host/host/enable-status-opensource.mdx +++ /dev/null @@ -1,39 +0,0 @@ ---- -headingText: Configure NGINX -componentType: default ---- - -Configure and enable the [stub_status](https://nginx.org/en/docs/http/ngx_http_stub_status_module.html) module to expose metrics from your NGINX Open Source server. - -After updating `nginx.conf`, test and reload the service: - -```bash -sudo nginx -t && sudo nginx -s reload -``` - -List the configuration block that exposes `stub_status`: - -```bash -sudo nginx -T 2>&1 | grep -ozP "(?s:.*\s)\Klocation(?s).*stub_status" | grep -aoP "\/([^\s]+)" -``` - -Example output: - -``` -/status -``` -If you do not see output, inspect your nginx.conf manually for a stub_status block. - -Use `curl` to confirm your status endpoint is reachable: - -```bash -curl -I http://127.0.0.1:8080/status 2>/dev/null | head -n 1 | cut -d$' ' -f1,2 -``` - -Expected output: - -``` -HTTP/1.1 200 -``` - -If you see a different response, troubleshoot your NGINX configuration. diff --git a/src/install/nginx-otel-host/host/prerequisites-opensource.mdx b/src/install/nginx-otel-host/host/prerequisites-opensource.mdx deleted file mode 100644 index 503e3cc2d5a..00000000000 --- a/src/install/nginx-otel-host/host/prerequisites-opensource.mdx +++ /dev/null @@ -1,12 +0,0 @@ ---- -headingText: Check the compatibility and requirements -componentType: default ---- -Before you begin, ensure you have: - -- A [New Relic account](https://newrelic.com/signup) with a -- NGINX with the [HTTP stub status](https://nginx.org/en/docs/http/ngx_http_stub_status_module.html) module enabled -- [OpenTelemetry Collector Contrib](https://github.com/open-telemetry/opentelemetry-collector-contrib/releases/latest) installed and running on a linux host -- Network access from the linux host to: - - NGINX HTTP stub status endpoint - - Anyone of the [New Relic OTLP](https://docs.newrelic.com/docs/opentelemetry/best-practices/opentelemetry-otlp/#configure-endpoint-port-protocol:~:text=HTTP-,Endpoint,-Supported%20ports) enpoint diff --git a/src/install/nginx-otel-host/host/verify.mdx b/src/install/nginx-otel-host/host/verify.mdx deleted file mode 100644 index 6a0e7ee56cc..00000000000 --- a/src/install/nginx-otel-host/host/verify.mdx +++ /dev/null @@ -1,11 +0,0 @@ ---- -headingText: Find and use data -componentType: default ---- - -1. Go to **[one.newrelic.com](https://one.newrelic.com) > Integrations & Agents**. -2. Select **Dashboards**, and click **NGINX OTel overview dashboard**. -3. In the popup window, select your account. -4. Click View dashboard, and see your NGINX data in New Relic. - -The NGINX metrics are attached to the `Metric` [event type](/docs/using-new-relic/data/understand-data/new-relic-data-types#events-new-relic). You can [query this data](/docs/using-new-relic/data/understand-data/query-new-relic-data) for troubleshooting purposes or to create custom charts and dashboards. diff --git a/src/install/nginx-otel-host/intro.mdx b/src/install/nginx-otel-host/intro.mdx deleted file mode 100644 index 88159369454..00000000000 --- a/src/install/nginx-otel-host/intro.mdx +++ /dev/null @@ -1,7 +0,0 @@ ---- -headingText: Before you start -componentType: default ---- - -Monitor your NGINX servers using the [OpenTelemetry Collector](https://github.com/open-telemetry/opentelemetry-collector-contrib) to send metrics and telemetry data to New Relic. -This integration leverages the OpenTelemetry [nginxreceiver](https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/receiver/nginxreceiver) to monitor your NGINX performance metrics, connection statistics, and server health. diff --git a/src/install/nginx-otel-host/logs/forwarding-opensource.mdx b/src/install/nginx-otel-host/logs/forwarding-opensource.mdx deleted file mode 100644 index 595031ca1fe..00000000000 --- a/src/install/nginx-otel-host/logs/forwarding-opensource.mdx +++ /dev/null @@ -1,82 +0,0 @@ ---- -headingText: Forward NGINX logs -componentType: default ---- - -Extend your collector configuration to include access and error logs if you want log events alongside metrics. - -### Configure NGINX log format - -Before forwarding logs, configure NGINX to use a structured log format. Refer to the [NGINX log module documentation](https://nginx.org/en/docs/http/ngx_http_log_module.html) for guidance on configuring access and error logs. - -### Configure the OpenTelemetry Collector for log forwarding - -1. Note the full paths to your NGINX access and error log files. Defaults are usually `/var/log/nginx/access.log` and `/var/log/nginx/error.log`. - -2. Update `/etc/otelcol-contrib/config.yaml` to add a `filelog` receiver and log pipeline: - - ```yaml - receivers: - nginx: - # existing stub status receiver configuration - filelog/nginx_access_logs: - include: - - /var/log/nginx/access.log - filelog/nginx_error_logs: - include: - - /var/log/nginx/error.log - - processors: - resourcedetection: - # existing settings - resource: - # existing settings - batch: - # existing settings - transform/nginx_metrics: - # existing settings - transform/nginx_access_logs: - log_statements: - - context: resource - statements: - - set(attributes["nginx.display.name"], Concat(["server", attributes["nginx.deployment.name"]], ":")) - - set(attributes["logtype"], "nginx") - transform/nginx_error_logs: - log_statements: - - context: resource - statements: - - set(attributes["nginx.display.name"], Concat(["server", attributes["nginx.deployment.name"]], ":")) - - set(attributes["logtype"], "nginx-error") - - exporters: - # existing exporter setup - - service: - pipelines: - metrics: - receivers: [nginx] - processors: [resourcedetection, resource, batch, transform/nginx_metrics] - exporters: [otlphttp/newrelic] - logs/nginx-access: - receivers: [filelog/nginx_access_logs] - processors: [batch, resource/nginx, transform/nginx_access_logs] - exporters: [otlphttp/newrelic] - logs/nginx-error: - receivers: [filelog/nginx_error_logs] - processors: [batch, resource/nginx, transform/nginx_error_logs] - exporters: [otlphttp/newrelic] - ``` - -3. Grant the `otelcol-contrib` user read access to the log files: - - ```bash - sudo usermod -a -G adm otelcol-contrib - sudo chmod 644 /var/log/nginx/access.log - sudo chmod 644 /var/log/nginx/error.log - ``` - -4. Restart the collector to apply the changes: - - ```bash - sudo systemctl restart otelcol-contrib - ``` diff --git a/src/install/nginx-otel-host/whatsNext.mdx b/src/install/nginx-otel-host/whatsNext.mdx deleted file mode 100644 index 71dcea5e1e5..00000000000 --- a/src/install/nginx-otel-host/whatsNext.mdx +++ /dev/null @@ -1,180 +0,0 @@ ---- -headingText: Metrics collected -componentType: default ---- - -## Metric data [#nginx-opensource-metrics] - -The OpenTelemetry Collector Contrib's [nginxreceiver](https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/receiver/nginxreceiver) collects the following metrics from the NGINX stub status module: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- Metric - - Description - - Type -
- `nginx.connections_accepted` - - The total number of accepted client connections - - Sum -
- `nginx.connections_handled` - - 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) - - Sum -
- `nginx.connections_current` - - The current number of nginx connections by state - - Sum -
- `nginx.requests` - - Total number of requests made to the server since it started - - Sum -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- Attribute - - Description - - Example Values -
- `state` - - The state of a connection (applicable to `nginx.connections_current` metric) - - `active`, `reading`, `writing`, `waiting` -
- `nginx.server.endpoint` - - The NGINX stub status endpoint URL - - `http://localhost:8080/stub_status` -
- `nginx.deployment.name` - - A unique name to identify this NGINX deployment - - `production-web-01`, `staging-api` -
- `nginx.display.name` - - A display-friendly name combining "server" prefix with deployment name - - `server:production-web-01` -
- `host.name` - - The hostname of the system where NGINX is running - - `web-server-01.example.com` -
- `host.id` - - The unique identifier of the host system - - `i-1234567890abcdef0` -
- `logtype` - - The type of log being collected (applicable to logs only). Used by New Relic's built-in [parsing rules](https://docs.newrelic.com/docs/logs/ui-data/built-log-parsing-rules/#nginx) - - `nginx` (for access logs), `nginx-error` (for error logs) -
-
- -
- -For more details, see the [NGINX receiver documentation](https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/main/receiver/nginxreceiver/documentation.md). diff --git a/src/install/nginx-otel-kubernetes/intro.mdx b/src/install/nginx-otel-kubernetes/intro.mdx deleted file mode 100644 index 126334b4322..00000000000 --- a/src/install/nginx-otel-kubernetes/intro.mdx +++ /dev/null @@ -1,7 +0,0 @@ ---- -headingText: Before you start -componentType: default ---- - -Monitor your NGINX servers using the [OpenTelemetry Collector](https://github.com/open-telemetry/opentelemetry-collector-contrib) to send metrics and telemetry data to New Relic. -This integration leverages the OpenTelemetry [nginxreceiver](https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/receiver/nginxreceiver), [receivercreator](https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/receiver/receivercreator) to monitor your NGINX performance metrics, connection statistics, and server health. diff --git a/src/install/nginx-otel-kubernetes/kubernetes/install-collector.mdx b/src/install/nginx-otel-kubernetes/kubernetes/install-collector.mdx deleted file mode 100644 index b79ac2f2131..00000000000 --- a/src/install/nginx-otel-kubernetes/kubernetes/install-collector.mdx +++ /dev/null @@ -1,163 +0,0 @@ ---- -headingText: Install and configure the OpenTelemetry Collector -componentType: default ---- - -Deploy the OpenTelemetry Collector to your Kubernetes cluster using Helm. The collector will automatically discover and scrape metrics from your NGINX pods. - -1. Create a Kubernetes secret to store your New Relic credentials. Replace `YOUR_LICENSE_KEY` and `YOUR_OTLP_ENDPOINT` with your actual values. Refer to the OTLP endpoint configuration [documentation](https://docs.newrelic.com/docs/opentelemetry/best-practices/opentelemetry-otlp/#configure-endpoint-port-protocol) to select the appropriate endpoint for your region. - - ```bash - kubectl create secret generic newrelic-licenses --from-literal=NEWRELIC_LICENSE_KEY=YOUR_LICENSE_KEY --from-literal=NEWRELIC_OTLP_ENDPOINT=YOUR_OTLP_ENDPOINT -n newrelic --create-namespace - ``` - -2. Create a `values.yaml` file to configure the OpenTelemetry Collector. Update the placeholders for your cluster name, NGINX pod labels, and stub status endpoint. - - ```yaml - opentelemetry-collector: - mode: deployment - - image: - repository: otel/opentelemetry-collector-contrib - pullPolicy: IfNotPresent - - command: - name: otelcol-contrib - - resources: - limits: - cpu: 500m - memory: 300Mi - requests: - cpu: 100m - memory: 100Mi - - extraEnvs: - - name: NEWRELIC_LICENSE_KEY - valueFrom: - secretKeyRef: - name: newrelic-licenses - key: NEWRELIC_LICENSE_KEY - - name: NEWRELIC_OTLP_ENDPOINT - valueFrom: - secretKeyRef: - name: newrelic-licenses - key: NEWRELIC_OTLP_ENDPOINT - - name: K8S_NODE_NAME - valueFrom: - fieldRef: - fieldPath: spec.nodeName - - name: K8S_CLUSTER_NAME - value: nginx-cluster # Update with your cluster name - - clusterRole: - create: true - rules: - - apiGroups: [""] - resources: ["pods", "nodes", "nodes/stats", "nodes/proxy"] - verbs: ["get", "list", "watch"] - - apiGroups: ["apps"] - resources: ["replicasets"] - verbs: ["get", "list", "watch"] - clusterRoleBinding: - name: "" - - config: - extensions: - health_check: - endpoint: 0.0.0.0:13133 - k8s_observer: - auth_type: serviceAccount - observe_pods: true - observe_nodes: true - - receivers: - receiver_creator/nginx: - watch_observers: [k8s_observer] - receivers: - nginx: - rule: type == "pod" && labels["app"] == "nginx" && labels["role"] == "reverse-proxy" # Update with your labels - config: - endpoint: 'http://`endpoint`:/status' - metrics: - nginx.requests: - enabled: true - nginx.connections_accepted: - enabled: true - nginx.connections_handled: - enabled: true - nginx.connections_current: - enabled: true - collection_interval: 30s - resource_attributes: - nginx.server.endpoint: 'http://`endpoint`:/status' - nginx.port: '' - - processors: - batch: - send_batch_size: 1024 - timeout: 30s - - resource/cluster: - attributes: - - key: k8s.cluster.name - value: "${env:K8S_CLUSTER_NAME}" - action: insert - - transform/nginx: - metric_statements: - - context: resource - statements: - - set(attributes["nginx.display.name"], Concat([ - "server", - "k8s", - attributes["k8s.cluster.name"], - attributes["k8s.namespace.name"], - "pod", - attributes["k8s.pod.name"], - "nginx", - attributes["nginx.port"] - ], ":")) - - set(attributes["nginx.deployment.name"], attributes["k8s.pod.name"]) - - exporters: - otlphttp: - endpoint: "${env:NEWRELIC_OTLP_ENDPOINT}" - headers: - api-key: "${env:NEWRELIC_LICENSE_KEY}" - - service: - extensions: [health_check, k8s_observer] - pipelines: - metrics/nginx: - receivers: [receiver_creator/nginx] - processors: [batch, resource/cluster, transform/nginx] - exporters: [otlphttp] - ``` - -3. Install the [Helm chart](https://github.com/open-telemetry/opentelemetry-helm-charts) using your values.yaml file. - - ```bash - helm repo add open-telemetry https://open-telemetry.github.io/opentelemetry-helm-charts - helm repo update - helm upgrade --install nginx-otel-collector open-telemetry/opentelemetry-collector --namespace newrelic --create-namespace -f values.yaml - ``` - -4. Ensure the pods have successfully spun up. - - ```bash - kubectl get pods -n newrelic --watch - ``` - - You should see pods with names like `nginx-otel-collector-` in a `Running` state in the `newrelic` namespace. - -5. Run an NRQL query in New Relic to confirm data is arriving. Replace the cluster name with the value you set in the values file: - - ```sql - FROM Metric - SELECT * - WHERE metricName LIKE 'nginx.%' - AND instrumentation.provider = 'opentelemetry' - AND k8s.cluster.name = 'nginx-cluster' - SINCE 10 minutes ago - ``` diff --git a/src/install/nginx-otel-kubernetes/kubernetes/prerequisites.mdx b/src/install/nginx-otel-kubernetes/kubernetes/prerequisites.mdx deleted file mode 100644 index 5f511a48bb2..00000000000 --- a/src/install/nginx-otel-kubernetes/kubernetes/prerequisites.mdx +++ /dev/null @@ -1,10 +0,0 @@ ---- -headingText: Prerequisites -componentType: default ---- - -Before you begin, ensure you have: - -- A [New Relic account](https://newrelic.com/signup) with a -- Enable the [HTTP stub status](https://nginx.org/en/docs/http/ngx_http_stub_status_module.html) module on NGINX pod that needs to be monitored -- Add labels `app` and `role` to each NGINX pod that needs to be monitored diff --git a/src/install/nginx-otel-kubernetes/kubernetes/status-endpoint.mdx b/src/install/nginx-otel-kubernetes/kubernetes/status-endpoint.mdx deleted file mode 100644 index e4a4964a2b7..00000000000 --- a/src/install/nginx-otel-kubernetes/kubernetes/status-endpoint.mdx +++ /dev/null @@ -1,22 +0,0 @@ ---- -headingText: Expose and verify the stub status endpoint -componentType: default ---- - -Each NGINX pod must expose the HTTP stub status module so the collector can scrape metrics. - -1. Add a location block similar to the following to your NGINX configuration, then reload the deployment: - - ```nginx - location /status { - stub_status on; - } - ``` -2. Record the path (without the leading slash) and the port serving the stub status endpoint. Defaults are `status` on port `80` or `8080`. -3. Verify the endpoint from within one of your pods: - - ```bash - kubectl exec deploy/ -- curl -I http://127.0.0.1:8080/status 2>/dev/null | head -n 1 | cut -d$' ' -f1,2 - ``` - -Expect an `HTTP/1.1 200` response. Adjust the command to match your deployment name, port, and path. diff --git a/src/install/nginx-otel-kubernetes/kubernetes/verify.mdx b/src/install/nginx-otel-kubernetes/kubernetes/verify.mdx deleted file mode 100644 index 6a0e7ee56cc..00000000000 --- a/src/install/nginx-otel-kubernetes/kubernetes/verify.mdx +++ /dev/null @@ -1,11 +0,0 @@ ---- -headingText: Find and use data -componentType: default ---- - -1. Go to **[one.newrelic.com](https://one.newrelic.com) > Integrations & Agents**. -2. Select **Dashboards**, and click **NGINX OTel overview dashboard**. -3. In the popup window, select your account. -4. Click View dashboard, and see your NGINX data in New Relic. - -The NGINX metrics are attached to the `Metric` [event type](/docs/using-new-relic/data/understand-data/new-relic-data-types#events-new-relic). You can [query this data](/docs/using-new-relic/data/understand-data/query-new-relic-data) for troubleshooting purposes or to create custom charts and dashboards. diff --git a/src/install/nginx-otel-kubernetes/whatsNext.mdx b/src/install/nginx-otel-kubernetes/whatsNext.mdx deleted file mode 100644 index 58ac2c40cf9..00000000000 --- a/src/install/nginx-otel-kubernetes/whatsNext.mdx +++ /dev/null @@ -1,191 +0,0 @@ ---- -headingText: Metrics collected -componentType: default ---- - -## Metric data [#nginx-opensource-metrics] - -The OpenTelemetry Collector Contrib's [nginxreceiver](https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/receiver/nginxreceiver) collects the following metrics from the NGINX stub status module: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- Metric - - Description - - Type -
- `nginx.connections_accepted` - - The total number of accepted client connections - - Sum -
- `nginx.connections_handled` - - 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) - - Sum -
- `nginx.connections_current` - - The current number of nginx connections by state - - Sum -
- `nginx.requests` - - Total number of requests made to the server since it started - - Sum -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- Attribute - - Description - - Example Values -
- `state` - - The state of a connection (applicable to `nginx.connections_current` metric) - - `active`, `reading`, `writing`, `waiting` -
- `nginx.server.endpoint` - - The NGINX stub status endpoint URL - - `http://10.244.1.5:8080/status` -
- `nginx.port` - - The port number where stub status is exposed - - `8080` -
- `k8s.cluster.name` - - The name of the Kubernetes cluster - - `nginx-cluster`, `production-k8s` -
- `k8s.namespace.name` - - The Kubernetes namespace where the NGINX pod is running - - `default`, `nginx-ingress` -
- `k8s.pod.name` - - The name of the Kubernetes pod running NGINX - - `nginx-deployment-5d7b9c8f4d-abc12` -
- `nginx.deployment.name` - - The deployment name (set to the pod name in Kubernetes) - - `nginx-deployment-5d7b9c8f4d-abc12` -
- `nginx.display.name` - - A display-friendly name combining cluster, namespace, and pod information - - `server:k8s:nginx-cluster:default:pod:nginx-deployment-5d7b9c8f4d-abc12:nginx:8080` -
-
- -
- -For more details, see the [NGINX receiver documentation](https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/main/receiver/nginxreceiver/documentation.md). diff --git a/src/install/nginx-plus-otel/collector/environment.mdx b/src/install/nginx-plus-otel/collector/environment.mdx deleted file mode 100644 index 836a314b9da..00000000000 --- a/src/install/nginx-plus-otel/collector/environment.mdx +++ /dev/null @@ -1,25 +0,0 @@ ---- -headingText: Set environment variables for the collector -componentType: default ---- - -Inject your New Relic and OTLP endpoint into the collector service so the exporter can authenticate. - -1. Create a systemd override directory: - ```bash - sudo mkdir -p /etc/systemd/system/otelcol-contrib.service.d - ``` -2. Write `environment.conf` with your OTLP endpoint. Replace `YOUR_LICENSE_KEY` with the New Relic license key and `YOUR_OTLP_ENDPOINT` with the appropriate endpoint for your region. Refer to the OTLP endpoint configuration [documentation](https://docs.newrelic.com/docs/opentelemetry/best-practices/opentelemetry-otlp/#configure-endpoint-port-protocol) to select the right endpoint. - - ```bash - cat < - action: insert - - key: nginx.deployment.name - value: - action: insert - resourcedetection: - detectors: [system] - system: - resource_attributes: - host.name: - enabled: true - host.id: - enabled: true - transform/nginx: - metric_statements: - - context: resource - statements: - - set(attributes["nginx.display.name"], Concat(["server", attributes["nginx.deployment.name"]], ":")) - - context: resource - statements: - - delete_key(attributes, "service.name") -exporters: - otlphttp/newrelic: - endpoint: ${env:NEWRELIC_OTLP_ENDPOINT} - headers: - api-key: ${env:NEWRELIC_LICENSE_KEY} -service: - pipelines: - metrics/nginx: - receivers: [prometheus] - processors: [batch, filter/nginx_metrics, resourcedetection, resource/nginx, transform/nginx] - exporters: [otlphttp/newrelic] -``` - -Save the file and ensure the `otelcol-contrib` system user can read it. diff --git a/src/install/nginx-plus-otel/host/enable-status-plus.mdx b/src/install/nginx-plus-otel/host/enable-status-plus.mdx deleted file mode 100644 index e99608ea153..00000000000 --- a/src/install/nginx-plus-otel/host/enable-status-plus.mdx +++ /dev/null @@ -1,30 +0,0 @@ ---- -headingText: Configure NGINX Plus -componentType: default ---- - -Configure and enable the [HTTP API module](https://nginx.org/en/docs/http/ngx_http_api_module.html) to expose metrics from your NGINX Plus server. - -After updating `nginx.conf`, test and reload the service: - -```bash -sudo nginx -t && sudo nginx -s reload -``` - -Confirm the API endpoint path (including version) exposed in your configuration. - -Record the full API endpoint path (without the leading slash) and the port that serves it. Common defaults are `api/9` on port `8080`. - -Use `curl` to confirm your API endpoint is reachable: - -```bash -curl -I http://127.0.0.1:8080/api/9 2>/dev/null | head -n 1 | cut -d$' ' -f1,2 -``` - -Expected output: - -``` -HTTP/1.1 200 -``` - -If you see a different response, verify your NGINX Plus configuration and ensure the API module is properly enabled. diff --git a/src/install/nginx-plus-otel/host/prerequisites-plus.mdx b/src/install/nginx-plus-otel/host/prerequisites-plus.mdx deleted file mode 100644 index 026f0e70f0e..00000000000 --- a/src/install/nginx-plus-otel/host/prerequisites-plus.mdx +++ /dev/null @@ -1,14 +0,0 @@ ---- -headingText: Check the compatibility and requirements -componentType: default ---- -Before you begin, ensure you have: - -- A [New Relic account](https://newrelic.com/signup) with a -- NGINX Plus version [R13](https://docs.nginx.com/nginx/releases/#r13) or higher -- NGINX Plus with the [HTTP API](https://nginx.org/en/docs/http/ngx_http_api_module.html) module enabled -- [NGINX Prometheus exporter](https://github.com/nginx/nginx-prometheus-exporter) installed and running alongside your NGINX Plus instance to expose HTTP API metrics in Prometheus format -- [OpenTelemetry Collector Contrib](https://github.com/open-telemetry/opentelemetry-collector-contrib/releases/latest) installed and running on a linux host -- Network access from the linux host to: - - NGINX Plus HTTP API endpoint - - Anyone of the [New Relic OTLP](https://docs.newrelic.com/docs/opentelemetry/best-practices/opentelemetry-otlp/#configure-endpoint-port-protocol:~:text=HTTP-,Endpoint,-Supported%20ports) enpoint diff --git a/src/install/nginx-plus-otel/host/verify.mdx b/src/install/nginx-plus-otel/host/verify.mdx deleted file mode 100644 index 3a3b74c5552..00000000000 --- a/src/install/nginx-plus-otel/host/verify.mdx +++ /dev/null @@ -1,11 +0,0 @@ ---- -headingText: Find and use data -componentType: default ---- - -1. Go to **[one.newrelic.com](https://one.newrelic.com) > Integrations & Agents**. -2. Select **Dashboards**, and click **NGINX OTel overview dashboard**. -3. In the popup window, select your account. -4. Click View dashboard, and see your NGINX Plus data in New Relic. - -The NGINX Plus metrics are attached to the `Metric` [event type](/docs/using-new-relic/data/understand-data/new-relic-data-types#events-new-relic). You can [query this data](/docs/using-new-relic/data/understand-data/query-new-relic-data) for troubleshooting purposes or to create custom charts and dashboards. diff --git a/src/install/nginx-plus-otel/intro.mdx b/src/install/nginx-plus-otel/intro.mdx deleted file mode 100644 index eddf0805a76..00000000000 --- a/src/install/nginx-plus-otel/intro.mdx +++ /dev/null @@ -1,8 +0,0 @@ ---- -headingText: Before you start -componentType: default ---- - - -Monitor your NGINX Plus servers using the [OpenTelemetry Collector](https://github.com/open-telemetry/opentelemetry-collector-contrib) to send metrics and telemetry data to New Relic. -This integration leverages the OpenTelemetry [prometheusreceiver](https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/receiver/prometheusreceiver) and [nginx-prometheus-exporter](https://github.com/nginx/nginx-prometheus-exporter) to monitor your NGINX Plus performance metrics, connection statistics, and server health. diff --git a/src/install/nginx-plus-otel/logs/forwarding-plus.mdx b/src/install/nginx-plus-otel/logs/forwarding-plus.mdx deleted file mode 100644 index 1a5afab77d7..00000000000 --- a/src/install/nginx-plus-otel/logs/forwarding-plus.mdx +++ /dev/null @@ -1,84 +0,0 @@ ---- -headingText: Forward NGINX logs and create log parsing rules -componentType: default ---- - -Extend your collector configuration to include access and error logs if you want log events alongside metrics. - -### Configure NGINX Plus log format - -Before forwarding logs, configure NGINX Plus to use a structured log format. Refer to the [NGINX logging documentation](https://docs.nginx.com/nginx/admin-guide/monitoring/logging/) for guidance on configuring access and error logs. - -### Configure the OpenTelemetry Collector for log forwarding - -1. Note the full paths to your NGINX access and error log files. Defaults are usually `/var/log/nginx/access.log` and `/var/log/nginx/error.log`. - -2. Update `/etc/otelcol-contrib/config.yaml` to add a `filelog` receiver and log pipeline: - - ```yaml - receivers: - prometheus: - # existing Prometheus receiver configuration - filelog/nginx_access_logs: - include: - - /var/log/nginx/access.log - filelog/nginx_error_logs: - include: - - /var/log/nginx/error.log - - processors: - batch: - # existing settings - filter/nginx_metrics: - # existing settings - resourcedetection: - # existing settings - resource/nginx: - # existing settings - transform/nginx_metrics: - # existing settings - transform/nginx_access_logs: - log_statements: - - context: resource - statements: - - set(attributes["nginx.display.name"], Concat(["server", attributes["nginx.deployment.name"]], ":")) - - set(attributes["logtype"], "nginx") - transform/nginx_error_logs: - log_statements: - - context: resource - statements: - - set(attributes["nginx.display.name"], Concat(["server", attributes["nginx.deployment.name"]], ":")) - - set(attributes["logtype"], "nginx-error") - - exporters: - # existing exporter setup - - service: - pipelines: - metrics/nginx: - receivers: [prometheus] - processors: [batch, filter/nginx_metrics, resourcedetection, resource/nginx, transform/nginx_metrics] - exporters: [otlphttp/newrelic] - logs/nginx-access: - receivers: [filelog/nginx_access_logs] - processors: [batch, resource/nginx, transform/nginx_access_logs] - exporters: [otlphttp/newrelic] - logs/nginx-error: - receivers: [filelog/nginx_error_logs] - processors: [batch, resource/nginx, transform/nginx_error_logs] - exporters: [otlphttp/newrelic] - ``` - -3. Grant the `otelcol-contrib` user read access to the log files: - - ```bash - sudo usermod -a -G adm otelcol-contrib - sudo chmod 644 /var/log/nginx/access.log - sudo chmod 644 /var/log/nginx/error.log - ``` - -4. Restart the collector to apply the changes: - - ```bash - sudo systemctl restart otelcol-contrib - ``` diff --git a/src/nav/infrastructure.yml b/src/nav/infrastructure.yml index 9fedca4fb0b..87788ddc398 100644 --- a/src/nav/infrastructure.yml +++ b/src/nav/infrastructure.yml @@ -209,20 +209,8 @@ pages: path: /docs/infrastructure/host-integrations/host-integrations-list/nextcloud-integration - title: NFS integration path: /docs/infrastructure/host-integrations/host-integrations-list/nfs-monitoring-integration - - title: NGINX integrations - pages: - - title: NR Native NGINX integration - path: /install/nginx - - title: OpenTelemetry NGINX - pages: - - title: Self-hosted - path: /install/nginx-otel-host - - title: Kubernetes - path: /install/nginx-otel-kubernetes - - title: OpenTelemetry NGINX Plus - pages: - - title: Self-hosted - path: /install/nginx-plus-otel + - title: NGINX integration + path: /install/nginx - title: NVIDIA GPU integration path: /docs/infrastructure/host-integrations/host-integrations-list/nvidia-gpu-integration - title: NVIDIA Jetson integration diff --git a/src/nav/opentelemetry.yml b/src/nav/opentelemetry.yml index da4af694dca..f173a0c06e5 100644 --- a/src/nav/opentelemetry.yml +++ b/src/nav/opentelemetry.yml @@ -43,3 +43,15 @@ pages: path: /docs/opentelemetry/best-practices/opentelemetry-best-practices-resources - title: Manage data ingest volume path: /docs/opentelemetry/best-practices/opentelemetry-manage-data-ingest-volume + - title: OpenTelemetry integrations + pages: + - title: NGINX integration + pages: + - title: Self-hosted + path: /docs/opentelemetry/nginx/nginx-otel-host + - title: Kubernetes + path: /docs/opentelemetry/nginx/nginx-otel-kubernetes + - title: NGINX Plus integration + pages: + - title: Self-hosted + path: /docs/opentelemetry/nginx-plus/nginx-plus-otel From 3b1c78e1b3525d2fa3708a835ea7dcfbc242bcec Mon Sep 17 00:00:00 2001 From: sairaj18 Date: Tue, 23 Dec 2025 13:50:41 +0530 Subject: [PATCH 04/32] feat(nginx): move nginx otel to opentelemetry nav and combine all mdx files, create a single file for each deployment type --- .../nginx-plus/nginx-plus-otel.mdx} | 268 ++++++++++- .../opentelemetry/nginx/nginx-otel-host.mdx | 424 ++++++++++++++++++ .../nginx/nginx-otel-kubernetes.mdx | 385 ++++++++++++++++ src/install/config/nginx-otel-host.yaml | 16 - src/install/config/nginx-otel-kubernetes.yaml | 13 - src/install/config/nginx-plus-otel.yaml | 16 - .../nginx-otel-host/collector/environment.mdx | 25 -- .../host/configure-collector-opensource.mdx | 65 --- .../host/enable-status-opensource.mdx | 39 -- .../host/prerequisites-opensource.mdx | 12 - src/install/nginx-otel-host/host/verify.mdx | 11 - src/install/nginx-otel-host/intro.mdx | 7 - .../logs/forwarding-opensource.mdx | 82 ---- src/install/nginx-otel-host/whatsNext.mdx | 180 -------- src/install/nginx-otel-kubernetes/intro.mdx | 7 - .../kubernetes/install-collector.mdx | 163 ------- .../kubernetes/prerequisites.mdx | 10 - .../kubernetes/status-endpoint.mdx | 22 - .../kubernetes/verify.mdx | 11 - .../nginx-otel-kubernetes/whatsNext.mdx | 191 -------- .../nginx-plus-otel/collector/environment.mdx | 25 -- .../host/configure-collector-plus.mdx | 91 ---- .../host/enable-status-plus.mdx | 30 -- .../host/prerequisites-plus.mdx | 14 - src/install/nginx-plus-otel/host/verify.mdx | 11 - src/install/nginx-plus-otel/intro.mdx | 8 - .../nginx-plus-otel/logs/forwarding-plus.mdx | 84 ---- src/install/nginx/whatsNext.mdx | 6 + src/nav/infrastructure.yml | 16 +- src/nav/opentelemetry.yml | 12 + ...ntelemetry_screenshot-nginx-dashboard.webp | Bin 0 -> 478491 bytes 31 files changed, 1093 insertions(+), 1151 deletions(-) rename src/{install/nginx-plus-otel/whatsNext.mdx => content/docs/opentelemetry/nginx-plus/nginx-plus-otel.mdx} (60%) create mode 100644 src/content/docs/opentelemetry/nginx/nginx-otel-host.mdx create mode 100644 src/content/docs/opentelemetry/nginx/nginx-otel-kubernetes.mdx delete mode 100644 src/install/config/nginx-otel-host.yaml delete mode 100644 src/install/config/nginx-otel-kubernetes.yaml delete mode 100644 src/install/config/nginx-plus-otel.yaml delete mode 100644 src/install/nginx-otel-host/collector/environment.mdx delete mode 100644 src/install/nginx-otel-host/host/configure-collector-opensource.mdx delete mode 100644 src/install/nginx-otel-host/host/enable-status-opensource.mdx delete mode 100644 src/install/nginx-otel-host/host/prerequisites-opensource.mdx delete mode 100644 src/install/nginx-otel-host/host/verify.mdx delete mode 100644 src/install/nginx-otel-host/intro.mdx delete mode 100644 src/install/nginx-otel-host/logs/forwarding-opensource.mdx delete mode 100644 src/install/nginx-otel-host/whatsNext.mdx delete mode 100644 src/install/nginx-otel-kubernetes/intro.mdx delete mode 100644 src/install/nginx-otel-kubernetes/kubernetes/install-collector.mdx delete mode 100644 src/install/nginx-otel-kubernetes/kubernetes/prerequisites.mdx delete mode 100644 src/install/nginx-otel-kubernetes/kubernetes/status-endpoint.mdx delete mode 100644 src/install/nginx-otel-kubernetes/kubernetes/verify.mdx delete mode 100644 src/install/nginx-otel-kubernetes/whatsNext.mdx delete mode 100644 src/install/nginx-plus-otel/collector/environment.mdx delete mode 100644 src/install/nginx-plus-otel/host/configure-collector-plus.mdx delete mode 100644 src/install/nginx-plus-otel/host/enable-status-plus.mdx delete mode 100644 src/install/nginx-plus-otel/host/prerequisites-plus.mdx delete mode 100644 src/install/nginx-plus-otel/host/verify.mdx delete mode 100644 src/install/nginx-plus-otel/intro.mdx delete mode 100644 src/install/nginx-plus-otel/logs/forwarding-plus.mdx create mode 100644 static/images/opentelemetry_screenshot-nginx-dashboard.webp diff --git a/src/install/nginx-plus-otel/whatsNext.mdx b/src/content/docs/opentelemetry/nginx-plus/nginx-plus-otel.mdx similarity index 60% rename from src/install/nginx-plus-otel/whatsNext.mdx rename to src/content/docs/opentelemetry/nginx-plus/nginx-plus-otel.mdx index a6c60b48c49..3a4a0ee36f5 100644 --- a/src/install/nginx-plus-otel/whatsNext.mdx +++ b/src/content/docs/opentelemetry/nginx-plus/nginx-plus-otel.mdx @@ -1,9 +1,269 @@ --- -headingText: Metrics collected -componentType: default +title: 'Monitor self-hosted NGINX Plus with OpenTelemetry' +metaDescription: 'Send your NGINX Plus metrics to New Relic using the OpenTelemetry contrib collector.' +redirects: + - /docs/infrastructure/host-integrations/host-integrations-list/nginx/nginx-plus-otel +freshnessValidatedDate: never --- -## Metric data [#nginx-plus-metrics] +Monitor your NGINX Plus servers using the [OpenTelemetry Collector](https://github.com/open-telemetry/opentelemetry-collector-contrib) to send metrics and telemetry data to New Relic. +This integration leverages the OpenTelemetry [prometheusreceiver](https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/receiver/prometheusreceiver) and [nginx-prometheus-exporter](https://github.com/nginx/nginx-prometheus-exporter) to monitor your NGINX Plus performance metrics, connection statistics, and server health. + +Image of the NGINX dashboard + +
+ Dashboard available through the [New Relic NGINX OpenTelemetry Monitor quickstart](https://newrelic.com/instant-observability/nginx-otel). +
+ +## Check the compatibility and requirements [#prerequisites] + +Before you begin, ensure you have: + +- A [New Relic account](https://newrelic.com/signup) with a +- NGINX Plus version [R13](https://docs.nginx.com/nginx/releases/#r13) or higher +- NGINX Plus with the [HTTP API](https://nginx.org/en/docs/http/ngx_http_api_module.html) module enabled +- [NGINX Prometheus exporter](https://github.com/nginx/nginx-prometheus-exporter) installed and running alongside your NGINX Plus instance to expose HTTP API metrics in Prometheus format +- [OpenTelemetry Collector Contrib](https://github.com/open-telemetry/opentelemetry-collector-contrib/releases/latest) installed and running on a linux host +- Network access from the linux host to: + - NGINX Plus HTTP API endpoint + - Anyone of the [New Relic OTLP](https://docs.newrelic.com/docs/opentelemetry/best-practices/opentelemetry-otlp/#configure-endpoint-port-protocol) enpoint + +## Configure NGINX Plus [#configure-nginx] + +Configure and enable the [HTTP API module](https://nginx.org/en/docs/http/ngx_http_api_module.html) to expose metrics from your NGINX Plus server. + +After updating `nginx.conf`, test and reload the service: + +```bash +sudo nginx -t && sudo nginx -s reload +``` + +Confirm the API endpoint path (including version) exposed in your configuration. + +Record the full API endpoint path (without the leading slash) and the port that serves it. Common defaults are `api/9` on port `8080`. + +Use `curl` to confirm your API endpoint is reachable: + +```bash +curl -I http://127.0.0.1:8080/api/9 2>/dev/null | head -n 1 | cut -d$' ' -f1,2 +``` + +Expected output: + +``` +HTTP/1.1 200 +``` + +If you see a different response, verify your NGINX Plus configuration and ensure the API module is properly enabled. + +## Configure the OpenTelemetry Collector [#configure-collector] + +In the below OpenTelemetry Collector config snippet: + +- Update the `nginx.deployment.name` value to with a unique name to identify this NGINX Plus server in a New Relic account. +- Update the `targets` value to match your Prometheus exporter host and port (default is `127.0.0.1:9113`). +- Update the `nginx.server.endpoint` value to match your API status path and port. + +Merge the receivers, processors, exporters, and service pipelines from the snippet below into your current configuration (typically located at `/etc/otelcol-contrib/config.yaml`). + +Note: Update the `filter/nginx_metrics` as per your requirement under the processor section in order to ingest additional metrics. + +```yaml +receivers: + prometheus: + config: + scrape_configs: + - job_name: nginx-plus + scrape_interval: 30s + static_configs: + - targets: + - 127.0.0.1:9113 +processors: + batch: + send_batch_size: 1024 + timeout: 30s + filter/nginx_metrics: + metrics: + include: + match_type: regexp + metric_names: + - "nginxplus_connections_.*" + - "nginxplus_http_requests_.*" + - "nginxplus_ssl_.*" + - "nginxplus_server_.*" + - "nginxplus_location_zone_.*" + - "nginxplus_cache_.*" + + exclude: + match_type: regexp + metric_names: + - "nginxplus_stream_server_.*" + - "nginxplus_upstream_.*" + - "nginxplus_stream_upstream_.*" + - "nginxplus_stream_zone_sync_zone_.*" + - "nginxplus_resolver_.*" + - "nginxplus_limit_request_.*" + - "nginxplus_limit_connection_.*" + - "nginxplus_stream_limit_connection_.*" + - "nginxplus_worker_.*" + resource/nginx: + attributes: + - key: nginx.server.endpoint + value: + action: insert + - key: nginx.deployment.name + value: + action: insert + resourcedetection: + detectors: [system] + system: + resource_attributes: + host.name: + enabled: true + host.id: + enabled: true + transform/nginx: + metric_statements: + - context: resource + statements: + - set(attributes["nginx.display.name"], Concat(["server", attributes["nginx.deployment.name"]], ":")) + - context: resource + statements: + - delete_key(attributes, "service.name") +exporters: + otlphttp/newrelic: + endpoint: ${env:NEWRELIC_OTLP_ENDPOINT} + headers: + api-key: ${env:NEWRELIC_LICENSE_KEY} +service: + pipelines: + metrics/nginx: + receivers: [prometheus] + processors: [batch, filter/nginx_metrics, resourcedetection, resource/nginx, transform/nginx] + exporters: [otlphttp/newrelic] +``` + +Save the file and ensure the `otelcol-contrib` system user can read it. + +## Set environment variables for the collector [#set-environment] + +Inject your New Relic and OTLP endpoint into the collector service so the exporter can authenticate. + +1. Create a systemd override directory: + ```bash + sudo mkdir -p /etc/systemd/system/otelcol-contrib.service.d + ``` +2. Write `environment.conf` with your OTLP endpoint. Replace `YOUR_LICENSE_KEY` with the New Relic license key and `YOUR_OTLP_ENDPOINT` with the appropriate endpoint for your region. Refer to the OTLP endpoint configuration [documentation](https://docs.newrelic.com/docs/opentelemetry/best-practices/opentelemetry-otlp/#configure-endpoint-port-protocol) to select the right endpoint. + + ```bash + cat < Integrations & Agents**. +2. Select **Dashboards**, and click **NGINX OTel overview dashboard**. +3. In the popup window, select your account. +4. Click View dashboard, and see your NGINX Plus data in New Relic. + +The NGINX Plus metrics are attached to the `Metric` [event type](/docs/using-new-relic/data/understand-data/new-relic-data-types#events-new-relic). You can [query this data](/docs/using-new-relic/data/understand-data/query-new-relic-data) for troubleshooting purposes or to create custom charts and dashboards. + +## Metrics collected [#metrics] The OpenTelemetry Collector scrapes metrics from the [NGINX Prometheus exporter](https://github.com/nginx/nginx-prometheus-exporter), which exposes NGINX Plus HTTP API metrics in Prometheus format. @@ -778,7 +1038,7 @@ Below are the available NGINX Plus metrics: `logtype` - The type of log being collected (applicable to logs only). Used by New Relic's built-in [parsing rules](https://docs.newrelic.com/docs/logs/ui-data/built-log-parsing-rules/#nginx) + The type of log being collected (applicable to logs only). Used by New Relic's built-in [parsing rules](https://docs.newrelic.com/docs/logs/ui-data/built-log-parsing-rules/#nginx). This attribute is only available when log forwarding is enabled. `nginx` (for access logs), `nginx-error` (for error logs) diff --git a/src/content/docs/opentelemetry/nginx/nginx-otel-host.mdx b/src/content/docs/opentelemetry/nginx/nginx-otel-host.mdx new file mode 100644 index 00000000000..1d26b1f736a --- /dev/null +++ b/src/content/docs/opentelemetry/nginx/nginx-otel-host.mdx @@ -0,0 +1,424 @@ +--- +title: 'Monitor self-hosted NGINX with OpenTelemetry' +metaDescription: 'Send your NGINX metrics and logs to New Relic using the OpenTelemetry Collector.' +redirects: + - /docs/infrastructure/host-integrations/host-integrations-list/nginx/nginx-otel-host +freshnessValidatedDate: never +--- + +Monitor your NGINX servers using the [OpenTelemetry Collector](https://github.com/open-telemetry/opentelemetry-collector-contrib) to send metrics and telemetry data to New Relic. +This integration leverages the OpenTelemetry [nginxreceiver](https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/receiver/nginxreceiver) to monitor your NGINX performance metrics, connection statistics, and server health. + +Image of the NGINX dashboard + +
+ Dashboard available through the [New Relic NGINX OpenTelemetry Monitor quickstart](https://newrelic.com/instant-observability/nginx-otel). +
+ + + - To monitor **NGINX Plus**, see [Monitor NGINX Plus with OpenTelemetry](/docs/opentelemetry/nginx-plus/nginx-plus-otel/). + - To monitor **NGINX on Kubernetes**, see [Monitor NGINX on Kubernetes with OpenTelemetry](/docs/opentelemetry/nginx/nginx-otel-kubernetes/). + + +## Check the compatibility and requirements [#prerequisites] + +Before you begin, ensure you have: + +- A [New Relic account](https://newrelic.com/signup) with a +- NGINX with the [HTTP stub status](https://nginx.org/en/docs/http/ngx_http_stub_status_module.html) module enabled +- [OpenTelemetry Collector Contrib](https://github.com/open-telemetry/opentelemetry-collector-contrib/releases/latest) installed and running on a linux host +- Network access from the linux host to: + - NGINX HTTP stub status endpoint + - Anyone of the [New Relic OTLP](https://docs.newrelic.com/docs/opentelemetry/best-practices/opentelemetry-otlp/#configure-endpoint-port-protocol) enpoint + +## Configure NGINX [#configure-nginx] + +Configure and enable the [stub_status](https://nginx.org/en/docs/http/ngx_http_stub_status_module.html) module to expose metrics from your NGINX Open Source server. + +After updating `nginx.conf`, test and reload the service: + +```bash +sudo nginx -t && sudo nginx -s reload +``` + +List the configuration block that exposes `stub_status`: + +```bash +sudo nginx -T 2>&1 | grep -ozP "(?s:.*\s)\Klocation(?s).*stub_status" | grep -aoP "\/([^\s]+)" +``` + +Example output: + +``` +/status +``` +If you do not see output, inspect your nginx.conf manually for a stub_status block. + +Use `curl` to confirm your status endpoint is reachable: + +```bash +curl -I http://127.0.0.1:8080/status 2>/dev/null | head -n 1 | cut -d$' ' -f1,2 +``` + +Expected output: + +``` +HTTP/1.1 200 +``` + +If you see a different response, troubleshoot your NGINX configuration. + +## Configure the OpenTelemetry Collector [#configure-collector] + +In the below OpenTelemetry Collector config snippet: + +- Update the `nginx.deployment.name` value to with a unique name to identify this NGINX server in a New Relic account. +- Update the `endpoint` and `nginx.server.endpoint` value to match your stub status path and port. + +Merge the receivers, processors, exporters, and service pipelines from the snippet below into your current configuration (typically located at `/etc/otelcol-contrib/config.yaml`). + +```yaml +receivers: + nginx: + metrics: + nginx.requests: + enabled: true + nginx.connections_accepted: + enabled: true + nginx.connections_handled: + enabled: true + nginx.connections_current: + enabled: true + endpoint: + collection_interval: 30s +processors: + resourcedetection: + detectors: [system] + system: + resource_attributes: + host.name: + enabled: true + host.id: + enabled: true + resource: + attributes: + - action: upsert + key: nginx.server.endpoint + value: + - action: upsert + key: nginx.deployment.name + value: + batch: + timeout: 30s + send_batch_size: 512 + transform/nginx_metrics: + metric_statements: + - context: resource + statements: + - set(attributes["nginx.display.name"], Concat(["server", attributes["nginx.deployment.name"]], ":")) +exporters: + otlphttp/newrelic: + endpoint: ${env:NEWRELIC_OTLP_ENDPOINT} + headers: + api-key: ${env:NEWRELIC_LICENSE_KEY} +service: + pipelines: + metrics: + receivers: [nginx] + processors: [resourcedetection, resource, batch, transform/nginx_metrics] + exporters: [otlphttp/newrelic] +``` + +Save the file and ensure the `otelcol-contrib` system user can read it. + +## Set environment variables for the collector [#set-environment] + +Inject your New Relic and OTLP endpoint into the collector service so the exporter can authenticate. + +1. Create a systemd override directory: + ```bash + sudo mkdir -p /etc/systemd/system/otelcol-contrib.service.d + ``` +2. Write `environment.conf` with your OTLP endpoint. Replace `YOUR_LICENSE_KEY` with the New Relic license key and `YOUR_OTLP_ENDPOINT` with the appropriate endpoint for your region. Refer to the OTLP endpoint configuration [documentation](https://docs.newrelic.com/docs/opentelemetry/best-practices/opentelemetry-otlp/#configure-endpoint-port-protocol) to select the right endpoint. + + ```bash + cat < Integrations & Agents**. +2. Select **Dashboards**, and click **NGINX OTel overview dashboard**. +3. In the popup window, select your account. +4. Click View dashboard, and see your NGINX data in New Relic. + +The NGINX metrics are attached to the `Metric` [event type](/docs/using-new-relic/data/understand-data/new-relic-data-types#events-new-relic). You can [query this data](/docs/using-new-relic/data/understand-data/query-new-relic-data) for troubleshooting purposes or to create custom charts and dashboards. + +## Metrics collected [#metrics] + +The OpenTelemetry Collector Contrib's [nginxreceiver](https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/receiver/nginxreceiver) collects the following metrics from the NGINX stub status module: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Metric + + Description + + Type +
+ `nginx.connections_accepted` + + The total number of accepted client connections + + Sum +
+ `nginx.connections_handled` + + 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) + + Sum +
+ `nginx.connections_current` + + The current number of nginx connections by state + + Sum +
+ `nginx.requests` + + Total number of requests made to the server since it started + + Sum +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Attribute + + Description + + Example Values +
+ `state` + + The state of a connection (applicable to `nginx.connections_current` metric) + + `active`, `reading`, `writing`, `waiting` +
+ `nginx.server.endpoint` + + The NGINX stub status endpoint URL + + `http://localhost:8080/stub_status` +
+ `nginx.deployment.name` + + A unique name to identify this NGINX deployment + + `production-web-01`, `staging-api` +
+ `nginx.display.name` + + A display-friendly name combining "server" prefix with deployment name + + `server:production-web-01` +
+ `host.name` + + The hostname of the system where NGINX is running + + `web-server-01.example.com` +
+ `host.id` + + The unique identifier of the host system + + `i-1234567890abcdef0` +
+ `logtype` + + The type of log being collected (applicable to logs only). Used by New Relic's built-in [parsing rules](https://docs.newrelic.com/docs/logs/ui-data/built-log-parsing-rules/#nginx). This attribute is only available when log forwarding is enabled. + + `nginx` (for access logs), `nginx-error` (for error logs) +
+
+ +
+ +For more details, see the [NGINX receiver documentation](https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/main/receiver/nginxreceiver/documentation.md). diff --git a/src/content/docs/opentelemetry/nginx/nginx-otel-kubernetes.mdx b/src/content/docs/opentelemetry/nginx/nginx-otel-kubernetes.mdx new file mode 100644 index 00000000000..99c8168cc70 --- /dev/null +++ b/src/content/docs/opentelemetry/nginx/nginx-otel-kubernetes.mdx @@ -0,0 +1,385 @@ +--- +title: 'Monitor NGINX on Kubernetes with OpenTelemetry' +metaDescription: 'Send your NGINX metrics from Kubernetes to New Relic using the OpenTelemetry Collector.' +redirects: + - /docs/infrastructure/host-integrations/host-integrations-list/nginx/nginx-otel-kubernetes +freshnessValidatedDate: never +--- + +Monitor your NGINX servers using the [OpenTelemetry Collector](https://github.com/open-telemetry/opentelemetry-collector-contrib) to send metrics and telemetry data to New Relic. +This integration leverages the OpenTelemetry [nginxreceiver](https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/receiver/nginxreceiver), [receivercreator](https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/receiver/receivercreator) to monitor your NGINX performance metrics, connection statistics, and server health. + +Image of the NGINX dashboard + +
+ Dashboard available through the [New Relic NGINX Monitor OpenTelemetry quickstart](https://newrelic.com/instant-observability/nginx-otel). +
+ +## Prerequisites [#prerequisites] + +Before you begin, ensure you have: + +- A [New Relic account](https://newrelic.com/signup) with a +- Enable the [HTTP stub status](https://nginx.org/en/docs/http/ngx_http_stub_status_module.html) module on NGINX pod that needs to be monitored +- Add labels `app` and `role` to each NGINX pod that needs to be monitored + +## Install and configure the OpenTelemetry Collector [#install-collector] + +Deploy the OpenTelemetry Collector to your Kubernetes cluster using Helm. The collector will automatically discover and scrape metrics from your NGINX pods. + +1. Create a Kubernetes secret to store your New Relic credentials. Replace `YOUR_LICENSE_KEY` and `YOUR_OTLP_ENDPOINT` with your actual values. Refer to the OTLP endpoint configuration [documentation](https://docs.newrelic.com/docs/opentelemetry/best-practices/opentelemetry-otlp/#configure-endpoint-port-protocol) to select the appropriate endpoint for your region. + + ```bash + kubectl create secret generic newrelic-licenses --from-literal=NEWRELIC_LICENSE_KEY=YOUR_LICENSE_KEY --from-literal=NEWRELIC_OTLP_ENDPOINT=YOUR_OTLP_ENDPOINT -n newrelic --create-namespace + ``` + +2. Create a `values.yaml` file to configure the OpenTelemetry Collector. Update the placeholders for your cluster name, NGINX pod labels, and stub status endpoint. + + ```yaml + opentelemetry-collector: + mode: deployment + + image: + repository: otel/opentelemetry-collector-contrib + pullPolicy: IfNotPresent + + command: + name: otelcol-contrib + + resources: + limits: + cpu: 500m + memory: 300Mi + requests: + cpu: 100m + memory: 100Mi + + extraEnvs: + - name: NEWRELIC_LICENSE_KEY + valueFrom: + secretKeyRef: + name: newrelic-licenses + key: NEWRELIC_LICENSE_KEY + - name: NEWRELIC_OTLP_ENDPOINT + valueFrom: + secretKeyRef: + name: newrelic-licenses + key: NEWRELIC_OTLP_ENDPOINT + - name: K8S_NODE_NAME + valueFrom: + fieldRef: + fieldPath: spec.nodeName + - name: K8S_CLUSTER_NAME + value: nginx-cluster # Update with your cluster name + + clusterRole: + create: true + rules: + - apiGroups: [""] + resources: ["pods", "nodes", "nodes/stats", "nodes/proxy"] + verbs: ["get", "list", "watch"] + - apiGroups: ["apps"] + resources: ["replicasets"] + verbs: ["get", "list", "watch"] + clusterRoleBinding: + name: "" + + config: + extensions: + health_check: + endpoint: 0.0.0.0:13133 + k8s_observer: + auth_type: serviceAccount + observe_pods: true + observe_nodes: true + + receivers: + receiver_creator/nginx: + watch_observers: [k8s_observer] + receivers: + nginx: + rule: type == "pod" && labels["app"] == "nginx" && labels["role"] == "reverse-proxy" # Update with your labels + config: + endpoint: 'http://`endpoint`:/status' + metrics: + nginx.requests: + enabled: true + nginx.connections_accepted: + enabled: true + nginx.connections_handled: + enabled: true + nginx.connections_current: + enabled: true + collection_interval: 30s + resource_attributes: + nginx.server.endpoint: 'http://`endpoint`:/status' + nginx.port: '' + + processors: + batch: + send_batch_size: 1024 + timeout: 30s + + resource/cluster: + attributes: + - key: k8s.cluster.name + value: "${env:K8S_CLUSTER_NAME}" + action: insert + + transform/nginx: + metric_statements: + - context: resource + statements: + - set(attributes["nginx.display.name"], Concat([ + "server", + "k8s", + attributes["k8s.cluster.name"], + attributes["k8s.namespace.name"], + "pod", + attributes["k8s.pod.name"], + "nginx", + attributes["nginx.port"] + ], ":")) + - set(attributes["nginx.deployment.name"], attributes["k8s.pod.name"]) + + exporters: + otlphttp: + endpoint: "${env:NEWRELIC_OTLP_ENDPOINT}" + headers: + api-key: "${env:NEWRELIC_LICENSE_KEY}" + + service: + extensions: [health_check, k8s_observer] + pipelines: + metrics/nginx: + receivers: [receiver_creator/nginx] + processors: [batch, resource/cluster, transform/nginx] + exporters: [otlphttp] + ``` + +3. Install the [Helm chart](https://github.com/open-telemetry/opentelemetry-helm-charts) using your values.yaml file. + + ```bash + helm repo add open-telemetry https://open-telemetry.github.io/opentelemetry-helm-charts + helm repo update + helm upgrade --install nginx-otel-collector open-telemetry/opentelemetry-collector --namespace newrelic --create-namespace -f values.yaml + ``` + +4. Ensure the pods have successfully spun up. + + ```bash + kubectl get pods -n newrelic --watch + ``` + + You should see pods with names like `nginx-otel-collector-` in a `Running` state in the `newrelic` namespace. + +5. Run an NRQL query in New Relic to confirm data is arriving. Replace the cluster name with the value you set in the values file: + + ```sql + FROM Metric + SELECT * + WHERE metricName LIKE 'nginx.%' + AND instrumentation.provider = 'opentelemetry' + AND k8s.cluster.name = 'nginx-cluster' + SINCE 10 minutes ago + ``` + +## Find and use data [#find-data] + +1. Go to **[one.newrelic.com](https://one.newrelic.com) > Integrations & Agents**. +2. Select **Dashboards**, and click **NGINX OTel overview dashboard**. +3. In the popup window, select your account. +4. Click View dashboard, and see your NGINX data in New Relic. + +The NGINX metrics are attached to the `Metric` [event type](/docs/using-new-relic/data/understand-data/new-relic-data-types#events-new-relic). You can [query this data](/docs/using-new-relic/data/understand-data/query-new-relic-data) for troubleshooting purposes or to create custom charts and dashboards. + +## Metrics collected [#metrics] + +The OpenTelemetry Collector Contrib's [nginxreceiver](https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/receiver/nginxreceiver) collects the following metrics from the NGINX stub status module: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Metric + + Description + + Type +
+ `nginx.connections_accepted` + + The total number of accepted client connections + + Sum +
+ `nginx.connections_handled` + + 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) + + Sum +
+ `nginx.connections_current` + + The current number of nginx connections by state + + Sum +
+ `nginx.requests` + + Total number of requests made to the server since it started + + Sum +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Attribute + + Description + + Example Values +
+ `state` + + The state of a connection (applicable to `nginx.connections_current` metric) + + `active`, `reading`, `writing`, `waiting` +
+ `nginx.server.endpoint` + + The NGINX stub status endpoint URL + + `http://10.244.1.5:8080/status` +
+ `nginx.port` + + The port number where stub status is exposed + + `8080` +
+ `k8s.cluster.name` + + The name of the Kubernetes cluster + + `nginx-cluster`, `production-k8s` +
+ `k8s.namespace.name` + + The Kubernetes namespace where the NGINX pod is running + + `default`, `nginx-ingress` +
+ `k8s.pod.name` + + The name of the Kubernetes pod running NGINX + + `nginx-deployment-5d7b9c8f4d-abc12` +
+ `nginx.deployment.name` + + The deployment name (set to the pod name in Kubernetes) + + `nginx-deployment-5d7b9c8f4d-abc12` +
+ `nginx.display.name` + + A display-friendly name combining cluster, namespace, and pod information + + `server:k8s:nginx-cluster:default:pod:nginx-deployment-5d7b9c8f4d-abc12:nginx:8080` +
+
+ +
+ +For more details, see the [NGINX receiver documentation](https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/main/receiver/nginxreceiver/documentation.md). diff --git a/src/install/config/nginx-otel-host.yaml b/src/install/config/nginx-otel-host.yaml deleted file mode 100644 index 8b221e9aa93..00000000000 --- a/src/install/config/nginx-otel-host.yaml +++ /dev/null @@ -1,16 +0,0 @@ -agentName: nginx-otel-host -agentType: integration -title: 'Monitor self-hosted NGINX with OpenTelemetry' -metaDescription: 'Send your NGINX metrics and logs to New Relic using the OpenTelemetry Collector.' -introFilePath: 'src/install/nginx-otel-host/intro.mdx' -appInfo: [] -redirects: - - /docs/infrastructure/host-integrations/host-integrations-list/nginx/nginx-otel-host -steps: - - filePath: 'src/install/nginx-otel-host/host/prerequisites-opensource.mdx' - - filePath: 'src/install/nginx-otel-host/host/enable-status-opensource.mdx' - - filePath: 'src/install/nginx-otel-host/host/configure-collector-opensource.mdx' - - filePath: 'src/install/nginx-otel-host/collector/environment.mdx' - - filePath: 'src/install/nginx-otel-host/logs/forwarding-opensource.mdx' - - filePath: 'src/install/nginx-otel-host/host/verify.mdx' -whatsNextFilePath: 'src/install/nginx-otel-host/whatsNext.mdx' diff --git a/src/install/config/nginx-otel-kubernetes.yaml b/src/install/config/nginx-otel-kubernetes.yaml deleted file mode 100644 index bb71907cdfb..00000000000 --- a/src/install/config/nginx-otel-kubernetes.yaml +++ /dev/null @@ -1,13 +0,0 @@ -agentName: nginx-otel-kubernetes -agentType: integration -title: 'Monitor NGINX on Kubernetes with OpenTelemetry' -metaDescription: 'Send your NGINX metrics and logs from Kubernetes to New Relic using the OpenTelemetry Collector.' -introFilePath: 'src/install/nginx-otel-kubernetes/intro.mdx' -appInfo: [] -redirects: - - /docs/infrastructure/host-integrations/host-integrations-list/nginx/nginx-otel-kubernetes -steps: - - filePath: 'src/install/nginx-otel-kubernetes/kubernetes/prerequisites.mdx' - - filePath: 'src/install/nginx-otel-kubernetes/kubernetes/install-collector.mdx' - - filePath: 'src/install/nginx-otel-kubernetes/kubernetes/verify.mdx' -whatsNextFilePath: 'src/install/nginx-otel-kubernetes/whatsNext.mdx' diff --git a/src/install/config/nginx-plus-otel.yaml b/src/install/config/nginx-plus-otel.yaml deleted file mode 100644 index e57c354d418..00000000000 --- a/src/install/config/nginx-plus-otel.yaml +++ /dev/null @@ -1,16 +0,0 @@ -agentName: nginx-plus-otel -agentType: integration -title: 'Monitor self-hosted NGINX Plus with OpenTelemetry' -metaDescription: 'Send your NGINX Plus metrics to New Relic using the OpenTelemetry contirb collector.' -introFilePath: 'src/install/nginx-plus-otel/intro.mdx' -appInfo: [] -redirects: - - /docs/infrastructure/host-integrations/host-integrations-list/nginx/nginx-plus-otel -steps: - - filePath: 'src/install/nginx-plus-otel/host/prerequisites-plus.mdx' - - filePath: 'src/install/nginx-plus-otel/host/enable-status-plus.mdx' - - filePath: 'src/install/nginx-plus-otel/host/configure-collector-plus.mdx' - - filePath: 'src/install/nginx-plus-otel/collector/environment.mdx' - - filePath: 'src/install/nginx-plus-otel/logs/forwarding-plus.mdx' - - filePath: 'src/install/nginx-plus-otel/host/verify.mdx' -whatsNextFilePath: 'src/install/nginx-plus-otel/whatsNext.mdx' diff --git a/src/install/nginx-otel-host/collector/environment.mdx b/src/install/nginx-otel-host/collector/environment.mdx deleted file mode 100644 index 836a314b9da..00000000000 --- a/src/install/nginx-otel-host/collector/environment.mdx +++ /dev/null @@ -1,25 +0,0 @@ ---- -headingText: Set environment variables for the collector -componentType: default ---- - -Inject your New Relic and OTLP endpoint into the collector service so the exporter can authenticate. - -1. Create a systemd override directory: - ```bash - sudo mkdir -p /etc/systemd/system/otelcol-contrib.service.d - ``` -2. Write `environment.conf` with your OTLP endpoint. Replace `YOUR_LICENSE_KEY` with the New Relic license key and `YOUR_OTLP_ENDPOINT` with the appropriate endpoint for your region. Refer to the OTLP endpoint configuration [documentation](https://docs.newrelic.com/docs/opentelemetry/best-practices/opentelemetry-otlp/#configure-endpoint-port-protocol) to select the right endpoint. - - ```bash - cat < - collection_interval: 30s -processors: - resourcedetection: - detectors: [system] - system: - resource_attributes: - host.name: - enabled: true - host.id: - enabled: true - resource: - attributes: - - action: upsert - key: nginx.server.endpoint - value: - - action: upsert - key: nginx.deployment.name - value: - batch: - timeout: 30s - send_batch_size: 512 - transform/nginx_metrics: - metric_statements: - - context: resource - statements: - - set(attributes["nginx.display.name"], Concat(["server", attributes["nginx.deployment.name"]], ":")) -exporters: - otlphttp/newrelic: - endpoint: ${env:NEWRELIC_OTLP_ENDPOINT} - headers: - api-key: ${env:NEWRELIC_LICENSE_KEY} -service: - pipelines: - metrics: - receivers: [nginx] - processors: [resourcedetection, resource, batch, transform/nginx_metrics] - exporters: [otlphttp/newrelic] -``` - -Save the file and ensure the `otelcol-contrib` system user can read it. diff --git a/src/install/nginx-otel-host/host/enable-status-opensource.mdx b/src/install/nginx-otel-host/host/enable-status-opensource.mdx deleted file mode 100644 index af455a70c38..00000000000 --- a/src/install/nginx-otel-host/host/enable-status-opensource.mdx +++ /dev/null @@ -1,39 +0,0 @@ ---- -headingText: Configure NGINX -componentType: default ---- - -Configure and enable the [stub_status](https://nginx.org/en/docs/http/ngx_http_stub_status_module.html) module to expose metrics from your NGINX Open Source server. - -After updating `nginx.conf`, test and reload the service: - -```bash -sudo nginx -t && sudo nginx -s reload -``` - -List the configuration block that exposes `stub_status`: - -```bash -sudo nginx -T 2>&1 | grep -ozP "(?s:.*\s)\Klocation(?s).*stub_status" | grep -aoP "\/([^\s]+)" -``` - -Example output: - -``` -/status -``` -If you do not see output, inspect your nginx.conf manually for a stub_status block. - -Use `curl` to confirm your status endpoint is reachable: - -```bash -curl -I http://127.0.0.1:8080/status 2>/dev/null | head -n 1 | cut -d$' ' -f1,2 -``` - -Expected output: - -``` -HTTP/1.1 200 -``` - -If you see a different response, troubleshoot your NGINX configuration. diff --git a/src/install/nginx-otel-host/host/prerequisites-opensource.mdx b/src/install/nginx-otel-host/host/prerequisites-opensource.mdx deleted file mode 100644 index 503e3cc2d5a..00000000000 --- a/src/install/nginx-otel-host/host/prerequisites-opensource.mdx +++ /dev/null @@ -1,12 +0,0 @@ ---- -headingText: Check the compatibility and requirements -componentType: default ---- -Before you begin, ensure you have: - -- A [New Relic account](https://newrelic.com/signup) with a -- NGINX with the [HTTP stub status](https://nginx.org/en/docs/http/ngx_http_stub_status_module.html) module enabled -- [OpenTelemetry Collector Contrib](https://github.com/open-telemetry/opentelemetry-collector-contrib/releases/latest) installed and running on a linux host -- Network access from the linux host to: - - NGINX HTTP stub status endpoint - - Anyone of the [New Relic OTLP](https://docs.newrelic.com/docs/opentelemetry/best-practices/opentelemetry-otlp/#configure-endpoint-port-protocol:~:text=HTTP-,Endpoint,-Supported%20ports) enpoint diff --git a/src/install/nginx-otel-host/host/verify.mdx b/src/install/nginx-otel-host/host/verify.mdx deleted file mode 100644 index 6a0e7ee56cc..00000000000 --- a/src/install/nginx-otel-host/host/verify.mdx +++ /dev/null @@ -1,11 +0,0 @@ ---- -headingText: Find and use data -componentType: default ---- - -1. Go to **[one.newrelic.com](https://one.newrelic.com) > Integrations & Agents**. -2. Select **Dashboards**, and click **NGINX OTel overview dashboard**. -3. In the popup window, select your account. -4. Click View dashboard, and see your NGINX data in New Relic. - -The NGINX metrics are attached to the `Metric` [event type](/docs/using-new-relic/data/understand-data/new-relic-data-types#events-new-relic). You can [query this data](/docs/using-new-relic/data/understand-data/query-new-relic-data) for troubleshooting purposes or to create custom charts and dashboards. diff --git a/src/install/nginx-otel-host/intro.mdx b/src/install/nginx-otel-host/intro.mdx deleted file mode 100644 index 88159369454..00000000000 --- a/src/install/nginx-otel-host/intro.mdx +++ /dev/null @@ -1,7 +0,0 @@ ---- -headingText: Before you start -componentType: default ---- - -Monitor your NGINX servers using the [OpenTelemetry Collector](https://github.com/open-telemetry/opentelemetry-collector-contrib) to send metrics and telemetry data to New Relic. -This integration leverages the OpenTelemetry [nginxreceiver](https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/receiver/nginxreceiver) to monitor your NGINX performance metrics, connection statistics, and server health. diff --git a/src/install/nginx-otel-host/logs/forwarding-opensource.mdx b/src/install/nginx-otel-host/logs/forwarding-opensource.mdx deleted file mode 100644 index 595031ca1fe..00000000000 --- a/src/install/nginx-otel-host/logs/forwarding-opensource.mdx +++ /dev/null @@ -1,82 +0,0 @@ ---- -headingText: Forward NGINX logs -componentType: default ---- - -Extend your collector configuration to include access and error logs if you want log events alongside metrics. - -### Configure NGINX log format - -Before forwarding logs, configure NGINX to use a structured log format. Refer to the [NGINX log module documentation](https://nginx.org/en/docs/http/ngx_http_log_module.html) for guidance on configuring access and error logs. - -### Configure the OpenTelemetry Collector for log forwarding - -1. Note the full paths to your NGINX access and error log files. Defaults are usually `/var/log/nginx/access.log` and `/var/log/nginx/error.log`. - -2. Update `/etc/otelcol-contrib/config.yaml` to add a `filelog` receiver and log pipeline: - - ```yaml - receivers: - nginx: - # existing stub status receiver configuration - filelog/nginx_access_logs: - include: - - /var/log/nginx/access.log - filelog/nginx_error_logs: - include: - - /var/log/nginx/error.log - - processors: - resourcedetection: - # existing settings - resource: - # existing settings - batch: - # existing settings - transform/nginx_metrics: - # existing settings - transform/nginx_access_logs: - log_statements: - - context: resource - statements: - - set(attributes["nginx.display.name"], Concat(["server", attributes["nginx.deployment.name"]], ":")) - - set(attributes["logtype"], "nginx") - transform/nginx_error_logs: - log_statements: - - context: resource - statements: - - set(attributes["nginx.display.name"], Concat(["server", attributes["nginx.deployment.name"]], ":")) - - set(attributes["logtype"], "nginx-error") - - exporters: - # existing exporter setup - - service: - pipelines: - metrics: - receivers: [nginx] - processors: [resourcedetection, resource, batch, transform/nginx_metrics] - exporters: [otlphttp/newrelic] - logs/nginx-access: - receivers: [filelog/nginx_access_logs] - processors: [batch, resource/nginx, transform/nginx_access_logs] - exporters: [otlphttp/newrelic] - logs/nginx-error: - receivers: [filelog/nginx_error_logs] - processors: [batch, resource/nginx, transform/nginx_error_logs] - exporters: [otlphttp/newrelic] - ``` - -3. Grant the `otelcol-contrib` user read access to the log files: - - ```bash - sudo usermod -a -G adm otelcol-contrib - sudo chmod 644 /var/log/nginx/access.log - sudo chmod 644 /var/log/nginx/error.log - ``` - -4. Restart the collector to apply the changes: - - ```bash - sudo systemctl restart otelcol-contrib - ``` diff --git a/src/install/nginx-otel-host/whatsNext.mdx b/src/install/nginx-otel-host/whatsNext.mdx deleted file mode 100644 index 71dcea5e1e5..00000000000 --- a/src/install/nginx-otel-host/whatsNext.mdx +++ /dev/null @@ -1,180 +0,0 @@ ---- -headingText: Metrics collected -componentType: default ---- - -## Metric data [#nginx-opensource-metrics] - -The OpenTelemetry Collector Contrib's [nginxreceiver](https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/receiver/nginxreceiver) collects the following metrics from the NGINX stub status module: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- Metric - - Description - - Type -
- `nginx.connections_accepted` - - The total number of accepted client connections - - Sum -
- `nginx.connections_handled` - - 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) - - Sum -
- `nginx.connections_current` - - The current number of nginx connections by state - - Sum -
- `nginx.requests` - - Total number of requests made to the server since it started - - Sum -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- Attribute - - Description - - Example Values -
- `state` - - The state of a connection (applicable to `nginx.connections_current` metric) - - `active`, `reading`, `writing`, `waiting` -
- `nginx.server.endpoint` - - The NGINX stub status endpoint URL - - `http://localhost:8080/stub_status` -
- `nginx.deployment.name` - - A unique name to identify this NGINX deployment - - `production-web-01`, `staging-api` -
- `nginx.display.name` - - A display-friendly name combining "server" prefix with deployment name - - `server:production-web-01` -
- `host.name` - - The hostname of the system where NGINX is running - - `web-server-01.example.com` -
- `host.id` - - The unique identifier of the host system - - `i-1234567890abcdef0` -
- `logtype` - - The type of log being collected (applicable to logs only). Used by New Relic's built-in [parsing rules](https://docs.newrelic.com/docs/logs/ui-data/built-log-parsing-rules/#nginx) - - `nginx` (for access logs), `nginx-error` (for error logs) -
-
- -
- -For more details, see the [NGINX receiver documentation](https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/main/receiver/nginxreceiver/documentation.md). diff --git a/src/install/nginx-otel-kubernetes/intro.mdx b/src/install/nginx-otel-kubernetes/intro.mdx deleted file mode 100644 index 126334b4322..00000000000 --- a/src/install/nginx-otel-kubernetes/intro.mdx +++ /dev/null @@ -1,7 +0,0 @@ ---- -headingText: Before you start -componentType: default ---- - -Monitor your NGINX servers using the [OpenTelemetry Collector](https://github.com/open-telemetry/opentelemetry-collector-contrib) to send metrics and telemetry data to New Relic. -This integration leverages the OpenTelemetry [nginxreceiver](https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/receiver/nginxreceiver), [receivercreator](https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/receiver/receivercreator) to monitor your NGINX performance metrics, connection statistics, and server health. diff --git a/src/install/nginx-otel-kubernetes/kubernetes/install-collector.mdx b/src/install/nginx-otel-kubernetes/kubernetes/install-collector.mdx deleted file mode 100644 index b79ac2f2131..00000000000 --- a/src/install/nginx-otel-kubernetes/kubernetes/install-collector.mdx +++ /dev/null @@ -1,163 +0,0 @@ ---- -headingText: Install and configure the OpenTelemetry Collector -componentType: default ---- - -Deploy the OpenTelemetry Collector to your Kubernetes cluster using Helm. The collector will automatically discover and scrape metrics from your NGINX pods. - -1. Create a Kubernetes secret to store your New Relic credentials. Replace `YOUR_LICENSE_KEY` and `YOUR_OTLP_ENDPOINT` with your actual values. Refer to the OTLP endpoint configuration [documentation](https://docs.newrelic.com/docs/opentelemetry/best-practices/opentelemetry-otlp/#configure-endpoint-port-protocol) to select the appropriate endpoint for your region. - - ```bash - kubectl create secret generic newrelic-licenses --from-literal=NEWRELIC_LICENSE_KEY=YOUR_LICENSE_KEY --from-literal=NEWRELIC_OTLP_ENDPOINT=YOUR_OTLP_ENDPOINT -n newrelic --create-namespace - ``` - -2. Create a `values.yaml` file to configure the OpenTelemetry Collector. Update the placeholders for your cluster name, NGINX pod labels, and stub status endpoint. - - ```yaml - opentelemetry-collector: - mode: deployment - - image: - repository: otel/opentelemetry-collector-contrib - pullPolicy: IfNotPresent - - command: - name: otelcol-contrib - - resources: - limits: - cpu: 500m - memory: 300Mi - requests: - cpu: 100m - memory: 100Mi - - extraEnvs: - - name: NEWRELIC_LICENSE_KEY - valueFrom: - secretKeyRef: - name: newrelic-licenses - key: NEWRELIC_LICENSE_KEY - - name: NEWRELIC_OTLP_ENDPOINT - valueFrom: - secretKeyRef: - name: newrelic-licenses - key: NEWRELIC_OTLP_ENDPOINT - - name: K8S_NODE_NAME - valueFrom: - fieldRef: - fieldPath: spec.nodeName - - name: K8S_CLUSTER_NAME - value: nginx-cluster # Update with your cluster name - - clusterRole: - create: true - rules: - - apiGroups: [""] - resources: ["pods", "nodes", "nodes/stats", "nodes/proxy"] - verbs: ["get", "list", "watch"] - - apiGroups: ["apps"] - resources: ["replicasets"] - verbs: ["get", "list", "watch"] - clusterRoleBinding: - name: "" - - config: - extensions: - health_check: - endpoint: 0.0.0.0:13133 - k8s_observer: - auth_type: serviceAccount - observe_pods: true - observe_nodes: true - - receivers: - receiver_creator/nginx: - watch_observers: [k8s_observer] - receivers: - nginx: - rule: type == "pod" && labels["app"] == "nginx" && labels["role"] == "reverse-proxy" # Update with your labels - config: - endpoint: 'http://`endpoint`:/status' - metrics: - nginx.requests: - enabled: true - nginx.connections_accepted: - enabled: true - nginx.connections_handled: - enabled: true - nginx.connections_current: - enabled: true - collection_interval: 30s - resource_attributes: - nginx.server.endpoint: 'http://`endpoint`:/status' - nginx.port: '' - - processors: - batch: - send_batch_size: 1024 - timeout: 30s - - resource/cluster: - attributes: - - key: k8s.cluster.name - value: "${env:K8S_CLUSTER_NAME}" - action: insert - - transform/nginx: - metric_statements: - - context: resource - statements: - - set(attributes["nginx.display.name"], Concat([ - "server", - "k8s", - attributes["k8s.cluster.name"], - attributes["k8s.namespace.name"], - "pod", - attributes["k8s.pod.name"], - "nginx", - attributes["nginx.port"] - ], ":")) - - set(attributes["nginx.deployment.name"], attributes["k8s.pod.name"]) - - exporters: - otlphttp: - endpoint: "${env:NEWRELIC_OTLP_ENDPOINT}" - headers: - api-key: "${env:NEWRELIC_LICENSE_KEY}" - - service: - extensions: [health_check, k8s_observer] - pipelines: - metrics/nginx: - receivers: [receiver_creator/nginx] - processors: [batch, resource/cluster, transform/nginx] - exporters: [otlphttp] - ``` - -3. Install the [Helm chart](https://github.com/open-telemetry/opentelemetry-helm-charts) using your values.yaml file. - - ```bash - helm repo add open-telemetry https://open-telemetry.github.io/opentelemetry-helm-charts - helm repo update - helm upgrade --install nginx-otel-collector open-telemetry/opentelemetry-collector --namespace newrelic --create-namespace -f values.yaml - ``` - -4. Ensure the pods have successfully spun up. - - ```bash - kubectl get pods -n newrelic --watch - ``` - - You should see pods with names like `nginx-otel-collector-` in a `Running` state in the `newrelic` namespace. - -5. Run an NRQL query in New Relic to confirm data is arriving. Replace the cluster name with the value you set in the values file: - - ```sql - FROM Metric - SELECT * - WHERE metricName LIKE 'nginx.%' - AND instrumentation.provider = 'opentelemetry' - AND k8s.cluster.name = 'nginx-cluster' - SINCE 10 minutes ago - ``` diff --git a/src/install/nginx-otel-kubernetes/kubernetes/prerequisites.mdx b/src/install/nginx-otel-kubernetes/kubernetes/prerequisites.mdx deleted file mode 100644 index 5f511a48bb2..00000000000 --- a/src/install/nginx-otel-kubernetes/kubernetes/prerequisites.mdx +++ /dev/null @@ -1,10 +0,0 @@ ---- -headingText: Prerequisites -componentType: default ---- - -Before you begin, ensure you have: - -- A [New Relic account](https://newrelic.com/signup) with a -- Enable the [HTTP stub status](https://nginx.org/en/docs/http/ngx_http_stub_status_module.html) module on NGINX pod that needs to be monitored -- Add labels `app` and `role` to each NGINX pod that needs to be monitored diff --git a/src/install/nginx-otel-kubernetes/kubernetes/status-endpoint.mdx b/src/install/nginx-otel-kubernetes/kubernetes/status-endpoint.mdx deleted file mode 100644 index e4a4964a2b7..00000000000 --- a/src/install/nginx-otel-kubernetes/kubernetes/status-endpoint.mdx +++ /dev/null @@ -1,22 +0,0 @@ ---- -headingText: Expose and verify the stub status endpoint -componentType: default ---- - -Each NGINX pod must expose the HTTP stub status module so the collector can scrape metrics. - -1. Add a location block similar to the following to your NGINX configuration, then reload the deployment: - - ```nginx - location /status { - stub_status on; - } - ``` -2. Record the path (without the leading slash) and the port serving the stub status endpoint. Defaults are `status` on port `80` or `8080`. -3. Verify the endpoint from within one of your pods: - - ```bash - kubectl exec deploy/ -- curl -I http://127.0.0.1:8080/status 2>/dev/null | head -n 1 | cut -d$' ' -f1,2 - ``` - -Expect an `HTTP/1.1 200` response. Adjust the command to match your deployment name, port, and path. diff --git a/src/install/nginx-otel-kubernetes/kubernetes/verify.mdx b/src/install/nginx-otel-kubernetes/kubernetes/verify.mdx deleted file mode 100644 index 6a0e7ee56cc..00000000000 --- a/src/install/nginx-otel-kubernetes/kubernetes/verify.mdx +++ /dev/null @@ -1,11 +0,0 @@ ---- -headingText: Find and use data -componentType: default ---- - -1. Go to **[one.newrelic.com](https://one.newrelic.com) > Integrations & Agents**. -2. Select **Dashboards**, and click **NGINX OTel overview dashboard**. -3. In the popup window, select your account. -4. Click View dashboard, and see your NGINX data in New Relic. - -The NGINX metrics are attached to the `Metric` [event type](/docs/using-new-relic/data/understand-data/new-relic-data-types#events-new-relic). You can [query this data](/docs/using-new-relic/data/understand-data/query-new-relic-data) for troubleshooting purposes or to create custom charts and dashboards. diff --git a/src/install/nginx-otel-kubernetes/whatsNext.mdx b/src/install/nginx-otel-kubernetes/whatsNext.mdx deleted file mode 100644 index 58ac2c40cf9..00000000000 --- a/src/install/nginx-otel-kubernetes/whatsNext.mdx +++ /dev/null @@ -1,191 +0,0 @@ ---- -headingText: Metrics collected -componentType: default ---- - -## Metric data [#nginx-opensource-metrics] - -The OpenTelemetry Collector Contrib's [nginxreceiver](https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/receiver/nginxreceiver) collects the following metrics from the NGINX stub status module: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- Metric - - Description - - Type -
- `nginx.connections_accepted` - - The total number of accepted client connections - - Sum -
- `nginx.connections_handled` - - 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) - - Sum -
- `nginx.connections_current` - - The current number of nginx connections by state - - Sum -
- `nginx.requests` - - Total number of requests made to the server since it started - - Sum -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- Attribute - - Description - - Example Values -
- `state` - - The state of a connection (applicable to `nginx.connections_current` metric) - - `active`, `reading`, `writing`, `waiting` -
- `nginx.server.endpoint` - - The NGINX stub status endpoint URL - - `http://10.244.1.5:8080/status` -
- `nginx.port` - - The port number where stub status is exposed - - `8080` -
- `k8s.cluster.name` - - The name of the Kubernetes cluster - - `nginx-cluster`, `production-k8s` -
- `k8s.namespace.name` - - The Kubernetes namespace where the NGINX pod is running - - `default`, `nginx-ingress` -
- `k8s.pod.name` - - The name of the Kubernetes pod running NGINX - - `nginx-deployment-5d7b9c8f4d-abc12` -
- `nginx.deployment.name` - - The deployment name (set to the pod name in Kubernetes) - - `nginx-deployment-5d7b9c8f4d-abc12` -
- `nginx.display.name` - - A display-friendly name combining cluster, namespace, and pod information - - `server:k8s:nginx-cluster:default:pod:nginx-deployment-5d7b9c8f4d-abc12:nginx:8080` -
-
- -
- -For more details, see the [NGINX receiver documentation](https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/main/receiver/nginxreceiver/documentation.md). diff --git a/src/install/nginx-plus-otel/collector/environment.mdx b/src/install/nginx-plus-otel/collector/environment.mdx deleted file mode 100644 index 836a314b9da..00000000000 --- a/src/install/nginx-plus-otel/collector/environment.mdx +++ /dev/null @@ -1,25 +0,0 @@ ---- -headingText: Set environment variables for the collector -componentType: default ---- - -Inject your New Relic and OTLP endpoint into the collector service so the exporter can authenticate. - -1. Create a systemd override directory: - ```bash - sudo mkdir -p /etc/systemd/system/otelcol-contrib.service.d - ``` -2. Write `environment.conf` with your OTLP endpoint. Replace `YOUR_LICENSE_KEY` with the New Relic license key and `YOUR_OTLP_ENDPOINT` with the appropriate endpoint for your region. Refer to the OTLP endpoint configuration [documentation](https://docs.newrelic.com/docs/opentelemetry/best-practices/opentelemetry-otlp/#configure-endpoint-port-protocol) to select the right endpoint. - - ```bash - cat < - action: insert - - key: nginx.deployment.name - value: - action: insert - resourcedetection: - detectors: [system] - system: - resource_attributes: - host.name: - enabled: true - host.id: - enabled: true - transform/nginx: - metric_statements: - - context: resource - statements: - - set(attributes["nginx.display.name"], Concat(["server", attributes["nginx.deployment.name"]], ":")) - - context: resource - statements: - - delete_key(attributes, "service.name") -exporters: - otlphttp/newrelic: - endpoint: ${env:NEWRELIC_OTLP_ENDPOINT} - headers: - api-key: ${env:NEWRELIC_LICENSE_KEY} -service: - pipelines: - metrics/nginx: - receivers: [prometheus] - processors: [batch, filter/nginx_metrics, resourcedetection, resource/nginx, transform/nginx] - exporters: [otlphttp/newrelic] -``` - -Save the file and ensure the `otelcol-contrib` system user can read it. diff --git a/src/install/nginx-plus-otel/host/enable-status-plus.mdx b/src/install/nginx-plus-otel/host/enable-status-plus.mdx deleted file mode 100644 index e99608ea153..00000000000 --- a/src/install/nginx-plus-otel/host/enable-status-plus.mdx +++ /dev/null @@ -1,30 +0,0 @@ ---- -headingText: Configure NGINX Plus -componentType: default ---- - -Configure and enable the [HTTP API module](https://nginx.org/en/docs/http/ngx_http_api_module.html) to expose metrics from your NGINX Plus server. - -After updating `nginx.conf`, test and reload the service: - -```bash -sudo nginx -t && sudo nginx -s reload -``` - -Confirm the API endpoint path (including version) exposed in your configuration. - -Record the full API endpoint path (without the leading slash) and the port that serves it. Common defaults are `api/9` on port `8080`. - -Use `curl` to confirm your API endpoint is reachable: - -```bash -curl -I http://127.0.0.1:8080/api/9 2>/dev/null | head -n 1 | cut -d$' ' -f1,2 -``` - -Expected output: - -``` -HTTP/1.1 200 -``` - -If you see a different response, verify your NGINX Plus configuration and ensure the API module is properly enabled. diff --git a/src/install/nginx-plus-otel/host/prerequisites-plus.mdx b/src/install/nginx-plus-otel/host/prerequisites-plus.mdx deleted file mode 100644 index 026f0e70f0e..00000000000 --- a/src/install/nginx-plus-otel/host/prerequisites-plus.mdx +++ /dev/null @@ -1,14 +0,0 @@ ---- -headingText: Check the compatibility and requirements -componentType: default ---- -Before you begin, ensure you have: - -- A [New Relic account](https://newrelic.com/signup) with a -- NGINX Plus version [R13](https://docs.nginx.com/nginx/releases/#r13) or higher -- NGINX Plus with the [HTTP API](https://nginx.org/en/docs/http/ngx_http_api_module.html) module enabled -- [NGINX Prometheus exporter](https://github.com/nginx/nginx-prometheus-exporter) installed and running alongside your NGINX Plus instance to expose HTTP API metrics in Prometheus format -- [OpenTelemetry Collector Contrib](https://github.com/open-telemetry/opentelemetry-collector-contrib/releases/latest) installed and running on a linux host -- Network access from the linux host to: - - NGINX Plus HTTP API endpoint - - Anyone of the [New Relic OTLP](https://docs.newrelic.com/docs/opentelemetry/best-practices/opentelemetry-otlp/#configure-endpoint-port-protocol:~:text=HTTP-,Endpoint,-Supported%20ports) enpoint diff --git a/src/install/nginx-plus-otel/host/verify.mdx b/src/install/nginx-plus-otel/host/verify.mdx deleted file mode 100644 index 3a3b74c5552..00000000000 --- a/src/install/nginx-plus-otel/host/verify.mdx +++ /dev/null @@ -1,11 +0,0 @@ ---- -headingText: Find and use data -componentType: default ---- - -1. Go to **[one.newrelic.com](https://one.newrelic.com) > Integrations & Agents**. -2. Select **Dashboards**, and click **NGINX OTel overview dashboard**. -3. In the popup window, select your account. -4. Click View dashboard, and see your NGINX Plus data in New Relic. - -The NGINX Plus metrics are attached to the `Metric` [event type](/docs/using-new-relic/data/understand-data/new-relic-data-types#events-new-relic). You can [query this data](/docs/using-new-relic/data/understand-data/query-new-relic-data) for troubleshooting purposes or to create custom charts and dashboards. diff --git a/src/install/nginx-plus-otel/intro.mdx b/src/install/nginx-plus-otel/intro.mdx deleted file mode 100644 index eddf0805a76..00000000000 --- a/src/install/nginx-plus-otel/intro.mdx +++ /dev/null @@ -1,8 +0,0 @@ ---- -headingText: Before you start -componentType: default ---- - - -Monitor your NGINX Plus servers using the [OpenTelemetry Collector](https://github.com/open-telemetry/opentelemetry-collector-contrib) to send metrics and telemetry data to New Relic. -This integration leverages the OpenTelemetry [prometheusreceiver](https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/receiver/prometheusreceiver) and [nginx-prometheus-exporter](https://github.com/nginx/nginx-prometheus-exporter) to monitor your NGINX Plus performance metrics, connection statistics, and server health. diff --git a/src/install/nginx-plus-otel/logs/forwarding-plus.mdx b/src/install/nginx-plus-otel/logs/forwarding-plus.mdx deleted file mode 100644 index 1a5afab77d7..00000000000 --- a/src/install/nginx-plus-otel/logs/forwarding-plus.mdx +++ /dev/null @@ -1,84 +0,0 @@ ---- -headingText: Forward NGINX logs and create log parsing rules -componentType: default ---- - -Extend your collector configuration to include access and error logs if you want log events alongside metrics. - -### Configure NGINX Plus log format - -Before forwarding logs, configure NGINX Plus to use a structured log format. Refer to the [NGINX logging documentation](https://docs.nginx.com/nginx/admin-guide/monitoring/logging/) for guidance on configuring access and error logs. - -### Configure the OpenTelemetry Collector for log forwarding - -1. Note the full paths to your NGINX access and error log files. Defaults are usually `/var/log/nginx/access.log` and `/var/log/nginx/error.log`. - -2. Update `/etc/otelcol-contrib/config.yaml` to add a `filelog` receiver and log pipeline: - - ```yaml - receivers: - prometheus: - # existing Prometheus receiver configuration - filelog/nginx_access_logs: - include: - - /var/log/nginx/access.log - filelog/nginx_error_logs: - include: - - /var/log/nginx/error.log - - processors: - batch: - # existing settings - filter/nginx_metrics: - # existing settings - resourcedetection: - # existing settings - resource/nginx: - # existing settings - transform/nginx_metrics: - # existing settings - transform/nginx_access_logs: - log_statements: - - context: resource - statements: - - set(attributes["nginx.display.name"], Concat(["server", attributes["nginx.deployment.name"]], ":")) - - set(attributes["logtype"], "nginx") - transform/nginx_error_logs: - log_statements: - - context: resource - statements: - - set(attributes["nginx.display.name"], Concat(["server", attributes["nginx.deployment.name"]], ":")) - - set(attributes["logtype"], "nginx-error") - - exporters: - # existing exporter setup - - service: - pipelines: - metrics/nginx: - receivers: [prometheus] - processors: [batch, filter/nginx_metrics, resourcedetection, resource/nginx, transform/nginx_metrics] - exporters: [otlphttp/newrelic] - logs/nginx-access: - receivers: [filelog/nginx_access_logs] - processors: [batch, resource/nginx, transform/nginx_access_logs] - exporters: [otlphttp/newrelic] - logs/nginx-error: - receivers: [filelog/nginx_error_logs] - processors: [batch, resource/nginx, transform/nginx_error_logs] - exporters: [otlphttp/newrelic] - ``` - -3. Grant the `otelcol-contrib` user read access to the log files: - - ```bash - sudo usermod -a -G adm otelcol-contrib - sudo chmod 644 /var/log/nginx/access.log - sudo chmod 644 /var/log/nginx/error.log - ``` - -4. Restart the collector to apply the changes: - - ```bash - sudo systemctl restart otelcol-contrib - ``` diff --git a/src/install/nginx/whatsNext.mdx b/src/install/nginx/whatsNext.mdx index 06725ce1423..d5589a0f30b 100644 --- a/src/install/nginx/whatsNext.mdx +++ b/src/install/nginx/whatsNext.mdx @@ -453,3 +453,9 @@ The NGINX integration collects the following metrics: + +--- + + + - To monitor NGINX using OpenTelemetry, see [Monitor NGINX with OpenTelemetry](/docs/opentelemetry/nginx-plus/nginx-otel-host/). + diff --git a/src/nav/infrastructure.yml b/src/nav/infrastructure.yml index 9fedca4fb0b..87788ddc398 100644 --- a/src/nav/infrastructure.yml +++ b/src/nav/infrastructure.yml @@ -209,20 +209,8 @@ pages: path: /docs/infrastructure/host-integrations/host-integrations-list/nextcloud-integration - title: NFS integration path: /docs/infrastructure/host-integrations/host-integrations-list/nfs-monitoring-integration - - title: NGINX integrations - pages: - - title: NR Native NGINX integration - path: /install/nginx - - title: OpenTelemetry NGINX - pages: - - title: Self-hosted - path: /install/nginx-otel-host - - title: Kubernetes - path: /install/nginx-otel-kubernetes - - title: OpenTelemetry NGINX Plus - pages: - - title: Self-hosted - path: /install/nginx-plus-otel + - title: NGINX integration + path: /install/nginx - title: NVIDIA GPU integration path: /docs/infrastructure/host-integrations/host-integrations-list/nvidia-gpu-integration - title: NVIDIA Jetson integration diff --git a/src/nav/opentelemetry.yml b/src/nav/opentelemetry.yml index da4af694dca..f173a0c06e5 100644 --- a/src/nav/opentelemetry.yml +++ b/src/nav/opentelemetry.yml @@ -43,3 +43,15 @@ pages: path: /docs/opentelemetry/best-practices/opentelemetry-best-practices-resources - title: Manage data ingest volume path: /docs/opentelemetry/best-practices/opentelemetry-manage-data-ingest-volume + - title: OpenTelemetry integrations + pages: + - title: NGINX integration + pages: + - title: Self-hosted + path: /docs/opentelemetry/nginx/nginx-otel-host + - title: Kubernetes + path: /docs/opentelemetry/nginx/nginx-otel-kubernetes + - title: NGINX Plus integration + pages: + - title: Self-hosted + path: /docs/opentelemetry/nginx-plus/nginx-plus-otel diff --git a/static/images/opentelemetry_screenshot-nginx-dashboard.webp b/static/images/opentelemetry_screenshot-nginx-dashboard.webp new file mode 100644 index 0000000000000000000000000000000000000000..929061adada75002e9034ec2a1d7864477969bce GIT binary patch literal 478491 zcmeFZXH=8jwl<823N{2(M5+`~s(|#aAWcB&9R&d?0qKMmvC*UxIt1w*kzS+HqzfUm z06|(nN`M4n2ua?!_c`y`&$GWV&XMoeJI49JAY$%yuQlgf*SzLjbFRoodKwqbUO!7k zMRh?-Q_X;i>ijDzs?(=wPgDNlxb|b1it2>5^ZomewC>;MdgSHd=M``;vF>-dSEx)L9gIE}8#`?V|3Xm>CKP44XmBX`Lc6)YsLk+gna0-j%95 zb!t@kQr#&LJ`bd6Iqc_K(UI`*a5Q1%MBpnqdp_ltUfuOD9n9X(nHIKyus33FF7@QP z%Lo-Y-;P?aY_(lFq4WV~XqU>Dy{9G;ToaVu7Go4=OI~fY0e$f~KSrMs@x_gK=XTdj z&HxQ3W893-<=un@yUY<96e1YjFI2qL<==JxT_JM>BoZ7PZo9eQRq7Hv$5oX&s4aD0 z?x95NW$}o6-PEghVnUvkLdY12%3wD6Co(T%0GFSBdHepF?0LSlE;^$pT-0yP3S$Hu z)kmNNi~_$_x%8Ug0LE)vCxqb|UEqAz%}aR~e@;GZ+=rNiT!7YIF-9mnI(euk1F}3p zouq=b#+IGAZU>grMnP(IUAod|cPqM>{`LHW5( z`Jtks&ZYj(znp)Sd-6X&pZ={YDxF~GYAPxfDlIitqkt2eb7!)wjHhK5YWZS#X=wRA z-1NDZG#_-P^wp`4uc!qguAFN?*Mbi5Hy(^awI4q&5O{J%O)U_um`ufJL4Cv5pNvv$ zt=)8=ar>k=f+spUtqxa?GMnN8&qsuzpy(zXzM|!F zg>ik<=F^>Hh$Pp8Zh7&#WT_GyK3??hUGr_Hx7croz5@CAQj99RVOY&xGuq?W2R)5f zj{5DE(fj2VA>5i(rwy3kDtC`DKZ>zPpYjsd1X#~yj0e%|Z zSJ+7RkBoo3Zz+=vQ$MFkV_uga_NROP9}5k+r^2lXSvxJLdK?S>|GVs;t=_^J*RLDc zcg}8Gy#X@)k<9<&%PmH^4OL3;ak%fgLaU48oC$*(qo%2E0{%D~Tk-ZKbun(yJkbVs z9h%2<1)_e9zTC&Kh%$z?av;N83a!C~D9>XlhNg@;p4OlpEOsn_Y8T*QC@{qfA8SKA zYNr_U&&u~&$1#aD;}elw?fpo}b4bGH&KAc9tM`sE4Az-4N=Qpk1=}&eN@YQp^0bFV zfZ8$Md}$5#+avkl%w&%PZ=W+MPtVuwL>vnUice91V12vB>sUY_O?kS_`3J*sM3$>h zL6LRXoc+qNtkvVJCJhkwmFGAjQ-NG9LBm`pKY8JhAW&n~>Ki(y7GIL1-m$cIHY_xO zR@`&3t7^&wa~wRO#kx|ghuA_$aa-Bg*nmydlozE-I2AG5W+i51WTd#gQ@rD=tE+om z%GH5YB2gt1k^DSy>0NC6?{|;}{jwcI7hjls`p^HTK_&9h1L%(!aDbJWjK>0~O-@ zwTHD>^K_)tQjGMNkP78-wM6*{#lP#qfl zHw~pRKB6Q=lOpN*4mWFTDALnu!LMctzP=g&-D*Irx?DySRM~`|68SiE?tb`1o#GG@ z_HMw=g3pgrP%V8${4*JE%`c5Nzx)ShbAI4})X3N`FbJ7|&;?t)NEq76!wAydP?&*jY zwxV0g%XU+r6%1D)5`_lp{&c^t{l+(IUXDKYg!AH0zrJ}pEY>5B%JL56qvJJkqu7d8 zzV~8pV+RSjzFrIw*f<7t;!tm^I9`|r+cB|(64#4S;f9=#&=$wt5#_F&DZ-B8dp-}?8?Sj~-U zsOlkpBs7DgiQFNshj185m*7`Cn95)89{@p8CEI;Dz-KHyY5_B$;q0(9F?*OqO$C%B zt9Y=M4^Blj+d9qp!sO?H4=n)u@BvqdGI3yq#A-`$*=VwKulJpC!!#DuPnt_Brp9mt zFOYoZl&ggu!3kIEFMxMv<~>@bSDkT+2io>MvAVi3G3yE8oZ6-~KwzL}fAYkr0EW1Q zhw7c-3^cRwXx}&FU7ih@%w87-k?dVuT(tN1_t(eugXI*Dc3!X3r&`a*0Tix*nXHt& z_Da`nC#^F{z%AvO_KAbOZw{%tgw0ypndeI}uunXxx}wk{gG1atSpZ?qf@Xp!30Edv z$O|Dw+iFQT4Qe_O*TQd~$4&c_@5zG7-6mJRH| z$#)p7N&sfYC(bIuu?Pi;zLRA&uJUtNE!~ahPj0Ld(fgA{g_hoh`L0!@AeTWx>S&|XUv6&tnvg+cRy%WrdDt6g9fj>(VAAUrhI8#IcbH8#~@74E=ZAw8C z6%#*Pt0YB2HklyW8&!=qQ7>^C9HBCR=6$JkZ`)sX*QMOdHUeBLnjH@ZQ62G2w$P^Q7|v zEZcuRCubO}op-O#86vM3!zTMlJlc!1@A?_GUlZ8zB>kc@UY~jnS>4PMxlF79^?pnB zA>#Y+(q@pm8~f!rgc1^fQ-qy~B|oxw`gU}^2V@Jm?saeQLpW5p9Tmd)+^PN};CVJM z^Gxe@hlbp4C#P;_IQ$c5djW}RdgHeaKSz+&6C*VwDkb=+JM{KD+NNTHY zpi1^JDjkc15+#Sy#3b4=Yxc05pe>l(_EzJ3MiP~c;D|?a>GbV`xRQdg2_X)RdunxN zD6>%BlUrFR@(VqxmaPWMe%G4pT?^|FYoHCDNlSr~w07Hhb|!e-v9ZHHFIdB=wr7A< z-Q>*Is7{L|EYKB-&ct$TT#pphK=Hn}T_B8kUSWdX+k!>V32jZU3v`@S?-z6ajOT^f zP`J*@3Vl*=l8B)~6FnhReC_EPQb=e_XmuKoa#_PsO^DJ#`NUouKi4 zv(BjoSMtTb^b1xel2G!@52O+r=rF)p?JT1=)WoK$Eub4}8v zc~9n@2CH;vu-s2}^uwBAX6pb|%;zJVu%%o2>ji!7q?`b5>kv57B7DSy)xvgj=Sg#0 z1*_NM=V}npz1-7CMRuKszu0%uT68W1XI*Ki_Nfy>3Q*UIiU>2c*$vO~!4z8IpA}D> z<&vHYxF6+mi&b|@W)9{!Q|SeIU^nPJqJ)rx#?qVE09P1iLEEy*BgTRpf}$FHV3-%< zNCUzH*%Cu;S}jX_?)3{1D+D%i9L_TDiRpUN#GZ_UY?jIYKAZ|z^6Nx3Uf6o@9J5Rhh2G`w$bx0i zFp4T>3)ZS_JcmtvHxmd;X0{~Fh7u~BjT+4TrXM?ed)-XkKVE96Bd+PtR`OmfbgLew zD=o!uWc1FzE$&%lq!Y>3l~HqLzu5b0cUgJWMAe?wO8xLWGNXLaD2av<6GCc6StO|l z3^ZdG&kY1tv~C}QRds@qzm_ ze1A>PA9YsDa-}P-;Ywgbe;@NMN2XG{Y-t()A>f{#P@w%G){i3k*xmV9BH;A}>8RrC ze;BT{3w^H^d}UPTA=JN&F}frO^srHP3w^H~S`O)cphk*5ibtIGr+`W6ZjqtOF?NVUx z!D8}l_A`a4-rV0lKJ8p(u^D>lxztna1PN-eX4H0Q(VUrjTAVGYKR`Tc_%KJyp7Unq zW~N(8&+4=2(R>XQ!Fb>rfXTz|bDgsxY%?o;%Whn2leDuGP95GS9~H?TlM{&>hl_J zQz>aRe$#LuHX+>m-iVw7XY-mr6d(=o$62~{MkMffJLDuGgr#%FCnt+b4)YoQEZF=` zpw?06*7Ok5*wf(sFq0&tjj!R%9kO6{$vpQ!7s4=uyi-MmcOdXw$gIavVbI6-N`! zr+d$A-0Ld#@cW1RhGv7+Lm7(YyjFF(f$7MAbJzXc{36v7$qMA)z{SYk=zX+kD{lxk z*^v2eA9uLM-e(&Mlarcr!}uU78- z8TDjl{8)DQ0 zTvxN>L;H6ADf4mjkp9 zHLOqfCBa2cB{FBY8)&5(6R2DjE zitlca0teecu8Y|{LyYz>QJA%J&QlEQ_ut~hCRFUX9aI^4uKx;j>)f9tmoF`pL3%@d zb*gqg&0Ul)Dxv)kotN1j;KChskiDadj)hAEq4FC`XDpw$E@v zyF8qDO_v~q9|F+oq)H5~vUF=UKKvfZsYjup5GS36*}x6)O^T%o6w0d42$XpF1oc!1%4?@j#*Eq=jpaGfJ8(1wQpX?O@gw?I%jlBF-DK zjTJ2IP$I_0xsukc4vn(vYW;*PL+6BFpC}2GRidAo!y^kGQ!#VjdbkC| zlw;6iu52z<5VICQ4%mjS)oN}!Bk~xBcNloC29Zk!1rNQ{az?&{bEN!suico2N(thG zUrT^hrS;}g&F0i_1K>p{JqP~4zi4-9(w&`+FnGl1ZK1LAhY7JTccyE1uBdX;6#z12 zJneqE!ewTvY&{e^C=lUo*Xg)kYx~RK5MfJ7&I#IXuG{=Hkg|XVG$NGdd{OL`aAliJ zDPilKObkpuPU+?a76slp-^py}iHdY*1fWsBb+aL;WjPNC9*XEc^&5FggT-V`ivxa^ zYTahMbx~n$;VvbrDwC2-*>62(XWqN|CjOnz#%~ab)Rc)l%ZHowH8sYV&n9dSBm6dm zrHdP(n6 z&ghcCnm1c1mhRf&(5-KE1dgDhwMp&8p0AFC&nIz(ofkotQD;T`=X^EvLd~VNS`Zx0 z*tJp2!-HS;Lv*T?XG$(B`Ok)0ZisV?^xeyZIz(fi&3BaW?k*Q-VIJBJy+mP1xUeOq zG{K@HK1Cwd&^e!uX{>|5{^&3d0M8(7w-@trgdEn56mF1xt)pl@0KCWajP93~0w9kh zpuXjPwl?48Y=L+*>>aCnvYx_v{(6RMl>ppKYUcU98COUMY)E=8EiYp@u}=GKYCCRP z6v_`7f(P8J&$MY#ybX1ilIeJ@o_F3 zqc)-V6>O?9QOueYJ}@)1+yWxy8y!)+PqxlVWQpBw535H(bSMsijmrvt?l-py=?(=ydaDVl>P#6Uukt`rQ)onS1DJ0;=Ab5`{R2v-|7GZ@1~N;FL#{A;Z5& z{;=8+J)5;UokKfAL&L+BW+&z(O2jP5j-9waHkd8}>!epqro0IQd*3z`4da$wI^%TY zA*X~0-g%SMFJu$45BJ%gR@N`hI*ja2ci6a_Fng(wkrfriCp|ab@k(t&lbwT-#Df-% z3clAPEUP7JgTbsGzx-PcI428;^a>?&Qwa;Z;R7PVosF|D8(EhycCIVY=e)@pUsZUw zXFkDF0}8=+IyT8m$x7Va2Ju@yHxSr$svX_jbnQSm6kK$!QS zxrD2BtY5H^)n9ePWUFJ=-BDQI4gv{SWs!ZnX8k+kb&HPC&|7&vYjPv+oaimE_o}qD z)4Oh|s*axP>qV@qTLZ&b%U%Z)S7vk=W|*ihw^!o7yqr5{D5DYd<1KT4L!+)7Zrccl(>2*M4djd@? zK6dKqlaP=Q3*d_J*l(Uj$<)pNfCC}1*TnK@HZ;b*y_%uWL>L9UR~B~3btZ9U-!gT)Wi$M_;o*4Xue)(Y-AZl+=Dt3#U& zH(2^)M{&P1Vi^g7DMA(SiHRZxo(Cz!f~9~_WCB(q=wLHe)b}S)h=)-z&#wRLAP*5daGsbMi za*^NieK)E%V1Gc#MM{oYHcW9_X9*hkG^kUNm-|MHB+Ypp)joYcvM|N^4oatpIrN{y zSgN%?eO#ZH!5g3U{ON|t5XEA?0CFgd8>`KzS#gho3qt~_;n_9ObT)zOg|W5IaaN3) z@)!00?BoWUMmgo<$Zz1Hw&wZRbKSKle;!|T8Ak?^@V^tHTNx~w=l&2yXlSv(-{*E6 z(G^@_2h^0Dyl!>dkyqeO7y%|}N>o&LV2=4S)<&5?%0K6#pzp`j2b}RywRBP3Hr*CI}H-A$p9(d>p5oPnx^5&c8d39ErLV{ew2d zHgt}o|N7thpQ&p6aSS=CfSA@A)Z%Aa4oQwrLyl#<>bcdc>OPj-n!483Kg(GG5Z*sa zA^mg7LYiEAH~v`{A&g*PwVu?FZWHx;cdXh7TA_!z2!U5ZZXay^FrD=u;$Lf{*!6H` z`l~<25(+|o7o_MT@a^2aV@WfcQj{?UlrTI_Z3#`FI>nBvKU&^8h6StSVkq;L^Kd-- zXNRf(G&+@PS|bSU*wnb7;PVg1utTaaCDHHqfy~DIukK1pVu2EZn#b4*m)0(X<@C3f z$T1Yu+@Opkb@1-31LJWJ;jl`DQi8T868PjoxgJfVuk-UXL6(9ua^2j2<^%ts#;{QC z_-OaLcL&#Nz?JB6$|1i&=d5gLLzM`8e7w-e5?0z5^YA#!y>^MZ`K+d);r%#c>e4h{ zN_n3{*{T=c1u(FQ|2UiQ(%Olz84j~|>c_G^TAwh=wh>Xed+t~kr2o5MlFnh^IO_V} zSO2+Ls{Fqza24X>VE+HzKD9DQneYCF)MnRDUC%V2>)&THaLTFyE{3 z@q(5XnaB_E-^OZ-rw#sU_21R0WpCx(gcrADb&Y<%Bh4*()wO4Z`if`hy?=8`J`cET zALdzEarY|RUGZj$4yid?!<7A4TI6py{@oU={*!yvuE6)iv#&3CPZGSIf5_-r5 zvAo5cVKyamE}*hV%Jepv<`WhB>%V!UFqcz{NB6Rv%(8C!*?EpX{Cj00u1B3 zHTKw-Thn?jcepz3_P^Qhxl1Lb>sAz2kwsN%{(12Hzm?7Um~cUyZlPG^#H^i0oP6vi z9`OAg^NfTaQQlB?#rbi4+23=Hl&V&rJ*)e=RVHQsmRN5w$c0e`1}fzXPQu&%ZE9D- zeln>F82&rPVxuSkX!Er3(O*sBPVT8Jk89uOT$lbj)Mu(I@wDP9@)M5#HiN%@@o$3v zCqkaoSK-$nTU8(f@bNJ2!LzkSP2=s0cr=5|+`Xi%*334y+)egc~0Nzwacw zoUV8CxUcJy1y8 z(>x@+$sIW!)yt&N8#qEV-jgF`&(-3tJa5_E4Zq;dI<`q166V zB4OgyqN0L0`n6zVTO4M$bu;`m=0tmG={3pr1Z1Ny8rf{PXNjwCqq`8|Dlt=~I~3n` z0WV`DEr&$mOgEe!VRy6INO(+9@aYvm)Rs$sXB|oK5fkCXHmc>_zcidkY8T&-;3G#GS z4BelOtqAR2BzWqQa;%^R+qfyvfPcv>QnDFJ`Q1j3B1#{&+~5Q)$s3=aq=4;r+ut1$ zsX3o={#!`#W5nsz$b~%KfKd5&DB+W&485p@WT#!I>xf^7VOc7f4b`FPeC}_7hav zCN77y$f}kwzEqSxB)}CE(DXXqmt5QEPgmvuzDl&ZDDuS9tK&Z>&GAefzHJjd*j;`n zVjTGd!*+`^Y;`wBsCQE{En>-U{>(s7lgP=pk3-2)-2}Na0vhol#Hm#r07zc_yxe0B z%u>3PY?U-wE5aGxPq@`kxRPenP`t--8Nn0=$EO)Gj&tAOI$%+ z)KT|6E_8qn5uV}CyI`_;)SrX|qasFfd)OXAm_qVwH!KfZ6C z-6Ts>plFYiOYluK8USE`AjVm1fkPT&4pB=cn~e!(tXlOt1e9|%&H*fVsdq~AksvXf z(9T=2LW*57+t_W~gaY4P0CEoZPWOo68yvlrQ_+K?s01TKc+y_k@*PTf!$~WOE2Kg{ z$<+97u@!3=rb!zi=!sgWZ(+-odL9#ODX1Lxa!^k#I9a797`!yCzZ~sPE|x8+p;dUL=H3>_&#Ci z-JG!M$Wmi~hpZ}qa!r5C!SlPE9%)hcoxmm(h~GM+o%$@E?OFRb zdjsCLUAt>BS_`NAk9*$ zPZ4}-)Q_;9n^12EfYQ>piPlUOMt<-zFyW=?Ip`uGEYtx{5lqcDWL3EY%DbJ`v)bq> zuT!XKoR2$aX?i^<=AHp2JmmY zWdiqj7`VP;sD1CJny(7Ng&G>%8ITXk(39}mXt+OpNs(QiAuKB4(jA}L}l>c*hIE!htoccT~46=Ch{ci zbduAbYGZYhj2qouX;xQHsd*XD7}H}-AWV&?tCQjocttBt^pC7Nqv;{lHyP8sxI)Yi?7>rl_p>&|CNGu<|*8barp~f$+sQv5UaP=#1k7l?n-E;ljraXIDtRSVcXdE0r8r`7F!CEjRdEiv zJ0HoL@`ZJo=)ZI%Gam<5oB2Jm19=GjL-X7Fv#tqkm(jhN?xTlkDp*s}wqY!k$qV8! z)yC=PNRFW|H_+wfM4gTqinB+K^p=ew;bi&RxG!ex)HH|DDo%?xXWR`T{o7`K(+;qO zFRu|*wt>X`N!Vbz&a~*LA9W5^BkXa0itg*v7xX;Zb{{nF_X0i+|0`ZnqOKHHNdY*0 zKf~4N=e3SQ+STnTuCH)U$}14qbr|eD3vmN_zVqD$9m;XNKBaU+x4I)^qXd^^l0-K2 z>G}~~3}~Ed$f|8w>=zs}Hnnb_a^*y|FCLlJG1~^$%vM85hwDxB!Ccn(UO|{L8EDP2 zO8-~>dMoU?Y#z_6t7qZ{zC3qbUFbea5`P7|THbwSZYkxpKTmV6RgKb!VATY>snSHC zZLZg;4OQ8f3Ib5IJHTc;pyZb_c2g){e9hR1?ydqZ#Og!4)ll$lEh+&*P$+tQMP>El z_dtzY-V@72Sw>dvdj@2=dUHt&HVDrng|d;ZN~1k!mr^GZs(Bgx{2t3T=T?B`Q)tVEGaqlG6mpb7HhsMX zCt8$!Qc4?p-zu_;oXqDJ4%5@zVaE-4W)>hn_QznU%0< zR8~?4DaPaC)K=Zof3=%y Hk(<5W6nH~+_hFbRGKo2?P_m`sCs>Sb_9?g^Cq9Z!2 z?oChSFyYD%osJd;`zr|D#MU@PsgP9|gi-(+EWXt~(m}M#w&#N$f~yPik^93F`|Uq^ zSS>h3hhNK}_m?YSG%OO153kF5J=3xaB;iraPE4ma z?^^G2uz!@1djDS>Jj{k6f~$_!;=T51PxU2QmGT3Z*=UO&Gfn-T7wv>FgI^M3@H1J3 z25L)rMcBYbIyEYL>_7&EY-Ac(U+mdRB*juqG|3$_1w&4HE@`jUdSBCqZQysQ&}iCU zF@jL~eq$f*6RI1EVPnj)T+&s8$Yep)LH3tn?eO8`r5hGEnBe@wy5^bgUYoB;ouYAD^BbeyU6n?rlyVCX2AWe`HQfG51A~bR& zcb0!&_5`nhlNz#Dsl7nB!%2)fagp zp@ao-rMKQ(w)B&jElykfTjHX~53FZfKLq!Ba)nSWCp1C*rm626D6Jd?y&Rx6ZOMNtd(#ov*%K5kX0{3V;43vC3JtFMB~xM~ zm@Tz6gki{5l>UJW6?BFI?>ze!Pbq?py|ocEs?JFYS8K4&Bci zAEiz64IT#wz4|4mmZM(`Agf}zaVl&U=?UMb<>36J<$cFKl{d_1D)}(j7q_;thLRmw zU4UzzX89~k4a(2cqkHa7x4`x}6M>Qzjh7wyQmWtmh)O`SY#OYHBJ|*vNg%?k!4fDsOyn*3VB@F}623(Kqjoc$&b7z0?1B z7r=Z`&*YxS=ywO5JNbIFl$KRq7d1xMLYB2O*^vqI6xPOiFQTZ}~*W`d4@)(f`Ff z$Z+0?LVXYA6El9dYh$m39Whm->&vT~<+Ar`3So8#|3^OUd!qcl||DB<3U)&&hEg$dsYoo%`s@It8* zg3u=+^81VU~KWz>{=&opAk(F zbgWK$jre3t^K3Xf%b;_Bc&!Rj709O|m3Z}M%1~sBA*H7}CEts3}p+Sm3srLqyfXvn5T-zy~{iA*70E0sdMZ z7O{iKVA}COz1(1tu2682%ITN^^oXWiKfxzhV{PmzIw>puF*DuuC2w)V9P+GmbuZjE zI%?_RrYR)|67XMslRtie;6d>Mj^L1nr`n-u%9iyUm%kB=iP}Su{X#}VQY?%QtRm-r z&qmMabtF;XP^PR*vMmFR@pt-tbRMG01=8I_04Q5^JyAzgH=HxnjIM3UF%vMu_sXqy ze`5XfN&0WqTk$R%F0gjoeCqJ8FZ5$c`byL86u-`zq7-oC={#xerz=?T@m|ZeEm<3R zc+d!|2JH{*=@ZGMnMq2y^jvZSGPKnvQ0GanCB0ICFNYZMEwnXPf&7(OPv3to#DBvT zUe!4)zm*)TbZFh&(sz12M!KjNWKBlQ4{Ej?N*2RE9mww7U#!)F`8)lEnAh1}n2 zdOPIXDU}grrKMWEY~DUIK_l&BEgMytHx*@X69qkbA*7>JU_+ztP5hU1r>umYYDJ~& zkOSHEx3>+)!u93EDjQ~8)>AS)w3uyzAGhrGWX{CeaBv2GyD{yOTDLD{S!`+xl$?99 z6tf=A#9uUN3&JE`0^>>8VzQ`k?V$Mjlzp65b=#hHATDTLe&onb<-PZwhH^&3tXFj1 zOj&uMJi0~D8bsV*5l9A9mt9{nag_`HwSzs#j};;+Q=0TP_stG*P;z$Y0mioA-Ma(m zHT#@1Y#%|}OIwDWv(4*0vq)1be6NqGO`LW)mP`RZt5;EM}I%&lfn%cob7MTrg-J(>G0n)Zx z(H%aW7o>~Mh2mQ8x|$sEixjF8=i@y6=7A_|2d;y^G8KfLoR}!2^rzj)4%!CdXTR+4 zdcn8ua~?gBTaJGXtDhuESbO#Ht#>i$--6cg>xVWN)iv#WjkZ7;v*6}~wsj%IBa~&h zN9$I@x{j?vyMi*WZG3)BjhJyy4E}*+Bt0a+u5>oAH`~8RV&v$lEQkTojP4*58246X zfV`1uBdx*w?btD$N=&6f#LsHmZvzOg0by8yR?_c&)TEL9qfbJ`;(D{y&8r#f5uNn< zl;+X<*C~!Z5KG}KBN#Ms1A``Mmsd<)ke-bv9W+9V4fSMYyYXS{{xi)+o~7?64>~fG z2;=<933>`Yh6-|ZL_1^@C4gSCraOZYBX!x{0 zb*wc*N&Qkjo8kR|+aEhGI)8n@T3C$ns&3lr_aWh-&h=#n3J+G=Cf>2SJxXCSR-G_+ z%EtC8lWr5&dX?R4rH1J3ruJV76Ec%W)l2zi1s48WfIOKVFiE-vk77&iVL9^Wf=;T_;ZK) ziuKOiI~AdsSTaN>l5m%yaof$aBKA$qJ7+vGQ@>SeMdh%lN5sZ%yZBHu?|Ldhz{8aH ztdHdYM7~B;jf*&o!pv>1S~d!gyhmP+7&)4qt1^)heF zmyPA>YqAVtjQRs&x##FdcR5N2wCWf>a`t9yyCs}AP-?WlG3{-_YxcnF<+J$hXCUu+ zVS*uV@WnoY2U~b64;^~w)M|(Te}-Cg6jwsTB-R4s4Wb-Nd`D1+C@~aJ-(8AfS^4qt zscd^yVYl)GC~(5Ow4+0b(pQ#Gqg|mG8a(5H41opB6XtyoTaBq=uCA^y79=@z#(PAu zAJfxQfhhU;gahTB+*7Az-g7EfRS=X2o7o4mJm+2Z4#o>8V-)Z{P+NS^ zEdWj9#{b~qRJzSkR0=BsQ^m$e=R9I`zc@_CZCJc&d$~D=6RCjI?3(tDQyfr0cc5|t zoO7gpbEN6Q)a0#5s>EJS5cgIGqRN>}JP_`aW-xl>49|Es5(l~~wv~30J5PPFH%+X( zAlq-YhGrq_#h8}!!RC?Rjm9~@IZ5Jj1!rmqAEPXHsbfBynkTk3Ru4TACLoRU(qX9{ zBPY=AwWCS;GfwFCft_DWOL6874-c1%mFMI(e=Wu8KoqwmR)B^iJ2}72G{zU=TlJ3l z6AXVXiKly3mK$aIDB9lZ}A#suq{Sp=uD=#M}X#h`keX z$hz|CisxvQN9YD(9R>ZR&eHhF=66UfCsREcm5%}4_AamR2ac`k*M~HWX$v-{mre5y zkDhlfy)?Gnox%?oDOQ7q4^hIUl!Ja${ye4wS6mDU4Gc}HpJ~_%k3}i`E>o@BH?agG zs^rm$O36%~dB#B;JPLM1L_TV!a}6m;Tx-cQeb$%NF&dCv1E%gb59~Q^}I$|Fdn#0?81uut__c zE${GTGbq98N~Bc{bl)g?0b{m0SUUYt@b&Q0uSmm)fbwOAhCD5Dug2`DOhR=h`F+qH=daohHmO;bAfaWH9ba}+Z|?vEI0oapNB zh8PwX3DFq>TMcYTo4tE}y4&N+4{X@NQi%R*H-Vfsg18{wu;s3JcaNIcBhf_FrM-^K zq#{Tj^0K+&F&| z*`SA7E_b-dA}+&k9b9QsW;~O?*@*_2R$aqf#F8@sMgb0IDabSGrFf ztq;nv1r3S;S#D>OGm%AG-gmxjH<1W0s8*ydVGE?PF}Aq-MpsJM zbi~SiyyUX6TweC!u6}`{_oKd8JSUmlbTD(*ZAyH7p|PXhx8;=LR8V#M7&&|Q_2Ol? z9!K!n2ZIYM;mQiNz62c71IpOMaN;r=h$eWc#{*!LenwkHb#9p@3bh(nsExz-z;?gF2LaR6KT@GcpaZZ+x_%KY_e8}HYdmB`JE zLX5`Rv3+5DtR5}qhu`V=Q=-^gH$Qo6ks^RqIVVMLt^(fYW+IOcUe#G&yfYozn>~gG zk;6Y#AMx@t#kkX5j@->xj~^~^&?pSSA(Y?3>&Zt>EF7wQ440UMC-4%&S~tY1v%Np{ ziL`FzY|?!&3)34!dCs{7{a^z&Cnh`+8&9?@v4bxKbVmS zSzH=|CNLiaf!4l%Sf{vb;l7KHD}6SsV(c@mta-}}deqtGGf|B)!ij>UP|D?5vtI~t z@=B(RA!UQBY58$BS=04;Oc>%m9y*LV>!(@MF*;%CF=xPsz0aRx6w0gtk9zL88>PAY z9KjvND-gF`Khm*+17`I`9)#hb(EWS#yGKrBbg{!siPES221H?{*V`%m;620cbtX=7 zfa@!r_pE?{VD}#f!K(3%>_DTLASBVEyyhUjAGo>O zEBJb5j`Jg#R2g~47GF))&h)5pNDnorGGpzN$=>Q60d3cIFV*yN2%_S@E-c)?s4Kd9 zR*=%5(W`(8yXwoqGmPmv40oz)ibIk2k+!al?3f@%Ey_I$gU)z`;rZs7&FO48|D~+i zBee_v8f<<3dg|w1J)O3Sn8tY=i+P7yA7dvKN|-!WmIA|Sds@uf(CF}|0u&l{_Z+qlqHu=e zh03AsMS)*i9>e;RHr~E1JFm`-iBm4q$O+kQ_Q78u8bLshDyz zKr(0#w1Gg1fW?0Ez`O31M|7D8%3T)L+dOGxW+zBekv=4cBVZv63$6~XKW{s$oNXT7 zlVTD4JB4E5-n{Bj#RS6UCnkFxGDD5v0$p(4{7P`6VAT^AMDWoWWg}{Sk+!uw_2H!n zgu5UeYk~}e7MHCIG1?&mY+@x=+3ZNVl7+~TP2;klu{$EXV>K}BP(TS*wv zf*MQXAD>=UHnk1W)w?6hqyFNn#E60+%j|Z?ZPOExQqLcSRPiQ4_m`y{`lFp2*)(fh z^>PA!{RkiUIGncqxGb|Ean_3x@PTRKDG7m_AuHu%a43a+!*ah2K^hRK+$`FP^u5Js zS{dDX{b$((835MUpa7!R%#|7$!8mg0-xm;+uUx=6m+NL9hEZMuL%qI z2NPM5nxbtx_gX~w!ADXM%gC#6b%Y83`ewz_tBX#=<>K3`oE!4^X{P}p5+%vBj5%qV zitKjE-cOpC?&)TcYtV=IS?E0kei~fL#4mE_Kg=+CFdev2*&p(-mK3-F#&GPi23b^@ zT!*WZU*K2PDJ?xh%)V0&sBFZLlt4yKaDVTZwLdcZ<_f<17sl0k)ahI%8c8DIu;sSI zmaNPuZ~bD>ZuAP9dVp`yRx>Kc0joJ)lI03q_;Lo;EF38jgl?ZysI?piz6ahc@%qey zzbBMx<7Zy_eD7=W?ZUI=-d@@A{HA>MqyHs6irb^%mQhYT8OTf3_!>y_T;l~|fwsz0 z@l2EY+bAU#*lc<9H636d6U!aP00I+76h$3Q<7&Gu?C>mm56! zufgc0Yi87*AHqwrShiy2x1Tq9`ZpZuS$oXw_rDdsx}T{ia&YMiUtY_%nT-l-0nILT z);wc@^ae|nEBbOKRT17)qjsU5HE)_UyB6#Ek}{c;1z-iaR_S%K)pcGSJ7aWpg$>tCHm#V0b*t6r+21zdA()-s{__(CIC9is=eXa;M zaIx@_xmR830kwHSv+}_AO;aF{UIX2AAO^)6TNNUyLm7iVtMl-zx5$5830#N+G8 zo@b}Q%aj%HHGIsybvxttL5-LE_DQrTTxwVeHLG5sstu9ds$Cz!pUvzAEi|z``3Rix z3}^4(T24~V&{Nng@ENFV6!GDps4!7wGZ_B+J(8BfqCvX512p%GHc)0(}xLX+IM@bYVp-=Iyc;gHo(ZJKB}bJ3L$F!gaYc`UX{Z20J1efEU?UpB=Wk{STg7|w!| zaE>dDjz$UZ&laE4pFgdN^LEv%aR8S`u|LP9!;L4L9;3trmV6I7#Po9No#9x0SBM<{{PQh5aWMrOX@h(xhkBYKBnE8pzJk=HMk)u0nOh z@WxZZvLFwm|3lbYM@89w>*InT-5^MJ2+}DrAV^4y3P^XibV*A$3@t56Bi-FC-5^8f z07K6F9zW-tb>8>9@A`e$nm<^qSvowk?|tum?d!U>Uw7}03Ktd2@Q&*7$u4yMq-m3n zH6c9jl`3P^)d%*aWZ1?xEp?G10j0h4qk}TLeWKyfJL{5$1R}{ldgU%;2NB{tbNsM&rXgqo<5g;Q5?3MMNhjKO5vb%a@T=2 zREhc!{s!NqKSqJVKJFm3&guY=3yUex@+~ov07wm|_*qW@Z|~w(f9hSG|H~R&o|VTy z!v2Hl>6Yb!dM8e(|9Z(~_h3?abD(56Re{M&@Dd8Au+KDO|PnoMRFy z-}42N)hY68z{Ayw(8HW&dqUs#rSRcI_f+FfmZoca=*CZ7Pm=|)92~)A+Sl)6-bAu% zl-EMix2+4Dn8uoqn|>U!1KLYJvm|F4=$D7*AETxhp5DzEg;A&PRQc^q@8(!K)lT2& zrPb6xg(Utqgul=WP)N*H(kNC|%ums)yXOQN&M+NKx1ba#USf`~U@Ms2kfZddbxxlN z!_>MQ#j7V?F4;PZ2QIn_zdF}(Gn(eyv#I$=sI}Sh%FfQtrgp5hzoNCEMt}X{M~q%= zCeat`^yH`Q2l*>&Z#_4Uy^q<(8A>*<@q8`gcNB*P-rw(im&jPl%iFiTFza_w{ZUc( zagkTE#@D2eV(-Mi-P$j4jE`fs#(8Y(zyQ%lKMot=3$mx4NP`-U4Z6Bmfd49Oe*E?; z3It}1Z-OdN{%P5(@K~&p#{ey#ZS75mn;9c4k}EEVoMFC+k>huN>y$B7UJe7n_NazH zYxQ%*rU`T)TEC+cmyK(tos_LZm{wv6&2AHV;(pZkkgPB~`;G#Kb<4!bZ$_B7RiI?= zjEBpov!1M#&pT~;#63P|Xe^|>Yc|f#%9OKJJo8~w6)sLBqVRG zmOS6mS4m-ICqGDzT{_dt=V3W^1x!B?@MEqEH(lc8gz+SLK( z2z7@n@sD^t@*(55+{Z^BVh}gJg#ZKjD;N63?D*jK`Ue05;0nj%=`5iUEfjFt0KhRW z=)BjDvNx|}%tGTUowi9*i(UNAB6SrQ4f5#gy-#dZo{Q{$eLVf)Cg)`G2@ZLZiYS2S zIW2+WOVzDdvOIuZ<<~doyWcDz^=Hdo21hTWsKsAq)P4~Yvt}!;0drRg0}Nn zip^Zsm48;kH}L%8;4tqs<%5c~%->E2cJ(Q9eX$g&Km_LxZCw)$@~PV~p9|Ty=X*Bm z7?f^y0@a*mUj$=59<{&E0Wpo}b`s?;!Zi+ozouS%vTw*|%@8;=P4OUAAn+K%n9* zzlW-Ma^Bo+?iCkDa#(&kKSWdhEi5U2`}8}%{=hxuo+o5SO?5_N3NiT)zRswBqx zg*}AmIA(~aB*4}MF&EO^q(@2%4j`q780s952MEgnt|qPIBVbs~BKo}_^q2IUtuEIH z69-!K_|6RdwcwJquP*Hsq6P6<((B>*jg!m3I~s$%n^JwhlO)sf)%l&SN~~BqCrZ|E zTA)v$SR#rX?OZX-@Upsr?rp$9tT9lLV7rNzY&a_%ps_(|Ux=ve@y_CWSo1C)>(v_i zSnT**+eF^Sp7lcs(=EuBu0w0ZZD6l)h4f*&h_>Z!(Smbp3S%7SqQO(|tmj01XG5^f zh){226fbRBnuJ@wyJH00VbLU3{4EM|%Zcxa@}D*kXl?JERGxn^jRuYn^d9eAbeg@Q zEx~QYER{F_X7~Z^NU@94ZmZf%Q+0(`mhAYN`z_*# z8sFg;b@FnQejZ*2QfP&@w^Dme{(73_QO`67N`H=(N-Gg5JSXz^FwdU5v{F;)nRn`^ z8Y+cf?0^cNy}+OaCg!>k?4fCJ7$M~t>P3@z=2x9iv z`05}KN5yTP@q_+lFEVC|CdOCd!Z-2y2Iyc}`Bz>itw&=jH`zPOjTsB0DGMuX7UtQ| zyV0?gG@Y@B96s1sD^zT!s&qAI&t&4Ol%R4DbnU+NL@=uS2ILQ~5xM!DIC9ocWs&W( znFA@)c1Rog{>G*2jAfqdPqq0?p!#QB7GyQQ6JjH@Vk#$6nEN{m^(4JS{+LYA7Mx44 zUDZ!hA6Xl=2iSq+%u)h4zax}<(E@(&UV4*HIwE?|dA3)aU3XAmw3P~wiEcJ=gupbb z&XJAh5$sB=WYrG=eD|kvAX|CADlF6Mol)(Y5r%cC_<9j+E+(DgMBU{6j1Bfd zAw4oZ?cZLFjNB%-pSt_)8YEP>j(o*^wUsSY)zWeHN9IA?G>%oVAD>UmG9r5 z*Er8ShX&C+d^0k8p?3ms-w=zQ9yp$`k!IWHeLQ?c3=2QEUOj|H*YTV|N`*$pvP6|n zR$E8?{dShFnp!bokFx#YsC=6@ZO<_Qx&`uN`acx5+Rw|TsQ(xR!$)QBmQK^weI0umh5vh_*kq7;(=$*c*b|@TsOsf;| z3~Obzv|H+#(hls$Bb!QJ;z2^AsM;a*llKw9k8azPot{}M*TFv}~U_3v; zJQb-k=OwqSW{fD}=_jAYtmKk->#f9ruiB1VcwrkGEzCTHLRj9JOWm(f=R$XDQ=_{u zH@~XA-|f~;xd`&hwrnvt6JEM@!VS;uC8 z054p)K5+dB4OH5W=^SW?6^T-;VdK^QkFtsEY0%vb*I|(HXMmdqt{H!c6&l)b^LR?K z&9R$QB=Utf>{vik&n4RPW?SSrSuj>87NmYrXN5LP$@0t|8= ziO@$eLGVG<(FYift{{0d`7x+o+8!86r7Ht*YL$4?Cxqz6L|I%^aXD$fx;=nE=L{^e zy>6|#3lbwi&haGNBSE=4n~Iz~im9A-S{xch6hij=Ie=>pS?krquVM+iy3Wct{q*M59oKzC>z>V=9n$ER2%**e(NJo6-59S-J^3!Xf{jR+_7KTY2fibUOje zvK_#T0N%d?KHFlM_kKpxeDRjg{diePbH=hQjM{I0S-_>J71Y-$5+ecu%br-=suscQ zKdEwRggcrqo^E%r)76A9%N#_t+HKceiOT&(Ll`GXuPsVp*PNC9t-gKCt zcGHv>+ca|;yMi4O+I;eK9w}3T8ooh&4Zv9{&-~&dG}W5PO(VE*n8Z`4uu^@-6^+$t z?c@Kw(*(Q7s;_En(YCbp)0|P`WDoz;K|4Pq+HtfyjbCX$Ai?{|jluqV-am82%Py3c2%QEY;y_>PZABMLKsG8LhgyU zrwI8pDivNjPN}~0F^tORbH%5!w5?bI^>7TK6xpX2! z=6yjT?~aXv^MSZ|4O|_A=@JjTRtsbXhvQiCRcIW#Yztj=zWQ z*TF7BQL(t=gPCaw93B?=TR%3=s=V`O^0mQL-m*AlEFj{M=gnh3)v{GG&XK-kul|n0 zod61ney>66ocRts#KJu$yV7i_7`60F#{m4&)Z_<0>w7AUPQH*a!g=$@R`A(?tv5c! zJZSpTixV#?H5OAOw=3+Q*_Q0HUbOC=8h=s_KjhP5wy9z4z{=TpjQ;!kgZs`Ld4~k| z`{IK;u_~{fxp}z2sc_F0L9F7kP%G>vq$O>3pR2znQ0?Y#R+!DsY0e3na&#sETf0>;mq!bbW@rL1(-GRBBc zW~Ix|Q#F2GwNqt&CB+r~8U2eS8pcmKq4>7&S2qdM2XV<(5x0Bzu4OAj41}fuO3>^oriek;*0=cZ%lX3cBQK@cn)sy#`WnSxn ztqol1METdujF}HjZkb(C`&zc3q5)^(-q>=zRg>Q=-}D`;a;n{)e>zCeVPR@&>Bvib z+FPiJWsb8nUHfk_5E1%ll!iZ$|HYAS>)!e8niMi+2^GC}I_W9O&ZFR{1`@)xV?(|7 zh3B-ARW5jBlDFOuQJ`2v$vq&>Xp0r%K?R^?EBKN;Mh zs^TZpN?*USb@33%bIMPbrVFoAX&g%I7&B1mvW%;W8Srd$#c)MmeKEQ}HvJjI&N0(F zq3Nl@QjD)md)RKcV{EiEh<{`}{7r>kEgOYW{9THLiDkv>zhu1s&VFCTQSOQ3iiUr} zE{lc_{Qv%F|L1@XBuU;rBH8=%rVBHHBhG*bd;D!F%l+88ESY)skW}Uk`>gjhQ;Z#c z1H^H(GWklTYHTXVbyP+u;Z7rjG4FqvC^!igSnFva^Lt6ks1nJ0Xg$XWruPzhJnPc+ zJe=FE$R5S0fgAU^v6W?|`A70~Ze<23%8gUrpn|5y;8Rg%Wnf3kLW3=`QS{A&P+KI8 znD*}57vETbn+}=K@$_e^0l6yTz`!j?N6^B8xaWRWq6WCrZV$`_rg&30U)O;n(7yoj z_-`*_u%yUxB0IOSo&MjxTj23H%XVPaqZwZk+e*6<>O~FuiY#S*;6M%|{CriNhiSZ{ zjzqN2LIBDA9I->f9#eiL({4MB(zDDPM67yOQWS{Daz73D1kfjACbd7<&|^moSX z7NzJLcNp6@6yGV!(2XNVFxh(1h8#vsUwIY3hD@{vc>1$8+~z{EFjX_x`Q9^V-iZ(fR7&WuXr=OC1!Mf6QW z?{9DZ`cePiKd*}+#jGkJea~GdX3|D8T1HahW|Mi1;{5v2=BgnLx*S~|&P9jxP_b_O z*n!s(>y0#RX-ICVE$XBvQ+YIrI1)J(xZn{Gk7R@iV3b!xU$zu58#l*-Ls4RBxPuvu z@C%G)W;R*qUavEtf2BGw`z8B#^EInVDOr6W;j!G!~9g9HTV za(Yj1>l*O9^0kpEQ6)sk?3z#l->S7 z5MWDG*_V%mf4xayNU!`nnt5qy^7dTyb3Xwy1@{q?HoLSoTt`xegBe-u={fWjk$eM? z2az|<@OOS!zu;CootKK2AQ}$jfBHFbSm|CSY5@Hgk(IR)TDV=8Ux%&Q6sm9X&KWm$ z7JtA5`mPrt`clwCmPhv2s#3BM+D>Zc zZ2}ICXgNDLZ7NXCr#N+ya4pK^m!I$EpUmAsg|L2$dil2>#CnQ@! z)XQ*WN?apzGZNLxwLf@OJ)o3&{0w99gY0fRFO=(`mJan|DDM|cS%q80%6zRsDaAnP z?;Pqjqy#^bMlVW^N|GfpelT5DWHup`pW*kR3Yh%7rl89;yUaKA!|aolHI1btYvC&* z4fl(+!R(U6J6&RC=AVzFX_PWb^u9>Q=GXiuDGSTc3Oo&x7I^cXi3~g4s5>+b{&2@| zg=qm{W}?K+*op;pUK}me4~p8u4^C81R@3yNo!gsTyVJN-3pFcNA;Cl{0Ew>p>HGh_ z&;EYHaRg+Gd#A@U*eIDX0Yv4ZC^DWdrHoJmqKE=Ve=UpEV#^<=tHXfXb4DxI^lUYi zum4a|iTST@zbdGx^ukk!{^tVx>oH};Mv@mrj-zxWJ|3`C{YK7#s~~6OVfEvPUJa=c zN3Nq{H{9>Zbo`q=g0Bl<{n&M`U$L&nwcqRPRn1X10+HT|$*3#Sx1}XK#V0(^7$R+! ze0-wBX(K-=@it_Cj)F)p7$r=3o@|6p>62rlRt-Zzo>D93mgqZ=23Xe6_RIPo_QKg= zPYQ0i5?lXV7IzN3p~p2?z8Db5?aZ!@_e8R#$A|xv)?w(vspwN>IzrHJRb6{#l}yuD zbgD^?>m5kY%}6dVOZIiK0&{E4c~3aeXkn^fR|_2IW>>vW+|aGIzN_&&4KmRPOc#%L zz&#r39oFoR+wM4POa~MC__o@KRK*4}ea`Pj;^?H6=;OJEQ(pA?fs9f(#Xqe|KRxcj zvdDBQ61n`Pq3rAXYb0IZ2a%1nuX-KVYZjmxwzV9dO`T}|+xhyh4T-4w!o>5Kk^(jdp(c{nHVF{qr(p+2#&o5*b2V$^MojdttZ#gk%tOvb(w&w#(dC8=-JXS0B*plpD`oJsAh3|ujHaziMm1UhM zmygAlsMw}JILxG(-FApeW!mx+ytewOYXY5}OQyOI0g^_2hZ&%^{d$Ow@}tSERV1;s z938UC_xAmJvo^QsIh@xRc%XYco6lN5EpthpWqR#f0o1HEbNIbyU6ZKWA@?6t24L_u zEwrz#a7+S|vrZ*IA}n^h+YQ-}qYfM}#!XNOr6A{e%Hh#}xDZtH`g_zf7chw!hknbz z{gH#efT9ucGf_9n1l5>+zcwRfa(K&v-zor{&`sB``7QMrSMox>P5gESCsNP~>N7pXw;Nf_5jW5@6;Iu9ApXH@w8d1kO-k=-yQHITt%-oh@zBuc0MaLJo z5_+zdK)_@Y+sL~!bd-+ZK-dJW*ez)&<`P3&FHzdt-Cb+CACE>}$MdXq7X=7&{B}(? zQJ=xuv|#^T^k{KoOV*NGqFs6d6#uKkL|-54cp*U#49zu0X6JXTA68H45{rB#&+o%}g z-ep0*Y?kG3!1aG+o!4^f1&JzslElzafO7}JaW!c5O7=Wy?S=d>XdMLT%T5qFY{SlK zpBvsgOYmf8I8h9!^Kh|sc-d{Kf?`TU$X3gNNK{q)^3!r+L;tFU(Tu9_h76zIy$5s+ z@!)be=iDsxL$jietr*xfQBL*MM;fDDCopS*P0IhyC(mvIQf;bTHWGP(1SKyzB6&9lDI6S48NyN&kuP2KkN>X?D4c zsNPLC{?Ve!<2LW2y`}mly0ScraeYYBxPb-yrUx|I;^}g9IH&KOMPk6AeNHXAE7l!F zDKcfd+Op6B?AD5wt-qKPcimTq3^1@{bmNMS0AJTi%iS?@RK!h`GVR&UhRdyw(&;s< zeXWi;Nha@-JzCt`t^4$hLDbsv_7(u2js|kYy*GxK-h&CJDhxnSSUYH*c4%Ek)cu$r z@X@XITw^Ov$Bgn?ToinGFlclxRHl6PMO{2qD(-wj8o>>$?tt7wB@Fvi!G_P-*98!Hh%{lVyuHc%VycB?<^uML`=lciRnWt`?)y?$ zI7_xE^H&ML;{X}D{ecWvy?7Yv$xru>$Wx<45ytQB2zY!{>pxA-r@n;-x|S#(#alhH z^HM%EFhD$+8z^o$dFQRC{jB8YV{x%48(V#v;#3LWTQI;FTvf^xp8RyO+CA~<>|$C~ zu<00bzX!eSaoyh+u!$?U^{9cVP0fv4t%`>C?o+HdtRLvq+lO>AST?yH%o?JIV8Y(a z8m_cq1VgLtiI!d>&9$|}Citm#c*E8W{1Fb#4}#vRhJ;r(Yj9sO+AV>CEO>>{%R#o{ zOyf{I8Y2x5weJOich*rw08DVUT+f8s`%wLk@aFs@yzLHdJqAJqT~UuAVdL}Sg;y!D zD;WR!@Dw2-3($M+p1^(f`nWPs8+N*HidA!r#$?{u6!02P>-FVJoN)Tg{aN4zKfLmr zirVW3V>E;%blh4!@SwapUU4Zb%zc(blL9ri=@PeX#z-@_tdNZs-xB-wrAtz(6gg)XKW7DQ22x}d zDt$Koua7oLYsUIw*HroM!K#G(yrl*wg%+m>!^*C7)F#*Ndc?b2H3mYA3}D=h3kg8w z=C>8%JI%i>20c7;LBqxc#FOQqAHd)gmZr#g^DWovx9b-I*(ge*F>#oW&AaQB6Tc;H z8abqGpU9^#szDGuYO#5r^~W^4CViCV$HC#bwXdA|10JIt9F7CEjVsf{E`D zGuBgP0LUYjj8n{!@TE(bU%WU9MYKM|YN>a(G`i5K&D$t*JltN^8>6N(Tc+opZC^}} za|cLv+;7qN*AGu$OF~hVpK?g70RrM@PmkNF(Mofy zfTZKg)=vkkE9Hq#o~A5t+c<)nw=T6EMyu_H+{}#hIGYbKT>##BhWl!o!>H(1$Db_- zwXRC4?2W*}!U+|V5gjcwnytQQKA)pm9mGFf)&i}NXzQ`>p#ZsH|CNTmIZ8s3Qxedynw5|ND%vTi^psOlH zjmJDAHFIVC49UR6yR*UV;dS=|?wI$3s{yy(sTk?pS&J;h6{gvC{vNutwi+XakutA* z1AWc?6{*4k?+|3jteR$Q-?Voie|(Ml#7-+szfih}zJQ$mf4l&M99BOK*tA?_h1zW5 zP|OPHuDLMdBA;C?IneNeqETDMH`m&QO+$ZS!QuY*TdCj ztr#4QMBM-?1>tam;AeOgQDQAzpB^rSI(}9!wnQMw2XeauY>HyAdcJJ_kv&v%?hu-+ z={Kd6#ydkU02#?L3B{o@ALUtwU3e=eGE>=~xZBQGBKe--cq6vAb~T4nxa>aRg;C_P zZHH2{S&q`XIGu#S^gT`@O94JpjsR~IzYbd~aP$Hzgr+quY% z_^7-L>FTe7keM4nR>si|gHQ8h7mLt;rvfMx{v;npPsFYfdKKH#o3ht?w$YpLR$(_T zC^pG^jgETxD1lk*cUuCli9ye1Dms4(Nu)F%CGNK;CyFx=X=F!q_4N25*YJ*?O@^>- z;Q$%wKauBztny5Xb$`zH0LJoSzw;hmATCP=MT~2+ELzeA(OW-TXD4 zocrI!hp|^KXFe zk@j&Ch$2YjPT%>VY~gU}e_k~4I}nRg2D6_J7<>2ujq~)#??DZ0jn_W8SQh( zPH4)EN^%?M-8%6v033l+AMHN78f*ULI>ZZ6hZKu!# z^s4s>1y&-|C(ZzU+G5P@;EnJ(3#_@QC=?->G_4}K!s)$}<*r4Cwe~f9Z!=cc_zqXj zS+A8`6Y-yd{_nB#T6!JuoiVPsLp+`vqf`sP$W!t*d}CR}L|(?%j(DJQyDHB1n$rC= zDTiI*dj~slm{z>$18gshnqa#|Cngg{}#hWt&+?9Gz{{YdY&20_}lmWK%3tnAr6vo8AHKcJ2W**4<0+? zr8>KB?Lu*{7l%hx&dIacs5t;O5M_C*zv0-+J6oGz?K5L@5B}ix;dT;etz0Q@gh0(1uo*yM zd)*Z<4OMZowiuZYS2>@86mF~kwFYRxN0)qrE+x`?|3MR1T~EBvcAa(jIsF7VQcURB zWKS7J55?Ti+65Q5KJRfFcwaLg&qQ~=r##bv_k`l>d?w^YH5Zrw#e4K+a9BYM5pUq{ zRvOEo5QdI6)iaPX?M8Z%5mJs~>NGp19DDbRY2nT-AwT^M7#{iPfFM8mbWBa{Ao+^g z+QzBBA<#pREkzW`uwZ#i-BWZnii#ocB+x4$8+DN;N|sI%$>L|A1kPm&tq3n*yki3f zHa}k0 zCrt!7XYlS8KnbkR=bO6ddU^c^FvQ7!b6asU-Zqf$?vVLxiIrIWni+rWc1%=%e}IyV zZ3;^+%=8sH(Tk7l#4Xn)RGG0|r10@c;Tmn>AtpHi0B?aeS%Hn3V*|rOLyXbW&A=ms zU6vVsxu+(xr9uz>8NTMtD}Hx&IKV&Jb7?0xG^7^Qwnxfbr}i7V>P;5PSoWu`v_g0$ zCK|rcZt-vm!syA_0DzV1BT=AxL@w=_%kd-xkK71bAU?P zWT8e}d;aA6_wR?2WI#_)QSq^MsHop@k?T-H>Cuho?GFd!4R0lWK!|8DIo?MBDw{Eo z9slPQN3VvCOoGqLYepNGGueF|o{gX=e#aj2HP22^@iT-D-8Jn=SKC{8=~rG431lby zZQVp0Z@E84FyXo=+O}GF>_J@AHd$L-b5_!ea}JuYdwl;v3DuXUcM)7J{U$x!2$ zf;y_{U_<2auZWM*bYt6Nq|5>9nPV|A#JYl4JBhOM_W zzwtc)fpAk!#u#XUYZ9Q=fVUWxDt>4*OY?_X;IGI-ouowd3+_`=o(k z5Zi0~VPXO`Kh*q3B`&eBLL4c^$rFZzjfxE;;T94@twkEs};ta)$T?BHqp0En_1;z3C3WaAs` zM@8Fy`>#`_nxVdEiit35a{cMxe@t^F&+ymRaVWV1@$`pPv%Hc-ZVD3ZFwxKeAS6y| zTHMAO!%HfYW6Ne$(mAkg9vG-fbF5!=RnCWEz21Q6}Oj;}X(`w%R^WFu|XozZo7Z^4aG0 z!p0p$BA_*S>lo8q#Yt{3HSAW9Sjj!ZxI&vKq3T^c7#y-kiQy>Eb@G!z4q(JTo+2P1XcVaQ_O31kw!J+sW zAJp}Uo!je)AGXLy^+HYFyznyt?^Br|mm!qm@4U<^QEdpnpP5H-2Bd|6xCG#A3_-QO zEmPhh%F;IrQ|IA2C`z83s_U85dNF89v`%Z3NeoIK%l>@*zXLxf%HsRW-7p|pJfVkM zBYb20G`FXyAGF$$8C=|oKJC3V%fsKVfA8b;M9$OpBT!UFo8nR4EUDkqoYMGR(}5+t z(h^nua;PQ~L9-43I_1c(7V(0$MV<@3o1#-^tK#2iTEzDGTa?GuI7F)*KqbwmA<8&+ zck^4Z=|b^vr~Q)RG$(vzO7~?avMd)O*_BMI52YO>?pWKxUvxMhddxyM!DhoL>4l0Z zU|8=h}ak{0q?uhPlO1{%Y6o| zTk^AOgJxy$7NIuC>jKaxVVR9G7J)9UXhu!*yHg9$<5O334B_821)x zX_w9)hdJD2Qyl_rWDKvxdfNY%n|S1Ur-w^}4qD>wQ2>DjHc--o1|oggU2wQp4}|#+7i5vgZAiBOShOi2X|~NxzPQq`j# z#gaLX`kuZYWnRvaok0v)pYDPvCeGmWRW7T2XK&qlXM7h$toGZ}eY09*d=eJgm~w+e zeiHE(f|Z&k_*E%(moal7w*o8XhF~n|b9%K16idH+$86>}N97^#XLto;9OX~JXd*IA z(SRJCeIC1UAw&CVO-hTg9-N|*kTCB%$(L2ps1ht^#n_HmoehJG__NUv@!6TnS1xpk zl9tFN1{;OF#L};#4s_pxfLi9B$ZLh1ff1qz{Eaa5v)4(^q|=Db7EBIsCkPZit!TId~`9n)ONMxC)nxYMdv zu}ubiVGlFqfD0D^1lZOr2sBCAjSp9AKN~vn04uuLM_utc(;!UO(rfc+eRB`lb$9aVMRZ8M(0L6$pYc z%0`oC!JWg3JjVm(@}$GCc@$Gx<~Co>0!aUk_f2wYy!_3ZNNDZF6Ad}pSZb3x`xWa^ ztUc(Nz%kL9MHlL@`Erh7S1=VL-3yW+m{2G#l{p}D%xJ^3DDowefgNAFzAWQ@=n+~e zXk9-14Lqo$1AsKj@#IignD%uI-`49@(sfr5jnaznNegroSe$KSKAMi2h~_6?S)?Rt zoxb541-OT~E%o1P1M|;az_({Mda4%rBap~gAGhBYkgR5` zx8hz2k^D>AbPRtK5=Gp0tAIE7^2^y9VY5!AtMa*CQkgKiB}4LHEGC6XZbnfjd2d4Q z7lkW=F1cRX6^}KfEIo|8R0;F+@3b%bUJTyWEqxr;XxLMf;u`!;9T>JR?AxtuSy9m^opQtZa&8l7awfGqY zjMDpL(yg$J)lgWj-lsod5b%gnH31Kf5j#_piy0wrG>!ok2IaU!?tf@Pz4DLz#N2_E z-*Rc=zQ@nEVmrY4eCaX(yYCmZKZIwRVC?|V z>RpIze15UAkVEU`hal}v|MBD%~X6U#?Y zfT;6h1jmvJ8UmCC0z#fd$NJyDozCx>|R4jdwX4FztSYjjL%%D_Bmdk zwIhvx&++cmz#>D>u`>kTjx)|aDK`&#g4;9W@X_cNy_dO+DXCzzT;ZQzs~kE?5DBDu zn6sWON%=?5ftdalmqA_Wp3V`xy{_&l3`r2Yr7AqRY9VjX3E(qBy1thJS4<9V(PT=S zWJH)}q+fzUU~j-O@qRQL>&>ggIE2eNKxf{SE;_YF1CY2|(P}vC{LM$by3_Fh67u|o zHV~#+M$vf6Xcd*vSXr8$~iNoA8V&py+t;z ziO5^W{KsA%*z(yePU2MLctY;%rhXuh6NOg2Y*HTTYww8ozfXXxtY@D4tKThOP-7kt zzMk5BP3l*meiuV6-pAZfz%}C8sB#r@@rnq4+A@uNizTR_y~66BPlSM#A1Pv(jXyR@ zIUJ2*7#&kj+^$^`B`_{*o}jl`+HmpOO)J0}#fdfQ4JA)zIBlUla#b7={s8r9PcMHv z@morY!zYt`eYs_Z{$D+nUZcDTf0UGQxTJ(!lAb^~DOw2OV45F2#ENy5r-u;$Y@6D+jW1iJa zA&2rC*pfp=tlx!6k^9N2U{%nQU_1sV#&DXk)EC_gRAzwtpj(@&|1BS+q&S-&pz!k~ zC%=b0oPdTY-R6z10}#rh28#ZyF<;d_Tak=QK%=xni1Cl{dMW-uvamH+eduep5wHl9 z3&L&y|9-Pc>cC&odX@f=o5P?m`}$x6zyb}sTgsJN(6&&B1plDD&^`);job0ERZN(r zaevHF5nyJe?rPO#)VLlPIdXpkwY|lFJ)HH^@Bu=!n!YrP9N#{2(I?nsh7zX%cpr0z zr&U3AsY_KR#c5)mn6izB4#19Lktp4!abPrqQIyEQ&Ab1@EgJ%#_}p~`7zl2aJqpaX za(*}BGA07U=K;J8?}_qrW;>ET)gEC1RZpGkQk!pvh~p#8CadinRWW-U4oyARXwS1= zQbT@io4(kb32x+FV9c(Xp_BN}-m9EwzS1bj)4dbf`)ItA4l| zONsONNI)I>t0-9sfC$Jj0e2BJCj;{u&<~e!tfws|6?Xv5VHg6K;w{YrmPtmlYLh>! zTR=RX=cYAYGYaMcPf`t?*+&^+YyLT{2E-*%pNGXXG&Lyv5mWP2s(pp_i5=95R@I_ezcfXD=-&SL=PAz}z2Tm(uqFSnPTV@1}u! z6x%_86~v$PM6^M@M^2KT`Pd4$7Y}$`;YWJu%@S|d>;G)Yqf8^ zB!!oH{mV=UsUF>taqkPuc^}cctG-_qmpwc7@88u9Vg>vf*d%@kll$sm_~|MTT_@v^ zLz&hoikMm&pIr`*D+4*#)`vnO(T4We>{Y-++*897y65E`9-O&I*^a3UYd^k7vf-aj z$66v|%oRvj@CU4GeM?|LRG7x~W9nz<}&#C>^!BGmZXB8os(u!Ig10&G|S8@%TDd%qSY!zwv6g5q(*R=k((=<3shx}it z2g2wRBO)-_l@uz3rbYh@9$l^$cEI^DFNf4xx_)6-Hv#n)7%PihV(4+Ym6$5ys%M)P5%(E(!*%ra<@geY1txBPHBztKNPGsIp`2Wte9<# zm~%Bo7@i-oCHc+6$qBbo)U(*61iGLwEe-rp$+d@L7)4p?CH%<2q2|&Y@$LI}-e$KW z-Wu~(D{7xpq-MP6rxT!m9?dbLhOM!+g#Zi>z1`yQbMgeMB-jc}Q?x@;&@FIopas++ z#V8R@9EVvDKM{3W-~K@ISca1F@G}hS`2OL<@r7@B<7R}aaYryMHX08ULg4@oTWol0 zE}r9ktex@33((;RFa}MCN#=Jyz!0PQn#CvMfLhoDP37ytI)ME09mJR40M4?DNcdAb zE_Uh6xcaGljV?W32(GfbvMh)H0K;MLlIIR{ZUC{AoKdz`gJW(hOJ|zpz(#B67EfHy zpPBZwjK>keeFU+o$R$`B1}pJ`9h;m4>3VFuzPio9;HqD}bs26l>SHESI%xpNU`MS* zgBnHi?r90Pz?$<6U`B45->6*yjLJTEBMOSllk`piJ5%3b3xws z=?Z6Aywhc`)E{dH*kgdK=3gEt{W^I7Ci=4@c?7Xuo|?xvKk{R`rZ+eEUv~vR$Dc_* zh&x$z#H;tRkriSDKM5{qp32JnlyuFBZZ;?NTl7h~0l5q2FJ?h;OYTL(VwOb#!b33508*3id#16Zv_tAwp*sEttafQAjU2L_=x3sF$$OQL*^f2-z z^nS31(Z@gHOgCgFljPLUz>E-GnYIp*@Sf+wz^CHp&Zo?|Jgioz0v%C>^Q`R0DnOdC zK!AQxUOu*a;KcnlO;h-O()W?9#&WB7Jyi*BDn&*4aoEveb;;8*yJCkmYa97_0JE{F zslC<*rlXZycen~)E$ssUz+v%*^^mpb)nmEoUr~^K)kJyN@rvD@pf3m?eZB`TCYU^0 zHluCj)ZfgEX?C)kyCkS6UI}aAYJY|zGna8N1~qt{RoDcN0gl2X2Pf}SFL_j+IgN7Hs0Z_G`R}J%QuV)Zul~h z+uTcWEG{yyvFNPAHpPio6RlMnck?3SC!DJr_lhb6n`42tHbKFAe>(ffM*shoPOJjR z3fRQpFBb4-pYmFw83Q(fu4l)1e&@wxvH99^n(I|$#;q|5e7wtsMi%*oiG!r?$yoB7 z-x}>F%;SyjHXF7QFVB${;qK{}?Ms_-P;WWn*^;d~$@u7?Raw43cHPoxkx19yNyrIp z5S||fbMlFGdZ89!y%jbF#h{_Vk7`2%!p%5|hP0D^;4kWgd^NaQX^H&GX4z+P-JxTM z%$fB9si+*Ar)cSJ0>aHiocBLsp!$Rt#FO0@vPix`U$VDIMV&{6N!>Be7u@sNoqSxS ztmWWhkjt|%DKQXfq!dfIi78?K!0pWX4RyBW211vLVbVW{+wYw+M#n%BKM8kULw2_D z-KTx9cGyYkEDG{gtU$o;2#q!g`NbF@?i2_tW9{jE`|KMApc4O4OP&NMDQ1|HD*T|>EhwX5R5{oPCG|6f-t zafjFBhlGYfl<-E&V&`^l=3$V!m9#kz-!VDn!rHsXN=&1s9C1n~?1gh-wnBD`VEEg7 zt8{INi(I@6KtGzEYT#G0A&wj2bXe7&{s$PhLEeZu5=h}M)g}7B|2;5GQdUYbrCqq(S!XP0k}Zfnx5L{L4feO|)NB^6;3sCEj4yZ*Gdxl8qj^X;=w7 z4XKgeDKXfpRqc$T#kZLLCK-fGgAw(k>q?yY}tADfO2)kg4 zb_Ev>lJFSxx{Iy|nT#*Xj+i{ys()#Kd@`>5DY@I}ycV8`-G@Y}x3Xyyl8(>9(UfVq z(I6`*a{;0CJ1%6gdBYQR>>4|tvZYOt_Z}&~`2Q$-3#cgjt$i4V0frt*8ipYxq>&JW zp<5b}kS>vyk{ojAQt56Gltz$lq(M??0Tq-6$^VV#y#Mbz&*OQ&?^$b>i39?B9=&r7Ev>8+BE9{9zauio8=&`V>bWWu|2mNa z6oQR_E^9`n&}!P3>7j6W+;Im`pr2fTazY^auP!hdbzqSKgLAHP!S3^D6PiAeBBkRF zS1xwKr|DxFz7nN^`7n{}PUp!2vzTK2JDDabPeRhV>YgqIr8QSd~( zW$;V}x8U>Tj>FV5ciYL(#|wjsThGc%T>(R&gF#<^n?Kr6eN_$_6iCFmJ*<5JzUfL{ z^g1AzEL5Qou#=KmbaXPb~N&%zg^o z0p=8+-IwT!e6jnK*1I_m?*}FyH5e`R6O}-xVJNoD(v9?Eq>&) zCShAl;2DWBkI{dYeSUZShhlNA9kC+uY@PjWRR$td_}9` z-9hIM)`}|^$fOQT-pERWvvSCK6i4juBb{^03U)S7h{40IdO`x(=4Mk+M6PUc=sfhyKxjxiyc*<)j|MW6qv(6(` z$`;h&ugA*{`5y81+I;@kl}bJ~y&!ToL$_hfUS^3b*z@-kNzOVjBc$c3Rz^qi_x8L0 zN< zm87`}mF;WDEoBP}gEc~X=t;wJ=%K%Q`3%}3AtEY)tnoh{lc4S*+o7G$Fki01;|qR~ z_10|hk1A^3#oSSrQ70~O^v)>4i6|so%KrAyEJ!{AANQOidGjhPf!kaS)-x7h2b%lI zg48F4jF1Z9FbDfh-?@oP8n3~T%P(|Z+v+;~6~_F(XI=jAGm?;W88xMd4ml1^dBqta z)!%pvsv(7 zeg_Ov-D`c1)?a3(}BEHU?K zCpcDAurt)&`3i_(%ubD;it7CpAL0@8NGzX|syxLgN`Ow7ubgln>ZUor%bo^LaMr!y z;$H8i5$eEX?bve7DA&syHho;n3e9BtWD5garYy_`EG=qS1B`V3AyWTm>&st$A(Q!c z5UOLnOMO=^8V5DOGWC;r7R3D+-OLU$Kq!T)TJfPc@a0O8Xr#!Q4jEIC0I{s!_p#%g z{#WvMsB5HywL6!^@fJjJ1K#q`()B$5yq}X^*Y^(fOD;KMN&TPY8b$%~t8kh902f8;K%q zhE;RDKo6GofuwtjzlCqhl$Sk}&EW;l3MV*sGsl;)r-U(ve8uG`lnS&CUYDT6N~Y2l zYiRH5@POBC5T+!RvHHljZVYj5muJNJ3G?@+bBA4huTz12?f#jsN;*uV%1_*4;#=6nRq&X7k7Q(B-~z^vDEuUYafi;vB0#&o33@*YfwpKAU_3 z@i_@%N#xNR#sXZT-6e3q=cY*pri;5&CD`VgX&Oenb{+9;{ zeC3>tzzm5d`;<1t_MHrQn{YWGda7bo1U*uXJ%jF*EF2|lyh~ji(o)66EBsnH!W{20 zdjlsle5~F-=JqWiTlMRjlu-k1XZ3OZBqYgsl@)G)u51nz%ywmy2s*Q*jKKTdQjB6^ zZIFhsA2}EtpvMNgN4>xQ#ihTfO(WvN)v~QwHh!K{^;mb`Pjx}5{Zl(qnJ1=}=GSr@ zsDsPoO-J9K2dbjzwU_`ZV-@85{ag|6h~tS1J{cID|8a=ozaLv*cv&7A*Nm0zY|oOlEz4^Uol{Tk=p=V@26{kLEEiDLvIS935|m`D3lQ z%&sDyX9|*Ky*3Q;Z@aU$D^2W4kGKC;IPik=3{3qo2dQA}K54$E_G)Ni1+E@+A25 z7FXZ+JB#n)X3yt@3%byrMs`6_R(x>LBq<}d?GvD0PaJZo$btc)yf1+xpm<-x4Lvr5 z$W46_FAjZ6CbuICM^--meM6uS1avG8oLW8+ijM`c4&t8oGH}gkV(oB6j;WYs$%0)v zrswJ;O9k0^5<0)*t!Rp{5_Aeqn$rHj_?OF;yKDBkFhNKhblE_ehzOi!t==xexo!9D zVcr!m4Cg(NqxQSwGlv!j2*a@zm41I>JpX&tLJER_0^Qf+RZJ8GCk(m{+2qEfdgMNx z1eb5i@?eN3G)+o zUj=0v5EuxQCu1Gb@O4eLX@0i`Ap*b`*i-fjq=nY>@=EP-pe5o-q!?F_4^1EWT)a9QSCm?Wd%or_9CQgwv3(f(dkf|sV;~=`CGwvnxZ!dN;+%*PB z(d+r*Y_`6u4p%F|1d(JVOAR(}DsaF`xJUM5Hpy#^;T9%d`aaV+-U5uK!!Ep>ycI!M zm|5quCflA#z6?zKrOtdq7_k?Q%A}CC^m%4z8p8Dbzh!}T0Sum(_21q3Ul7^9-;N;y z&E;|5+uO)&-y>$qjth=YaG{CCqhhbeCy>2wcNGW^Q^M3^UVLkl)-Q~HoQ{hs6y?8% zS;bAZ(8dRs=ou3X>Ay3zr)L(aVUA7TToo%##{h%WGj$!fW%qTCUy}-^SCN@7*(YI#kjod59LQ=&tt*msLGBs9KO zz>P47p9-rs-|-ElOz*{DRUd1RP7qXxpDy_oqM1Un9aT@cf>V-7DVhy8MxBWA`}@+Q zEC;-N09~#M!v8Q9Z>mr*Q*xNOCEinWqeW9a4mKYR1r!+Gy@g`sc$n5G!Bn;MzfzIf z0v~;reA8f|n|4m|Mx>emX@ECRrn%?U)dBY*>D zjo;nXxnazuPO5-BDUrOkX4i{zpR)cif~#eM}zRKa1Gt6cCLe%7rJYJ5M3|deW%y@$M)o zRppyG4S8pVs6H=KR)J;%e`y^VV&Q06`HH1$k zi1DtY8wlw0@3I(WH^1XI+T8-sM>R{1Y<#uD^PR?jhNf$!(1OI%B+U(B8*dIb2xbt_ zs3`(Tm;9K!WE&2wTCh;~ED`+iG;AXVMZfeo<=cw|y@4#It?>5jqWB;RRL%}$hc^gN zED_W)XO8CfDlY+8$2;h0IH`co#qX=8Ru#Yrk#Q8u+AvK*XaRW=vfI;Z)7eHU*|o>E zRVq@>oTxQu`8{cmXUQ&G1_gFL4X}-GuT=+CwBnt1NE%jZxEMLO{Tc z3=PCfHt6Y8J|g>5T>Y<@3%SpP5P*an9ND*QBFICcgJIDn3fqq3N)CRf>!umyMl`B zK)KFKdqDcGs8wRJR;aO?!G-R(LNlBOHM1=-T>lullpEBdwWMZ=wU;v{82r-;u*9Xr zmv@oNpDO;P2sCyN{Y>Z2k{JItcK+dq99%GjvWUX8|XOh3H;@m7rq1-IHp%}z0FDWvr%8Q- zO&fl{j}5cNCkQgV#Z=PFZCPLzDs9r16=ATAL9dLmZJrZI3ugM-bwM1o5*7h_9mxR)>*HWd$N6Pub$@i?X-3%cg;|pdtO+5QqA23{m9SQTKH$ZQFcjh9V1ciYsu% zfpkMJQrfT|_gZ3@h?iymy^a`mFEow86w2N#O~}i~hg0BDX#)YEsui}h*l(gJlL>H( zBYTq*e?}nx?SX0dgJH}0znHKR{BkB1E}aR$wThped(g(7<~y%@rjP|GR^&WHu6~%k z2jOcF7HfDs^a4$GlHAw~Ood+mQNJIP*!>e}YIR(%nE{7Nu0zpI@__H@_N>PjwuB)jpE>x98$LBCiKq}}vC&lSj) zay#=)wdkT974E)S^W&k(IlWA{@sCq)x&?=MP2o)1LR(MhfUEo>4}f!5dzYOz|11z# z2BXRP0IAsN^z%{G8Pj=rOnE&|<9@|1T+DX=`=oWV*y8x1D2^Rg4P-shF*1c*dKxA_ z>6tq_l@^nQ!(25QN}WpxJ;e(03N!zJ>(57*vo#Y?t2Zni70ejh;Lh{xzJUd>*-0ra z1SW0Gz?bqvF&90y?|s&oWj|KnVh zcWLjSx2(lXs~*k9x-d6QaUCz>>tL$B^{gKEOtQYk`6JnRJ8vbp&*XTaNsgUV5j(D{ zhd`$8CcY!Lq3u?of3H*je{oOGq65i4F}dV=2zA$z@>QRSpzNp`RvJw+Vcm zvdxFquVVSwP6|^%BvhEOikL2N0|vO<5RrzW8tLQ1 z`{J*x@l?Gl!m}IDBmz`HRjg3T*=?J*5MF?Lyg{-#JWM#UtnqN$+E-D>Xx%b>2R*ig$Se2C3;)PVNX#V5 zMAfwR;*?dK?NBm)Ge-C`E!7cPb}KsuBpP;+4L`&!Qr2kc!9KT8iFBmqonTOF$h{U| zZErQ(eYCmRlFtftj4mpMowcY@3^ovhH^`5_&(24C8VVE<=P*XyoQzpF@+8ErC|Ah< zM}aqgiqyK>tI_fq-fBRWZp|9QuT1&u6-^g8AC+!FczI@KF1dj;MMQ9f4f>fnTwlJhF(8S}NS7gmrF~Ac?lziE% z29d>hTBe_J;)T&espb!rC@bNF4S95Lx#n@ zg@VA&PnCC1*I)0)h0xx)P@PD@B!!R~KfQs`-i~dt@qj6%q<9%wJO_`~k&JnU6r_K6 z>$k@Hi#IizMI+QQezDXNdbp6sEpmHuSBez1(-!sKZ)a$)p~Smn|K{@Jtw1AwblZ#h z;qMNCIqs_Pn7b~%7RHUc#s>}OSJvO2c!&NQ>-;y5`}fb6Igpn3Wv2~eN;3%(c4VU) zB?yD$dmTi$E!QD?f|VNjDoEj;l!41iEsx^IWE~3fJ_C>kQ;|X1xKA1o&P}+Jn6zBk zejJbl?=QrQTR)^wqdPWItnr=h2*k zs)CR>1&-*kPP|P6V67m(R4g9fpE={VHIoH(wau`;CIO<6dp{1wHV4iK77DP!aZ@>$bpgTi9vi1GZ1cQHgt@0HtmFOpIdO8@bhg@)Bf3 z>tN_P;e+h4TSvEIw=l@_c|JbuOIS72$2mTTCLqJbJ{Ou3zFa_|X`A%32~v*Ur*6@G z8(=FN>9l`l0*W9a%h_mo%hLg~AjOV7)ItO)sxk7gAlF;&QVZDsM#Y^s@GX(R(ST9W-2Z3+g!nb-pA_pizx>7p1p0Y!hB7$S z0P~$$wuO4fyU8YdN|QhXQ;nZkYYb3h`DeGPQ6Z>o*)#; z8~^VIL>6>XZ0+e*jSJqM?ibyf^&#VX<7&Ja$ee7t?BhkLIe=q ziYr#10lwYI3hC;Lw>o7SFuL7-h({4gt|s$Lxk^w0#v_Y^FnZA8{lyYB5}tQmkQ#}r z$>4ARLr)7aFCP;YB;1o?v*MK_RYYTzEfVJ=C;(vzwd&!sps$Fl0SSdxpP-Y`j(JJb zx7kM^RV>lKW0MGSmc*hb1{~gan~QmDg4Uh=vKH1m;-gTwjDu!v29RmM0YS$M{-DE( zN}+LJNH&zmcRZR#c%&%!j_--Fz`>8yqWW7lLjSa;f?S2QTSBAXGc15s90u<|E0Iu& zQkq^XMs9`WFf*1}qE$1Um*%~*_i*}Y-0$&APeiF{xD;}+C-yQSC6e!qm8_{!qoNV; ze#EYsB+spJ$NXNdmH?7-acyb>X`z+kLk~@#DSUM5D%+tvobD{2-CsaGvrjU4dP<(O zUzrD|``EB>R&cVD<$`3iiuV?l&oVNEJsoS+tIc7aS}5YS?lpM0_LTeI>m1_!K$S+3 zsR)?q^W$wh?#5ks&9kp02RlyJ;lF?-&sJsUN5V5kJ`TqxM)vTkECu^Wk{{ZQ5Hvz{$u>p%yH=6c^+>Q_QzO$(bBLYM^#d>jnChGSbv&kDKbgeXZG zKs%O6BKr|+Eb|16EG*)3TcSW zuJ_FGP^~Ps*Pfpx0*|VE)Y^5Rq*!{V>UF{`wY|fn}EOK2DAO40nxhkLy97 zyN#D!Fe+vB&2}IndVnEQ^{t{DO0xg;S{O-mnh<9IU5zv?lf)oFVk|TBvb4y9 zIorb}xKQqd22EH(y=(Q!)8Sgdh?0N#N1#RP0gdAe<}mEpCWmrBi_Iz@*I}-@XX@kh zjY=oggMaKt$*9m-gH3e~C<_>Xw)ANrC8uR1d3Mh#n8{56HrQ=D?Nx0Ds&a>P%|5og zs4+H1B{>X&^flI%Ryah-Yu{FkHKd@$&qfH%rvdw;WfaxApsFx(KiXxDF?nbEH6dd& zqFAxh_pBcIlCU26!djfM_kI6Lg|JF-r@^a@r=N~LzQ74lg6+|ju06C9PCn+&L>n~| zNuE%pqzbjkFihp{v5d*%614T`up$2X1S`z+)pK7b#c6Xm!=`n!+~w~id4e@iJ6z1_ z^L(B4dViWcIkkv$PU-71e*3+we{2m2Bso+Cgm8o+;&kX40WN*&swpkS*9m~IuQC}0 z%SP+v^#Vghk*zSH2rKg;xK;ec4MTwq-dqn9xbA1qNi zQtD7Sfi;Hn<=QSK(Kzc6(U-?;TebN!OkaCaK~fG->-b&DT1^f=)?#-R94{Hgn5v?p z3`bQ?Cn9uuL$*qub8z-Cf^)*#4q%~lekcD~yPs}Ys4HIMh|SI>OcCKu{fGFQUGR1T z9o>S61lIVneCHq3nfw5WrllO(`)U{J8_>C#q+~8cP}zN4^~p zd*;08Z!@?1*F?)+kmuyffSZ=lYvLnmfbG!ZSBjtr0fO~F8h0%&SDCh7{TUnnc8%yvrvXXzvrU~+sJ%Ip8>;&Zy%3H|+{E5CD9xMnX^n&Vwyeb-Hy z@Bja6juBv1Cz!v@+)xKO& z2#UdzhoaQf>Sl^(C0xW^^>SMCatDraB1d&IVDYmt67T3Q-H0Tz*~hC$$+cOk6$0VU zeK!D>l1OHv9jrPDUTx+mz6>d3G*lU67iAF9GJ1t~LrdC%fUYt4-F#*> znE&KB{scz$46zsb`N&VCDk_Pp8@u55ti7!yx($!y@sBwsQpNKHZIyq9HWA~{97H<2 zeSQwwSs6kZpPzgC^BaktbiqoU*e(n4?hM}Q7j9-W^2t)qOz^DAnRXQ1_0g0fJncsHISDvN^1CRyA9+Z=v0Z-4EVm zx@z&@=(`2g$#BLLYdslba7bSZcsapjN}vxNp9Bn@N)^No;(MnCQc6XnfJv5CGBH%% zgw`r>ku0BJXJktUk9#VtP=!I#QZlYaCRazE?GmJ!#(yHm!jP;grC-W@fj=RYWuOWO z>%P8eOPC&hlljj|JV+3brWX+ApS(HBO0DdujlqpR{BZ^u7ksS|f0IU}lkl5o50+|t z%J_uWg-Jrm_TyRJ4h+}E1Q$-sEG}r8cb>$mq>ZRoShTtR!nE+6tC;Xbk6#_TjG{hj z5&{ZNRE{79TIRAHSlL6u88uCB$_U7}pN@beROG?*8>yfP*Pj{5M{5b+p@S{{+ij`| z=GUPBVl)99LAQ=$s5LZ2o{34E^@6_t8DFZ@U#S5%fBf0J*JG!}gEs9XXZg@h#DHWg`4%H@OASK^0m zDK02(clkT@UO5GdTJLsv3{ih4d|d+aW1sCKO{1<=^Hspso0x~sI>nkgGUx{&p1hv6 z{c?DpHL$xO|GGot)ok{p+GW7;B8GD1n#94S=Lz$=;<{`+7$P4iib~bV=`JzFl6zBO zys+tgX_;O8v1;k;k%Z8UM6>-I?h8v%b`>(Y=j~iZk8MWn!O~9tKz~qJbn5L258ZM^k1fcQBpHcw-be zQ2@zH0_lU14pePES`WxNpEU#6Y>4k-b-F+Rc zz;|w&b1w#x91iT$xn6eitP!iaZ4A|w0;656!pCzR!KYvJmHuj<)QUOH#eJ`quRG^z zHfAgDH#?lyI|%W%7j8zW_grKrDRw(tU4+(q?ZL&KZM{8m{9b3F5O>Qjk^NNi3HMd@ ze4R})o%ey_c=jX`?u-j0G-ExP5a?)hWrr<|%WE_S_bMKs^8EbF)!KCfxFz$fxs_xq zef7D+rmc$c*rD?=sv>QMF20&0B_Jt}y9>-JOpvoD8&i%?lJd~kMNbHvHqY!MC^v|n=D2GlVd?@LFVR1bDb@7~dLs^duJu>s{W zCTJp>UgckRo{sIls~Y_HnoTTCigo3zSA6)KjhfS&r^CjWhdkk9_>Hv(A}p_t^sC7{ zZI%BRgA*8?PsjY%WJ>qIl`R|hnon0%K2a>Tv|N8W1?g%8r8~9Gakm`upHE6&1~A?! zJKm2XC&Oph+1h&Rxp>CiywXdoo zYm|y7j)$H6x`0XLM{V}qo95S}q6b4Y+YSET8yqf9_qz6WarLO=FDnw?Qn#%VlF^=*JbvElg*GpJy&z-d7>SpKUPZoeM)TMgMRauA7N{$2HDa=o29>;s@B}I>SpUy3G$BGw zn=aBbd;)g5njjSDPP(Dpdu2?wUKUE#d)6?S45n{kg&ewK_vT<6DjQy=hbwix-yf9T zg=ol*Dgu;yqiA3PLIhjZh+z{B0M-c_)ZC;#sS;M<^j4DGOwb9&RvVq$cz`+)K4J9p zTt!$u)Y1}4Y1JQnz>yRME$)Y*<6i=e8Ni+(uqBusO|JRR{vM_b2DXOT146CW)P?Xm zokxIZfK9+IHbc$Y?brp$>jOyx^--^8K(F4QEVeBAKkqu?fVgpgkaGvvqj2mQa3bun z&-_u{TFi3?P-GXH%l6wJ-5D&eq#lQ84PJ{T!=6~&hBNyg*JM|5y>al^oU5`3cbI+K z0f;4n8LK~gjs+0c%@3@!6} z^afX9Y)?_SIy~5%C$NqJgFi(~1|o3_@b;@HA2fMD+Nr?a+thjNy@o6UP<;qX9g#zqSrp@w&S(ywaMuAN=zwHt z%n=N?0NV-gmi@7Eh_Hv|G;VB@b8Z(>m{bcdi9fyB8sS4L=|3dowD?racXzR4j=4+9hd2^ z+Zf8?NdQ%V>xafiuSnd+R{rX0HH<-|tg6n$cqZOfpL1@6|F*`N z>iy1z#OSVx+}qm|l%@PKl9hi+ZXQl1&!b%>&qrRDcd}dD5;i@N~5e`}6 zwmZ*o8<7)C5@oqwdNiT*%vpuhdHmMyU700n6!IZL;~oAgB5WaWjDK*T0!+9D)LR9m z51qQL(@|43(PM$+yOZa;DRh*)8GJ<@1oEWB!?Mw{ogaS*K)p9kE8nfYwS`rxlMMk?qkXOUIvk2 zz$oWZ2)b3?{oQsG{2x5lOh#coKi`dx7V<^LAzPlEsXXTa1_qNnUZfW9yt2l-=)$^V)orDO+m-a4ni6 z0c>BG?&IFh{3Sx z;xVyM@@tFF9ebH7Ii<2wF^YJcjE{l#kRpsTJ)^0h%rQQCf^TCJ0ekN8aTCyRL^f8m zqAcc$^4gZLag?S6rEr-w*vuj?yU0vGPygkS)gkeciD6R(jge?QQ_nFi1AiZl5oKm8 z2iTU{;)ZSer2pG(o2tgRx+VZA^H+ji^kbC~V`T@~zyk@gcXS710qUcv2-y5MGtpqmM+rh z-+>7$A@;UeM`*dQiwN#s|dd@fR2XEHS_rMl&Vo}L z23D-Z`9$&+y{4$p{KYT4oq78=buT#|&h0dOHoHCxpdUV$wc_DvTg9~jnLSGiIKMi1 zI+}G~$Y=WL`Et0#)1N*|_v@`b+!6z(JjZY6mMHxVuja`whu*c{?oD^BPIqj=jKaFG zee2cm*+Wc~CaG$xoizA-U--s5gi_4R0=l}6KYd1>W+ksbND`!}oVKm~qA)T|7jh7! z)*e)%sA%fv3VG|WAas5aOk^N=<*7r0ti~C`4?^TT0HZc3^SCr<6+8TT^{R3PN0q|KRRUL?WIJr5+}izHK}I84jp zA_F0{wCGe)29-*>jDIo z9Gw6$4r5Cq`45V$zf@}Gn(FKwHe>0I0e#K9f>jEi$JS?W;v}AIX8L3aqyu{@?xl;k zGpXM&OGPSL=OHJ{=|g&h-wHyZFhcntB@^|CkD` zdz%>tMh}1`J9J{tD4&3sH%3e7ZvtCkB71}S-^M9(QgOy~0@i1VeP)^+8-TQD@p$z* z%iC@9k*9i`-#M`I`gZn(oNig)wSwgJ^5%9c000AiQ4lq5z0fXt;5M$9B@d4KDY5VT zXcL&CF7#UtX5g2n_1- z%+v`YBYX431FEd?*0y!uUM2i?4v#|LVG>J;%zOZA(e(<3C)g471|tc)p7yd}1;4GV z`L?Ue4aCr4HK6}!!%_0(-!?SStqZe%=a$d1USsV(&Vd3XAk(>NN=lC4wEum^*hZE^V$xj3d{f!o_+`4O&Ii! z2lO8uZ`bUp7$T<@nuh`REw)W3zp={-y_V<5tY^4B4L@EXx-R09%`CSA=c2YQ4n`YP zxLbxQBELUuvZC7mP-WhOn=TkpH4|Q}!(jyo)?DmnJ_lAg{wD=W4Cl4mXBKnlwfk{h z#}1^jB9w@xre+6I)Ak4HcR$z;@(61wG^Wv=wmDw~h2Dzmd2RDa#IV*Y!rfg{*Gb4_ zLe^$~sm15vDU*exKw5ve#3Av*JGWxLA8y%oNw?2?FQN&d$UGdxslvF#!w_p7C^h0< zGQu7w!L$G~ne8?KXEanw2K=z`fog|G`E6<@UNA2Qs$2kWB`7Y9HQ%Z5eak3Xkpyxl zz0zDK{wcLLoL{k;VspSUW-~`bL|*f)))6FLKvh^!*J{^P7OQZBWw}>D_Ig=AD<#zC z1|o*c*oPzm0D#XjhhpYpx*cFRd^Hr1-lK=87-hPiq-*_+H=x)h^yqn!Up3R=LUAJhfQUi2C z74T#56;oGM+rgMjF{RY;c1Lvt=0v5jlrex5ZQCxsd;=-GXs|!}wz*wI<5a!i*gU+r z|Egj3Mv5Kqyw!E~CPGfQd!lXR#OjnR%#xaSSeO(g$XouB45M(7gcohcCRYbPfk1Mw z18iJD-aj9jnoU7-0a@z$DFH$47kQt=Q ziV#qg9D<1j+fYm2vzBpFTCwWxiQdCugAb-79#g>=?6o#H0N-JLvDenl+B z-)HYh|HX&_^j=6rzHzU^ih)SY-2<0#G=Hgi6 zE5B@Y4MTC5h1JJP2K={wsFg6s?%yZdPzpZ!%x*RYgOi)KsSgPqv`-C6+TMin3g=#V!4^&6T5Jzt#=d+kNH6%{dpSI0|T(QsfyjXSR&9{##*hKWT*LyS9U5<@cSk23wFFkhZ zX5L}vBQKV*uO)__?e{Y+)$Oe{GH>m^7)0>pG5_t5vpE0ylDq(tO}-e9^S8ae`gtv+ zKZt=%Gz$nlZNohPLBWq6E1?wgX?N|hmU!F&9r3vla5VrFZoq~=uL1M$fqIwk#LQBh zVjM$NL+j4Y@tDjK2Zs-!h23%T$t}+B*0o6y@m(z?@Dr)_h!aN8w#?Y*4|1p}H(oa~ z;TAtA#PCa&HHr<7yL`^{Vm><0uU5BOc;*4wppb|Qrl&l+SvMiBnRauz5zO~eHdy-l z-g=C?&ONiBuSV36F!tvU%c9X9hT4!}QA@o@%SK~D@;-SWAd9|NE3n(o&mnbH1-6h)^Fss0q_fo9^Q_U~_;gq!y{(#1Tp#v9|a zfmA)ct-S2b?RM0DrQwp5LL6-s{p;N`36!hEk?^&T#&%Z{ouo^r(9>?=Ejd=)aNNSV zn&8W34;S`YO|2hzXI`sQg|NO8GCfS%*|sHsCJFy!(HotrUwRB`-YZJq!C*Me*_nUW zUVOPstLoxO3~&4A9(HXzSetI&GxoRB?j2G@Y2L$2Ad z)K@rf@x1yX~K5~3HweY$c;UEm0Z5!)WN>2%@dQ~dD^!^ggX>yI4Wbd7*La4m-u&Qj`d;s$aX?1XzydR8i% z7ad4hAJhh%6$iW(b=#O1TXZRpD%$HNmE=5s5v6XTwOeD8JA4pi=&fuwO@zt;Abc#e-E);hc!Z4tTM*DWM_jlb4#>5|%h zCb#*Hq$;;w9lbFl4td9&ws!2WuszdurF)h8oD1}k5rMofyP|}m`lVWR?_p$7zr=)e z@3*PeQrBEl$vR=^&ptbjluxIt+Pm|7oL&WQDqfB-;# zrLZEZ2Fvg%v!gG6c>h@zE-=v+^ez~{W?5SXu`4ug2J33Dciu?WdouyT<`NPl&i$t8 z5hohK2~r&C_k!^4kCLsS7w6JUxARjhPqm3-kSR9q_os22_5scf#es}1xuyE$&mtN~ zJ?6JQC)^A3%^$g2Pf2cY%O#-x*MLO&1M;yMpCfd&sMrhi?0|Kjyh=vaZ(axVR8TIA z?^PSS4ip|&dVaPxx;z51<`4Sv_bk3rd3@A$sw&Q==XV)Oz2CQZHr+6{x7O#dFukbh z_PGk9EsfFGrH7nt<_#7B{hjYX#wWU6Lq|tK81O4OK*H+|QJN%=spmS| zD?OdR{@F-Y*0(=i)_HWr2+so)YtS`g1OEAv4SeWQc+ok({Ih}V_HM*=c&6LpyZYxx zPsH@rujOcj9cp#>t&)wNr&jp?5M8M8j2j)fTyp1xEBEH0J#Kc~5)a0XuIjjUZ#yjH znP<2hzqSB6JF|D@T4=^;JY)CRTev`9Nc&kC@fBplTkl$b=7bG(&y$d01MG>!WLZEW zTnBZG{TQ#i$e5TnsPUe(#=MIGY-=X~)5=mOo|={4^cfkhvxP_lDbb8Hz!ITUf@%B} z_D0z#)HptG$~cQPoW^}T>ld7UspN=vrd=^wcp(Q<4YcNq+&Z6Gp_XTcMvQblo3}{+ zAGnSosU#SR6JktGTs9j(g=2}W7%t8vd0IxdHSDAdJB>A1qKvUguoSNS?Os z954KQYq<1_+s9%_BK^Kkz2U%-&-L;BW?=nXng6$ukq757OO2z60|oaF#~O^UN_7TX zFL#{ge_k$Lho1bX!)Ca6`e@V2GRd=T^S%e5QaIa7K;zJn7wU2Ki$b5a&GNcS@+z2a zw8BzMYa?CQsoL-Scs`LuWqVZe7sWAui0WjKx^QGlmj5^Q=8buKY{^0XH--%(z}6P( z^UI}c3?D!lb$hGIqJI1J7mq9N&1Scjw;R7Y*6RMYY;Rc6^k8EcNG(R&c>@YB8?KN0 z>dm^I%gTtG77vRL>t)n*vZ`m9$4Oo_?#xzMynPPH*@pu)DommC+DFGRPW~3o*S0hR zNTBPWYpLp_^`zxEJm6HJ{>RIQ(Rh@qX@WSt7d21m@lW13KL7aAs@7Nj`A-5_$BV&- ztxwx77Z~cU4-J8TjwG+23|$i|462;VnW}=ISDO7PosnaF@(&T;e#e<#x=qG*3u|!# z+b|A!P%obRDk@H)g*4`kk1xmB2j4>mSA z*@uZ7slYYL9S~^&#R@gCLNc|!dLi^1N;&>9YPbOmtVR(9_#m`324FHJfR_XX6c*1Y z3H*rFA5n@^-u&9S$5R0k$R=a9NIiYuY!eOZZA!S`CETe5)5i&GZ-yzxAQFF-MsE}d z96JaA2zuCBeboGuEy1+Fw$Y~x>X$K@*fre%sCo))OE~Tx6`mcXzb@r6sBL7mYB`!V zyy2IRotAut2)Fz8Q{L6OtjV7rz45oAFma8Dy4Cs8LPbq5=(XQ3tLF0`o?;k^tVvai zi;vHNo~I}2CZ7eZJ#MQX&)Ggj~kYr?0J1k{0Hg4d;I{K;*xR;nOF!XvT0crm%gGeVy^4 zt=8ySErd)7&G;qgidkICH}(ioB8m-{!o5qwWy({H%N0QRkU1RfEFy5c?v{{Nq)X{W zIwYjK!T*cX>&->hU-Pe8HyItxbK-=e~BUA4egr45MG?8WQ zHxIZXp=3!`tO%j}myr*GnQ55V_+}ER%ridv%H=&aB90TF$o#+>Y?Ea0$ z6XZb8hZ_n><9nRvgvBtLpv9aMV&oL^pPidOnc%qMP(k@ymeL~$Brn{kw#trf<#Iox zipE=%&jJ@dHhbhq(!0Cvvq-zOp6?SS;}C^6a*`eS>|v$Re`N!SEPx#y9cKXb|McoC@)aX64)ed`@tGBq1YME>+6l#BOSq!SgQ z+u-$-h`@_jeGa-1cm~AXWV5L+@P31D+jr=}-}C$u6!03idDM_ha3kD`E^$M+2~s0J zlXT2}<#h)|@M+5k)DdXw4j~(aD=JT>Y2OX?CL9noz3r%T$qc0R1AiI~H5z{jdMtY` zHZ#9mr|+hka;6Or$1W}5#KR%lHE419qmtEFje^%6%}L+!@$^(kR32ygqZ?uw=-tl9 z0fwl0baieDPM)#d?Vb?9$r@2)33BDzbDc$J&TCEPARi7u+p2dbb2==o!L}0X?1!}U zR?Xd$!VSy^Zsj`kmvD_muupGgJ(5?F$D!k_?34VHV*g-Ay%X+pdt>;$qyF-mr+clpY;_&OQsO-Nc`+Y}DYDz3B z(kOZ?rjO$HwQw|4!E4NB)mMr=3~_90$~5|fA4~9>B=SWBXbgom=G?T2aVjoWg!O*( zwp|M9@$N*0BUyxS0niB|ri`0ov+nRvV#SscVkTzPCw#fTA*lSWYLRmW)?S;UH{U+Z zSrsAvWRQs6VzjNG59%LK3k0W;To9#gnvlf1+s32wUhi8vaejL(mM^Dqo;!0(0&iR2 z{q?&3VJ{*zSo&Sz?gw9%3gyzQPd0}d^zO~;NckN5hIrScKOg#`?g8?~)q8Udb1R)8 zlOO(868|gTmnQ?dn@|$DeQ>6cpfB$TGA|XxGq?Fw-B8g-b<}{|30#R(TfLoog!7aFA-N$;2a*LUYHtYitVe3ZA%LY zTVaEZf;yVIa5b_M`|^@_dIVF+u|wj3cKx*d09IpCNOKPE9cOqkFdI1XXqD0z*8=|N zgU_%-)P_ll;X+d%bWk6${Z3*tSATz1z?v1IA8j>N;Q_(bj2Fp|fR39;H@tjLH%HKN zm`(G(MD)jt=rO~}|MF=E<$SPM0C8rAcT1iZbLc#aTVyMmA9QX$0&OBaO!h`c~Df6#BF8jK3mxfMw{Knp1q-OOAZ^Q(})&pI534Jl5@1 zgCwR&jG@nHUiPDVm;+iENYPXG8uTb1QzT~TDUK8$gv?p57#bY^x{#h#GZorLpKb`iSi-O_JGl7(|7P#9DdRb{=uv`6f)_YU!%zRr#L%;jSs+#|0n<>a))fW zw_xtUX~E}s8FQ5LUv670?IVi3a3SBiXP44;{DJ$jGsx{livD1ICR}1B`h7X;;G;*e zqVz3RF3K^WYs3h#3{(nYLEjezfNsnR(@rg}h*90e5P~ljHHw%X$_WvA048y8yhOGt zC><=^_+rqB9+9#r;qsWIMvg zvinJ=2`vB{zf2yAd}9vdfhvHWHOnz91S|(W_kLBGBJ{Te`=617|Ao3ghyPBUi_+#e z($G`xjsVK+w-YpU zO+JEL8Fo7pJLJan2{m!C`bp#nFpBSB_W`^(X&~JBS?C!aO4n!`9Kf!V(@Rt!o; z+`Ly3iy%KrJ|&u5+Ot*^92~NgwHb}U=cql%Jop!@L<%q5nBlWh+S4OBB>B2~bIMtN zeUw3(LK;=9Bnk(E8fdicPrr>;8yKueuRm9)*$tv;eZt?mqo zS!P@dksduf_i^E5qaq*zJ^fJho?eMdO}#Q+t4=ujJ}Wuuni)l+27Oa*zPF9EhY1#G zhD0S>m!^CbDbtoBGBQjE_=nf5JoJifGxM6;;8YO^LKwiHknJ^cwML8h*yM}e)`Szw zWC~BzcKf`Ffsn(3Pj1D7B*~STmiQU3T%2CM3RVk z2c@Cg;|XWhd%ww$0mwM1DK+1Ii)#Hpjp5&a0KtOS2)S0e8N9C2Wl-~VE^B>_Hd8f~ zL&)#>$V$wBR^kJ01B{{xOr%bc32%U+tHrGVj+{I+1FgKev<|!gD<^sy>RaqzM~Jv8 z2TW1Le3gX%sTX{N6l+3_V)KQYtPEzo+ex@og97s{XsQ-EE}hlSlAVIH zg$z>8V3zRGqQ{90us!o~S;*?2GnPN|{D1hZ$afICZwy0@ou)Dj0|P_}kaE%5rx z<~0qJmvD)dH=Ld zNk2WvARI;kFt=!df|nW~0#HL>Dz-sw=9v<%l4&ACkEY>j$fYh<* zRt=AV;>0+gzGXzanUAVqt{7kx&~!O55jh<2=A+QI zyfHo}MxJE4MydECBOEFkp6){Lh1{?{mj|5`<2eyV9l-P`=0Z>*~P*y zFMj(UA-{1(?_U?*Pxz#?{%r$)MhPal<%)hky;L%dNIk=Zzq*Y7ePY=`AZ=6Lb$D9Waa*Kc?cKc5i12P|z5MBT@TdG-@r*iv5Ot0wbkxvuD^xzAXq;!NOA@}p}Ly)%S|6DQU`97f^KPX60u=}^BAjI8S1d$qBcX+MNg2Ze0g#D zBqmysH{4(qQsW@_PB9c1Esv1lkfjT-*#V8=>q5Jr@}}ZI0xLeZx6v#TS1 z7Na5z4ZCeb5AZjI6p-gsA#`*&A#^!^Tcl}94o`ZNEYpGVEOefr!c$#xLpE7_O1OhY z{l6rsf83mG7?7$4Kv68W_)y~|s8kfNDg9@L4^Rp3 zR6|%w8L`JKWi+tY%}AGE0A-;+t*yK*s9n=_w-u;$sJ}`=77LE|8*ZdOKcp8p%U zsnTPo0RpF2>4+_yvz(v2l_b!PSm!cmp#)9^QS^?bQ`CRdGr(#jyI5m=P9>m)NAlSK zK4&GeM=Vt&^{s4C4$$WFW%E5Tn4t7c9}DM=%4#EQK`O)}WaRrg_XPckU~jtO%m_w& zq(ZezYx|b%7CW5H&`Be?!f(M^dK{poC#7JV_wheEq#{~a;P4}wdYiIB8}+xHoRjonRsR6MX(sHk zmw+dl!H)R3t!6caXHN_HO=))5Kr4jM-{e`o(#K(AS7kJT|Bf#KAKHveJzY1i9#u@E zA;Dr0)~TG9Oq6U#IhX+$j2Axv;@d*D(Q^Z#IHIn)ya$t!NCE_0N3EVDJXA6=VEIQ) zUPZf|R%7#}g%3|%BJt5?-mm-pC{eikFKTqVmb>oDvFXIZA8FLuQxn>BlDc&QqHm8| zPu+URbRzXM96~Q%21X@Nbm5HsU$^E1_zl(v-a7K|$#P#8M+AxdKWZEEKo6IeOQH<1 z0pAa-zHJfXcyLsGhw$4fT*$m9Dl7rRjT_Wcm9*c_2cLFIL0NrID^aCk+iH8$1izv9 z%y4~r3F??apydlbrG-eq{|wHdL^TRTzw;oz3z7h8w_xHe^o=9CGJ9jlz`BAQvl#wB z<};4GRJ3e#Q|r-7tg}R=D~W7RWQiUyyA!dc_wNam5g6!xhly^t{ClIra6#+c5y;&M zGqG9qUtKp}K5SkK*;#AB)+M*YtoAN4k0<^Uv08BJr8TSXmd@7pZdeRcFn~ab4Y#z} zaUm+SbI>~P`N}(uZ*ccW&A;~p!TIO=?dZ*u&nyRRo#i7YoxNtC1>o6kvpu(<-cCw? zZSMWSECpVDxv3vf7~x}uE7`O?in}Roc6eE3=$NloXPr}Me|xdqfZ7+vUeo49!botI zFs~Kj{iU?y-4F>fWm`TGy2MKmBlLev%pjlWoDE>i0}RNq=j2#qiK}n`*GZ7G^ENgR zj@^@$6-wP;WNA4Ce#zui>$cf6r{ z#_Vg^jSq*nCknJY&P`1w6#>wb5|lWkKhIm!P{C_yGW<#lo+n!R@z2ID2OlFm&k1V$ zNw5FZ$>yS7-por&Y&9#|+$cQCxSd*^pcz&Y2$qz!Z4>-M0{pXQpQ@(N}79qS|TagyX5O_RuUC=yUQ49sEU6{#D7ba zQr|xsgx*2m9#B43O2=iBg-Z$L&56p%w3;XdW-is0q2-jmxC82sTQxbYHua2PU5yO+DwCBLUFD_7o9T{PWhZ zD~#x8(^DrMADj8OCtnk5Lo|%C35HpfPqpJY{C`k>kZfX(Koi3;9}a9!?zn*v;N|1Z z(tKN1;Ra4V4h%R6Ma2@Qs^5Mx!iUFGUb`Qc+Y^`3oVYBJuoZXU^~>E&6E%Ij_n(<% zcg?|{>lTz*fOpw|%JHZ|R;~?p=IV6Ixk+jT5v1Jco}m94}0& zjLCXMiP7UKJ1s5y(P^w@1Pvc70$1(h1CN$}w97H0M`3k|_fAr9QDdvZZdM!ODG!;eLf+ibqjj&oC%jY|6a&P-WxagR#)iu4fgwHI201Uf^|B8DcpEX{+_UXWlior4 z?U&gly<^LvC#*(qy1;()=!^z{txr+kdY0^1{=Ie^g!u$P?lJH9^(KCe3{JgcvHd5oV3$?iqrv$ogQN3mlZ3HWKoj}B8;VR0KpHC`qFa<=?;{AZBTH1Taw|U zFC3|IIBvR>h%Ll}V*Vs^Iz=Q3!N|cLHPgA2rOtq-?T1fEBkIZVEz~?IE$t}HCLaeK z`;q~en190(0Zn6_&S7a-Rqs2>RFGAF0f0Ez9Ddy4wK<8u69%BB0X9SY%x55P6U_NN z!6)G>V!0hb6sV%-TJYMDOY~V`Gy4wUj99dVZ4Bo3gW6&{xM9@G--a;-;*@eRNh_Fu zYfb`ZlYYEWB37mmjSz15f;J5EO8C(hFAgkj2z*YM@jS7&TxMRwuYh(IkKO%jtbpX$ht8L~70P z-)HGfN#U1V>U&$xK}pY}VBc0zS0a=g@YFIjE$ECr${Xz`R_O_T<`T5d0XnMIb-XY7 zfBft&bXC9Rpy5gLE`KBf_VcAQ__QW|usddUG z>RWMq)FXu9JC;0hvF5DVIc<42)f^UGKOtnLxSjB`XF_l2n}g);QH;({60&%M_#8!k z2&`y=Yl>7e^}aY{$A%z&xp%tEP>U7m>5{`S+7ye(+eD1z=(!a^$H-33^mNW9unIaa zgV~>H2DoLAN~DJvR772qh`y)VS!#uQrW>dLKh1@X!<5ln4G+_d%0r%i-K~!j>UJEI z@qXB#Wj2zH@FeN?6;XaK!tg2d(Df|{dAxX>J5T{vB#FxQl#)wNnMHPU=a5^E02@yB zt<+faX+oCCvxKjSDw)mYr;4D|AfHwY2II3J6cj#J(kqLS5iU+ugcBL^gb?vw?D?Sn z)gAevM(m-Y**UL)W9?39_d`ijxy=%jiDXAcoINbxchKd)xrmsqS}(OJ-9_;u6k3mQYB6G zLKgDvE^|Zmz#B9+ps#a+m+bQ|8sA^wCfJh=G=Jq5vQx`l{zVbFMc}2)5Kx7+&Y+C3 z2aGTr2f}b8p_pEd&32MX|2Pxmy9#dh3Q-Wvwhxzz!+KLp@diX>s4$^Agf`anNNlzM zlEI9j8WD9`I+YW~m4_AJw@3)@?#$#rav=boHd%4+3&y+1?l5;W7i@yC`*_K?lxkC+4pU;)A^w#OA2Cs(M=|k+Z$-bTG~ps!;IT->I;? z41B>BZO3K;7PWL9#+)wUx@6Rn7x!L(q3*y|mZ!ouEcWZ+4dIh?9F#`7CKPYIecGAu znOTkI#9^7rv@{jFTfMPl=>6VX7Uj0yhWx@_xNDL@>PUjm+Th$MrScK4^pWf~XS0h`}P zSY^A4(x9&-m54>{(Zz={SeH)Q(8iuMzy7JTXKzUie?Re#P*k-OmQd}*R3`xiT3QaJOd z^<5Hx^9Y;n?`p(aG=!vy&NuoOi+i%HR^tnzr2_Nlz`tIM@2lvDv*Pd#j9sX(W`zbY`VjA+DK4_yd{E4Fq)WnP=E|=Ce_FNY~Y)CgjAO zOpDzfG(~a^bBVbKCyr$9P0+vBsGe7ceVmJ6t&nceA#tuGzj2xx`w2BCyf|qwD(7Cd zGb$Lp4qIoyfyx#-dn>3a{6USJh9hex>-LK!MbJI(c@h#CLwohE@C@~EfkzGn zmDGFA(l9`)?@wg*f4(t-#9+O7Bf=rXee7Q6^#T^dj%<|k6otqP@uv`yUr&wSv;vPm zXAM?BGfoI1`@Oks@Zt0aN?qfVpGJ{XtX16+@LSXaP>pBv-z{IlVtVS;uOD3x?QDtf zKok%=EG6L?S_Cym!Pr?SccYXNpeEdt$CUmOt{CT|G1okZw1v-drQc{>n6!=Z(|U0i zrC znQEJy$-);LhPX=9VSXKkuOqVZq_VJRFxeYu>PFtYy3!2&mg4+HQN4Tv z!Q~9w^NLR~U_&xM0jKYZed_w^Nh^7I>Q3Nv^gX*#xE%+9Y+mR}M{h!nP9Jy`q4hp> zb$e1;0pB;t5QU+k)xFwKy;cXq!P`ri*|nktV}5vNwc@oyR0uqsSs+`VTDbN>6CWF4 zO3%TjLtY;AM_R7O+!gyqL(UR;x5<*X2DdPhJ-3=HVg14M zy^A0w7I?T-+&#Fy=hF+68uIIj5!FV)pmPkHqC>A%QtO&3DUXxI@()X~b;`7jmB%Ob zzqFf%S9U_*Vq|5xUJLb~9NoWpX5B(E&@ie%pq|JouE+grcSkq+roynP+A^VP&2>gq zdUBhp`NhSZ&sE`3!4e(0Y{r<{rEcE1)SeFVxZ8wTzWvGk1?28x@Y1G<%~qWDj7 zB@s}#YIDRUs%(v&<0`At5h@@qHsuMa^cmb z4830>Rz92O(1*TgF#F>#tbUfF%7I^iQRrFh55UxLG65mmXDkg$p%!JzGkzg=Y|rz5 zg^$^22h5j(J8CiX8ORr}noU-};2h-e*|OjTN6`W4J7@NDDymue-}IzXNg!u8k1aP4 z%4&RSkd)zz*DL;O%YEg!+-o~I-av%N6)R`UzFn+)>FPOL+I3V+h7dzynE=wE))dfp z-4IDQ{Jf7piY6=3O|pJ@ap_$+_U!seikCg&z=ogm!+L~*ELP^a4_lYKH-MWjr8&x# zoUUW6iI!-;{f?-BuYup+=D@s8ydTn=PBakB=7G_m^4w5TIFXGd%jVvNLp7s^INAkc zLkE-XNc}C-x@SEWI#;Ge4j44RBESSev|S{3T*!Lp zM6&r_#T3aYe3|mRV)SHQM~wt7*M6&Y+=9C#4zrq?S?AA_|M67+{zjhG*G6E@NY8!M zG$pIzzV{(XVWjGVgxfb5s&}Wxin=j(oLHI?8mNYBm;R8k0#T^K5ZVpj3vPSB8vAHeM_8}$pX@|s6Ja`yS-`3T2PtUyUF=Aq*6HRZY^%H=Vk zh75%;<4zUOe%BQTm=7 zv*Tbhx<4>EFgkj+KFi_-qZ*A-BRM(jiBad~*)H+-)(+2rxBUlnUIHjdX>&%AFU!-& zwJg}kZ;q>Osg5f0nLO_!UC4gwb}rGy3Zk1w3knOk>984Tvz-~tU3Jx;?9KFfZTBk; zZz{Hogydocl;h&%X9~hL{|-%>8pA7uBZ?)m`=OoD6Nv_?$A1Z2N}+#~Pn3U?E;7kAs#J+Uhpav{v z_LwQ&oft8fT6KBT{Pw4KawnDsti%x>@JxWT$EKKRmNCdd{HL<{4YG-k_-XV#I^hj> z-T@`AE_V@n$zDntz4O2h=g62;|IuWMsD4$X;j6ta^;!;Ny3F|ZbYXjW?4cqx*z}S- zSQU8@03Xw7ws5965&nawvJtiKgw*D-VXgV}IO;up>WSFv#2j@B)uz=?vD+F$_pf9i znM}hvRxRCAMe_W(bBF>jy!hpBZ^zD`Nkp{|#uBt0%>dO-LmGXQQw405E1iX#iJI3~ zE28$gnSjw=(!9{E0bz$#v-Ukbviu92Z$WeKrHN=vnXZ3X5%ey6TZ&XanyWcmLyaUX z7k5b?JP=-SQ~oq)7aNKQj>7cVFKZ{@Zoxd!Cei9xL*41U$1`qQ>sTYzyZP}wp8fm^ zNW|T%?e6cZ;M$VHdjWa1miyy=d@mhK?@berC!D!i!Ts6?dxjN7w@sr-v6QD=O~nQZ z7VdgIhM9M~9AnkpQ7f5m*^Crx&Uo}15<=2@w(J(uUl$MoZ&XmMlV3!;il7-O_X^Y?Pm6g7It_I0c=?2bj%j3N|gzf?|jB&uJwL&nNuW4;|YNqS*lydqHocv%{5-O!WC`OuH+P2YGXX4HD{L=Iv7q|)MQ9xM90dnL&y{-d`g08S~B zqt|%f@}YDT-vEo`x%}nO1S5P0YXiNF(pKMT><-ZrXP09!r6tu!c26{x$wRd(vB8xC zL@cq!%8|=BZ%Zz{6HOwsgwsRU4$Cx+N87!KdJXoyA%@bBdlDmHdPN|La?TCas`Esx zvD~PdXz&s1)O;J?joY=`^e4S5H~jG>4cbJuFr$}^k;F};Es6VI_~0DW<%!8DHg@EU zcXLd3oU;@y@Vg^c3^-DkjJ=JB`#siseT}(kFNIbHqth;o_ty7(k>9U zP3|#7&hPR3a#3pf=5aK)-jCes%c|(0%I?D>1Ako?;f79WLA3_*+xQ+@`9E&frRJjd z0=0YpaV;7JS;zxS{&1WA_tztk=_6NrqmGvIu2HFDARRB(0QMUVi|cz>nzq(|qk{Ar znj%Xe=CCG|X(`PR0-WO^@bRl5@W;>1e6STIPH)Ent|!hkE^=zhG4!=xP2cvB_{NZ9 zk;)2$+aY$`8_PxrjX1DX?7c*1_tQ@oy0Rb?)h$k^jD%)1M)l|s2vPyG)a;6{x)ExS zF*=I;q>z@6+~k~o>5r@LQWDlx_KP?gnRYwx5C;W0Un-}jvxQGnQNEg3Naj*pIpUzW zu^}$>^<7Lps~kqWP`kder8aE$g5CPH)a*QyxZ`_bG-hTx9g-qCcB2n+r84fl7ms}I zX0)vXz;24*_}nz`=2I%EU~%4NxQ-EwYKTJ5WhvM)tPQX;`E=V#A!P0A?EPd3S3 zhjggTY#w3F^Ayw1rQ&a&E&;nS^BEgu=HVLsIk@os4x@s4sL`zh!hk!c~m zjN?pWiKj|xk_*Z3JFyYB)V6sn*;Mz`c$eW_;FW!U+dY3AFL!!TD=qQ!jevfo=)FUi zo|DKoLG<0AmFMijQ5s(J<~?iy9v+lWV2_rCj`aoddqUEQy1?(|Ko}W%??%X=m@qti!P8Qa zpLKJax}I{^9sS{%1J?m5(T8u*FG-@C6((xT)?2>vecbmxZ;28$O02x#s6-4(KP2h8 zm@Qdy#h>(g$dihnh?Wk*uKuxMF_8)MOvNLVY?d25%4-)da)FiJIbc?rz7Msbt>*){H}b9<)C{$vrbs2Y2$bu>tL(+T42-^*!_8(33{?NX z*m11wx$t6jXNi30dxO88RWo*bwG9%ASBhpGdXM33`GGq{1;Hq{(aqIPUQG5(!)q>d z3W2Nv@=VLC;g*Hu$7;G4el{(I8Zoh{&7nC&H#9p6=HFV$9tzE7K`kNomKK}frKg3J zHR&r&ZJWJ!uhy~jwQ5m&J(QM|D}oY}P$UfwPw+9V2vl9wVV*=}Mz6ovoL+=p(Pcj$ zbg)6%@RHuR?Q$(iy}-V*JR{bN$2cX36otj)D*P1rE2Zdnw~Z26Vi3%JcR@iPRJCwf z?C^?$vFnjE+}(~osNLD`l?v479gyf4!FAQYt_F{-rpHvn?WO5Gfq6bh#&X5(@Id*H z_Pl9wuYHEddPqcuvaKP-bt->1CSD$Q0cl)p={2Dd6uyC$K*FKmIujK>hh43*(uDFg zO6*O09AfAzoc`fHNgQi2&qB3SEhnJ^O5e6!_K|Qq^VjlvDI%qvQw`1+SE5~k+dnil znIkZ|`a~ii%8^?loW^X7Qp%L#Z)FWb#Pe|ox)Gfm736RQa%*Kf-07e34!IXG5A0>2 z+C>jkbHh7$m=p9+&-0Lb=hia*G<`S+!pPr`T!`H-z|8dXHt4w9GXha;T-1x$Y|??{ zSpoeds?waOOX9Fe?eseME(}j2XgF;N-#nO=)apf`4}n#bG8s8s(L^zwE>u{R8swcp zkKG}~B=6$wQejFLYiB^*y~kv-OU`71()wj4WjMVIHT0m<^|F3dXb{@+QLk^5 z%YcyWUI|Z_5OLsv!?w*j1Vcz^V1_LY>&H{!;TiD|k}2!Zf#it;^SR?c5E-jEv8M1p zn~eWvzKHN+hooY?nY9uBfw(5}MY7w{yq2MC48_x1Gwyc+~ViwgUN ztv;x1z>W6E{v>2YHCmPE`N}4OE)VXy5?Fds0TPy|@ILV_M2m>L?4Dy4GH5&n?`Mkg z-b~1ln)tlct?=2DTCPv#GqdF~-W8rzj0gpsrLbyqM0@@-y3b*93a=1{K=3uh=+N}{ zar{R=@wYe%Sc})IBlN{PLA|eu>4{^c+>Dl>YMz1(`?djJY>9FQ}JrCEA^MVl3)%Y)-P4q!;>b5$I zi>$w`%LoOz6;*OK4w6+HZ~k(5cyZcbxA$H*_U>>;uD%E^Co%ox1S6W1^T+%};$^mL zn$<`!WbWdgG%-;T+m{%NejOE^NYs1+{OxagWPmf8O%|DmfWBWswcEfowWXVGRcJJK z`lSh;gMQ2JQ!fbvLDd0`#Z*!42^26fgQaCY5w#~6)n5>!dYi1)Sr2b#wz#~l(E^z) zWUOsd9Zp9gFRbtcQOq=og_EgjxK1p0c|JvSsQXL@y6wrdBW2+F>#6Tczp^p4bZEV- zG%BSZK)9tE2wCA{eHF_=mm9^2MvpC(EQ?qFoX#ME#F?QMo|T^4koi?7;k(;lLQKf_ zpdP@E^I(oFm=#|M2Yt|A326?u^(|(1o!nodHOcs^(C(3Vk z5SVN5#+fC&82ST)XZiz!4`}5`HT(JV^XvyS0a!E2yXQGVL8QIo42TV5Hx*CCo$S>qP} zwfJu1NPU45q>U$W;_=g0AVdjgXslxdvzN&ges!Q;@|u8KHEwVd_WD+2IvLt~lqUTJ zM{<&=ygp0Nm!shupNM`spU;j7e^+ji=!9&ZjPFw0_Mqk_s>I^14d*jfY?(6ZDTYX) z7b^|ns%)nYmV;$dD+0qhky0oXRfO-gGm{^$zPJ79IN6MXAlJsuDmL22jV8m24y-|f zD6R84TIZ1??%8xo* z;P$dt*kD+&Vb;2J@7>MrrjQY!)>Cdhn4l$&JQC3+F`A-i1ilPG##I+WX>Xp*W!uZH znTqx~I9cJq5A(AnDO;chD6$Nq9?9wTTA`0Nf1Kd=?0r$|8GItvc>T+njYrC7=@rw0 zw$tbrL(bXoy=?a`(^|>0q}=!fCS0uiNq7Hkx_^MN&*&g^Ay6Il!B0;{DL)++r^Utk zao@tGE-VBxgN?B&X|&6A^nNf-k#hK}%rqT)c^Hz;@u@L|l{L-Yf1NbF!N=ZEf}|#! zbKf(vW+oYFeJ)fUlEl_0&!fNY;@Gl*Ts6GPHH%j8>d_0n=SCIh9(>^>#k~4R+Mmt~ zarF7X+sTl^HpAXtqy2W0uB%|m9XCL1*td!HJ3n>)w6^k`>Xxo=)0Wt&QS#>Yq_+kY z?D5NvU58^eLL7Z?3Cs`|0*gqtMc?KieLgh~eRM^2!xD+I+j_U7zyd_h(elm{pZWN^ zcq7~!<{PHG50iBtT!+z4a1T=CT6`Sx7Jf6Uvo(LuYz~KNjcpaA*?5%|j1xUfRc{0Q zxu-d=lo{ZVzcJFNUu}AJ1$=W*#Go7@0eNI$J`r<6@xT&ykizEKlVW`F9k5r4>k7Xsri#{D-8_n*ClJxORC2P?At){aDqlR7Y08%Ck) z__PwxBRj$9RhdzO*o-@ld&)vhBka!Z_-gAawGm;EbFRdK0_Zr*Q?OI=RNhSTJJ8coA@WI}^C?o9Q zVY^U7uCT4~;3{zoRGil`N~yF=1HX<+O!wihQ#3QugIHl;!>lq@dj(hw%MFGM1@o$E z9XmM{Iq;O8hsTN_awEau&X(A+6Hd6%dA2yLh1#`&5sMTc$HdY468D5|)cHAvmak;c zjtWM`i-+>914IevqdCn!dzhG+1+>u`Sp7|F(RdQ;|ETa7=^)$z^X^U^$AeH7t6@z& z_A;sG33;j*GO#wiCo-y*c>f1K^LKUlo4n=t79!)IVpd_fhX!9)ZXnB>1?7i zo-9DMcWZ}4i?>h%Pjcz1$h^;D3UeA~5cTNgmd!Y;F`ayzCK0`hkkn5?gn&)Q9^zf# z$Xvze2hI&~2~6@0Jq_S&G8DKV`yvplW_MgTjaaEflql#F!S=RATmR z(A!#iCYVFT0mE=H#5%F-PFgS$nlkVCL!;0vz*g3*E75F>|E5h`? z{7O4osK*q#YxK#a!jAu``)?A{gxfW939G;*pl)4llIc2IZ{?4t{U<(SF5#2wOq(j| zk^Ac~a53WQ;?_&>A2O$Z;o&h^#p~5h&l{Q`ON7Ji%i))>5X4XK#C64N@PLy zekueBkHzf6RQZkbO6Znv>#$>~EE>$8OGC}Twpvczo>Q?-4o;wPLh^{lTxDl?X2YIX zLQoB%9`tlrb+lIg;#-8Rn2CVj*T5bs*jR|1sCQW`G0QR$INPjzrKv2Hms#|ObY3V2 z6|)!sQ=BCQ|ys7nH~V3oEWxq$3& zin$fy*Tc+|JAw^{1&-;Q3?~%egmaN6wx(*fPB8fo!}=?$h&beGSUiqUJI{9l|JBu* z@blsEn0GSkm^%1Ik0QkceJc0g1LVKSpK;)Pie&B(Lo>AO?3oYWbQLiq$L(EgC-=>934W$$;N@V%vgebW282|$HKJY4)j!7K8%bLJU5;3-m zXZ>ljCPS6EH`!h2n!f#$OoNdvK())iOOq`{?F=P$Na4pTDo(F3xW&vrlXV>ya&xEAIi%56-#RO=7L9+|Gf33 zQu>codE9;^5xLuoPo1wB>-_nf2UID$1@sZEU-eB`_gYWz-!#qtC^M8JA5DDK2wI_M zpVZ`E3G-d$^(BfV!d;6Bj)@iBFEhnhF8v;n%vy!u=Ne*L1M$;5A_TDZA{1cUg4($@ z*utSXN~tU4U9`%H1?!J~yrqS(8?DiaTbqDcuXQK$)Fz6EC9?vjS$b8XCU-4J8p^nh^H?|(Cq!&)(Z>Z0>Nxk0W)1F>9adQt zCMgwTcgWQ>vYd!rn2O1-7`S{M^}O`Y&UA>{`JC)OFZd!Myh94*>!LQfF^FB8gX`s1 ztF@4125;6FSbgs(YlFxcvko~4MVihmSx(bS_QOjfl}v_S>?qQ7BW?2YyM}KU0}*ju z?#9H^gwJ2@>A21@AORMkg!}+XdGi5GR^+wbISrGHb!jLdHN*2OS;*?sARulTMJ{Yy z*$jVE`H#md(hvEgD9|kYu7dRsrV@hT3v{`pklH8n8evih;9_qT7ChT-;r4Ju_u5@`g65wb^7yUu$nqekU3j z+d%(~3}y*&JM~257UoJ2>>i;@dEb=C&6x;a5Y5#h|BC!)vTiM|@9Co!NhWr??m>0G ze!YMGs}K<G9o;Qh2Jj5B^~eTY(S7`dCrA}~1;cH0-5A3K3(xy3y` zKlbAW*EY#9z=P;DyS}Z5ni=butlA;Ydb6LmLlmv;3&}(9Z?F=4_XhF4d;W;EKd~$v zjwN+{SIN2aoPeeXqF%xbqE9Y3Dke9I#CbElUXCt=++#VtS!mRp*=xribe~kGo)X7T z$GdqJ?(|;iGd$bJCzauf*D8H}PVH((&0f40r|H4{NOO}RJ~~O zfz-1bYrCI0Dc?Oq+sGc01g3TRZXX*V%Lv~Qq5>_b=L3>Rr7y^PT!!6?qY#RR6W{eVn@8U802KR6%YtBsbaZud5AFlsgJMgGhXIqLswl^bW2 z4-V<-DMa>Md?dNYyqC0w!qUHQ@I@qjsqFyLDwmhg9b!N-WTo&Nv?^_aLeGdps_X#o zRbW@1m>G~Ai-m{XbS^9X6}hU~d*H{((cTw=D^UqqHtWyGgx_1o0TB=jdaftAw?854 zuNkRPc#>kn41ftkpPH ze;IwHRy~%CKQkrTMg&H!vj-y?t8#oiFXHk^gns7@de|0GbxuVg`KiX@O;x2G$tp>o z1shymVmO4Wr;b-$Z-}ZKwWs#<39HW`8OoVGmIET@7_1ZdP?w-5{aH^k-278J52AO& z^Ez*kzuWMtuJ7mYMica);>eDweo;(XZ=IQ;#ABHHioc;p0Wr3S z9?;7=kQA6&71(v(OJ!yL|44i5sH(cIeVFC|0*7umbVx`@cMFmt4Uz)VNH++AbR*q@ zbazR2cM3>%H-201=Y5{%zUBSn{e5Gc;n-)(*k`Y~W?XY#bFKACdim2sH;A0LJtaML z?KxSk=OXohd33VPJ+L~>$D5}L3!RV#^3IA|EDmc8t($yEs8GQIhE(U9ryAZwfP=oB z80v0&vp27jUz+3(^h8Yn>iXV1xd~LZN2=XY*(yoFXP-SFVON|A({{^ZDIN51tH9OZ zdvQdI*E9w!$_Bsg(@m%$IF4O_Ts~ht~Y9~7dq8E-HaED&}TZ8 zHSC3!!&O{e&;DwEL;T(TcG3g13}jgTS1j?lplp?f1TUF!I&>b@lj&NQIi!&`E0p|Y zg!%I6F)SQLa9?>njp-gqmMs<16G9;Jh0^3S9W~m6bDtKzzz@r7FCgJiP6lHeiuP=% za(ukv6dPso4+b09wO~C{zWRb~34=XX-lPx8a3S}3JzIy_f)(A4ZNTBL*p%3duz?OL zV8_-XLrz7UCu-|*ZW?=edMRlJ{et;@M{=yW+5*nBCCj^71Qu{ z*0NF*6$kTZII`b1y3nxE_`W7uALfO7d&Q{~?)>%Ik*?)miHl04 zF#Q`-;8Zk;qduA64)rg$`tN^RF-#dKrSk^ce6>xm(*rRwxoGA6Ob1cs%fT#m>khOX zF*dB?rM;ZwdO0@9=!t};2zL8&7|ip0wAv1<=G9#A1@8_sB)@z`7{Gk?fm`Wegl{gN z6FTr6&ql2+B85#3IgGr@@GhRHJ@QxzIANOJzlVn%RH7d=LYZXe>naIn`%ygX1@_eV zXUPl!teuV=arT6JgDL^g{i8=+} zK9hr4;fbXIFMHi5%dlhJGLh9vQfkE0>_~+&=5zUVn1WXD2Oj#}-qM7r0n2MWOMjk_ zB9}}qg@yfE3pkPRyUssq^o!d0?@fM$fqg^Ry`B`%we0&47^%y=-wqhzDq{0{(*DR%R` zjtnPrEh;7%!2qR(`)EC-9n=G2dlI1gh#!7T4imvp{=J)b%x$QST5-T`YrJ00O`V+X zWxCWOF28@!0VziB0pbV5V?2&ko_137&t51$QxlCqJh7iHe*K6r!M|RK4QiM2(O1gC z1ej?Re%DuzX=QrXUmJ#vVR-(_Pxnm-U_Ra&{h-!vA_x_&rwOOYsP5 z* z3>ng-WEWu@7UzlI++Ub>ho3D)e!_3z+nkf$9KRC4R)vL&4M|A_e38Rx9JM5DcT8%7hg=v)_-<3={r2P*7xjGx_1X z&;BLx!iFS&5{K+1`oS*GRgA--*Sn)C&cFQpfbtri#?O!frsq~GdEC76%?C?|f=4k9 z$uojGWjW0WLaCcxx@%5?7{7K+H&uo49FS{1+fG!?_$xY?y`p>nh&7nX0&B4=jVlVq z9(!qm-!JVCyfl?I-EXHQEQ|5Ea-#gVck7~`4BB^HD96In*qd?eA8J^Mxm*lO5i0+s zd7cVxzkFgk2`Gdu46WcHa(Y3Q>n-Ub+g_1oqmAM1otET~=GtYCg`_L(!jy$VrJ>k{ zaGc@ESXZ(e73Ula%sCaf8P|4cW+#05Z0|3NmP-;%c%9o`jb*il4BJ0)*@w)tbHiOjdgWqAKhw_bz!O`R3K(|hWD-AQ-|C2TjSN;hJYNQFdVaEuxTs8dY_dwD5 z^EWmWo00E+2uh9WLu2R`d{U~2zkfY~?*1k{56{EL?6Y(YOxdAya@Cv4vks^K*$V(p zT#wslZV!K*lOn05`^U?=sdaHQTW|B68G>nz1@3s*%Ta{rN# zrs3fvcGXlnizjbGEOt8RZ_UdomyB^aGrm(D+#&w-x)_4x56TQ(vu1!E1EoNu4@t~q zSOAzJM;`>48eYP{Q2eGF@lGr;$oGs!8bvurx@1gt3mOx0pX6j$G25`WxSz8`{nI72 z|6pi9{(66JZ)yF0xS@MzmMKlinbWwJ+9qCfd||jw1Dt&zl;xz*5AP!db|OeHMFe?6 z{qYf21$%?3p5|q~1Vcgp`Kf?|z_H3AUkgLQ*?ojUArg;}bjX+-!`DzYU>x11Cg9{B zN*L0ZPVBN#q@F*9$&9kBaqM3SNwo^;K?pD=g{+q85zWlRtLfF?F(Q0tn{5S68$%=2 zbQ+YVDz!Vg$w~5qY6YNIe0(|tz9Kyn|4=b`l?r_gpVOJ0hQ6S37g;?TXObcy68O1= zN3R(Je0GS?2eoKQe8eZJoctqh-Rp@Tq+I@9z0wjcTyF6cM1i3tOzJ#fSTXgqe1)H- z?^@A0=Xj25Xh+jcZ&*2L0O--%SBCfgO8?dE?w~JIc=1YJZeT$`&2!}F;uy!(E+w7+ zeUk4OFxo#qClF|m$jNsJsd$k;|GPC5RBOPMVO_0ey3*Er19H0+e7N4DCMW&%pb#WF zSa$i>2&o3s{WadPWo3?RC3gqC2<7=L_09RsEr|N_CVf@rdyBZy79vgT5t zyxS?X8wiuadyh?Ab+EUvXz@DfX-~hQj@R&ftLh=B??{(Mi(g{a$idU}h~LpQs;TVr zk{dJyMsCj{%?D3yUMx75lly|hT!E`uIw&PMn{`A~kO=m3i$}%`1;-W)K*#Jewjtb}+r6LU>QzazXMgPn;LIMJv4w;--@}85A!moEF|PD|KC}z6{wgE_gz~ zJrZ25!rCSl5nofnD^rJF(^yR6b`rK|0_W5hjoPPENB8b-Hl&F|;^;m54K_7mT;vNL z3K&dMyC2Wja08uiQ=_9w$mCtT9H$;QNbJDhE`5f^ePd}cI{Tx-oE#B}$CIe=-#>Gg zVs%wpACUBgDAWc_I@@u0UN8029Fm2Man_!EK=6Z9pV>Ttfk6H7iBN(8`x?bSh7cby zM{o}aT5XT!>|7X>nH!0C5nUiCfvv1TSk-$_Zku2gYi#EIWiy>!eq72>=mXpgelR>w z-1pHgkG|57+tr@hmkv_~cTYq4qy0y{oZa+~T$J(M<6iTOSuJX8rQcYOToxWNt_*yh zDe8)X<(!iU;}O%H_V=;;IwdZN5SaM{=j<{hWIQK<&@CTwKh=ujtL-niqAJ6z@a$mX zvOD@>@=6an5Db-#PT7(Z(Dym*{rVvrKs_x4jQnu_Q7Y+9=WAtFvqOJT*Zm$H}4v=UQ82kaHC{?baH zGP8x1lQ*lZwGRle-trhp2tFsRv!I<0hPNZ0cpicH!q(3461`-ffIyAX7X9$T3^dB;Zf^i7}G}&d(H>=^1Df#{uPP;5=go{KJED*X-KP+iM zK^5=P+FVv-{M_>uXr}cA*&aT_99FC}YY>~Hoe8X$2O0bOG}Q8Zp?GDA63&4=|FVj1 zeBOd%qv7@s7*CD$y38EAP-uBLBG#W+aq8|>$KNI)=?d5OU-&N8zfX*8z$e!1qCc%* zn^*_wB&w-1$!y#zlI^*Oj1Zaao*FLmLuVc9TTePVY_6z2&RiYUFB5 zo0DCZO;NRxN>TlE*lxy9raV)sB-Q%qwu+=6Mg-Ho9)&N<;8ZQUs84KkYh8KA9k&<0 zp%^Jm78k4gggyCKc5}~EkDuv9;^+#i)L7O;VTH4Na5QQ-E`_DhY*-xGiU=*rDPtzG zKWXZ{)c7{08NGjSgpcPUAlYhMLpd9vXJB!ZzsuMYISl8iY8%*+P+oPZrt={=m2AI? zg>MbVuz1{5u+%b`Zz;Iia)7eCdYI3F#pZJTJ|ad~*W`g})hQ?09sQY`z=XOz2iob) z6;=qhsVIhE^|~=LP1sKJReK zy=R+L9@2QQ9?%!gTUT2Zx#n-qM-x~Rnr)2LyKECCaJ67HJRIzV5aGxj=R59)4Ok#% zH`bp@=V|1JWhCDX*>YS=*7jtx>W7PbTw^lS4b|z5L3v)W%C#Jw=>_&zFZL}s4KNNd z?W!DL`|WgDt+o4Kn^gG}<+xc@u)hEKwd({Qhg{87SJC6_s3`fpj1v`RCfz&)p8Uq? zd4F>I^+Ma$@_>5f!e+Vk!2^G36V$9r;fBJ+D(yv6qsOZzRUS-V{HoE#u1`ut?CkQP zV$9>CZlCgBh4#KA4&Qcl>dE^YdAt8I#DDSJ0mFSzDY#6v-I%FUdrfX zd|iSdi1u@f@fwj9>+LkbzT$7ut8a}OEw|&}s^lQsk8#BgZcp{F%f4k*jGZri_Cv2< znB+ad+nfBZSCXhRDoG?3Ir=1T@Z9$MWXe7i>-4@(W3v!*bcG&$PCHoq!-z_|$3pO3 z;YpG3{zS4+AS1lr!O_weEUwW0>S7T8 zA+MPZ79u!APYL0~tb{Xbb&OYES0}60vG}po6QP(UM0slgw-L=^9P6SKrftSU??bmE zJT+m>${Q_*dmkg|HM>6qo+r=|YP z9c&W3Tf77r)DEl|R2#Wd1Ux>3|c)<1MS29sT)2Ea;-QG zNCHT|Y;M6N4b@v){h0Grh-F1Yz+UiHv_9Xgv<#da_BX`1T6>~eTLOkF_^(Y9Ohg(q zv|mV*77bx#an3};2RJz^jH8rD-x8=lk!J67Rg+|Bql<;@K1@J_Oh)3`Pm z51jPYEFdbf38x*NY+N$MppYlUEhfYGZMa(z*Fi(p2HUImuV}*hi77L?V7XcoFp3HB zlDSa2KaO8}?@l8+B~LrcoA{(lsIPVUN?=5#AVJl#P~Z#78Y4eAQ$Sl1Z-t&*FB!6u zB9gKU9kj$GU;NZOfF07i3aOc(?Z!P!azT83>5jW}c){m~9V$!{ic_K$8AGg2lcRnd z#@OBFV7>-bG*mdlnuw*reX!@T2-}rW-@V`5&5$Yz{vy6>*WYI8wyeRQzMQXhQT?w8 z4@YwjIWU_DthSz)1P&^x3SgI7El?|v|9~JQa($B1*lk0JiG^jh)L4g&QN}Da=O^>e zq@eW`5{Knj$jzC{XLt9`L+CggV7r{elW}ZbXGKP<_^|Km>k=hR7YMN!tM{Kj;}i0p zF%>AMdOg7BYu5$6P9FV6WZfuPR(2urbK#=-{$_L_VccwYsz_3z)|l82V&$SSLicBi z3hrV59pS`mwa5c&YHHdi#m`cDVs9;e(E8H1^9_AQ3Gu2WVutUOCXhVojpJ@Wh_Vn~ zH!n>@C5of&%qh~fiT!3Uk>_$|!$m3O#u)NMf`nCXn2y3ZM5%16h9krQ;yiLg>9)~E z#yl_9=*_(on=BS`#f|xau9&|#qzkOpbNR6r{RwIy$#Xiq+yu@&BLvAz@W#H?dz6~k z?hgc%HbVue5{+qC9<1z+b(S~j#qcGSL-&=HT0CAoKVhg(0K z16L&R^@l;{@CsgXSVRQNBTPfKP~oAI;se&hVoqt|X1>q*5y1x{KuqiD7mWZ8!hSXW zMa~r9_3cHR7i{7cnt--PZ~TFff*zQ3tdFRR9{vi1^+W!6aqc2q}Y4Q$Y6 ztc3l0X56}u#9=+^>w38(mg|og5f})ncVB0lEY?UO{NxV}bF!Ih-I9{b%qm?FSM*hC zIJNwzq-aF`>wIM!=~8ksYZmJrCO`T;B%`f)Yh_>X!#oPucZTwV^CPnic$iX!Ul#5` z-+Tk>G}Cf>(D2pml2k>qHm%9y7I%NKMvV})qr1C7?6kZZ759TuJgU`c{&5Q1l*5Eq zrwR*h-L6l}q=P@i7qyR@DovN7#_H)=MA7@BZUPi7w6>n{U^UI^XL^(hCHGj*VNDUA z*QrHq)!<#;@4cZ%Sg7_z?6)n?nM^#cjMjC|#{*|s*`^u)ba}Fc>Ia`>bbQQPLu1@& z{r2mVmGf&0ltBrEfNGuotuoH$3FjX$mfhL;;-PZQ2&1(A>0g!e@dqQNrBc#nwO9pX zK%y67Bz%M@`KSjRG!w~sW=UeOT9cm5xNS3>8u$YJCZg&^G1#e;(U{5?gEormIVSxu zK!@w`U9uf*m*l><_7FxX!KtR$6{+f>VD)KBT(6!~Bjq+c)ZJZ#0l3MDkd>4ijze#A zNJ(;yO2ANj@Wt$?8Z0?-OHE(;hPWWuH{WCnU;Ra<19R$TEzHo{w~RVC}%PnaiQHkW4w5ol<9TCJ7e>j9h z;bw2Bow=N+DcrWcG%tx2%A5{*p8O;ckO@E|%LH25;(%jx*2=-ziW#D3!tu=ZdsnOs z%C(^DtL%HZo9`JPrb0(ap1)i8e0O(ey51*55+rP&tB3b@CH!|e20{d?jRjLRs&F@5 zwe_D(=eL+nm9!{+OBXI%?QNfE`I*JxuoGj^8Y%Z${YeRjp;ptk@GP3rl@!nkKmG$Z zMQq~R6{=1chPcqhgt})Ba@F|C8aV~-b!O)MB5dF#A^RtG{^TiattRbO9;L(;M?M0q zrjkxK{EfqxZ<&u^4Ca|8-9$?z5UQ7#l}JF0U!#Sf71?m}DGof3 z#7s*!DR~O1Qyh$;G(K{1LZ=@aHGZ*J#gC{=`SO6r`K`1PK8lif#G3Gg!bO)){*YML zhX-Dp@f0L64obtf-*hp~x(@RXHOi{$He;kCFG5e9DK`sS{n_Ne;7j*JC!(emAv}E( z`En+mB|Hmnulr|9B9<%8CuO7f822kKWBs!35{#$9eQrL@;hgVd1?ej;b1u=T@4){3 zF2^xot2B+&+1GUnE}YX;bzLa*nc)6gkyO3!f@+jHO!Tkwv0oRdhB_Y3$xW9?DfuJg ztkcraB!(e>NMSdTpJ$3kwab59AR-Zj9e3?`xFjbcBBD~_>A~y1Rq9xrzvc~APZsjh zjmupfn9|CX*I{7XnQb!1(`j~}&6oQHIgoLlj(v^J6;BFR&za>rIzGnTR7ro8EB}rH z9LvSY88;J=_hOAkg{~5;6I}l#s`O8o_zcpjE2RFU+^CmA4lc7CP*Md(v}b8Buqepn z0bJBHG+(K5=LOr60!qSJ?m2hW2syTpkddF*yN30(W=4ixIz{bkG`Us<$xj2vuMw(b z8X?6uJAEmjNQQeP_X6O*i=Vd^lE{prLH7_zrTz9Q_8ndzE_0%k7=n;Qb8~Y{gq}vi zyckQ^TR*0dL}v$MhIzRet!_dhodUt~Q5GV2q;?Mlt_d}rzT+aca?DI0ej+(0j7Tg4XLNa5s(^r$&8^O%3myQwNFWJdul$Hjvo%y(eg`a$OMOq2QEO~FI zYozDhS(Mcmhp{BrARw+ZotgK#-^bpmuAyj-?=oD-bRXEC)>cb0@IbHnzZ_66N*z}@LRqtM~va%K@zl*KWZ+F`og$^G* zj_x*V|A?w;#{DN0{_Bo38FUS%uXy1bsmi5#O6cp8R|7@(W%-|;iT9&^W0AfPos}+y zbQG5{ZTexDTGj4siJ^g|DP2n~+jR_b%rD$q4H!9nH!YR5-!XH#3SH^!*fP&WTkI8G z4BijGBmA^-tBWD$BpNS|EO_=Xy#0DQ79>9WG6=$GfWV_`w^AWZz*jMi>%!!`?tr0= z-A1nAEEIuobV9eTKzNKGWr9mYN@Vc^*t(&jZ=#B%0iqWV6)y068~ za?ZXO5{63=?v^LkfOZxUDCBr2w8FC%GgTpDkz#fH?A1E{Rj+jdRcwB&3|R?y&I(*jqo~ zjQRKnlT;xusPnzqU}l|qDt|~ZoAEFu$zjVIS6Gb-*Vuww^XuK|0v0?`ZkH1(xl|7N z&>AczN=M;VDxZ-NMPw{e)o>CatCJ^2k7g$?T8sQ3$YsBBBGp1V|A6=}%sQmmu}@V& zVjR>digo-pPgN(^V`I+?Ldd|43JnF6XZj*x_KGZ+GdKQHpF0ywoOuRYc(P}9I! zKB7rWHD&z0vi$TRGafwg!n(}ocioc()225eW^>l~=Bg!MT7BWadfeWAb+1EC1XD3G zhVVM>(Wx2UZ|BlulaaCXFV{G+qzJtKxa4Y{_w`1FrsCZ00seIJm0TGiW3b%C;j)yE zkIzJN`+b}9Dt^%$vW%dCk+`N5{;?Xe>&j)97nz|C6#BN&g$lY-Kb7?4x8bPCM|4abNyE6OQ6C4h9Dvb zt`LqqhmUX?$0MroB9vXe-@j~tN&_F41|>4_iK~bv^Il1VnvLy)7zvizV{rJ|C2&D-5|U#rDt39mcW08%oJ&w$}dQOm?o zS(A|^p7*n;TB4(&eF1_$;grW}$BTOBV_$tjsgt0J$^6&A(RsUpO%aeki6U+qFg7+7 z?!ieWufy)hU=o+v-t?Q%V=Ve5w7a|e?Ot*PWOQ=WY`kuN6g+O`m)WXxZ+w6Qt|gOd zEaFn1rljgce^YZ;Xney+Xd~t9{IY}#d?C(b^BqEN$|H+KD-T|xAcraYW^ohYC3R(b zX8LZSHgbiheVGJEafj2(mfhE)rt#a`7Wm6^uA2NLAjET3mWbqmNpDy<(EsU0gdyfg zwK?D)Tcea`C;cqSqnVvQkQ3UMJ;DXA^i&Af0t+RguXAK6$G2Y5Bed)h_=Ogqrk7c6 zL`!^v=%sqIUC0+7!(g~6D5i>FcF0KXAwgK+afwUIO2B@QAB@mE%(WJlo zPl)Qlfh*Q-O8!EoG+o?V>kYN?X%Mq7Fjj{rna^nm)3Aw$%w>tUtv7Pk^))M><3TzQ zuRsbAlHTFz_&8>rmyviBuge$W=#+dBo;VLM$ddr4+Rhh)vx}a!nT}>s@6R`cZw%gx zrT8-ap{?GqFrPjdK#O2FXLM*(cutB#i1Enqq=fE{U_%Lcf@a!3vXg^?xk){LU~H2c zd~2-J_Uq5Eji%{XVvI*oz-mnQF33>I>S~LAf#X8x##+Y&M-AGh9I3T6Oyt(*_YZ_8 z+Vl5qBiepHEGXu%Gx5bF)q`2>^g-v2X7sN4HcvXTjl*_~%1s)K{s``J@%7e17WwRM zXIIX_@R{d50uc6TRhnE;=c;z!3^&tR*bIK0W_-|twHK5G;%^TIjBXo|f?ZYar>Irs zodB*7;yScE?7z*h9S-!d*2e@zVSH1Y+V|Zv;4WiWg6Q*|`D&A8t9;$8`jEK_w{XE1 z2WbNDB?%L=Uw|?2nDO0U%@%5r9CoKthLQ#4UggSVWM@-ljbkAG@zQ!?5MJIy14BdO zy=D(LOXW|is|MR!UQ+%UUOLD~NEy4+CAqy>6mwP9EKNe?P?Q^SyA)`+QTAXI#8R$^ zDs$BW1D!br3{cuxAUNkGSQLTU(e`UPe`=j{Bn5}n-gF}9M_g^HSCOXNuP0&ADkx)- zW1212<4l&QDMZz)Is3Rc&G2#TEw0qXExZ^#EM9Do9mSrHdBiBag>`AZtBOVhknYeg z%<5(U!_(A>raFdSdQ;mt_!^obLfe67lmMNoHM6kD`USdxE|;>nCg}_c$3OgZD=bp7 zphr-i89ReUIkrx{J-r;dOe{kJ4Kq8XbIu5JO2fz?5f@#$F!T;(h+Xr;j%*!WKz=}| zKN=}-9$Jc(`BWiwh}%t{#IGYo(g~jN2w)KWS&ygk>5ne~u@L+3jRl5>$D4&cSRYV| z6Yt$eqW(y#F%`LdZf*6N)XnS1(aF=2zWte!mhv0so=7?W&*?zk2^2ia?OkA3*w9FL z#j$9^aseQzbboXHwLqN%N|!Pr#N0=MWG}%V7R794zPn2+Rx( zgG~1}tF;!xZeaUKNuO>=I2jre5)w8J4)rBZ@w-@tO6#AV+eMP=D;)sRA!Me(z{Gs% z=}GTM0RYdj@fX6vQ1A!{eQX;1ogQq+&26ynoSkcbIPFZ(*W0bjBwE~5+pVtmPo?VK z-`!;&1&x3H`8;@9O>iUKI#|Gpp6B?n47&WIa39x0A^rTia*0lZj1K9RXei~>*-j2U{s-{@i(61DX%QUVd;eUc-I+Fg)UJN8l6GEoY z7{&DaSN;|-yv^Z;l6mC%W|l5qvaqn^tCvOHEg4s)d-okuJ6h?6XPO%vcSfW`kWta9 zZ}Qw3bV&-Bh=wa7`~+SX>m&(UL$s%aep*DrM8EO8P8doPQYg}@`T`*T{CB0#MZ6`z zmrh&h>vuM1_)jqc;~nWRmwp7Xu&Jg$q| z%)xszL5tx=eSKGhRQgmUd8rY0VO!^t6E9woC{efsihm?u}eww zNzg!r%-sg-ZH-3xft;^MOS4A~ErtVT_+?drn)!O!hyj&tJ-~<5{1pZC^maSrD@LV zBe89CzM+Bj_AZKA#uKqHvat*j3Ig{}I+yCi*1EmKrZbD{?08Rn|Dbi$L3h`iY%*Y7 zL?C{t5DxEtJKH`5`G5R42E8ID0?+AeJ-4TuuaxyMC~7mD_<7Wx2L&Z1y=36!YhE|B z(SWy^jUy3Yau^NDPRLE=*N=NP(>J8l2Yd<~g@JhFlUtIJ6;+CCr18sD@0(5b`e zAkFK#lNs?Mgw?R?B@<9tbX2zZhJk|1Tp2Wt#>lrfx!UY@IGKsZsGYrEu^F1?dFNB_ zwELyXAIqQlh3n;K9uMAGtoS_9B_hG(#OIdzF4QZ|xP(R8^tihfa8@OjfRg(VpAvhC zIp3>SI2Kvrwy%wnxkKj8LL7_xR8&BkOL&)EF=U=29gqRb{weHX`s88Nh)lftQd7VA zZ1kHu3=E7Ls=W_?NXlEd8hkVur>UK1be=fbk=E+#V^4b#W{1Wc265rLXj^Y&4DBGabusFz{QmCO>J_I`}EW;-HNX*lJ*4!Y z=b=xK3?Y_RQc_}|*U{p%KZNirW;?@bL>IG#$gg%Hz8&~1uON7hEx`JN{$}-5p%j#j zL6^jRQJB%T(jGuhPyd31&nHEP_&JqwUe;}Hub;>yH(xa42& z-&9~}x`HXvsQ8GswG&SpD1`G=1yX!^6iHdB-;TgqeK1m2v1xPyv!ax(Ra0!&VmV*U zv2%|;VP-eqtfRH>e0{Qxjfa;@GC%LKKdBZd@X?0bVTVI5;Rb7ewn=J1?Dh7nN$Que z9NAZ=8<$EyJw41ehUwc90|U<|$|X=-&@Rm{l6maXesWYFtBh5?z`D6TY%z9)K2DMz zOr)n^e6TcL`yw~zG6Vexd$vdM-BA=gdA#-6v?};dEcjd7elWnWiXO3E?3U!cEt*rQ zvomH;$paS!b4#0duHqZ_#SBI@1CcQy2%G+;EG|pt)3#P=f3X0x(F@D@JdyT~V_H2^ zMOv?;+AT}fsX4{nxfdMN2(DRqR~G8tgMIORM2P<$M}EZ^^=b>G6d|v;dZ$D3Ml6m< zz%Ob8B>~Q{yX(_H0sK6$%L9LBM+ev~xRyzf0QGWlI@$MwnCWbJj`EYJ(UY^y!^H+E z5N4cgWWOOS;^>cFtllZ2Pl7@hpMvn7e$k)t`tDtAZN0hW^|M!kQ)8svXV`?iM{Y&V z@I0|<;`jlLEfpU3m1iQu<+Xy_%2ySCf6?R&(OuOd4s-$_1IhTN@FWsN)`r&&|Nd~x zi^FUc&S01Wptu-NF+|DczwhtMq2Mun;aYN!c2;`gF;l8fsZ(bci;RbsrA#fWe7-Yj z)-tpvck8%cx#!A4t(iTD=2q@1H-`;*qR&RVGw1X_G6-B4X7) zREm~nr4osx?G9X)D;}e|@Et2Mz#39N06V~=WKp_#yw4&2@n>;Xn)>LlJ) zcZ986uT4YmFpbMo9^{+Ewy2LCbhiODH{irpdE%1Doo5Oj^2%Bc(}P(14W0AVI)HGwDQRjq{=g| zkWrLp%zFq{&a=&;yLMD&hnUg%g`Z0ZeejF$>wGTfcL~O!n3BrVd90lhqJ<<3iYlsA zBKW5j{Dpxc&R{SZ8oe!W3f|3qWO!ZBm)G9*3h_^nB)migCL!6cTm$f={3+yv31+Q5 z{baPsdS7g^qc1!}2CglEWt-opSf?h;3UmzP)((UuK_H?ktEgcAN)`A_E>i`Ru`?L* z)oP08axmJLvrJN5HThKeJUu<_;ss2AB8V(`kj&b?1NlpRCI6;Ym{=8ZX)VIrK`u7) zhU)#wSGY{NjGgkDekm#S^%rQa?vU;I5_y0+$eyFVxcVjvM#I3^{p$78)2`*kS{H{@ zW!;PDFDF+*D`v(U18JZ9A&kj&o2hQ5fPIW*9TeS?o_meUuVe)H zYwYFZnnlx8nvK)s>lDPjG#NrHiwNk#v}9PyLNOmrc$WRIu^`lp)^tQ zMj_aM1vlP-RD@gK2kcr3_Jc)XQU_)7TT@X}kFHY78YSMGE|(cjqoj!woSiik#PZ(kqh__9*XAE-l) z6fS$d3ivorDHZq<V32CH|ZKx=ub{42!#%ac7(nw(W{pl zzbercaNXF>tun>+nE6X3nI{q`=7wn#vU%|s)f z#+QujJU6!p9*8ltGS3HRGf`7@n6EQyn+_3y(vC_@OOWGBv>m@T)mSfoDf)RQcFAcj znIjXIt=r_Hg}~1r@|r;{BaDPEKi_om24qS0hzda>7U32oL719( z(vFuT=a2Saag&1`_RFJ*rH4R1Kz4xT1^#a}`_Hv7#&>v%uJANrvQPr1tR0lHZdx)A zaWb!)uNwp%^YckNQ$=D}v@an5>Pk3_I)XddE!4=zx9neM!d~J^b+rS*!@-T*W=Wcv zq2T7=Xm9!Bz}{Tu;yhKuL;=V_%}S6QzEEnhZj(&6e@%pf&@H1*ux2vALI0v80iNfJ z8zE=*_jElC_>%B4n^A+;DdS8iYAU8I{K4**_vjtQ1+9h6*Ei-2>ZOpBaDD~D76K_e zW^KjZj#L44P}|BV^!>Wye6^+h<`79;%fn&IdYl9g`sFs|$E6Yz_~s^q>$bDPm42L;Skz3XK{AyF+}KVasrwLwv)n)k^1@mr8~xyh~sBt1sEIaG5)XB@jlqzX%Jg)+|5rE=}zv)foR}SMoh06Dv^2lgN8Qr8L!a!Ob)ww_0Fb%|-NQ~X- z@sXP4sO<$b99pN2G?<3^%l@W41_z}ESIUxabP5cxMY`_F|>GF8Nl951bwPyM5w zXdz74Ksj^13JDeFG08yrG>Sr)nZrJ!5lM=ry2}LdM@9L~qw53)1!a}Rc%G=o3T%Q=v zmFj23Q3?vC#xZGqAxT<%fy=D4gh{GIBbWM8tysH?cfV^esS*HHJ2ThN+teKlqjumj z!PnyNSK8ot8Tm`L+HcOicG2nEe73q6DAE4o75>x75e#zn$7Qy-m-R^ra=$yBeIdf} zh}i(hC_7x(K)Y}otI5N?w;xeg<|V9{Iu|Yq3yYqf^_!VsEIF+uEx-i&O$S9` z6BCO^M&boZV9#Q}z`*=+%*fcJT)?^D@{E|ao8Kutg$(3@c$D*gbLhoN5MK?R<)8ds z&;0KewCG=*r?8LV2q>6Ynbue`4hUTDl_-eDXx;{Wrw{v0WA$NLsQo*f<~sG0zk_TN(df4x=(CZnv) z2loFwuK)Op|8fF0Z-8Y)wTD$A{g3F{|HHhr1_6_y%5C%iI=X*|=m0>Y;L9dX)Bh#e z`M=JQI>u2IuU!@_PkJUTB6;li2E5Lk&Ce|fX+Zi@?x z{!JqFsK$IDRA}jH3@M4fjp{oooH|%}Zw~WBuJ(WwRrINF>mX7`=OWOXJ)|t^-l$c@ z!NVFEg^BgDhQ7gq_9YjH;L$k3pZI&FUf2yBHr+F2GJWf`P^NO;i9(HFQa1|-lU98U zF0-~UP@`2UNp0HdTkp3QJaJc53qN`EY2^t}L|UIwb%E`Ry%w0RCOk+ez)lTxrs#i@ znGviHu&EgKE}6VY&4rPeWgZM1f0}oADG#XUjkncOpk2uj740iKyQpnW(oKc~fvFseBJpGorX`Ku-J@eW!f zhq0{*_PzMqlnQfV2A)5anT91YG7QZ_hT~5Hn%SZJ{XV)l(vGA)<$p1_1s`DpbsZ;Dgt>p1Rm2B4gA=ukh^>8|85k8* zm{f^$J-uf-;q^Njp_1m0N*$Pbp!rcM;;CAR;y{rWVJ1u)lf^iV-1c$`UlsS>--h>Z z?e%T|2cEJVFw2SX{X-q?0-@aCe_@hm3a!gRY1acie^zh*dTBlBUrfRc?#EHLo>KX&KNI^!-KA*RU60{&P ze}af#e%yy&x^>?V0#t@`CFK%F{E)DInVZW|RC@oTf=dYb(FckNaiOCkCW=c7J1Ugp0{rDIyG}<9;MQq*Fp7CgIfK?2g-J z3yTrfA0aOQl@v~e{L6i&{^dT!TqMKrUjQ9C_;JT$vMh>rr)2EmiF5?s1|Je)RvmT+Hqv`K0;Zy;M0g2s^P$WO!4hmG&lQdu_osZ5j46b<6x)C&dNg zL9>U42LOS;1PC3VPxkIUv0JP&2xq&^AI1LR&P7bl+jVoarTx?e=%YouB_(8N4iB>Z zb{IsAjK|CvzJIK|-{i21d)!Up`)j57MSUz-eEHd9+!z$-hbIiM&Sm95G-AQ4YUGg~ zP;d{R{YoI^JS#7TB`Y|c*%Kxm&7c0{XPEm^O46aE_FKD}W@9MNos*HdT!Y+EzA$&V zY_WACNG%AzL|7z-+{$;yAFJb;$_1K|9V?I_b0w6Yd2-#Nx_Gc5Am<=&MPQ} zu|y-H!uwFQ#g{}I?JQ)Z=+Ts}op<-+)A|IORTz&}o{sSm1WAaBiEgD8X>Ffvk57Ej z;k)0iv-8^BkgL2K#yMLG#HcIxIvDvHQ16sGEO;*-l6iWwjW>>~pGx=1tjzf6#SX3$@rqCPHO?Q}3tidW{ng&o?{O!&qqA>ixiR z(sY?;JO0*wJ;(!E50?{K;)8HK=tqZphV_<^7U%ewalctdPvnhh!F}Q9H$WSQ1cv$a z$cTQs>BSz8>iG)95Nl2)w{-@O2 zQR-0NlWf_I=`O~|`_yV- zaNa$OIM_MIO3}LEUF0t9QE{Q>^Au+Umuqza!|ZNRbJR> z)o%anVWCPMVs>6q1PFr6;y1ZmORmw(;<}~0c!-b)5;(~)*R;M^mdz*-72ljc@4v%A zA<`WazfTA$`^Tw59g`IAENwBpnTB?HVP{XJmGf!c9B-iEr5#VwtFn{kNQ3!HxgT1^}F%A=8dxz#KTw9R*OX)G)$TtxkD+uqh|!{3zBfFg+gIt zbSmiVjPqFsi(Vyd z-7M9K=COW|alHF%l{PDR%r(r%$ihP};(P_l#N1!btw4EW|2ZDZI^{Yb)A6Tdh68B^ z(e1|Q{waMy>&8>%Pi=%~U$(Lewl*Sh(E1aEynNd2seFZ1F=nbg_>K{w|EJ{K+Jv;w z?2+V|$UE3X^l8dCu=VDAiuC{E?5m^V>a#6B2m}Jbg1b8ehX933p+SN}(BKxlaCdii z4Nh<;xVuAecXyeao_W1qPk%jYz4@!C1x3NF-#xO=-un<|K=hemDXAt0+h3%v_bVcKq^H*|&SS^X0T&5Z-LVl?Dhgf&leFNxfwF~7U&dcjBJW5c6Xb3EP+ua4daSueasUBSO zA^dOgzW+-2|5gQfgGARlbxvcgb#3ToU6L-fU7e+^2^cyWD$SHqo+QufX)Y?zfZ723 zi=(MfvOZiQq*W4o&Rs?*o>x)A0a%dw6ne;?-<>3_q!k1f6(j4FhL_~e+VcA}L4~$U z@pRX9d$m|np|(w@m z^@evOpSXbAE1{(tv5-Khve8nFm8EjROV;P(MRtcl42^axLIB#10OsKZa*hpS&V|oc z=+A^`LaVYF3}HnN(e+LmY+0WPc_*0-$cUF3x`r-HV`u8s1hp2vPdI8g#Ftlzr{+8LPTnTY>JxUriV<*C+`p}*+y#>fGJaf zrlUzzVFVrNZTiJCW;1c=^4>U8F`3mcEyL_ay9}Ug4C1J_g&Doxq^i|E!qO?^_)T40 zY5SAq6&T>%f@ELVpi)2xS%N8y3xDEu_1fop5Jj9g>y(lQ@^S{>s( zT*XHIP##LqXj+`mF*ddn0R)lz)p7#S+vg2zcqqkMxn&FHCXUEeWxf^He=zLJi`kLX zt(M6OYn>$v(}?@F&HJsk$CKQ`47ZpBMs?w30Zd57`Z+VWbh6EA#0*csiU2qnl9XS7 zN#^#wIH+Mv%kfyFw&rPca5L-xG|KzXg|gM)c^yB!iyBFqk?LIfN09WeYB#2!rk11< zxHvI}d9$M)uiCDBsaIR99DIA9@#r{7(J0l#PkdU^vTXDWD!Tx9HqmWQPj&+*kEd<< zeQ?MW9$3ZW&8kcTMMfidbW&FL$vtDO7u$o;nVFeeVQsHB<^>Dzhnbn*2KEB6cHq+jaLGwu8 zYGYi1YUj@fkNxB-$HhAB8DYD-@8E8fbgy21nGRnVtUq_mL}8vHV$!Q%oUWaYXAUI{ z9`@N~*Vi}RBB(B9#@?$2-BMxEO;@OnnX+At;dpT2KVGW_tSSsIUuq@4@co*2B-v$D zDvEM5I={tl+r8(FIBw?KovNSMYaDdeLR$5BEJIyMOT6T)Ur>i^Zzs|Mpn8VegDF50 z=#F1J0mVvQ4-$T~JEQy|3T$zKlxMQG0lyfWJ zMk)lEIK7-b*zccKq`8o++s@CID-BG$%oo|MKRE+f&=-wX7mkq+0kgNub||uFC5?Ev zpAMEfdn-?Y@j}aoqqxk1eoXU)`RhZb_Gx43YR|I{p<=rCwx^b}GSUXVU5=JZQf~}e z^>FYhxqvB3#LI2w@KC^wg<8LULMm78{;rNkzNigj+oK~Z5-?t&mpfJ}l^4Rh7YmIK z^+XRC^A|>(*v<5y3pW6$yi0|dE{Z#bzD3sUj}f0Uc*OoN`sI+FI}b3h0^QggOWCzD zI1agZY&IyWyidE&H_I+q9_u z*<{1JGL6wLD@0i` ze+fM1b{jSbKzdp5I=v}dx_UfX?U_f857-sSkZ4WdFq@9*y+JzQ$^w)Vdx<(8(HrkL zKLAtVauxR5T%+)m$*ASIu|zBc@@YVTvp=J%UH5^(lzRTC@@%JhAod%+ML&ZVhO5RCN;)#lo$1NxU zz6x#C_ltiJc+C4Ug)p*M>v`4$^qmJ^`{FqZTkHJ!*{`7;g@{KKsN483F z8(Qh2^+yW7@9zBTCdU3^irb6Bo~EiK@k$&ANrM2zUQ}uX{e~u<}R#i%K6Is{WV@&!g zCbA6jg^qZna&7{QlKRW%iDoAz=hD}yW>q;ltp?`f>eA0v1t5L_(IA%wSq4!c+Z2~N z?T)7ZG|3p6muP`hZCnjiRBs*ZM)LCru2erXjf_@5*iG|~1p-9A7$JSn`ygv?%T_bv?aAH>UBiuo#1M(HarpE@@qyK;I$e+a%nv+LivYG|Er z^lY=~?Y1{}KS9zqHaGXLlG&zJ;a$}*a!`OqI-#`A;u%bQe8FEInCVc_e$&K`ymJ$f zh@3b^XY>QbsE-ZtCsoicUgTl*MkBjU7(NzSRpsd1Todubh2`zYFcWSg-0)3be52Ng z0}xcnIrUfTt=l1^d=i(tBk{L>2f2g0N4O&n)mdgSkXdKM==0%ZfvNk;U1gTf^`OtG zh*j8#Lbpf1rI+eYQOPntvzvCp;JuD;bwi71ypAEDAT!8p@5Uk+EfhCWP_UQHi^Ut( zY`2|X+7HGN{{T4h>`p5nK-80i+R0CTl37vqexJ~W`I2GN7@$^yw=UCnsgR$J)*d74 zr4t!rZ2gz}V_}OLhheNc99e<72|>ePK(0=WvEBKmwh7Fu%#*dgSki-392D z=+t4@r&X{f*$@>fN=ihVZB$Y`yxf@~-s13xj1m>lQS@z!!Va&~iF_`Vtm)dOX*)gc_N#i!n+kL?NL!**ywvlGNt|jA684I~ohwpi z*Xg4iGA`LJDLD$;0fF37!AIpaQ%11s-vhD#0qhkd!A5Vm7(-F26gH<@SQp`Ow4$b5 zY0{t9e66%)<-h6V@4C7Y7yn2swN0cW40LQVg&xE-A(D=2N>`pGd%f9)N_h*?0D2(E zz~5MB7tm#QL|JG0J0Jv;UYyV6Cg!bZS|lv`pVsFDi$3uc;C==a{)9uoqW;sG7070m zZ?7vCE$l_9Ubfq824GdJE|9+W0*L89$binp#=} z!rA?_QNm;j^mWRd8o%=q-LJQ?Cx^Ta$f;G(ddK;?L(T(Hh-WW|1U!VluTc7vtRg~1 zAdL9Q2ZV3zj%G6DLc~r_yAiHZ{qHvSjWgnVRnnNar zkBvUe4q2}ne{BR)qDL5PR@D1JX=~jg(=#tOh2B2VX8ozZv(=%&c7<%st`a!J{ zqSCwJ=HYU%cKP;QmosOS`-?wz%{iyl6AUek^XF2P;a;m2R*s74cGF8Em*Z<-Rrr>j zvxW6aLp0Ie^1y*MjrvbN0%^C3?>LVmpG;*|tHA(^8?jHgXk)`8^|DXP1sqfav9MEX zf3em-g+}rxT?O!F!?a^!wp%*a&2?L3fcDh-2r3Pa4o0UQn3)hjqZbxn1SNh#l*QvJ zutK@cdS!rSI3ad*)$<;06|U(F@FCGuOcIKtvmod+MH;Oj#8_x;#r<(Fs3DN;!`(yGZH%!W*t`8jgEDl(IdWHpmI(E2 zmj(OquOrCL%x`=yGZ|me1yAdFlCfu_Wyn4wJx%{&SE5YKW#8~qOtfoixpd*fIFwII`=@tgy%`u6XtC@cM=p@{vP9b%U{7CO_kt zyJV2E4&=r2f_qjLVz21kid(5`KMW=wDhjJqHso1K_>dFuyV+5*RiJ8@6YH!h$?>9o z)l>|}=KHq#P4=0|oNxFXA;&@twTl_PL@#dg8{>$FJR`xN`;p||t*@OzQKHuBb(s=r<*`rewI z-rXFmo$6$|zrX@wL*W(QrxkwN>BpO&uHRbf!F71yZ~xn{`=5{i-~-CP!vBx?&fk3- z__^;lWL#eFbVQQ{jzuHW6vg(WTD}5`1x@P2!dM*vg@Y8u$;Z_ z&*5BU?^l&m7Ju3>lwvTke^AP|HH_yNNkFQYEB#9U{#L`pl;LF!GfK4- zbp_2&uM~j+OV8I5E9mhGz-Fh*#JxQ?zY}BzLw!-^eUKF1V*Qrkx94IyZuCq)9ECDEO z@y!8bY;2i=N~?nEnN z{nzq_BN>;0y?&bs>MH9y{0oS~ANl#c`9EyHxa*2sCUojf&!Y11Iec#{>rPszagL>e zR&c0V+x%7jl|w|K&B!+sCns3&6ES|#<>2$tFLC0e*G{p}D}~N+du~k349gHKMtw|c z*N)BI<7^QQDCk0Z6hy$>p?aj8Ftt(DIn2-Be`0UKptQFi;9u|e(SF$q**h}rRlhSh z0m;I2IxmDm)_aP8|?k4Fiu;0*CFM0T;Jr5kY^+Wf)< zy?*GCc}CJfq#OtDH@1HNGN)d>l{R7!*tP6@=^qHEa{zgLzDRnl{44T)4Aa2ZmwtNy zy_@*vE*snX`%}I&w*PnLpcAZ&H%e>DFAG_zfYw+m-B_9X;h1~fQYctZ=?S~j6*JP3 zQ)(72xAF21eyv}_yTN~_m8m>R&oDYAFm|WjdpoYVfjh8~T;3@%=}HdP_!^0X&pBw^ z?JLY)B)mVF??0E$gJ*!5nZYQ)%LR{?`oX)1RZSif8(VJVMAlt9iEvETFEKdi8Z8B0 zH2(ev5;~@}wYibUKtG>uoxn0bn_Xkdp_;_6O)=OuVn8}6S?zC3jZBxhV9dViddVOx zr(8gu2My(1%k?;6V=dsr-UM{5w#KQ6X9oHQF-*>RUhPNR+!Gqzxsq?O z#fY&RK_li_!$Z-99;u~y0>n19LT4Ew9VgTM-`i#FNe$A?KeRpWQ*O@XQ49^-MY<`_x#79g2e5i@xN@?hC^;&s>;dGspuoWv12Tfl) z7x64IG!8(^v}SV^8fn$(sey@8*UyMWCYN(GJKb+XTHWT?X~j<6lVdil5ud>zOsl8& zzdJcyhBW^;#q#OU* z8C0v)c)^;in(l-#Pf`)6R@IclOef%Yl*GPbAo7t@jhIGde>Wbo0AJE}hHzfRJipVS z{mE>um=WoB8;ZnrTXYP~D}ps4rlKQ*CkpZBMxzG_PBMc{Np0CBg*D7OXa60f&a-%b ztl^OGta~?1v$=9gBtR)LTfj&52LC3$BC1nlyWO^DerY%+UwZGi-l;^csCI4oebqWg zC~?voxbo1hCF@q&a@T{92Ljb{H*Qi5TFUThWe-;e8;_-^jKgfEuGzb=3i80L4ib0H z689T;+r2YV*`VwX3!h$(7VHk>mkJ7ziP&n*W&t?%xgb8*5wh*E!symE+U`ZiE78(tpTw$fcGT>ynXDA5Ght#oi?Rx z-@%ePqm!9M2Qx$wSJHmgGy7`mRgXq}_id^8JpgepR_i*BCO6$B<{aJa$YEJ=asc6} zd?RD!;n9F-+)0)OJ=dgJt5m2%RC5HD-!FYlweu-g$Z?1B~=R&#IQ`dYnC7g>Rw;VJap!Zbd0 zR}&d?Fp-+G*vJOAB4w;(yTmmR7SSTCxYwJ0`^TWgQulc~BKgA~@KmK$NAMLsds&=R zTX$3Qxpn^lRB|w7ZRu~A)|>HH1mPpoNOeb1x`Uq+`9~2Z82Rl}bRv+;J0_Ti4(R&X zvV@8HSZYaY7*OBAo3SZ|+5NeCrgO6sP$u)KpC1e@#q_R&gU*O6!&wzli}2&bYs80J zFmYPAF_E1`nt%x8S0z$kB=MX|U%f0F4<}punKRH0r(J_X^sA=J@VB?17~K^1|5H7ETD*YplZ(dHt#^3)Udi8DsZ59y zXt^hxr%il~8zRD=r~j3x<8G-8M_joVuzMSTgeOs9cjUkT0;Tbz5{XTsvtGJq7oTDE zb;9KuS5LT>w%%xC`y^(xy5Lg<`eWv-BJIF*4JQNJw{&ziA`!T(#i#0ym-4s>+HP)q zTzb!*%<%6(OLpnMFU_=-#@OAH#ZP_3?R;;n=v$J%%UiNvYR$~;4)TwoF|=d~IDHIv zJLPk^zMh*(^Eg@NzW<%q0tr|O00x%`a%{BFx>$baPO%t5Eta1*Hw}jpr=DOD_pViS z^4nimbZLy{GP$?jtz#;-wExjEl?zv1mfd}H*yw)ejupA$gii|pUK->=kbV9?nsup* zu03RMW2j{_VwyiA_oOYx6(r1u!udmaK*PorNlt)YCnx`aj|KuY(%d%}*@H|Lxeexh zwf5D#*eb1Zuu`icl)Rqe)Sh*mbTf%)imkku40CB8%kP6eZ06)+$5x8VO~lKhUoFTS zSF_l>f{EjW!vY_#&;p9Snv8EoqF1Pl_KJ(YtqahL^4lz6eGNFXQ zcr>~Ar!rpsXBSiXA>kMwSOj?!lOl}l@9q{wgC#uG=jiyEbZ;~`W9a2c+8G{m)WlXe z&**CUA_@`gKmG1EN!&Ep)b~HPgj%V^hfrQ801l{vq*3cLD}kXifq>*&Y(u$x24U@& z5N*jNUaj$crsw4SWwhha9CfA8(!q?mA9mEoZei4&4>T=Ud#Z?lB}#N5AR%HIDSjyS z(Eg$JPUvczcS&yS`nyU&wLPb3IOf}8bzI2s))+AfNNU+eLzby3A!|IXEyssZsR}A{NG+z_0Ji zt4---x*Imz4BZDRt*XxVZCf4UhXg-uL4yePo^4MD^y|+C{CppG3aDi9zEouJ<#`>I zWuL+a@a996Ei`&xQc_al;pz7E_U@b2wwvGli7R1dKS)~=WMZ`=p!bMbu++V(Am<-w zSLJ2_$xlmuN z`Y@*m~99x~Kkrh{~b{dVqczGV`w)py0 zQ5WQO52rb8J2U@yxVny8gmZ8XCKM7*W43`tGP1Tcf6+=M!JXBGB-n|5ulRCnXzqfV zf5AMkpp46H#?JTpbSV=-43&vt)6{K@qu=$2itL!3MmPs@! zu+BVB&&P{|1RfsxfB2w=+EC*HzkOPnX3LCA{?v}nS=lWPmcN@Jk1HRSws${d*pBX^ zg37!zW)#k7D`j@EN`20!7&GWR9dDy{nxCYP+V(O;XJ2+Xp!Q$|S_lf!Ku&ol37LnlQ{!rfQ4=tLI|x~}dAfV&Bc|D(P^MYra&3lzPMFIumC0=<-Xrqv z@@$>l{@{xC!3#5P1RfMIkBFghS^e_^MoDZ;s)f4iMcj{rH_}a+5s!Xvkq9KD6F+`I zaE0?hp|K_~TG}k-SF6e3GpE8dw8kLu>o9hdcvQrbq3zwpeQKH_9w@_} zU(lS8vr4|XXEBN(;Fb~#0hxQ!{&HSzb2ou=rP%8Z*%RM@4^m(OjVTJR*+5Z5E$FyO zZYIg+!G=_cn;-M6OiV0j{ceBtQuOm@C2HZpfz<|QF+e)J9V`ua{Co+9@gX0)_gIpb z&v8b#((Emi@wX^*eH=1C&PseYo`Pw(SYTG)pPQw)YHwNSSx{_?u^ zPj@z{WLWz-Z$T0L3Z-LRl z8}17XJspzSJ`}b68jn_0j5hh@uT2(>9LXiy>TEplURU+s*U7G-CW>yGPicI*Khz1g z8IKVWRS)PG`)>4BxL@ztfwey#P3g68IbSP3nm9!KT;K1I2!K=@%jJJZoFtiz0&*fc zGaEuniPzRtWwv2!psE*b%PNKHwDy1@Z}To}7+3cZL_8TBNmT8^#9D`@jaeCxjLCKw z+HBWx^qgc#KY6_C(Uk&)q7VV%-1`~ROm$$Z__+76uDpg`O?3^DLne4%0GQ)7Eek`Sp{T?%7=<G(}OBuv{Dwzpip8W)zS6yG`!O$iK4H2F4V6x-`|K1fy0>xbY=?eAXis z`&Pn6C-jg6&5HUTmxDj%gnz)EZ!@3TSNZ&$}JGjz%{#-jwiCMp=N30V~B ze6EhEcR6CA@{KSretJk@rvR(jI(6Vgo{Sbm@vq2UqV& z8{dcP};yW2t^XI2;#<>uNZ7bG$fl zpiI9|E#{KHg;?=M{(>V$a1H5Kw7;R#lE3pCZ`)=CW)HQ%(H}|4B}tJ@;Q7CdJBWs`vTb6+T%IYQV|1w z0@-9Xs-_tOMFb}d!6!+oKqOIkAnn${({z?AkS;tkoA~c}Qyvk}gm{-2JfCQtZ3Y(V|=b`PD^SsYi zxF0@i9R<)dY9-a!H0n(B_&j5v=MDZ$md{j*_@eh8Ge7A;k7RIvNpE7^9RZ;n@C*}t z{S7yv0_m~=pFJNvL%rc{$8O#A3WGv%bP_q4Ok--VDL-r?3d=p0;eRVMbmuOu!(|79 zyK8;}6Kw3C#?83u9d^(BIn%QZTK1a=^djQy`xEjg$hkYh<%ta-CMWOT{HtKu_x~rg z!$be@@A!>wzQ6M#7FYGnE+ibG{>RQqaQogxN_1S=sKgDy1-^gUVu!Xca`Y=3C9P#P z_5eKzdirZ4zL@{KhEN?XMR071r(72MeR1^a6c*wWkMp8WEscBhgS~qn0YWo@wK{0y zf%d_ANalv@0@;veBF>8NPTg4Y0^a+2Lz_Ua0pNRN}g{{;dcQwpG z(3$R>$$&mobhXlOnMrLyQaxlWG4tWZS&o*=lfNqmr+|&npl5fCN>(4`y$+4sTfq8| zoQ9|i;Yz$MuzL$@6PnU4N{Pz-Ci$nIcAW+8=u-OU43Oz{U8U^|oyN_0}an>#dfGTh<7;UHqm9bcdFOrb8KV z15-#oCs{5N)rBDS{90*URUY&Lrf{m?ipsz8P^T+Yv4)?pDCPNohAfF)pTzDu%)dWVP}$x%EJFyg`mX zJJaJK_#?|ivDoF#5$>K2{RD!gzcA^+Xet-eNb)5|wxxU;oN3qILg&%qU&3>u+US)= zqN1YXr!sE`GZ=Xt^eL^UVZ|hk_3KbZ%1I**Iv1zgbp&R_Q73}0O zCck)deXV>*9fT;~5P0o~sz5?pHJZWg8ql}ccR(ccR5UEgb_8oaTdqv3933m5W?f|+ zhQl9byE`hD%K4mo6LfRDGy#w>O%7^I>9|UB40Kvtu2U{3{2d4f_YpmxqWN0wDQSA~ zV6|1=+P2r2FR3_F{c^|8cWbm|PZ~(c410{Lvyxq=Yhyv_j4hN=I9!p@zGaR?Aq0s6 zr%pLT#pBt5*82{Qg*PJ_%W$?-A=YU0E^Wt+PxaV0De#QUk=}|>tO@829yU%0zHfWP zm=(5JsUM0@6-ARzS0lG<3nt^k3`2votDJ;^x+mv7Dh7UaUx?$ARzNEmNB38YucnBL z$T5Y9iPY#Ob5qqav&Y?A%>Was$T&Z5(AJR2w-0J~pgy&&BqP07E=|Hgr+n!0OvKC@ zeCNB1gj(HXf07q*#-oS9c8NqJs<`Q_*FUbuetO6~xP*Dbc_cTI+AjVAwbOcxbGmH< zZxDHZ9!R`5v{1eOcrQR>yHygdlP`WGCh59iXE>VbPGk?uTm4AVV#BcJsy5>4Qc=I^ zn2%tt0m=%3uSP#v#ERBx+`2bY>mj0-;3pe|wQaI{X zOOA$Y9Qi#uMT@n|cwAHIzQw*LLWeu6=I#G3j-rXvg)_J6`N-({g&-@4ffJYb7y2o< za%A$GkRU253=Gu2Jb3pJcq0gD*F39T8F(bxy8gc_sY7_Jd%O3rJLRJMrqxvaZg!_` zj=g9?$@Gqeg@c-$49r3Z@2r;?r1~hLOF7^sf5#reAG46lGdu^y8>Di%%BoXy<=7;iC_(aLT%ZIZ&wpY0QqYnv5C=*Fz3&am2M5XdnAlmvUj z$<-loKCI!;hIbKwjbmlD2)rK@WY;V4J*JfaTNE|hlezi|x*PUY5$d{>=GP7= z;~9#Ex8^e>K3*nv+h)S76St+PaOTtU$QA&-=zc3G*^Vcl{APyx2SwkbBfxIM6Qys(UAFTb$FO4Ky`}Akh--i1kzYOmw=La<*A3@iif<*HtXmhpP}|)H zUBho?&1qWGiYY?=^3I_ee7TFYc{k__&2@JB&3q%}ldjAb5YJk8-d_*}eM$!cX34s5 zP@<9{Y@1=ptPDa^QA~a}Pjlk!ho}+3EidXofHN~mT#Cy3ZBITQRK65DKk-u=X^qc1 zC`I+Ol8mW$9dHCtXK{*)mk+2dWuSk$af_Aw)-o!%mTvH?6Bfq*T3Lq3&)=Sr1jXwK zzJlyVJ1gp~Hbz#5>-L~wde8LvY}t|-3f`zx!?UxF&uu!rYWaAKj>q=1{`<{g!RkMS z8ltT<7ZrEs66W`AZTo})Q9hTeJlsq<@P{w@M(#FF$PKmv1$g+0dvG_bQ2uTp`qoiD z4tR-d%cKUIxK?~q$4mrquu?}WZqfGPE}&s)wZ`*@Yid#W)IUpjtC2z@r(f@ z-8_X{0VVU0WZPi?mw7vvZ)fcBazEPC{;T-X)t z>%tD9<-^X^W4`6O#*W1A4HQ{Gqk3bq5nlbbipSWPP-ATbYco|}jK%Cd%GAGf!a1B6 zG1=dJu1=DyA3&>VMTu2Pe5#o*NK%m53z)--vRjKErCGX5_5jFe2d;=a1Z`lx!HsAZ zJENhKCF$fs2;0dVXIxHJJxjxNWVX-u>yNa zM8Qd?I&vNpJHG@9g8$j&{aZ|nQiSjRNoFf!AGg0jMEU!jT>I75cIIa^cA3>RkrY&w z-G!t^#T)h|QMDexf>YqPo}nwa8@778Ar{$TcO*5C5k`mVIG?1-HVc#xTDC8eO&D@s z4u7#T5Sx0bEk77Gda*N@9DDN9_DuFhBWf{*_6t=$BtJg?OLN=wM`D%G3f6mADf1tc zM!iD4?{R79HM#iRAMAf(-lY8o1HeffwqLdJ7?pgQT>{G7@aRmb`cHXy`dksc--O=! zI>4fvnHEVpPupGt9x8hwXD(WA=yt0}m2XaQeet!Vz(RtMFmxiT7cTp+g$w-lhDZ~J zoAAPNFK+jH(CB2tYX-xqN;uu0ARFFXh^ljFE>h~ zl@FIfMVyTBBR{??tuBOdoKjP7lmF#&)$VeARH8s34pAwRuZoFpi6&9_fJS1GRZ;cI zjb6QpYv@G~?2Pz+89*qcZFjurZ#M_|bieuwv-ca1cBgZkCsGPb!PXtUsxaMx-hMKV ze6HQnHP=Zvf7*=X&oNUjQLM$|P_hR+RFj@;rli7H3pnsUpJ*GE7FwoqhIDI{xrD=v zm60Gdv#u&0I+Fs-NxC8N8%`-UVG8hJi{YoaMtm-|}Cx z{s|j2rcX-|m`|Yi*$uC0*Yr#xxi`{<3o(Dxi2;k$k-$K>-zHQUbdF)0$tzim{`WWH zzy8jDJcC%E(WsQ-AQ5xMgX$lJQbRX8=I5+!K9xrclPv75& ztYaJ{gZ;xjg;5CDG882$j~hqC_=WH%+DbKUOCi5xgrfLB z3IN!S@Q`oAY233PN7sK~S3ijU&1dna)C0qt_`sEWi$Y0$DUI4zYpLYfkgJ-n4*5$l zEwU#GUY{v8(@ezO%B?WRV8HjaM=`S0*Ysh#>P8ZsPoV!>lrGB4**OXEh=j!Vr^p#x zy7=rQnozh8Tdmaw0_jBjJu)mYy#P93V{@>-ElXrU2p0X17tA|On1mCs7SGxAM3gGl znn;&GAZ`5kpP=}1lC_W(zbY+KADoG(%744HKZM9t-L@lbf~J&}BOOZ$N?=KMHmPU<-+wEAQ} zPA$;m=l>;o+#p2Ir)H#)Kvfi7B+58im7`Fd@6A*Hlt4DAAFt-^WO{30H*e6FtL5gs zK@>b;7#kFW0m}dM_Xm@ch(>i+MbrOV>iIuMIqCtvA%{!PA{aSB59kodRQ1sL%0<9s51Oq$! z{EJLNn$R~?@M9=Ezb`}`gL6$!qDu#T;YO$8KNdfbQn99-5b}v7wnv`6krfx>L6s)i zMs%LI?0Hh2e*o97;_2a!`=Pp4kY?~}ZzXevTk7+_!KK{MZs2$s%)x|-k&!rKF;9b8 z7Wrcpo8xZV_!5J${sdJmJ?r9*?x+i)mDvaT4qr(k@$<6?x!jh3N^lGzIG0AG z_CksMf&w!XBo^p~FuuWFOMB33X`qtIEI}Ftw=dRPAQU+tOfO&pkG6$&sAXFIZ>Rqs zbfPFY%!I8l=za(-1WEPvc(8171gYZ4NCp+R#92mxni`@e7?sRn5TmgficOGI3*9Tu88 zHNg7zIA^Ht$B;SAVaQ?|QNV-0;S48V__r1r^Lf(Yd9Y4s2)@jFfKnV;7syzk zC32E~H)I8q_Ag#yF1^^OXg$by6bE+lJ@{XFQUap_6rh7G6=v6X2V4))ckUl z&^Jx*Ar7OFfy?#jr#D7(xw&9nJnQo?9w6Q~2gLcLz%_3w$v^+sOZZ>UIz)oKP=$yF zt)j)>{asA~4=wi|x_nO-M+oh23XeQG=o@WRw-!*58z-^Lk0?(c7cMQ7{IfKo**n5g z(20Z(pT)bsepyVc5)RCkzrzfzg#4v;q#FD2x$>xr(ho-xgQTrPI>6XVfN_j3^T*qB zXo8%DSYO^GqMF0RJH<^AlFy<6XMvGuZddvh*xniITe1ml$d_lit}HS+XO)z)-HE#V zTdBQOg}AZ|LXsIWf{m`UjNTpIoPN%CG1d<>7dMS1izYhwi${d`xjF9opX9V~&{o_| zB1EGTuU{NIz%~tU6@$P{G(2(q)M73!G2lg-i*WM6y_RwUj$h;=`K)xB??I5W-9ew# z{gV|DWJw_s4MCUbOS?U9k*`|V0T1P3YH2|QwTRg*jdheDCp({5)DSC zTF(nDNX?k&p7P!ep={@;nlVC6Swn)=mQQ;pLHrBtDVVM_qPOPuGjV~PT^~*^QAy?h zzK^O7TpDrz1@`!7;$F)Z1cDTb7?%Y3-$yBLYAh(#E2=OOK6}w9fP*C;n!reF5D><$ z-_2~GNy+dpDIiJ;{%(s-_yu4$dg_*D(;*i?U|Z_$!iR>*_=_lS9vw7}6$Pr9ca5OIa#i{2!skNn%)FLwok6*J zS+(WBn?Qzh`v&#`&5n(RW+Uul=kcbuE3DEA?A&2dF8>^?c#n%hTp`zeUjY3InBnbT z(lRbgA&r*rcpnjeicb1ua7ZlEUR@f@Z;IEb{*`|lOrirJ(enT^pvMa@kc*0%_>l!e zTv>mSM;v+Y>{n)s*83=AB-DYOAu-0{C>PI6of^pHJ>M>V23Kp_qYAo=)yQbA$;H9N z;wE{mrBeBjjS*7tSzSYB76`+a7#I(JRM|PKpCb2hz?1xteHk|&j2JqH>~S``OdBrj z(kK2-9K^oJ0ZP4r)MipyKdxBwoPk@jr-%HDyzuP)(r65iDOP6kH;gbOOjQ1&Z$&i+ zNN={6GJP?if1!0p`e$FyaKn>~iUyIK@4=%{5Fx(%5BI8%;2_Bv^9LcR1R5ddi>>Tc zl~Rf1RD1c5#6)bsNj%tUwM`ZcZQvDYR0#9R_IH#0_JQ&rnR@OJit_RoIXUTk<=z(!$&k2L85jMc$vfxvv~UVhn|{!H6CnbiL zm>5AYjlDBAJXUX{+XW2^E5T$CSYH{SU8EkH@KICz7Vwp1|7xZMiE=3HeQx+-1T1Wq=>9=fl9Z{_u&=M{EXMB6w({-IF(_Y54I?7e_nd=W<#JkLS-Kz7IwG zP6s!s+)b>jCw;f9Y?cY^C`3?B!6YjvZTksWkjfqP-%U(k(s>;AJ9`ihpzifdQ)@*2 zTZ!P^hYR&OC`%)9JxANY8xa25rJCNN`ATC6-%^1E!w(IX4XFF6HahCEb?_iPXZ$*2h-(JCCobs-%( z@w(luqA#7MZ_i7=M#NzM5!bu{#&eLIpCH9vJH>0RS9KD08 z=Ss5Z9v*`dX=bNc`%6Q~EZGjc|WH`;0-H9`DN z5+gQS_1IzUsWPacAYwVI$t{#TMq&&Bm7`0lxktF!G^?@vD%5_Qxb#GF)5THVS+gsi zJo2!^=!iG>!#}-wBgxopE5BLD#Cx~RjgblHDfmYT|+n= zFv33yB%TC@?WU-^Hp&T4HA%oG0WtT-TPKdgH`1Q%j~m{<_Y5@7!3Mp6p#-VReY^{; zEkISp`UY%WZ2|@-n%jCWphpY6;>UK?v>m6Kx*6AP%m@2r>XzA=Yyk0z$-RfSg@^aR zX3}OC`((Em1n0)`XCkKeTgR;IS4wP>{qdwfPmMKLZ(Z-|utFMLI)Uyy;$Sq1h1_r; z#vkwl6=GotaI*X{=>w-pgRzy!eEtR|pU?G%&L^fCYr8+%x23gp=8mI)tr-sY67+F> ztDF{v92vOJyXGCimKu#4TpEpL97y>5%M*sdc4*$ycQ4WNAQy~G-miYKTFKTwo?C}! z>ZBjQC{UWmad7i5?530Q3qQ9_2IB_eILuYddcu^@{w2?PPlHLqBjF6s)`^>wk`52e zCv0zYS%W;4L-QXrm;E--sFg%m1JxtE-Lx5=V7$+u&Jios#!&uBCW(L%hN*Sjh2Gv1 zRxmbwoq{des*RoPCJEeXKCif5rcU_ESqVw67d8AuTn6%A8&*~f%J;i#m+$|G?wW>| z_NpXy5=qVou}c?w^Z84#SM2n%Vrx@de@O~QHH$@f7^2C~AbyPsYGJ(d_h*In`Jt25 ziE`u7QX{nB!X10_AuIa(?vz!@ti9t;UZ0R!Tm=VLc=97iyaZbomIBGogRZHL{Y7+c z0Rs{iqJ%4L9C+k!It2R$6;48m#1+u*6xEeB(M2`MrNHS~lM~cGd{3oP@b3j4dg~*2 ztg#>9_^_ShnpSL{K76>QqZdSA4i(2x$a6qTZ=h74bm784pIA3tK|Z9svrShus~ z0<3CXw!LnNBm`IV2Zth@M#!X_^ql%I-5vc4E z9VJtKjTb*^?VG%9tP9UAr<5z8x$naLRciFn)7*6;3;Xhp_5!z`{Z>SnOh2i^NWC58 z1@!`U9S@gBM}n`?6~`vu``)WxL*peW1FH0~-mW%n;;Ig*p-wW9^K?Mc7S_Mlk>7jQ zRKIP!=n%};$c&3ZMu7_J`-Ju%F{ZU(A>HnNg}8RX7bSA-c;V+=~Csi5jS2!HTN)+V2DaLg#QMBdV$v`HsvM2wwmARM2o6TE34N0^!1si z__5Y##N>bR^^W0nh3)ol%%&^0ZQE$7e!^ms+nK*13={NGSEET^ixvcZm#qgoY0>GbfE77!r&Ch4T&Y+oj|!NfL*WlKn#CVT47?_Kc;ra1MHccXVM4xth$o)Z>5pA!jWlm07T3rKD4Dql~V4 z%!qXSyN1P-c*Tu1<6~WMlwC-V`UaxF4M*WI^DvW$w~eeOxeY_Z^1+37 zJEmJ;k|Ldpg&zM}thOL>l$tCEMWOCFF+@fm_+5lFA|djRtfKpijO4q3LRka;pneLzQ&W z@o|!EIRDcg;*4h=6dvvY!Y4SXt3h@P(VH5a)mYOLvEE#sD9x?z9rD7fPMN?{qE@{k z2^e;@+h0OAE30~j32wtq)8h2CUX9wam_5hK&;zwcr2M$5DQyz%DG8rN(VT5p`f-b+ zD7?23Oe872g#!Dqcld%p@&hMTi>Q*<|2pzX5{)D8c+Kh2i z5x=HQL-3S|(i-wky4a#(Xx2Y8Z`nmEmtIE8C%MWb!?TC0y6Jr^Mui*Ger6-VF0L-G zg9Q_^g-d4~V(;pl5Silfq(E0oS9~6hkj~cFure9ZUctafQtUU4N^hj`^ZguqE&bmn zv;Vpj{hvMYR;{Gb;6&z+$$aL{m+O;!KV-xekj*tvH{?+wL;97{#UAhtvWR(;9EOH8 zvJhL2?Qbw5P&PIRg@LE1mw5fRp~fG*s`BMaiSJ2Ily1{GR3R6nhu)xH$o2oXP>6vb z$h;|{uvSKi216_WLyS)@!-*mKKg7&)5vWRux17zAA8IWj*Ftj_x*@N!$gehK1EPEe z4Eq?mwavUwp`VE@C~;W zOp>T5I>e&{2?{321?P>1p49J?RKFU`JxUKM$)py4Ja=fvRk?(>FA{2-BWAduQm*i) zh#Borw2%~3GYUt~-3*!~y`Hc;O7P0r{NFSoKIqsnCxz|_K#929}Kj$7_v0e z0Oak7b4KR!@88?=I=@q%LiWb%9Ox^a`Didql74-k%XGJzdqkkuU*VR9r6!(smCukc z39)mUwG_lmeCY3sN#q=p{cW`c!CS?WhBRw@D+W3KUJr4M@mH}MP0Z}(7t{syWzo)P z^VW$uUNbf}Y79Yi@75KB7|i>lS4A=WSro0DaILBftDsC&<&X?$o`?P&j6%Bz9y2rw z`tV602GXzQwmC?of2R#G^Pncl#795E(Ka9_i0{n~!aFz&+%$8VWd7m!hL`{EY-Tdk z55`RU!%j5*uR`a)Ggv3Qrct|EpwUN9g~-gZ;j^?yEWm{Ua*FEFrjW3ed|1?u8CX%W z71TkOwH`3ucu4y4SwQ{Y+HM9{Hrp1S5gx){Tr5SbYfl8UK7?OCVE^a0;=fE39%E=8 z_nGb`wyErM9j4MJnZmC(vvcIQa0E#C51;WcT z8tKh_sTu6nTM~iEHWRq8IVj!(XoIQl@9+SVPMfXnzJz#xkpmJ|6t$Kw zbU+6!nz|{&5!*CdgXgG4nxHxTdRu9woHn}U(66kOw&`H&g$mcOYuPi4Ybr9e#R!y2}M3eJUPWxcBeW1Fqy&`so!FHv*Ounw&o` zJa0)@o6OLjI}Cwg^JdYc`uoWU{M81!;X(?O@YQ5 zk$>weV0ZKW{LN%PpCte^u$v|1U1F2W{3Sr>#&)w-!q^RH*YK)#cQ`+!PXMASKko}B zsXyc1M_?No8gj)L_%^cbk9A*k>HdCIeD#*ZcXt&v&zPR-ZA(>~`0D znl9^I_KwizdCE1a7v{_I*)r?-Y~zQ&skOS@QlmS($kTaa=w;w>&nUc*i$I#0~=i#5ap99J%Um1J& zh5gJ}Xf}P{zmIcS(zH0JIiFH%h@95~LGPr-pY52&jJKZEJg#R8nQyQ6h9^SCm8={G zL{XW_o@YM${_mfkB_??`y`KgQ!q*#rlI~TwXDm>o{-izz*nWMNXX%i^%BmK9f4wJRGJGQ#&*YhG$s!0> zLLJKz5@_FOwk9Pdn!3>mG{mGJA@VTpTex6 zPZLVdE^N}OKN4mKzsv-$uEu2x=r;L%Kfl(SWp^Gp91(pvVbZB{=Zb)WICy*^=_o#X z5iH5Z(O(~AJ+EQG+4Hs8eDl2-Oyf$xG*)g#^md3Js4i>su+Z-`{(jAjWy<;`tij@- z(g3+;!2N73RW5;635#B1$Ff&yuE~zzM5p9r0PXiqY6F%j7P0*_NJdHD16$TQlHi0V zcp7{69wY>}>V7md341nI?+vg8|1w0n3xcSqWzWX>vzHH*I8u(-Z`r5@av8h>d^=Cv zmPpRB$1{Cd>L(cvgG5ab1UZ3q8b0m+kljlox~eV_{-Oqwgqd1ZYl>M9Zr}=X^sA+E zh9GPReq3Y7!T&~Dk;9dt6(PzTkmMnQYtYp&loo?q@C(217n)#^Ku2S}7!&hT z1dXpCt~l`zR<5kv__B(DYF3`HESWici;1esN0zaq=;6VLLe72s7N@Rc;0?|E5! zdve*`PkO)fZBkb4inO+P0agc|wEQ89V6 zc$@T$SF82HMbVR2&~ho_hPk9JsATXLUmgF95uKYip^|?aQ+zJD^bh$m5Ma}xaY0S` z9e3~p0=a5n7N#vJe<12k_qxHrJMUQxg}8CaZdKdvfr$wdyH`JJFj3*4-aPuSz156| zHu>EHcAgBiYSae4wLXRm|1O&8MC>O6;Zxd`FKdF-c`v`J5G1j;xJCxmI{c|3L2k*k20<2VYO$Fs%9_nVsNL)0p`zF}byxA>ej^krgf_WTSx$@JQ4>m+}xwezWcANy@J(&}~S zW)y4q3_6@7=~l5lJ3GTeb}ChKemuN3Z%#>SxV?Q3!&OPM0}l|CjjMRf`Tlqof924^ zyPg~Ve6#Mp$pC9*s(oiHB4RjThhNq?MrY^VUm@xL@R%Y?PJF=Q z(5gQKq^gcxZb@(i~oDPQ-F{mSp{k61R0QE`E@hYFq z^r3lKOOk))_%x=)p7vR@>_Kqz9iJM`EQdvQ5b!6sov)Ox=mXVoe>bbM@xkqzC#F+PGm8RPUEz*AR?fBpLNWhifA+}5J?rR`8F-i)kYNN8y4&6E3-gfpV(}#2OcC%* zXxHir#>Ob}Q|n7~-|!2G`k7eN?A2~ZByeYwK2aUh;&AJ;Ht>VvSH7r9Q2#CPtQP=7 zLJ8OF+9P z@2(Zl^zRfru*_!jq<7nb>t`Eu%*|RiztJCvHx375%4D*6CWbtr;g8^b09Wc2?zepl zJ#s7s>-rqTnmiCgP>gvh1_yP8{^J*2Jk4xqEQP24oI|yKI>_OYm@f!1ddc7K#Ssz~ z7M3n<6Wo?`)Y#p@^dx9H5NYkRvypS;7e5GmR<0$FHr1*&)Vw{<@CheuBz11bw$9ay zDc^Qv?AJRojo#K;Ex~c=W2a)YtEVgHB}876*)PkME&&&&T-6~__}t{E#Yr#M5i7I) z`Yj1ndhOz_si?6Wno8Cyr(7o)oZq9Wc>dUgGfR`){^o9lPxCdHGWD1XMY9HV6UPQ8 zUJ`KGmMrWSji<2@YpQd&=m;&ktiJfX&JijkPq=k1E;=$AwDB?-{1!bEAw@Lp?B&3e zL8)AAQ2!xQ;M`=n)1oR$33*jbj{)Y?M*D$>nnOxT5Pgxl_))>@`|p;Rz=qJK zAfFb%w^`ORnV-^{>d`^DPgm*PT=ncqfUAWmc$3&6^_Z<2TYy@YLP=_hAy-*G?^A zdUQXvTs6TdCtER_6v-}~zl_iJhq-4VJ#@V6q1+^9)tG;@wbQV2a|zGFNr4@b6~~Gh za>;y>NpDI3swHBNhbn{iF2YYwk~@uGxO6ZJ5AC~E@aqCfu@$->C7+oD?#svLSh=-$IRUNp=zi-a0s~pw5De;X*+Sefdm|ZOE*wAB^SkUtrGy zqeQPr_ zNA~JlzmBJ@4nEziX}#_3Drdm#)x!w-yr#AW4dDH_l2?c<02M$6SA`3eLLaU`e-Pwb zGIVeBai6i-8LSXG-)q`i_8VhY{$}xESC9@0F_hwh^M=B(m#6VcekV+TmV%BgPH|_J z9U5WBQ<(_rO~Q~QC+S$Z#QDW4f^Cdh=c({4S1|7g`K;#wmaKC%%%Yl4Cyw0h$VDm$ zUPk+ujGt8rEO^>La0ZT^Se
  • X+yxV#)G|xLlob#iZwX3q5XcSeSAkdy@Q%$x`t* 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⪻XYt& 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ℜ42#NDyS8J@)m*d9`zIpdeyr+v{)VyWTu-N$Mt9%YSD zj8wR|Ae=Z>Jd2Dpzw1esq+Yr#xkN%DzkWwz@y_w5r~RVW>scmW8S}_7pEo2SZi(El zN_1bZfkRf^dK0nrvJMBSjsua0LAzD#_zX(yghLg+$P7v^WQ)UOVA9~kewOGd*Ltk9 zjLf)L-qIl8C+gN->$L1Y#+W4cBv|>|jE8=GxX}^hdg_}qmv-^O#V#WO0jJ$(4+x8f z&rN~3p?cwqoe-bJ%uwyBmNAR7i~@@3O5)y)Wn#8OXu1|#k9xnwaIyYWvL4Qp!DHii zUA+}jLsO$?DsEw$PRFQ}x*DRxZJVkH<9kBD&%kwNuaFP+d=o4CmWZ6Hy>Q{dEK}HZ zIeR6?bi5!QH@}VCb7#Q1jT1i`TPeebMO{!FJudPr9t}Idp0D6QhqJ3XTFLk@Aj*3- zu$93flwx_LFkyRfvI)J95){UZ0FRIoZ?c|7x*~YbuDAC zN