<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0">
  <channel>
    <title><![CDATA[Infrastructure Monitoring - Elastic Observability Labs]]></title>
    <description><![CDATA[Trusted security news & research from the team at Elastic.]]></description>
    <copyright><![CDATA[© 2026. Elasticsearch B.V. All Rights Reserved]]></copyright>
    <image>
      <title><![CDATA[Infrastructure Monitoring - Elastic Observability Labs]]></title>
      <url>https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltad972c1c27dbefc6/6a88d9782904ea5e8511d473/observability-labs-thumbnail.png</url>
      <link>https://www.elastic.co/observability-labs/blog/category/infrastructure-monitoring</link>
    </image>
    <link>https://www.elastic.co/observability-labs/blog/category/infrastructure-monitoring</link>
    <atom:link href="https://www.elastic.co/observability-labs/rss/category/infrastructure-monitoring.xml" rel="self" type="application/rss+xml"/>
    <language><![CDATA[en]]></language>
    <lastBuildDate>Fri, 11 Sep 2026 14:17:03 GMT</lastBuildDate>
  <item>
    <title><![CDATA[Skip writing alert rules: 6 ready-made ES|QL templates ship inside the NGINX OTel integration]]></title>
    <description><![CDATA[Elastic integrations come with alerting rule templates, each one an ES|QL query with a threshold already set. Create Elasticsearch alert rules in minutes, tune them to your traffic, and catch silent data streams early.]]></description>
    <content:encoded><![CDATA[<p>The NGINX OpenTelemetry Assets integration ships six <a href="https://www.elastic.co/docs/reference/fleet/alerting-rule-templates">alerting rule templates</a>. Each one is an <a href="https://www.elastic.co/docs/reference/query-languages/esql">ES|QL</a> query with a threshold already tuned. Install the integration, create a rule from one of the templates, and adjust the threshold to match your traffic. You get working alerts in minutes instead of writing them from scratch. This walkthrough covers the full setup, threshold tuning, and how to use an idle data streams rule to catch a service that stops sending data.</p>
<h2 id="prerequisitesforelasticintegrationalertingruletemplates">Prerequisites for Elastic integration alerting rule templates</h2>
<p>Elastic Stack 9.4.0 or later.</p>
<p><em>Alerting rule templates have been available since 9.2.1, under the integration <strong>Assets</strong> tab. This article covers three things that need 9.4.0: the dedicated <strong>Alerting</strong> tab, idle data streams rules, and the NGINX OpenTelemetry Assets package, which is in technical preview.</em></p>
<h2 id="step1sendnginxlogsandmetricstoelasticsearchwithopentelemetry">Step 1: Send NGINX logs and metrics to Elasticsearch with OpenTelemetry</h2>
<p>First, get NGINX metrics and logs into Elasticsearch.</p>
<p>Enable the NGINX <code>stub_status</code> module and make the access and error logs readable by the collector. Then, configure an <a href="https://www.elastic.co/docs/reference/opentelemetry">EDOT</a> or upstream OpenTelemetry Collector with the <code>nginx</code> and <code>filelog</code> receivers to export metrics and logs to Elasticsearch.</p>
<p>The <a href="https://www.elastic.co/docs/reference/integrations/nginx_otel">integration setup</a> has the full receiver and pipeline configuration.</p>
<p>If you want to reproduce this example, you can use the <a href="https://github.com/Delacrobix/Creating-alerts-from-OOTB-alerting-template">companion repository</a>.</p>
<p>Both signals matter for alerting, and each group of templates reads a different data stream:</p>
<ul>
<li>The <strong>log-based</strong> templates (4xx and 5xx error rates, error log spike) query <code>logs-nginx.access.otel-*</code> and <code>logs-nginx.error.otel-*</code>, which come from the <code>filelog</code> receiver.</li>
<li>The <strong>metric-based</strong> templates (active connections, dropped connections) query <code>metrics-nginxreceiver.otel-*</code>, which comes from the <code>nginx</code> receiver.</li>
</ul>
<p>This is easy to get wrong: the Fleet <a href="https://www.elastic.co/docs/solutions/observability/infra-and-hosts/collect-nginx-data-otel-integration-fleet-managed"><strong>Nginx (OpenTelemetry)</strong> input package</a> collects <code>stub_status</code> metrics only. Its companion for logs is the classic Nginx integration, which writes ECS-based <code>nginx.access</code> and <code>nginx.error</code> datasets, not the <code>*.otel-*</code> data streams the log-based templates query. If you rely on that pairing alone, the log-based rules have nothing to evaluate and silently never fire. Run the <code>filelog</code> receiver too, not just the <code>nginx</code> receiver.</p>
<h2 id="step2installthenginxopentelemetryassetsintegration">Step 2: Install the NGINX OpenTelemetry Assets integration</h2>
<p>NGINX OpenTelemetry Assets is a content-only package. It ships the dashboards, alerting rule templates, and SLO templates, but it does not collect data itself. The data comes from the collector you set up in Step 1.</p>
<p>You don't need to install it by hand. Once the NGINX OTel data from Step 1 starts arriving, Elastic detects it and installs the Assets package for you, which takes a minute or two. Confirm it under <strong>Management</strong> &gt; <strong>Integrations</strong> &gt; <strong>Installed integrations</strong>, where <code>NGINX OpenTelemetry Assets</code> should appear.</p>
<h2 id="step3createelasticsearchalertrulesfromaruletemplate">Step 3: Create Elasticsearch alert rules from a rule template</h2>
<p>Open the integration and select the <strong>Alerting</strong> tab.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt06c9c544b653b81c/6a85c98d43c0b7f8172f05f0/02-alerting-tab.png" alt="The Alerting tab of the NGINX OpenTelemetry Assets integration listing its rule templates" /></p>
<p>This package ships six templates: high 4xx and 5xx error rates, high active connections, an error log spike, dropped connections, and a generic <code>High error rate by service</code> template that points at a placeholder <code>logs-myservicereceiver.otel-*</code> index for you to repoint and rename. The five NGINX rules run ES|QL every minute and group results by <code>host.name</code>, so an alert points at the host with the problem. The generic template groups by <code>service.name</code> instead, since it is meant to be repointed at whichever service you choose.</p>
<p>Select a template, for example <code>[Nginx OTel] High 5xx error rate</code>. Kibana opens a prefilled <strong>Create rule</strong> form built on an <a href="https://www.elastic.co/docs/explore-analyze/alerts-cases/alerts/rule-type-es-query">Elasticsearch query rule</a>. It runs the template's ES|QL on a schedule and alerts when the query returns rows.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt2d9547363761de4b/6a85c991bc5bb36529f81acd/03-create-rule-form.png" alt="The prefilled Create rule form for the High 5xx error rate template" /></p>
<p>The query looks like this:</p>
<pre><code>FROM logs-nginx.access.otel-*
// Flag each access log entry as a server error (5xx) or not
| EVAL is_5xx = CASE(http.response.status_code &gt;= 500, 1, 0)
// Aggregate total requests and 5xx count per NGINX host
| STATS total = COUNT(*), errors_5xx = SUM(is_5xx) BY host.name
// Minimum sample size to avoid noisy low-traffic hosts
| WHERE total &gt; 50
// Calculate 5xx error rate as a percentage
| EVAL error_rate_pct = ROUND(TO_DOUBLE(errors_5xx) / TO_DOUBLE(total) * 100.0, 2)
// Alert threshold: adjust to tune sensitivity
| WHERE error_rate_pct &gt; 5.0
| SORT error_rate_pct DESC
| LIMIT 10
</code></pre>
<p>It counts requests and 5xx responses per host, keeps hosts with enough traffic to matter, and returns those above five percent.</p>
<p>Three things to get right while the form is open:</p>
<ul>
<li><strong>Send data first.</strong> ES|QL validates column names against the indices that exist when the query runs. Open a template before any NGINX data has been ingested and the editor reports <code>Unknown column "http.response.status_code"</code> and the form shows errors. Once data is flowing (Step 1), the same query validates and the error clears, so collect data before you create the rule.</li>
<li><strong>Set the time field to <code>@timestamp</code>.</strong></li>
<li><strong>Leave "Create an alert for each row" selected.</strong> Because the query groups by <code>host.name</code>, this makes every affected host raise its own alert.</li>
</ul>
<p>Add a <a href="https://www.elastic.co/docs/deploy-manage/manage-connectors">connector</a> and an action so the alert reaches Slack, email, or PagerDuty, then save and enable the rule.</p>
<h2 id="step4tunealertingruletemplatethresholdsinesql">Step 4: Tune alerting rule template thresholds in ES|QL</h2>
<p>The thresholds are starting points, so confirm them against your own traffic. The threshold lives in the ES|QL <code>WHERE</code> clause.</p>
<p>To make the rule stricter, change <code>error_rate_pct &gt; 5.0</code> to <code>error_rate_pct &gt; 2.0</code>. To require more traffic before it fires, raise <code>total &gt; 50</code>. Use <strong>Test query</strong> in the rule form to confirm the edited query parses and returns rows before you save.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt8b66296ca6cd70c8/6a85c994bc5bb3f376f81ad1/04-test-query.png" alt="Test query results after editing the threshold in the ES|QL query" /></p>
<p>Three more settings are worth a look:</p>
<ul>
<li><strong>Time window</strong>: the look-back period the query runs over. A shorter window reacts faster but is noisier on bursty traffic.</li>
<li><strong>Rule schedule</strong>: how often the query runs, every minute by default.</li>
<li><strong>Alert delay</strong>: the number of consecutive runs the condition must hold before an alert is created, which filters out single-run blips.</li>
</ul>
<h2 id="howdoyoudetectidledatastreamsinelasticsearch">How do you detect idle data streams in Elasticsearch?</h2>
<p>Threshold rules only fire while data keeps arriving. When an agent goes offline or an output breaks, the data stops, and a threshold rule has nothing to evaluate.</p>
<p>Many Elastic integrations include a dynamically generated <a href="https://www.elastic.co/docs/reference/fleet/alerting-rule-templates">idle data streams template</a> for exactly this case. It is named <code>[{Integration name}] Idle data streams</code> and appears in the same Alerting tab, though it is generated automatically rather than bundled with the integration. It alerts when no data is written to any of the integration's data stream patterns within a set period.</p>
<p>The NGINX OpenTelemetry packages do not include an idle data streams template, which is why no such template appears in the Alerting tab from Step 3. The end of this section covers what to do instead.</p>
<p>The default period is 24 hours, which is usually too long. A production service can go quiet for most of a day before you hear about it.</p>
<p>When you create the rule, drop the period to match how fast you need to know. Fifteen minutes to one hour works for a critical service. For a batch job, set a period comfortably longer than its run interval, so the quiet gaps between runs do not trigger it.</p>
<p>So why is this example left out? The template is generated from the data stream patterns an integration defines, and it is not generated for input-only packages. A content-only package like NGINX OpenTelemetry Assets defines no data streams of its own either. To catch silence in a collector-based setup like this, recreate the rule by hand with an <a href="https://www.elastic.co/docs/explore-analyze/alerts-cases/alerts/rule-type-es-query">Elasticsearch query rule</a>. Use the query DSL or KQL variant rather than ES|QL, because an ES|QL rule fires on returned rows and so cannot alert on the <em>absence</em> of data. Point it at the OTel data streams (<code>logs-nginx.access.otel-*</code>, or <code>metrics-nginxreceiver.otel-*</code>) and set the condition to fire when the number of matching documents <strong>is below 1</strong> over a window of, say, the last 15 minutes. That reproduces what an idle data streams template does, scoped to the data streams your collector writes.</p>
<h2 id="getstartedwithelasticintegrationalertingruletemplates">Get started with Elastic integration alerting rule templates</h2>
<p>Alerting rule templates turn alert setup into a few steps: send data, install the integration, create a rule from a template, and adjust the threshold. Treat the bundled thresholds as defaults to confirm, not numbers to trust blindly. And where an idle data streams template is available, reduce its 24-hour default so you find out quickly when a service goes silent.</p>
<h2 id="resources">Resources</h2>
<ul>
<li><a href="https://github.com/Delacrobix/Creating-alerts-from-OOTB-alerting-template">Companion repository</a>, to generate the NGINX demo data used here</li>
<li><a href="https://www.elastic.co/docs/reference/fleet/alerting-rule-templates">Alerting rule templates</a></li>
<li><a href="https://www.elastic.co/docs/reference/integrations/nginx_otel">NGINX OpenTelemetry Assets integration</a></li>
<li><a href="https://www.elastic.co/observability-labs/blog/nginx-opentelemetry-end-to-end-tracing">End-to-end tracing for NGINX with OpenTelemetry</a></li>
<li><a href="https://www.elastic.co/docs/explore-analyze/alerts-cases/alerts/rule-type-es-query">Elasticsearch query rule</a></li>
<li><a href="https://www.elastic.co/docs/reference/fleet/alert-templates">Elastic Agent built-in alerts</a>, for monitoring the agents themselves</li>
</ul>]]></content:encoded>
    <link>https://www.elastic.co/observability-labs/blog/alerting-rule-templates-elastic-integrations</link>
    <guid isPermaLink="false">alerting-rule-templates-elastic-integrations</guid>
    <category><![CDATA[Incident Management]]></category>
    <category><![CDATA[OpenTelemetry]]></category>
    <category><![CDATA[Infrastructure Monitoring]]></category>
    <dc:creator><![CDATA[Jeffrey Rengifo]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt20e53c65a0321a28/6a85c997078290b06c321742/01-header.jpg" length="0" type="image/jpeg"/>
    <pubDate>Fri, 31 Jul 2026 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Elastic z/OS ingest: five architectures for mainframe data]]></title>
    <description><![CDATA[This field guide walks through the ingest architectures I've seen work in production, the data quality checks that decide whether your dashboards actually work, and the ECS mapping that makes mainframe data usable to the platform.]]></description>
    <content:encoded><![CDATA[<p>Mainframe teams want what every other observability team already has: anomaly detection, machine learning (ML) on the batch windows, and alerts that fire when something's actually wrong. Most of them have the data for it. What they don't have is data that the platform can recognize as unified, connected, and operationally meaningful.</p>
<p>A customer described it to me this way: A single transaction passes through three products on its way through the mainframe, and each one names the same field differently (system name, program name, user). Getting the data into Elastic isn't the hard part; getting it to correlate across products, so that Elastic's dashboards and ML jobs recognize it as the same data, is where most projects fall short. </p>
<p>Done right, Elastic becomes the speed layer that mainframe environments have never had: a near–real-time view across operational, transactional, and security data, while the authoritative systems of record stay exactly where they are.</p>
<p>This is the onboarding process I use with mainframe customers, built from architectures running in production at large financial institutions. It covers the ingest patterns that actually work, how Elastic Common Schema (ECS) alignment makes the data usable, and whether the data quality holds up or fails quietly.</p>
<h2 id="validateyourmainframesourcedatabeforeyouwriteapipeline">Validate your mainframe source data before you write a pipeline</h2>
<p>The most expensive failures I've seen in mainframe ingest projects are the ones that don't fail loudly. Pipelines run, data lands, dashboards render, and weeks later, someone notices that half the events from one logical partition (LPAR) never parsed or a quiet typing change has been silently corrupting a field.</p>
<p>Two mistakes come up frequently:</p>
<p><strong>1. Format inconsistency across LPARs and time windows:</strong> Log formats vary across LPARs, between batch and online windows, and across shift changes. A format that parses cleanly in a dev LPAR may not match what production emits during peak batch. This is the single most common cause of partial parse failures I run into.</p>
<p><strong>2. Sample configurations treated as production configurations:</strong> A common cause of "it broke overnight" incidents: The upstream collector configuration was based on a sample structure shipped by the vendor and then never replaced with a deliberate production configuration. When the vendor pushed an update, naming and typing changed (fields renamed, types shifted) and the downstream pipeline started rejecting records mid-flight. Treat sample configurations as exactly that, and replace them with a deliberate production configuration that doesn't move under you.</p>
<p>Before any pipeline development begins, walk through 24–48 hours of raw samples from each source with the mainframe team. This review should be treated as a recurring requirement rather than a one-off event, because the conditions that produce format drift (vendor updates, configuration changes, new message types) keep happening after the project goes live.</p>
<p><strong>Worth knowing first:</strong> For mainframe environments, the <a href="https://www.elastic.co/integrations/data-integrations?search=ibm">Elastic integrations catalog</a> is short. The <a href="https://www.elastic.co/docs/reference/integrations/ibmmq">IBM MQ integration</a> is the most complete option (Queue Manager error logs and performance metrics, ECS-aligned, with out-of-the-box dashboards), though the metrics data stream requires the containerized MQ distribution rather than native z/OS MQ. If your architecture includes Customer Information Control System (CICS) workloads or you need end-to-end distributed tracing, assess <a href="https://www.elastic.co/observability-labs/blog/end-to-end-o11y-from-cloud-native-to-mainframe">IBM Z Observability Connect</a> before building custom pipelines: It's the <em>native OpenTelemetry (OTel) path</em> and covers more ground than the architectures below. For everything else, read on.</p>
<h2 id="ecsalignmentfromdatainelastictodataelasticcanuse">ECS alignment: from data in Elastic to data Elastic can use</h2>
<p>Before choosing an ingest strategy, it's worth understanding why ECS alignment comes first in practice, even if the pipeline gets built later. It's the decision that determines whether everything else pays off.</p>
<p>A mainframe team's core mission: Trace a single transaction from a REST call into z/OS Connect, through to an Information Management System (IMS) application, and back. That flow touches three products, each emitting telemetry with its own field names for the same concepts (system name, program name, user, transaction ID). Without normalization, correlating that transaction means writing queries that explicitly union three different field names per concept. That’s expensive to write and fragile when any product changes its schema.</p>
<p>ECS solves this. It defines a consistent target schema (<code>host.name</code>, <code>process.name</code>, <code>user.name</code>, <code>event.code</code>) that every source maps into. Once z/OS Connect, IMS Connect, and IMS data all land in the same ECS fields for the same logical concepts, that cross-product transaction trace becomes a single query.</p>
<p>There's a second reason this matters. Elastic's OOTB dashboards, alerting rules, anomaly detection, and ML jobs are all built against ECS field paths. A <code>job_name</code> field that Logstash extracted from a JES log is invisible to them. A <code>process.name</code> field carrying the same value is immediately recognized and processed. ECS alignment is what makes the platform's built-in capabilities recognize your data.</p>
<p>Skipping this step is the most common reason that ingest projects fall short, despite the data being technically present.</p>
<h3 id="mapwhatfitstocoreecs">Map what fits to core ECS</h3>
<p>The mapping below is a starting point drawn from what I've seen work across customer environments. Field names in your source data will vary, but the ECS targets are stable:</p>
<p>| z/OS concept | ECS field | Notes |
| :---- | :---- | :---- |
| Job name | <code>process.name</code> |  |
| Return code | <code>process.exit_code</code> | Ensure integer type; hex strings are a common mapping mistake |
| Program name | <code>process.executable</code> |  |
| Elapsed time | <code>event.duration</code> | Nanoseconds in ECS; z/OS typically reports in hundredths of a second or milliseconds, so convert at the pipeline stage; unit mismatches silently break ML anomaly detection on latency |
| Message ID | <code>event.code</code> |  |
| Timestamp | <code>@timestamp</code> | Normalize from z/OS format to ISO 8601 in the pipeline |
| LPAR name | <code>host.name</code> |  |
| System ID (SMFID) | <code>host.hostname</code> |  |
| User ID | <code>user.name</code> |  |</p>
<p>Reference: <a href="https://www.elastic.co/docs/reference/ecs/ecs-process">ECS process fields</a> and <a href="https://www.elastic.co/docs/reference/ecs/ecs-event">ECS event fields</a>.</p>
<h3 id="extendstrategicallywithcustomecsfields">Extend strategically with custom ECS fields</h3>
<p>Mainframe-specific concepts have no ECS equivalent: job class, ASID, SMF record type and subtype, sysplex name, WTO routing codes, CICS transaction ID. Flattening these into <code>labels.*</code> as untyped strings destroys type information and makes them effectively unusable for queries and aggregations.</p>
<p>Define a <code>zos.*</code> custom namespace using ECS's documented extension mechanism. It keeps your core telemetry ECS-compliant while retaining the operational context your mainframe team needs for incident response.</p>
<h3 id="useecsmappingstostaycurrent">Use ecs@mappings to stay current</h3>
<p>Include <code>ecs@mappings</code> as a component template in your index template (available from Elasticsearch 8.9 for custom index templates and from 8.13 for Elastic Agent integration templates). It provides Elastic-maintained ECS field definitions automatically and keeps them current with each Elasticsearch release. For custom pipelines, this is what keeps your ECS alignment from drifting over time without manual upkeep.  </p>
<p>One important caveat from the field: <code>ecs@mappings</code> provides the field definitions but doesn't enforce types at ingest. A return code arriving as a string is accepted and mapped as a string. Monitoring these discrepancies is critical, and they can be identified using the Data Quality dashboard. And because Elastic <a href="https://www.elastic.co/blog/ecs-elastic-common-schema-otel-opentelemetry-faq">donated ECS to OpenTelemetry</a>, the <code>zos.*</code> mappings you define here remain valid as OTel semantic conventions and ECS converge. The alignment work is the same whether data arrives via Logstash or OpenTelemetry Protocol (OTLP).</p>
<h2 id="choosetherightarchitectureforthesource">Choose the right architecture for the source</h2>
<p>Most environments I work with run more than one of these ingest architectures, and different data sources have different latency, throughput, and licensing characteristics. A single architecture rarely covers everything. The table below maps common z/OS data sources to the architectures that work well for them.</p>
<p>Kafka is commonly added when there's a network resilience requirement between the mainframe and the Elastic cluster. If Kafka isn't already in your estate, the operational overhead of running Kafka should be weighed against the resilience benefits. From IBM MQ 9.4.3, Kafka Connect can run natively in z/OS UNIX System Services for MQ connector use cases, reducing the need for an off-platform Kafka Connect cluster. </p>
<p>When Kafka is used, Logstash is the recommended downstream consumer for the ingest paths in this guide. The Confluent Elasticsearch sink connector is an alternative; the self-managed <a href="https://docs.confluent.io/kafka-connectors/elasticsearch/current/overview.html">v1 connector</a> supports Elasticsearch 7.x and 8.x but is deprecated with end of life (EOL) in April 2027; and the <a href="https://docs.confluent.io/cloud/current/connectors/cc-elasticsearch-sink-v2/cc-elasticsearch-sink-v2.html#features">v2 connector</a> is Confluent Cloud only, making it unsuitable for on-premises and air-gapped environments.</p>
<p>Reference architecture: <a href="https://www.elastic.co/docs/manage-data/ingest/ingest-reference-architectures/agent-kafka-es">Kafka as middleware</a>.</p>
<p>| Data source | Collector | Notes |
| :---- | :---- | :---- |
| SMF type 30 job accounting | IBM Z Common Data Provider (CDP) | Binary SMF records need preprocessing before ingestion |
| z/OS SYSLOG | IBM Z CDP |  |
| JES job logs | IBM Z CDP | Batch export is an alternative for historical / proof of concept (PoC) work |
| Resource Access Control Facility (RACF) audit events | IBM Z CDP | ECS-aligned RACF data works with Elastic SIEM out of the box |
| RMF performance data | IBM Z CDP | Consider time series data stream (TSDS) for the index template |
| IMS statistical records | IBM Z CDP |  |
| OMEGAMON agent metrics (CICS, IMS, Db2, z/OS, network, storage) | IBM OMEGAMON Data Provider (ODP) | Outputs JSON natively; no binary preprocessing needed |
| IMS transaction data | IMS Connect Extension (Rocket Software) | JSON output bypasses SMF binary parsing; requires Rocket Software licensing |
| CICS transaction traces | IBM Z Observability Connect | Native OTel; covered in detail in the <a href="https://www.elastic.co/observability-labs/blog/end-to-end-o11y-from-cloud-native-to-mainframe">End-to-End Observability from Cloud Native to Mainframe</a> deep -dive |
| Linux on IBM Z (zLinux) | Standard Elastic Agent | Full integration catalog available; different problem from z/OS onboarding |
| Historical analysis / PoC | Batch export (CSV / FTP) | Not suitable as a long-term operational solution |</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt62803cc80ddb77ae/6a7f0d2b448e4eb6415c072b/image1.png" alt="Flow diagram of the ingest paths from z/OS to Elastic" />
<em>Flow diagram of the ingest paths from z/OS to Elastic.</em></p>
<h3 id="ibmzcdptheworkhorseforzosoperationaldata">IBM Z CDP: The workhorse for z/OS operational data</h3>
<p>IBM Z CDP is the most widely deployed first-mile collector for z/OS operational data. It reads from SMF datasets in near-real time and forwards off-platform, handling the genuinely difficult part of getting data off z/OS without burdening performance-critical paths. In the environments I work with, it's the standard path for SMF type 30 job accounting, IMS statistical records, and z/OS SYSLOG.</p>
<p>CDP forwards to Logstash, which handles parsing, field extraction, and routing into Elasticsearch. Kafka is an optional middleware message queue:</p>
<ul>
<li><strong>CDP → (Kafka →) Logstash → Elasticsearch</strong></li>
</ul>
<p>The trade-offs: CDP is a separately licensed IBM product, binary SMF records need preprocessing before Logstash can parse them, and the architecture isn't suited to sub-minute latency requirements.</p>
<p>Worth noting alongside CDP:</p>
<ul>
<li>IBM ODP plays the same collector role for performance and availability metrics from whichever OMEGAMON monitors are in your stack: CICS, IMS, Db2, z/OS, network, and storage. Unlike CDP's binary SMF output, ODP converts to JSON natively, so there's no preprocessing step. ODP consists of two components: OMEGAMON Data Broker (a Zowe cross-memory server plugin running on z/OS that collects attributes from OMEGAMON monitoring agents and forwards them to Data Connect); and OMEGAMON Data Connect (a Java application running on or off z/OS that receives data from Data Broker and forwards it to destinations including Elasticsearch; the destination settings are configured here). If OMEGAMON is already in your monitoring stack, ODP is the natural path for getting that telemetry into Elastic.</li>
<li>IBM Z Operational Log and Data Analytics (IZLDA) packages CDP's data streaming capabilities alongside analytics and dashboarding into a single licensed product — I haven't encountered it in production yet, but it's the direction IBM is heading. If your organization is evaluating or has recently licensed IZLDA, the CDP ingest path described above remains the same — IZLDA uses CDP as its underlying collection engine, with Elastic Stack as one of its supported destinations. <a href="https://www.ibm.com/case-studies/bcc-iccrea-group">Gruppo BCC ICCREA's deployment</a> is a published example of IZLDA feeding an Elasticsearch-based monitoring stack.</li>
</ul>
<p>Reference architecture: <a href="https://www.elastic.co/docs/manage-data/ingest/ingest-reference-architectures/ls-for-input">Logstash to Elasticsearch</a>.</p>
<h3 id="imsconnectextensionforimsworkloadsthatcanbypasscdp">IMS Connect Extension: For IMS workloads that can bypass CDP</h3>
<p>Rocket Software's IMS Connect Extension journals IMS transaction activity directly as JSON, bypassing the SMF layer entirely. Events publish to Kafka, and Logstash consumes and indexes. Some organizations standardize all log streams through Kafka (rsyslog → Kafka → Logstash) as an optional resilience pattern.</p>
<ul>
<li><strong>IMS Connect Extension → (Kafka →) Logstash → Elasticsearch</strong></li>
</ul>
<p>This works well for IMS transaction performance data and application-level event streams. JSON output removes the binary parsing problem. Kafka gives you decoupling, replay, and a buffer for downstream maintenance.</p>
<p>The trade-offs: IMS Connect Extension licensing, Kafka infrastructure to operate, and IMS-specific coverage that doesn't help with z/OS SYSLOG or other SMF types.  </p>
<p>One thing I always validate before committing to this pattern is Kafka topic naming. Banks and regulated environments typically have strict topic naming policies, and IMS Connect Extension's default behavior of creating topics itself can clash with those policies. It’s cheaper to discover this before architecture commitment than after.</p>
<h3 id="batchexportforhistoricalanalysisandpoc">Batch export: For historical analysis and PoC</h3>
<p>Export from IMS Problem Investigator or similar tooling to CSV, transfer off-platform, and ingest via Logstash or Elastic Agent file input. This approach has no real-time capability, and it doesn’t require any new z/OS software.</p>
<ul>
<li><strong>Batch export → CSV/FTP → Logstash/Elastic Agent → Elasticsearch</strong></li>
</ul>
<p>This works well for historical analysis, initial PoC work, and demonstrating value before committing to a real-time pipeline. I also use this to get ECS mapping right before the production architecture is in place. It isn’t suitable as a long-term operational observability solution.</p>
<h3 id="linuxonibmzaseparateandeasierpath">Linux on IBM Z: A separate and easier path</h3>
<p>This path is often overlooked. Linux on IBM Z workloads can run standard Elastic Agent (Elastic Agent doesn’t run on native z/OS), no z/OS-specific tooling, no custom pipeline and the full Elastic integration catalog is available.</p>
<p>If you have Linux on IBM Z workloads in your estate, treat them as a separate (and considerably easier) onboarding path.</p>
<p>For all options, please see the <a href="https://www.elastic.co/docs/manage-data/ingest/ingest-reference-architectures">reference architectures with Elastic Agent</a>.</p>
<h3 id="throughputandairgappedthetwoquestionseverymainframeteamasks">Throughput and air-gapped: The two questions every mainframe team asks</h3>
<p><strong>Throughput impact:</strong> Anything that touches z/OS performance-critical paths is a nonstarter for mainframe teams running thousands of transactions per second. All four architectures above use off-platform collection deliberately: CDP, ODP, Kafka/Logstash, batch export, or standard Linux agent. This is the right design for the environment, not a workaround.</p>
<p><strong>Air-gapped environments:</strong> Most mainframe estates I work with are network-restricted to some degree. Elastic's <a href="https://www.elastic.co/docs/manage-data/ingest/ingest-reference-architectures/airgapped-env">air-gapped reference architecture</a> is a documented, supported deployment path.</p>
<h2 id="buildthepipelineanddontstartfromscratch">Build the pipeline, and don't start from scratch</h2>
<p>For the CDP and IMS Connect Extension architectures, log data lands in Elastic reflecting the limited structure of its source. Mainframe log formats are installation-specific and partially structured at best; no off-the-shelf parser covers them, and writing a pipeline from scratch has historically been the largest time sink in any onboarding project. AI has changed that. For mainframe estates that can't call out to a hosted model (which is most of them), both tools below work with self-managed local large language models (LLMs), so the capability is available in air-gapped and network-restricted environments. See the <a href="https://www.elastic.co/docs/explore-analyze/ai-features/llm-guides/local-llms-overview">local LLMs overview</a> for supported options.</p>
<p><strong>Streams: For data already landing in Elastic (available from 9.2).</strong><br />
Open the <strong>Processing</strong> tab for a stream in Kibana, and click <strong>Suggest pipeline</strong>. Within seconds, you're looking at a complete, validated pipeline (Grok or Dissect pattern, date normalization, type conversions, field cleanup) with a live preview of how your actual documents parse through it. Nothing writes to the stream until you confirm. Under the hood, generation runs in two stages: First, deterministic fingerprinting groups your log formats and picks the best parsing approach; second, a reasoning agent iterates to add normalization and cleanup, validating against hard thresholds before handing control to you. The result is a working pipeline you refine, not a starting point you rewrite. The technical detail is in <a href="https://www.elastic.co/observability-labs/blog/elastic-streams-ai-pipeline-generation">How Streams Generates a Log Pipeline in Seconds</a>.</p>
<p><strong>Automatic Import: For building a new custom integration from the ground up (available from 8.18/9.0).</strong> <a href="https://www.elastic.co/docs/explore-analyze/ai-features/automatic-import">Automatic Import</a> takes a different path. You upload sample data, and it generates a complete, deployable Elastic Agent integration package (ingest pipeline, ECS field mappings, event categorization, and related.* field population), which you review and approve before it installs. Where the Streams Suggest Pipeline structures data already arriving in a stream, Automatic Import builds the entire collection path from scratch. Supported input formats include JSON, NDJSON, CSV, and syslog, which covers z/OS SYSLOG directly. Supported collection methods include Kafka, File Stream, TCP, and HTTP Endpoint, making it a natural fit for shops already routing data through Kafka or receiving ODP output over TCP. For mainframe shops adopting Elastic Agent, this removes what was previously weeks of custom integration work.</p>
<p>A less obvious benefit that applies to both tools is continuity. The engineer who wrote your custom GROK pattern eventually moves to another team. A tool that can regenerate a pipeline or integration from sample data is operational resilience.</p>
<p>In terms of scope, Streams works on text. Binary SMF records need to be converted to text or JSON upstream (via CDP or IBM-supplied utilities) before either tool can do anything with them. That conversion happens before Elastic is involved.</p>
<h2 id="configurethedeadletterqueuefromdayone">Configure the dead-letter queue from day one</h2>
<p>Mainframe teams know the <em>dead-letter queue pattern</em> from MQ: When a message can't be delivered or processed, it goes to a holding queue rather than being silently dropped. Elasticsearch has the same concept for ingest pipelines, called the <a href="https://www.elastic.co/docs/manage-data/data-store/data-streams/failure-store">failure store</a>. Configure it from day one, not after your first production incident.</p>
<p>Format drift is a recurring failure mode: vendor updates, sample-config-as-production, new message types appearing in batch windows. The failure store is how you find out about it before your dashboards lie to you. When a log line arrives in an unexpected format and the pipeline can't parse it, the failure store captures the original document with metadata about why it failed. You can query it, alert on its growth rate, and use the captured documents to fix the pipeline.</p>
<p>Without it, parse failures either fall to default handling (records indexed with raw <code>message</code> fields, expected query fields simply absent) or get dropped entirely. Either way, you don't know it's happening.</p>
<p>Configure retention based on how long it takes your team to triage drift, typically days to a couple of weeks. Pair it with an alert on document count or growth rate so the queue itself is the early warning, not something someone has to remember to check.</p>
<h2 id="verifymainframedataqualitybeforeyoubuildonit">Verify mainframe data quality before you build on it</h2>
<p>Don't build dashboards or alerting rules on data you haven't verified. The Data Quality dashboard tells you whether your ECS alignment is real or aspirational.  </p>
<p>For mainframe data, silent type mismatches are common: return codes in hex mapped as keywords, elapsed times stored as strings, timestamps that never coerced to <code>@timestamp</code>. None of these fail at ingest. All of them silently break queries and alerting conditions.</p>
<p>Run the checker against real production data, not synthetic samples. z/OS log variation across batch windows and message types means edge cases only surface under real conditions. Expect to iterate: Find the mismatch, fix the pipeline, and run again. Two or three passes is normal for a new mainframe data stream.</p>
<p>Source quality validation, the failure store, and the Data Quality dashboard are three points on the same loop. Together they give you confidence that the dashboards reflect what's actually happening on the mainframe, not what you hoped your pipeline was producing.</p>
<h2 id="gettingstartedwithmainframedataonboarding">Getting started with mainframe data onboarding</h2>
<p>The mainframe is a first-class observability target, and the path there is more concrete than it was a few years ago. Managed integrations cover IBM MQ. CDP and Kafka-based architectures have well-understood deployment patterns. Streams and Automatic Import remove the blank-page problem for custom pipelines, including in restricted environments through local LLMs. IBM Z Observability Connect is there when the OTel path is in reach.</p>
<p>Recommended order of operations:</p>
<ol>
<li>Validate the source data.  </li>
<li>Use OOTB integrations where they exist.  </li>
<li>Align to ECS early.  </li>
<li>Choose architectures source by source.  </li>
<li>Generate pipelines rather than write them from scratch.  </li>
<li>Configure the failure store from day one.  </li>
<li>Verify before building anything on top.  </li>
</ol>
<p>If your organization is working through this and you'd like to compare notes, or if you're hitting a specific blocker, reach out to your Elastic account team. For the OTel-native path, the <a href="https://www.elastic.co/observability-labs/blog/end-to-end-o11y-from-cloud-native-to-mainframe">End-to-End Observability from Cloud Native to Mainframe</a> deep dive is the next read. To try the building blocks in your own environment, <a href="http://cloud.elastic.co/registration">start a free Elastic Cloud trial</a>.</p>]]></content:encoded>
    <link>https://www.elastic.co/observability-labs/blog/mainframe-data-ingestion</link>
    <guid isPermaLink="false">mainframe-data-ingestion</guid>
    <category><![CDATA[Infrastructure Monitoring]]></category>
    <category><![CDATA[Logs Analytics]]></category>
    <category><![CDATA[Metrics]]></category>
    <dc:creator><![CDATA[Anna Maria Modée]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt2cddefcfe7021906/6a7f0d2ee88c65adaf00b6d0/image2.png" length="0" type="image/png"/>
    <pubDate>Tue, 14 Jul 2026 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Use Elasticsearch as a Drop-In Prometheus Backend for Grafana]]></title>
    <description><![CDATA[Use Elasticsearch as a Prometheus backend for Grafana dashboards, autocomplete, Metrics Drilldown, and alerting without changing PromQL workflows.]]></description>
    <content:encoded><![CDATA[<p>Elasticsearch is already one of the most popular plugins in the Grafana ecosystem, and we have now made it much more powerful for metrics usage.
If you run Prometheus today and use Grafana to visualize your metrics, you can now point Grafana's Prometheus data source directly at Elasticsearch.
No sidecars, no adapters, no pipeline changes required.</p>
<p>Elasticsearch now implements a native Prometheus-compatible API layer, which covers <a href="https://www.elastic.co/blog/prometheus-remote-write-elasticsearch">ingestion via Remote Write</a> and <a href="https://www.elastic.co/blog/elasticsearch-supports-promql">querying via PromQL</a>.
This post shows the Grafana setup end to end.
Companion posts also cover <a href="https://www.elastic.co/blog/promql-queries-run-in-kibana">PromQL in Kibana</a> and the <a href="https://www.elastic.co/blog/prometheus-remote-write-elasticsearch-architecture">Remote Write architecture</a>.</p>
<h2 id="whyuseelasticsearchasaprometheusbackend">Why use Elasticsearch as a Prometheus backend?</h2>
<p>Over the last year, Elasticsearch has become a state-of-the-art metrics store: <a href="https://www.elastic.co/docs/manage-data/data-store/data-streams/time-series-data-stream-tsds">time series data streams</a>, ES|QL's <a href="https://www.elastic.co/docs/reference/query-languages/esql/commands/ts"><code>TS</code> command</a>, and storage and query optimizations that deliver strong <a href="https://www.elastic.co/search-labs/blog/elasticsearch-columnar-metrics-engine-30x-faster-prometheus">metrics performance for Prometheus-style workloads</a>.
The Prometheus-compatible API layer makes that engine reachable through the tools your team already uses.</p>
<p>Many teams have invested heavily in Prometheus-based tooling: dashboards, runbooks that reference PromQL queries, on-call workflows built around Grafana panels.
Elasticsearch's Prometheus-compatible endpoints let you move metrics storage while keeping those Grafana workflows.</p>
<p>This is particularly relevant if you already use Elasticsearch for logs or traces and want to consolidate your observability data into a single platform, while keeping your Grafana-based workflows intact.</p>
<h2 id="whattheelasticsearchprometheusapiincludes">What the Elasticsearch Prometheus API includes</h2>
<p>The Elasticsearch Prometheus API exposes three endpoint groups.</p>
<h3 id="queryapis">Query APIs</h3>
<p>The core query endpoints allow Grafana to evaluate PromQL expressions against data stored in Elasticsearch:</p>
<ul>
<li><code>GET</code> and <code>POST /_prometheus/api/v1/query_range</code> evaluate a PromQL expression over a time window and return matrix results.
This is what powers most Grafana dashboard panels.</li>
<li><code>GET</code> and <code>POST /_prometheus/api/v1/query</code> evaluate a PromQL expression at a single point in time and return vector results.</li>
</ul>
<p>Both endpoints implement the standard Prometheus response envelope, including result types (vector, matrix, scalar, string), status codes, and error handling.
For <code>POST</code>, send parameters in an <code>application/x-www-form-urlencoded</code> body, matching Prometheus client behavior.</p>
<h3 id="metadataapis">Metadata APIs</h3>
<p>Grafana's metric explorer, autocomplete, and variable dropdowns rely on metadata endpoints to discover what's available.
Elasticsearch supports:</p>
<ul>
<li><code>GET</code> and <code>POST /_prometheus/api/v1/series</code> return time series matching label selectors.</li>
<li><code>GET</code> and <code>POST /_prometheus/api/v1/labels</code> return all available label names.</li>
<li><code>GET /_prometheus/api/v1/label/{name}/values</code> returns all values for a given label.</li>
<li><code>GET /_prometheus/api/v1/metadata</code> returns type and help text for each metric name.</li>
</ul>
<p>These endpoints power autocomplete and the metric browser in Grafana.
The <code>/metadata</code> endpoint additionally enables Grafana's <a href="https://grafana.com/docs/grafana/latest/explore/explore-metrics/">Metrics Drilldown</a>: an interactive metric explorer that displays all available metrics as a grid of live sparklines and lets you drill into any metric without writing a PromQL query.</p>
<h3 id="indexprefiltering">Index pre-filtering</h3>
<p>All query and metadata endpoints accept an optional <code>{index}</code> path segment immediately after <code>/_prometheus/</code>, for example:</p>
<pre><code>GET /_prometheus/metrics-prod-*/api/v1/query_range
</code></pre>
<p>This pre-filters the Elasticsearch indices that the PromQL query runs against before any expression evaluation happens.
Scoping queries to the relevant data can reduce query work for dashboards that span large volumes of metrics across different data streams.</p>
<p>You can configure a separate Grafana data source per index pattern to give teams scoped access to their own metrics.</p>
<h3 id="remotewriteingestion">Remote Write ingestion</h3>
<p>Elasticsearch also implements the <a href="https://www.elastic.co/docs/manage-data/data-store/data-streams/tsds-ingest-prometheus-remote-write">Prometheus Remote Write protocol</a>, which lets you ship metrics from Prometheus to Elasticsearch using the standard <code>remote_write</code> configuration.
Adding Elasticsearch as a remote write destination requires a single block in your existing Prometheus config:</p>
<pre><code>remote_write:
  - url: "&lt;es_endpoint&gt;/_prometheus/api/v1/write"
    authorization:
      type: ApiKey
      credentials: &lt;api_key&gt;
</code></pre>
<p>Metrics are stored in the <code>metrics-generic.prometheus-default</code> data stream by default.
You can route metrics from different Prometheus instances or environments into separate data streams using the dataset and namespace path segments:</p>
<ul>
<li><code>POST /_prometheus/metrics/{dataset}/api/v1/write</code> stores metrics in <code>metrics-{dataset}.prometheus-default</code></li>
<li><code>POST /_prometheus/metrics/{dataset}/{namespace}/api/v1/write</code> stores metrics in <code>metrics-{dataset}.prometheus-{namespace}</code></li>
</ul>
<h2 id="howtoconnectgrafanatoelasticsearch">How to connect Grafana to Elasticsearch</h2>
<h3 id="step1createaserverlessproject">Step 1: Create a serverless project</h3>
<p>Sign in to <a href="https://cloud.elastic.co">cloud.elastic.co</a> and create a new <strong>Observability</strong> serverless project.
Once the project is ready, you will land directly in Kibana.
To find the Elasticsearch endpoint, go back to the Elastic Cloud console, open <strong>Manage &gt; Application endpoints, cluster and component IDs</strong>, and click the copy icon next to <strong>Elasticsearch</strong>.
The endpoint looks like:</p>
<pre><code>https://&lt;project-id&gt;.es.&lt;region&gt;.&lt;provider&gt;.elastic.cloud
</code></pre>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt37ae7707a46a689f/6a7f1a1c42a11755a895c2f5/elasticsearch-endpoint.png" alt="Elastic Cloud console showing the Application endpoints panel with the Elasticsearch endpoint and copy button" /></p>
<h3 id="step2createapikeys">Step 2: Create API keys</h3>
<p>Create two API keys with scoped privileges: one for ingestion, one for querying.
Using separate keys means a leaked Grafana key cannot be used to write data, and a leaked ingest key cannot be used to read it.</p>
<p>In your project, open <strong>Admin and settings</strong> (the ⚙️ icon at the bottom left of the side nav), go to <strong>API keys</strong>, and create the first key.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt0c7a05f18adefd4d/6a7f1a1f448e4e7e355c0b5a/create-api-key.png" alt="Create API key dialog showing the name, type, and role descriptor for the ingest key" /></p>
<p><strong>Ingest key</strong> (<code>prometheus-remote-write</code>): restricts access to writing metrics data streams only.
In the <strong>Control security privileges</strong> section, paste the following role descriptor:</p>
<pre><code>{
  "ingest": {
    "indices": [
      {
        "names": ["metrics-*"],
        "privileges": ["auto_configure", "create_doc"]
      }
    ]
  }
}
</code></pre>
<p>Create a second key for Grafana in the same section.</p>
<p><strong>Query key</strong> (<code>prometheus-grafana</code>): restricts access to reading metrics data streams only.</p>
<pre><code>{
  "query": {
    "indices": [
      {
        "names": ["metrics-*"],
        "privileges": ["read", "view_index_metadata"]
      }
    ]
  }
}
</code></pre>
<p>Copy both key values before closing. You will not be able to retrieve them again.</p>
<h3 id="step3runprometheusandgrafana">Step 3: Run Prometheus and Grafana</h3>
<p>Create a <code>prometheus.yml</code> that scrapes Prometheus itself and forwards those metrics to Elasticsearch.
Replace <code>&lt;es_endpoint&gt;</code> with the endpoint from Step 1 and <code>&lt;ingest_api_key&gt;</code> with the ingest key from Step 2:</p>
<pre><code>global:
  scrape_interval: 15s

scrape_configs:
  - job_name: "prometheus"
    static_configs:
      - targets: ["localhost:9090"]

remote_write:
  - url: "&lt;es_endpoint&gt;/_prometheus/api/v1/write"
    authorization:
      type: ApiKey
      credentials: &lt;ingest_api_key&gt;
</code></pre>
<p>Next, create the Grafana provisioning directories:</p>
<pre><code>mkdir -p grafana/provisioning/datasources grafana/provisioning/dashboards
</code></pre>
<p>Then create a Grafana data source configuration that points at the Elasticsearch Prometheus API.
Create <code>grafana/provisioning/datasources/datasource.yml</code>, replacing <code>&lt;es_endpoint&gt;</code> and <code>&lt;query_api_key&gt;</code> with the values from Steps 1 and 2:</p>
<pre><code>apiVersion: 1

datasources:
  - name: Elasticsearch
    type: prometheus
    access: proxy
    url: "&lt;es_endpoint&gt;/_prometheus"
    uid: elasticsearch-prometheus
    isDefault: true
    jsonData:
      httpHeaderName1: Authorization
    secureJsonData:
      httpHeaderValue1: "ApiKey &lt;query_api_key&gt;"
</code></pre>
<p>This configures a Prometheus-type data source backed by Elasticsearch.
Grafana sends Prometheus queries with <code>POST</code> by default, which Elasticsearch accepts on authenticated HTTPS endpoints such as Serverless.</p>
<p>Create <code>grafana/provisioning/dashboards/dashboards.yml</code> to tell Grafana where to find provisioned dashboards:</p>
<pre><code>apiVersion: 1

providers:
  - name: default
    type: file
    options:
      path: /var/lib/grafana/dashboards
</code></pre>
<p>Finally, create a <code>docker-compose.yml</code> to start everything:</p>
<pre><code>services:
  prometheus:
    image: prom/prometheus:latest
    ports:
      - "9090:9090"
    volumes:
      - ./prometheus.yml:/etc/prometheus/prometheus.yml:ro

  download-dashboard:
    image: curlimages/curl:latest
    user: root
    volumes:
      - dashboards:/dashboards
    command: &gt;
      sh -c 'curl -fsSL https://grafana.com/api/dashboards/3662/revisions/2/download
      | sed "s/\$${DS_THEMIS}/elasticsearch-prometheus/g"
      &gt; /dashboards/prometheus-overview.json'

  grafana:
    image: grafana/grafana:latest
    ports:
      - "3000:3000"
    depends_on:
      download-dashboard:
        condition: service_completed_successfully
    environment:
      - GF_SECURITY_ADMIN_PASSWORD=grafana
    volumes:
      - ./grafana/provisioning:/etc/grafana/provisioning:ro
      - dashboards:/var/lib/grafana/dashboards:ro

volumes:
  dashboards:
</code></pre>
<p>The <code>download-dashboard</code> service fetches the <a href="https://grafana.com/grafana/dashboards/3662-prometheus-2-0-overview/">Prometheus 2.0 Overview</a> dashboard from the Grafana marketplace and patches it to use the Elasticsearch data source.
The <code>sed</code> replaces the dashboard's <code>${DS_THEMIS}</code> data source placeholder with our data source UID.
This is needed because Grafana's provisioning does not resolve these placeholders on its own (<a href="https://github.com/grafana/grafana/issues/10786">grafana#10786</a>).
Grafana waits for the download to finish before starting.</p>
<p>Start both with:</p>
<pre><code>docker compose up -d
</code></pre>
<p>Prometheus will start scraping its own metrics and shipping them to Elasticsearch every 15 seconds.
Give it one or two scrape intervals before opening the dashboard.</p>
<h3 id="step4openthedashboard">Step 4: Open the dashboard</h3>
<p>Open Grafana at <code>http://localhost:3000</code> and log in with <code>admin</code> / <code>grafana</code>.
Go to <strong>Dashboards</strong> and open <strong>Prometheus 2.0 Overview</strong>.</p>
<p>The dashboard shows your Prometheus self-monitoring metrics, pulled from Elasticsearch via PromQL queries.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blte3f55bf517eea926/6a7f1a22bd21988de17584a9/grafana-dashboard.png" alt="Grafana dashboard with Elasticsearch as the Prometheus data source, showing Prometheus self-monitoring metrics rendered by PromQL queries" /></p>
<h3 id="step5exploremetricswithgrafanasmetricsdrilldown">Step 5: Explore metrics with Grafana's Metrics Drilldown</h3>
<p>Because Elasticsearch implements the Prometheus metadata and discovery endpoints, Grafana's <a href="https://grafana.com/docs/grafana/latest/explore/explore-metrics/">Metrics Drilldown</a> works out of the box.</p>
<p>In Grafana, go to <strong>Drilldown &gt; Metrics</strong> in the left-hand navigation and select <strong>Elasticsearch</strong> as the data source.
Grafana loads all available metrics from Elasticsearch and displays them as a grid of live sparklines.
From there you can filter by label, search by name, and drill into any metric without writing PromQL.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt8e51ae030a51c63e/6a7f1a26eab5be665d20aaf0/grafana-drilldown.png" alt="Grafana Metrics Drilldown showing all Prometheus metrics from Elasticsearch as a grid of sparklines" /></p>
<h2 id="currentlimitationsandwhatsnext">Current limitations and what's next</h2>
<p>This is the first implementation and updates should be expected.
All of the following are actively being worked on:</p>
<h3 id="promqlcoverageisnotyetcomplete">PromQL coverage is not yet complete</h3>
<p>Queries using group modifiers (for example, <code>on(instance, job)</code>), set operators (<code>or</code>, <code>and</code>, <code>unless</code>), and certain functions like <code>topk</code> are not yet supported.</p>
<h3 id="formencodedposthasdeploymentrequirements">Form-encoded POST has deployment requirements</h3>
<p><code>POST</code> requests with <code>application/x-www-form-urlencoded</code> bodies require security enabled, TLS on the Elasticsearch HTTP interface, and an authenticated request.
Serverless meets these requirements out of the box.
If TLS terminates before Elasticsearch and the node sees plain HTTP, use <code>GET</code> with query-string parameters instead.</p>
<h3 id="onlyremotewritev1issupported">Only Remote Write v1 is supported</h3>
<p>Remote Write v2 support is planned.</p>
<h3 id="instantqueriesarenotpointintimeyet">Instant queries are not point-in-time yet</h3>
<p>The instant query endpoint currently runs a short range query under the hood and returns the last sample.
It will be replaced with a proper point-in-time evaluation.</p>
<p>Coming next: broader PromQL function and operator coverage, Remote Write v2, and exemplar endpoints.</p>
<h2 id="frequentlyaskedquestions">Frequently asked questions</h2>
<p><strong>Can Grafana query Prometheus metrics stored in Elasticsearch?</strong>
Yes.
Grafana can use Elasticsearch as a Prometheus data source when the URL points to <code>/_prometheus</code>.
Queries use PromQL and return the standard Prometheus response format for Grafana dashboards, variables, Metrics Drilldown, and alerting.</p>
<p><strong>Do I need to change Prometheus or Grafana dashboards to use Elasticsearch?</strong>
You do not need to rewrite PromQL queries or dashboard panels for common Grafana use cases.
Configure Prometheus Remote Write to send metrics to Elasticsearch, then point Grafana's Prometheus data source at the Elasticsearch <code>/_prometheus</code> endpoint.</p>
<p><strong>Why use Elasticsearch instead of a separate Prometheus long term storage backend?</strong>
Using Elasticsearch as a Prometheus backend lets you store metrics with logs and traces under the same access controls and retention model.
Recent work on the Elasticsearch metrics engine also delivers strong performance for Prometheus-style workloads.
For the benchmark details, see the <a href="https://www.elastic.co/search-labs/blog/elasticsearch-columnar-metrics-engine-30x-faster-prometheus">Elasticsearch metrics performance post</a>.</p>
<p><strong>What PromQL features are supported in Elasticsearch today?</strong>
Elasticsearch supports common PromQL query patterns used by Grafana dashboards.
Advanced group modifiers, set operators, and <code>topk</code> are not yet supported.</p>
<p><strong>Can I limit Grafana queries to specific Elasticsearch indices?</strong>
Yes.
Add an index pattern after <code>/_prometheus/</code>, such as <code>/_prometheus/metrics-prod-*/api/v1/query_range</code>.
This pre-filters the Elasticsearch indices before PromQL evaluation and can reduce query work for large metrics deployments.</p>
<h2 id="prometheusapiavailability">Prometheus API availability</h2>
<p>The Prometheus-compatible API is available now on <a href="https://cloud.elastic.co/serverless-registration?onboarding_token=observability">Elasticsearch Serverless</a> with no additional configuration.</p>
<p>If you run into issues or have feedback, open an issue on the <a href="https://github.com/elastic/elasticsearch">Elasticsearch repository</a>.</p>]]></content:encoded>
    <link>https://www.elastic.co/observability-labs/blog/query-prometheus-metrics-grafana-elasticsearch</link>
    <guid isPermaLink="false">query-prometheus-metrics-grafana-elasticsearch</guid>
    <category><![CDATA[Metrics]]></category>
    <category><![CDATA[Infrastructure Monitoring]]></category>
    <dc:creator><![CDATA[Felix Barnsteiner]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blta5c8286ee6ba2a92/6a7f1a28ead8ec2d18baac4c/header.jpg" length="0" type="image/jpeg"/>
    <pubDate>Thu, 25 Jun 2026 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Migrate Logstash Pipelines from Azure Event Hubs to OTel Collector Kafka Receiver]]></title>
    <description><![CDATA[Step-by-step guide to migrating Logstash pipelines from the Azure Event Hubs plugin to the OpenTelemetry Collector Kafka receiver.]]></description>
    <content:encoded><![CDATA[<h2 id="introduction">Introduction</h2>
<p>This article is a companion guide to the <a href="https://www.elastic.co/observability-labs/blog/migrate-logstash-pipelines-from-azure-event-hubs-to-kafka-plugin">Logstash Azure Event Hubs to Kafka input plugin migration</a>, covering an alternative path: replacing <code>logstash-input-azure_event_hubs</code> with the OpenTelemetry Collector <code>kafka</code> receiver to consume from the Azure Event Hubs Kafka endpoint. For the reasons to migrate, authentication considerations, and key behavior changes such as offset handling, refer to the original article.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt75cfba706792ba52/6a7f0d796693f89756663f69/amqp-vs-kafka_OTel.png" alt="AMQP vs Kafka protocol path comparison in Otel Collector connected to Azure Event Hubs" /></p>
<blockquote>
  <p><strong>Reference</strong>: For detailed OTel Kafka receiver configuration options or parameter default values, see the <a href="https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/receiver/kafkareceiver">Kafka Receiver README</a>.</p>
</blockquote>
<h2 id="convertingyourconfiguration">Converting your configuration</h2>
<h3 id="tlsconfiguration">TLS configuration</h3>
<p>Azure Event Hubs requires TLS for all Kafka connections on port 9093. The <code>tls: {}</code> block enables TLS with default settings (system CA certificates, no client certificate), which is sufficient for Azure Event Hubs. Omitting this block will cause the connection to fail because the broker expects a TLS handshake.</p>
<h3 id="encoding">Encoding</h3>
<p>The <code>encoding</code> field controls how the receiver interprets each Kafka message payload. For events consumed from Azure Event Hubs, the most common options are:</p>
<ul>
<li><code>text</code>: decodes the payload as text and inserts it as the body of a log record. Uses UTF-8 by default; use <code>text_&lt;ENCODING&gt;</code> (e.g., <code>text_shift_jis</code>) for other character sets.</li>
<li><code>raw</code>: inserts the payload bytes as-is into the log record body.</li>
<li><code>json</code>: decodes the payload as JSON and inserts it as the log record body.</li>
<li><code>azure_resource_logs</code>: converts Azure Resource Logs format to OpenTelemetry format.</li>
</ul>
<p>Additional encodings such as <code>otlp_proto</code>, <code>otlp_json</code>, and trace-specific formats (<code>jaeger_proto</code>, <code>zipkin_json</code>, etc.) are also available. See the <a href="https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/receiver/kafkareceiver">Kafka Receiver README</a> for the full list.</p>
<h3 id="basicconfiguration">Basic configuration</h3>
<p>Minimal configuration to consume logs from one Event Hub with SASL/PLAIN.</p>
<pre><code>receivers:
  kafka:
    brokers:
      - "&lt;NAMESPACE&gt;.servicebus.windows.net:9093"
    group_id: "&lt;CONSUMER_GROUP_NAME&gt;"
    auth:
      sasl:
        username: "$ConnectionString"
        password: "Endpoint=sb://&lt;NAMESPACE&gt;.servicebus.windows.net/;SharedAccessKeyName=&lt;ACCESS_KEY_NAME&gt;;SharedAccessKey=&lt;ACCESS_KEY&gt;"
        mechanism: "PLAIN"
    tls: {}
    logs:
      topics:
        - "&lt;EVENT_HUB_NAME&gt;"
      encoding: text
</code></pre>
<h3 id="advancedconfiguration">Advanced configuration</h3>
<p>Example with multiple Event Hubs.</p>
<pre><code>receivers:
  kafka/eh1:
    brokers:
      - "&lt;NAMESPACE&gt;.servicebus.windows.net:9093"
    group_id: "&lt;CONSUMER_GROUP_1&gt;"
    auth:
      sasl:
        username: "$ConnectionString"
        password: "Endpoint=sb://&lt;NAMESPACE&gt;.servicebus.windows.net/;SharedAccessKeyName=&lt;KEY_1&gt;;SharedAccessKey=&lt;ACCESS_KEY_1&gt;"
        mechanism: "PLAIN"
    tls: {}
    logs:
      topics:
        - "&lt;EVENT_HUB_1&gt;"
      encoding: text

  kafka/eh2:
    brokers:
      - "&lt;NAMESPACE&gt;.servicebus.windows.net:9093"
    group_id: "&lt;CONSUMER_GROUP_2&gt;"
    auth:
      sasl:
        username: "$ConnectionString"
        password: "Endpoint=sb://&lt;NAMESPACE&gt;.servicebus.windows.net/;SharedAccessKeyName=&lt;KEY_2&gt;;SharedAccessKey=&lt;ACCESS_KEY_2&gt;"
        mechanism: "PLAIN"
    tls: {}
    logs:
      topics:
        - "&lt;EVENT_HUB_2&gt;"
      encoding: text
</code></pre>
<h2 id="configurationparametersmapping">Configuration parameters mapping</h2>
<p>The following section maps each <a href="https://www.elastic.co/guide/en/logstash/current/plugins-inputs-azure_event_hubs.html"><code>logstash-input-azure_event_hubs</code></a> parameter to its OpenTelemetry Collector <code>kafka</code> receiver equivalent.</p>
<ol>
<li><p><a href="https://www.elastic.co/guide/en/logstash/current/plugins-inputs-azure_event_hubs.html#plugins-inputs-azure_event_hubs-checkpoint_interval"><code>checkpoint_interval</code></a>: Direct mapping to <code>autocommit.interval</code>.</p>
<p><strong>Units</strong>: Azure <code>checkpoint_interval</code> is in <strong>seconds</strong>. OTel <code>autocommit.interval</code> requires a duration string (e.g., <code>10s</code>, <code>500ms</code>).</p>
<p>Azure config:</p>
<pre><code>input {
    azure_event_hubs {
        # ... other params ...
        checkpoint_interval =&gt; 10 # Default 5
    }
}
</code></pre>
<p>OTel receiver equivalent:</p>
<pre><code>receivers:
  kafka:
    # ... other params ...
    autocommit:
      interval: 10s # Default 1s
</code></pre></li>
<li><p><a href="https://www.elastic.co/guide/en/logstash/current/plugins-inputs-azure_event_hubs.html#plugins-inputs-azure_event_hubs-initial_position"><code>initial_position</code></a>: Maps to <code>initial_offset</code>.</p>
<p>Azure config:</p>
<pre><code>input {
    azure_event_hubs {
        initial_position =&gt; "end"
    }
}
</code></pre>
<p>OTel receiver equivalent:</p>
<pre><code>receivers:
  kafka:
    initial_offset: latest
</code></pre>
<p>Value mapping:</p>
<p>| Azure value | OTel value |
| --- | --- |
| <code>beginning</code> | <code>earliest</code> |
| <code>end</code> | <code>latest</code> (default) |
| <code>look_back</code> | Not directly supported |</p>
<p><strong>Note:</strong> Since the Kafka receiver can't read the old Blob Storage checkpoints, it treats the migration as a first-time connection. To avoid reprocessing data the legacy plugin already handled, set <code>initial_offset: latest</code> for the initial deployment.</p></li>
<li><p><a href="https://www.elastic.co/guide/en/logstash/current/plugins-inputs-azure_event_hubs.html#plugins-inputs-azure_event_hubs-max_batch_size"><code>max_batch_size</code></a>: No direct 1:1 mapping.</p>
<p>In OTel, the maximum batch of events processed cannot be directly controlled by the receiver. The receiver only controls how much data is read per fetch request using <code>min_fetch_size</code>, <code>max_fetch_size</code>, and <code>max_fetch_wait</code>.</p>
<p>The actual event batching happens at the processing layer via the <a href="https://github.com/open-telemetry/opentelemetry-collector/blob/main/processor/batchprocessor/README.md"><code>batch processor</code></a>, which groups telemetry at the configured pipeline stage.</p>
<p><strong>Units</strong>: <code>min_fetch_size</code> and <code>max_fetch_size</code> are in <strong>bytes</strong>. <code>max_fetch_wait</code> uses duration strings (e.g., <code>250ms</code>). <code>send_batch_size</code> is the <strong>number of records</strong>. <code>timeout</code> uses duration strings (e.g., <code>5s</code>).</p>
<p>Azure config:</p>
<pre><code>input {
    azure_event_hubs {
        max_batch_size =&gt; 125
    }
}
</code></pre>
<p>OTel receiver example:</p>
<pre><code>receivers:
  kafka:
    max_fetch_size: 2097152  # bytes (2 MiB)
    max_fetch_wait: 250ms

processors:
  batch:
    send_batch_size: 125  # number of log records
</code></pre></li>
<li><p><a href="https://www.elastic.co/guide/en/logstash/current/plugins-inputs-azure_event_hubs.html#plugins-inputs-azure_event_hubs-threads"><code>threads</code></a>: No direct mapping.</p>
<p>Event Hubs distribute work by partition. A single Collector Kafka client can read from multiple partitions in parallel because the underlying Kafka client (<a href="https://pkg.go.dev/github.com/twmb/franz-go">franz-go</a>) uses internal goroutines to fetch and process partition data concurrently. This concurrency is handled internally and is not configurable via a user-facing <code>threads</code> setting.</p></li>
<li><p><a href="https://www.elastic.co/guide/en/logstash/current/plugins-inputs-azure_event_hubs.html#plugins-inputs-azure_event_hubs-decorate_events"><code>decorate_events</code></a>: Not supported by Kafka receiver.</p></li>
</ol>
<h2 id="performancecomparison">Performance comparison</h2>
<p>These results use the same test environment described in the <a href="https://www.elastic.co/observability-labs/blog/migrate-logstash-pipelines-from-azure-event-hubs-to-kafka-plugin">companion article</a>: same Event Hub namespace, same number of partitions, and same batch/thread configuration. The absolute numbers are environment-specific, but the relative difference is what matters.</p>
<p>| <strong>Component</strong>                      | <strong>Payload</strong> | <strong>Throughput (events/s)</strong> |
| ---------------------------------- | ----------- | ------------------------- |
| Logstash <code>azure_event_hubs</code> plugin | 100B        | ~5700                    |
| OTel Collector <code>kafka</code> receiver    | 100B        | ~10900                   |
| Logstash <code>azure_event_hubs</code> plugin | 1KB         | ~1500                    |
| OTel Collector <code>kafka</code> receiver    | 1KB         | ~1900                    |
| Logstash <code>azure_event_hubs</code> plugin | 10KB        | ~170                     |
| OTel Collector <code>kafka</code> receiver    | 10KB        | ~190                     |</p>
<p>Across all payload sizes, the OTel Collector <code>kafka</code> receiver outperforms the Logstash <code>azure_event_hubs</code> plugin, with the largest gain at small payloads (~1.9x at 100B) where protocol overhead dominates, narrowing at larger sizes (~1.3x at 1KB, ~1.1x at 10KB). It does not reach the throughput of the Logstash <code>kafka</code> plugin from the <a href="https://www.elastic.co/observability-labs/blog/migrate-logstash-pipelines-from-azure-event-hubs-to-kafka-plugin">companion article</a>, but it improves on the legacy plugin across all tested payload sizes. Combined with the removal of the Blob Storage and GPv2 dependencies, the OTel Collector path removes two pieces of infrastructure that need to be provisioned, secured, and monitored.</p>
<h2 id="conclusions">Conclusions</h2>
<p>Both migration paths eliminate the Blob Storage checkpoint dependency and improve throughput over the legacy <code>azure_event_hubs</code> plugin. The Logstash <code>kafka</code> plugin is the lower-friction option: the configuration change is minimal, the offset model carries over, and it delivers the highest throughput of the options tested. The OTel Collector <code>kafka</code> receiver is the better fit if you want to remove Logstash from the pipeline entirely and align with OpenTelemetry. It trades a lower peak throughput and no <code>decorate_events</code> equivalent for a vendor-neutral ingestion layer that can run alongside other OTel Collector pipelines in the same Collector.</p>
<h2 id="nextsteps">Next steps</h2>
<p>With the GPv1 retirement deadline (October 2026) approaching, starting this migration sooner reduces the time spent managing storage infrastructure that is no longer needed.</p>
<p>If any issues arise during migration:</p>
<ul>
<li><p><strong>Usage questions or help with configuration</strong>: Post on the <a href="https://github.com/open-telemetry/opentelemetry-collector/discussions">OpenTelemetry Collector GitHub Discussions</a> or the <a href="https://discuss.elastic.co/c/observability/">Elastic Discuss forum</a>.</p></li>
<li><p><strong>Bugs or unexpected behavior in the Kafka receiver</strong>: Open an issue in the <a href="https://github.com/open-telemetry/opentelemetry-collector-contrib/issues">opentelemetry-collector-contrib</a> repository.</p></li>
</ul>
<h2 id="relatedresources">Related resources</h2>
<ul>
<li><a href="https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/receiver/kafkareceiver">Kafka receiver documentation</a>: Full reference for all OTel Collector <code>kafka</code> receiver configuration parameters.</li>
<li><a href="https://www.elastic.co/guide/en/logstash/current/plugins-inputs-azure_event_hubs.html">Azure Event Hubs input plugin documentation</a>: Full reference for the legacy plugin being replaced.</li>
<li><a href="https://www.elastic.co/observability-labs/blog/migrate-logstash-pipelines-from-azure-event-hubs-to-kafka-plugin">Logstash Azure Event Hubs to Kafka input plugin migration</a>: Companion guide covering the alternative migration path to the <code>logstash-input-kafka</code> plugin.</li>
<li><a href="https://learn.microsoft.com/en-us/azure/event-hubs/azure-event-hubs-kafka-overview">Azure Event Hubs for Apache Kafka overview</a>: Microsoft's documentation on the built-in Kafka endpoint in Event Hubs.</li>
<li><a href="https://learn.microsoft.com/en-us/azure/event-hubs/event-hubs-quotas#basic-vs-standard-vs-premium-vs-dedicated-tiers">Event Hubs quotas and tier comparison</a>: Tier requirements for Kafka protocol support.</li>
</ul>]]></content:encoded>
    <link>https://www.elastic.co/observability-labs/blog/migrate-logstash-pipelines-from-azure-event-hubs-to-otel-collector-kafka-receiver</link>
    <guid isPermaLink="false">migrate-logstash-pipelines-from-azure-event-hubs-to-otel-collector-kafka-receiver</guid>
    <category><![CDATA[OpenTelemetry]]></category>
    <category><![CDATA[Logs Analytics]]></category>
    <category><![CDATA[Infrastructure Monitoring]]></category>
    <dc:creator><![CDATA[Álex Cámara]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt5714aa83da345966/6a7f0d7ce88c65c3c200b6ec/elastic-blog-otel-kafka.jpeg" length="0" type="image/jpeg"/>
    <pubDate>Fri, 08 May 2026 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Ship Prometheus Metrics to Elasticsearch with Remote Write]]></title>
    <description><![CDATA[Elasticsearch natively supports Prometheus Remote Write. Add a single remote_write block to your Prometheus config and use Elasticsearch as Prometheus-compatible long-term storage.]]></description>
    <content:encoded><![CDATA[<p>Prometheus has a well-defined protocol for shipping metrics to external storage: <a href="https://prometheus.io/docs/specs/prw/remote_write_spec/">Remote Write</a>.
Elasticsearch now implements this protocol natively, so you can add it as a <code>remote_write</code> destination with a single config block.</p>
<p>This lets you bring your Prometheus metrics into the same cluster which can also store logs, traces, and other data.
One storage backend, one set of access controls, one place to query.</p>
<h2 id="whystoreprometheusmetricsinelasticsearch">Why store Prometheus metrics in Elasticsearch?</h2>
<p>Prometheus local storage is designed for short retention, typically 15 to 30 days.
For anything beyond that, you need a remote storage backend.</p>
<p>Elasticsearch's time series data streams (TSDS) are built for highly efficient long term metrics storage: automatic rollover, time-based partitioning, compression via index sorting, and downsampling to reduce storage costs as data ages.
Your Prometheus scrape configs stay the same.</p>
<p>Recent Elasticsearch releases have significantly reduced the storage footprint for metrics.
A dedicated post with the numbers is coming soon.</p>
<p>On the query side, ES|QL embraces PromQL: a built-in <code>PROMQL</code> function lets your existing queries run unchanged, while the rest of ES|QL is available when you want joins, aggregations, or transformations that span multiple datasets.</p>
<p>And because metrics land in the same store as your logs, traces, and profiling data, correlating signals across types becomes a single query rather than a cross-system investigation.</p>
<h2 id="howitworks">How it works</h2>
<p>For a detailed look at what happens inside Elasticsearch when a Remote Write request arrives — protobuf parsing, metric type inference, TSDS mapping, and data stream routing — see <a href="https://www.elastic.co/blog/prometheus-remote-write-elasticsearch-architecture">How Prometheus Remote Write Ingestion Works in Elasticsearch</a>.</p>
<p>Prometheus sends metrics to Elasticsearch via the standard Remote Write protocol (v1).
The endpoint accepts protobuf-encoded, snappy-compressed <code>WriteRequest</code> payloads.</p>
<p>Each sample becomes an Elasticsearch document in a pre-defined time series data stream.
Prometheus labels become TSDS dimensions.
The metric value is stored in a typed field under <code>metrics.&lt;metric_name&gt;</code>.</p>
<p>Elasticsearch infers the metric type (counter vs gauge) from naming conventions.
Names ending in <code>_total</code>, <code>_sum</code>, <code>_count</code>, or <code>_bucket</code> are treated as counters.
Everything else is treated as a gauge.</p>
<h2 id="settingitup">Setting it up</h2>
<h3 id="step1getanelasticsearchendpoint">Step 1: Get an Elasticsearch endpoint</h3>
<p>You need an Elasticsearch cluster with the Prometheus endpoints enabled.
The simplest option is Elastic Cloud Serverless, where this works out of the box.</p>
<p>For serverless: sign in to <a href="https://cloud.elastic.co">cloud.elastic.co</a>, create an Observability project, and copy the Elasticsearch endpoint from the project settings page.
The endpoint looks like <code>https://&lt;project-id&gt;.es.&lt;region&gt;.&lt;provider&gt;.elastic.cloud</code>.</p>
<h3 id="step2createanapikey">Step 2: Create an API key</h3>
<p>Create an API key scoped to writing metrics data streams only.
In your Elastic Cloud Serverless project, go to <strong>Admin and settings</strong> (the gear icon at the bottom left of the side nav), then <strong>API keys</strong>.</p>
<p>Use the following role descriptor in the <strong>Control security privileges</strong> section:</p>
<pre><code>{
  "ingest": {
    "indices": [
      {
        "names": ["metrics-*"],
        "privileges": ["auto_configure", "create_doc"]
      }
    ]
  }
}
</code></pre>
<p>Copy the key value before closing the dialog.
You will not be able to retrieve it again.</p>
<h3 id="step3configureprometheus">Step 3: Configure Prometheus</h3>
<p>Add the following <code>remote_write</code> block to your <code>prometheus.yml</code>:</p>
<pre><code>remote_write:
  - url: "https://YOUR_ES_ENDPOINT/_prometheus/api/v1/write"
    authorization:
      type: ApiKey
      credentials: YOUR_API_KEY
</code></pre>
<p>That's it.
Prometheus will start shipping metrics to Elasticsearch on the next scrape interval.</p>
<p>If you use <a href="https://grafana.com/docs/alloy/latest/">Grafana Alloy</a> instead of Prometheus, the equivalent configuration is:</p>
<pre><code>prometheus.remote_write "elasticsearch" {
  endpoint {
    url = "https://YOUR_ES_ENDPOINT/_prometheus/api/v1/write"
    headers = {"Authorization" = "ApiKey YOUR_API_KEY"}
  }
}
</code></pre>
<h2 id="routingmetricstoseparatedatastreams">Routing metrics to separate data streams</h2>
<p>By default, all metrics land in <code>metrics-generic.prometheus-default</code>.
You can route metrics from different environments or teams into separate data streams using the dataset and namespace path segments in the URL.</p>
<p>The three URL patterns are:</p>
<ul>
<li><code>/_prometheus/api/v1/write</code> routes to <code>metrics-generic.prometheus-default</code></li>
<li><code>/_prometheus/metrics/{dataset}/api/v1/write</code> routes to <code>metrics-{dataset}.prometheus-default</code></li>
<li><code>/_prometheus/metrics/{dataset}/{namespace}/api/v1/write</code> routes to <code>metrics-{dataset}.prometheus-{namespace}</code></li>
</ul>
<p>For example, using <code>/_prometheus/metrics/infrastructure/production/api/v1/write</code> routes data to <code>metrics-infrastructure.prometheus-production</code>.</p>
<p>This is useful for separating production from staging metrics, or giving different teams their own data streams with independent lifecycle policies.</p>
<h2 id="whatgetsstored">What gets stored</h2>
<p>Here is what a sample document looks like in Elasticsearch:</p>
<pre><code>{
  "@timestamp": "2026-04-02T10:30:00.000Z",
  "data_stream": {
    "type": "metrics",
    "dataset": "generic.prometheus",
    "namespace": "default"
  },
  "labels": {
    "__name__": "prometheus_http_requests_total",
    "handler": "/api/v1/query",
    "code": "200",
    "instance": "localhost:9090",
    "job": "prometheus"
  },
  "metrics": {
    "prometheus_http_requests_total": 42
  }
}
</code></pre>
<p>Labels map to keyword fields that serve as TSDS <a href="https://www.elastic.co/docs/manage-data/data-store/data-streams/time-series-data-stream-tsds#time-series-dimension">dimensions</a>.
The metric value is stored under <code>metrics.&lt;metric_name&gt;</code> with the inferred <code>time_series_metric</code> type (counter or gauge).</p>
<p>Elasticsearch installs a built-in index template matching <code>metrics-*.prometheus-*</code> that configures TSDS mode, <a href="https://www.elastic.co/docs/reference/elasticsearch/mapping-reference/passthrough">passthrough</a> dimension container objects, and a 10,000 field limit.
The <a href="https://www.elastic.co/docs/reference/elasticsearch/index-settings/mapping-limit">field limit</a> is configurable via a custom component template (see the custom metric type inference section below for how to use one).
You do not need to create any templates or mappings yourself.</p>
<h2 id="custommetrictypeinference">Custom metric type inference</h2>
<p>Metric type inference is based on naming conventions.
Metrics that don't follow Prometheus naming best practices may be classified incorrectly.
You can override the defaults by creating a <code>metrics-prometheus@custom</code> component template with your own dynamic templates.
For example, to mark all <code>*_counter</code> metrics as counters:</p>
<pre><code>{
  "template": {
    "mappings": {
      "dynamic_templates": [
        {
          "counter": {
            "path_match": "metrics.*_counter",
            "mapping": {
              "type": "double",
              "time_series_metric": "counter"
            }
          }
        }
      ]
    }
  }
}
</code></pre>
<p>Custom rules are merged with the built-in patterns, so the defaults still apply for metrics you don't override.</p>
<h2 id="currentlimitations">Current limitations</h2>
<p>Only Remote Write v1 is supported.
v2, which brings native histograms and exemplars, is planned.</p>
<p>Staleness markers (special NaN values Prometheus uses to signal a series has disappeared) are not yet stored or respected in queries.</p>
<p>Non-finite values (NaN, Infinity) are silently dropped.</p>
<h2 id="getstarted">Get started</h2>
<p>The Prometheus Remote Write endpoint is available now on <a href="https://cloud.elastic.co/serverless-registration?onboarding_token=observability">Elasticsearch Serverless</a> with no configuration needed.
To get started with a local cluster, <a href="https://www.elastic.co/docs/deploy-manage/deploy/self-managed/local-development-installation-quickstart">start-local</a> gets you a single-node cluster in minutes.</p>
<p>Once metrics are flowing, you can query them with ES|QL using the built-in <code>PROMQL</code> function for PromQL compatibility, or write native ES|QL queries to join metrics with logs and traces in the same store.</p>]]></content:encoded>
    <link>https://www.elastic.co/observability-labs/blog/prometheus-remote-write-elasticsearch</link>
    <guid isPermaLink="false">prometheus-remote-write-elasticsearch</guid>
    <category><![CDATA[Metrics]]></category>
    <category><![CDATA[Infrastructure Monitoring]]></category>
    <dc:creator><![CDATA[Felix Barnsteiner]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt17c564dfb7ca5dd6/6a7f19d65967e538655dd6b1/header.jpg" length="0" type="image/jpeg"/>
    <pubDate>Tue, 14 Apr 2026 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Migrate Logstash Pipelines from Azure Event Hubs to Kafka Input Plugin]]></title>
    <description><![CDATA[Step-by-step guide to migrating Logstash pipelines from the Azure Event Hubs plugin to the Kafka input plugin to eliminate offset storage costs and improve performance.]]></description>
    <content:encoded><![CDATA[<h2 id="introduction">Introduction</h2>
<p>Azure Event Hubs natively supports the Apache Kafka protocol, which means you no longer need the <code>logstash-input-azure_event_hubs</code> plugin or an external Blob Storage account for offset checkpointing. Switching to <code>logstash-input-kafka</code> removes that storage dependency, reduces costs, and delivers up to 2.5x higher throughput.</p>
<p>This guide walks you through the migration: why it matters, how to convert your existing configuration, parameter mapping between the two plugins, and how to adapt proxy setups.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltd8b3f0a2ba2bc771/6a7f0d71b6b73409d4e48e22/amqp-vs-kafka.png" alt="AMQP vs Kafka protocol path comparison for Logstash with Azure Event Hubs" /></p>
<h2 id="whymigrate">Why migrate?</h2>
<p>The migration from the Azure Event Hubs plugin to the Kafka input plugin is motivated by several factors:</p>
<ol>
<li><p><strong>Azure Event Hubs already speaks Kafka natively.</strong> Event Hubs exposes a <a href="https://learn.microsoft.com/en-us/azure/event-hubs/azure-event-hubs-kafka-overview">built-in Apache Kafka endpoint</a> on Standard, Premium, and Dedicated tiers. This means the <code>logstash-input-azure_event_hubs</code> plugin is no longer necessary. The standard <code>logstash-integration-kafka</code> (input) plugin connects directly to the same service with no extra Azure-side configuration.</p></li>
<li><p><strong>No more Blob Storage for offset checkpointing.</strong> The AMQP-based plugin requires an <a href="https://learn.microsoft.com/en-us/azure/event-hubs/event-processor-balance-partition-load#checkpoint">external Azure Blob Storage account</a> to track consumer offsets. This means provisioning and maintaining a storage account, plus paying for every checkpoint write. With the Kafka protocol, <a href="https://learn.microsoft.com/en-us/azure/event-hubs/apache-kafka-frequently-asked-questions#event-hubs-consumer-group-vs--kafka-consumer-group">offset tracking is handled internally by Azure Event Hubs at no extra cost</a>, removing the need for external storage.</p></li>
<li><p><strong>GPv1 storage retirement is coming, and GPv2 costs more.</strong> Microsoft will <a href="https://learn.microsoft.com/en-us/azure/storage/common/general-purpose-version-1-account-migration-overview">retire general-purpose v1 storage accounts in October 2026</a>. Accounts not manually <a href="https://learn.microsoft.com/en-us/azure/storage/common/storage-account-upgrade">upgraded to GPv2</a> by then will be migrated automatically. The <code>logstash-input-azure_event_hubs</code> plugin works correctly with GPv2, so existing pipelines will not break. However, GPv2 can bring <a href="https://learn.microsoft.com/en-us/azure/storage/common/storage-account-upgrade#billing-impact-of-upgrading">higher transactional costs</a>, especially for checkpoint-heavy workloads. By switching to the Kafka input plugin, this concern is eliminated: no storage account means nothing to upgrade and nothing to pay for.</p>
<p><strong>Not ready to migrate yet? Reducing GPv2 costs in the meantime is possible.</strong> GPv2 transaction pricing is significantly more expensive than GPv1's flat rate. Increasing the <a href="https://www.elastic.co/guide/en/logstash/current/plugins-inputs-azure_event_hubs.html#plugins-inputs-azure_event_hubs-checkpoint_interval"><code>checkpoint_interval</code></a> setting above its default of 5 seconds reduces write operations and lowers the cost impact. The cost difference can be estimated using the <a href="https://azure.microsoft.com/en-us/pricing/calculator/">Azure Pricing Calculator</a>.</p>
<p>Example for East US and Local Retention Storage. Write operation cost comparison (per 10,000 write operations):</p>
<ul>
<li><p><strong>GPv1 (flat):</strong> $0.00036</p></li>
<li><p><strong>GPv2 (Hot tier):</strong> $0.050</p></li></ul>
<p>That's roughly a 140x increase in write operation costs.</p></li>
<li><p><strong>Broader community and active maintenance.</strong> The Kafka input plugin is more widely used across Logstash deployments and receives regular updates aligned with the Kafka ecosystem. Moving to it reduces long-term operational risk and keeps your pipeline on a well-supported path.</p></li>
<li><p><strong>Better throughput.</strong> The Kafka input plugin consistently outperforms the Azure Event Hubs plugin when consuming from the same namespace. See the <a href="https://www.elastic.co/observability-labs/blog/migrate-logstash-pipelines-from-azure-event-hubs-to-kafka-plugin#performance-comparison">Performance Comparison</a> section for measured results.</p></li>
</ol>
<h2 id="requirementstoenablethekafkainterface">Requirements to enable the Kafka interface</h2>
<p>The Kafka interface is built into Azure Event Hubs. You don't need to enable or configure anything in the Azure portal.</p>
<p>The only requirement is that your Event Hubs namespace is on the <strong>Standard</strong>, <strong>Premium</strong>, or <strong>Dedicated</strong> tier. The Basic tier does not support the Kafka protocol.</p>
<p>See the <a href="https://learn.microsoft.com/en-us/azure/event-hubs/event-hubs-quotas#basic-vs-standard-vs-premium-vs-dedicated-tiers">Tiers comparison table</a> for details.</p>
<h2 id="convertingyourconfiguration">Converting your configuration</h2>
<p>This section walks through converting an existing <code>logstash-input-azure_event_hubs</code> configuration to <code>logstash-input-kafka</code>, starting with the simplest single-hub scenario and building up to multi-hub and advanced use cases.</p>
<h3 id="keybehaviorchanges">Key behavior changes</h3>
<p>Before changing any configuration, be aware of two important differences:</p>
<ol>
<li><p><strong>No more Blob Storage for offsets.</strong> The Kafka input plugin tracks offsets internally through the Azure Event Hubs service at no extra cost. The <code>storage_connection</code> and <code>storage_container</code> parameters have no equivalent. There is nothing to provision, maintain, or pay for.</p></li>
<li><p><strong>Consumer offsets don't carry over.</strong> AMQP consumer groups and Kafka consumer groups are completely separate, even if they share the same name. When the Kafka input plugin connects for the first time, Azure auto-creates the Kafka consumer group specified in <a href="https://www.elastic.co/guide/en/logstash/current/plugins-inputs-kafka.html#plugins-inputs-kafka-group_id"><code>group_id</code></a> (default: <code>logstash</code>). <strong>It will not read the old Blob Storage checkpoints or resume from where the legacy plugin left off.</strong> It starts fresh.</p></li>
</ol>
<p>|                     | Event Hubs (AMQP) consumer groups       | Kafka consumer groups              |
| ------------------- | --------------------------------------- | ---------------------------------- |
| <strong>Protocol</strong>        | AMQP                                    | Kafka                              |
| <strong>Offset storage</strong>  | External Azure Blob Storage             | Internal to the Event Hubs service |
| <strong>Creation</strong>        | Must be created via portal, SDK, or ARM | Auto-created on first connection   |
| <strong>Namespace scope</strong> | Scoped to a single Event Hub            | Span the entire namespace          |</p>
<p><strong>Limit:</strong> A maximum of 1,000 simultaneous Kafka consumer groups per namespace is allowed. See the <a href="https://learn.microsoft.com/en-us/azure/event-hubs/apache-kafka-frequently-asked-questions#event-hubs-consumer-group-vs--kafka-consumer-group">Event Hubs vs. Kafka Consumer Groups FAQ</a>.</p>
<h3 id="authentication">Authentication</h3>
<p>The <code>logstash-input-azure_event_hubs</code> plugin only supports <strong>SAS (Shared Access Signature)</strong> authentication via connection strings. The same SAS credentials work with the Kafka plugin through SASL PLAIN, as shown in the <a href="https://www.elastic.co/observability-labs/blog/migrate-logstash-pipelines-from-azure-event-hubs-to-kafka-plugin#single-event-hub-basic-migration">Single Event Hub (basic migration)</a> example below.</p>
<h3 id="singleeventhubbasicmigration">Single Event Hub (basic migration)</h3>
<p>Most pipelines start with a single Event Hub, SAS authentication, and Blob Storage checkpointing. The following example shows the baseline <code>azure_event_hubs</code> configuration and its direct Kafka equivalent.</p>
<p><strong>Before</strong> (legacy Azure Event Hubs input):</p>
<pre><code>input {
  azure_event_hubs {
    event_hub_connections =&gt; ["Endpoint=sb://&lt;NAMESPACE&gt;.servicebus.windows.net/;SharedAccessKeyName=&lt;ACCESS_KEY_NAME&gt;;SharedAccessKey=&lt;ACCESS_KEY&gt;;EntityPath=&lt;EVENT_HUB_NAME&gt;"]
    storage_connection =&gt; "DefaultEndpointsProtocol=https;AccountName=&lt;STORAGE_ACCOUNT_NAME&gt;;AccountKey=&lt;STORAGE_ACCOUNT_KEY&gt;;EndpointSuffix=core.windows.net"
    consumer_group =&gt; "&lt;CONSUMER_GROUP_NAME&gt;"
    storage_container =&gt; "&lt;STORAGE_NAME&gt;"
  }
}
</code></pre>
<p><strong>After</strong> (Kafka input):</p>
<pre><code>input {
  kafka {
    # The Namespace name and the mandatory Kafka SSL port
    bootstrap_servers =&gt; "&lt;NAMESPACE&gt;.servicebus.windows.net:9093"

    topics =&gt; ["&lt;EVENT_HUB_NAME&gt;"]
    group_id =&gt; "&lt;KAFKA_CONSUMER_GROUP_NAME&gt;"
    security_protocol =&gt; "SASL_SSL"
    sasl_mechanism =&gt; "PLAIN"

    # Need to create a 'jaas.conf' file storing Username and Password (username is always '$ConnectionString')
    jaas_path =&gt; "path/to/jaas.conf"
  }
}
</code></pre>
<pre><code>KafkaClient {
    org.apache.kafka.common.security.plain.PlainLoginModule required
    username="$ConnectionString" 
    password="Endpoint=sb://&lt;NAMESPACE&gt;.servicebus.windows.net/;SharedAccessKeyName=&lt;ACCESS_KEY_NAME&gt;;SharedAccessKey=&lt;ACCESS_KEY&gt;";
};
</code></pre>
<pre><code># Inline JAAS configuration (substitutes jaas_path)
    sasl_jaas_config =&gt; "org.apache.kafka.common.security.plain.PlainLoginModule required username='$ConnectionString' password='Endpoint=sb://&lt;NAMESPACE&gt;.servicebus.windows.net/;SharedAccessKeyName=&lt;ACCESS_KEY_NAME&gt;;SharedAccessKey=&lt;ACCESS_KEY&gt;';"
</code></pre>
<h3 id="multipleeventhubswithasinglekafkainput">Multiple Event Hubs with a single Kafka input</h3>
<p>If your SAS policy has <strong>namespace-level read rights</strong> (not just a single Event Hub), you can consume from multiple Event Hubs with a single <code>kafka</code> input by listing multiple topics:</p>
<pre><code>input {
  kafka {
    bootstrap_servers =&gt; "&lt;NAMESPACE&gt;.servicebus.windows.net:9093"
    topics =&gt; ["&lt;EVENT_HUB_1&gt;", "&lt;EVENT_HUB_2&gt;", "&lt;EVENT_HUB_3&gt;"]
    group_id =&gt; "&lt;KAFKA_CONSUMER_GROUP_NAME&gt;"
    security_protocol =&gt; "SASL_SSL"
    sasl_mechanism =&gt; "PLAIN"
    jaas_path =&gt; "path/to/jaas.conf"
  }
}
</code></pre>
<h2 id="configurationparametersmapping">Configuration parameters mapping</h2>
<p>The following section maps each <code>logstash-input-azure_event_hubs</code> parameter to its <code>logstash-input-kafka</code> equivalent, with usage notes and example configurations.</p>
<ol>
<li><p><a href="https://www.elastic.co/guide/en/logstash/current/plugins-inputs-azure_event_hubs.html#plugins-inputs-azure_event_hubs-config_mode"><code>config_mode</code></a>: No direct equivalent. Kafka doesn't have "basic" vs "advanced" modes. To consume from multiple hubs with different settings, define multiple <code>kafka {}</code> input blocks or list multiple topics. The basic mode conversion is covered in <a href="https://www.elastic.co/observability-labs/blog/migrate-logstash-pipelines-from-azure-event-hubs-to-kafka-plugin#single-event-hub-basic-migration">Single Event Hub (basic migration)</a>.</p>
<p>Here is an advanced-mode example with two Event Hubs in the same namespace:</p>
<pre><code>input {
    azure_event_hubs {
        config_mode =&gt; "advanced"
        storage_connection =&gt; "DefaultEndpointsProtocol=https;AccountName=&lt;STORAGE_ACCOUNT&gt;;..."
        event_hubs =&gt; [
            {"&lt;EVENT_HUB_1&gt;" =&gt; {
                event_hub_connection =&gt; "Endpoint=sb://&lt;NAMESPACE&gt;.servicebus.windows.net/;SharedAccessKeyName=&lt;KEY_1&gt;;SharedAccessKey=&lt;ACCESS_KEY&gt;;EntityPath=&lt;EVENT_HUB_1&gt;"
                consumer_group =&gt; "&lt;CONSUMER_GROUP_1&gt;"
            }},
            {"&lt;EVENT_HUB_2&gt;" =&gt; {
                event_hub_connection =&gt; "Endpoint=sb://&lt;NAMESPACE&gt;.servicebus.windows.net/;SharedAccessKeyName=&lt;KEY_2&gt;;SharedAccessKey=&lt;ACCESS_KEY&gt;;EntityPath=&lt;EVENT_HUB_2&gt;"
                consumer_group =&gt; "&lt;CONSUMER_GROUP_2&gt;"
            }}
        ]
    }
}
</code></pre>
<pre><code>input {
    kafka {
        bootstrap_servers =&gt; "&lt;NAMESPACE&gt;.servicebus.windows.net:9093"
        topics =&gt; ["&lt;EVENT_HUB_1&gt;"]
        group_id =&gt; "&lt;KAFKA_CONSUMER_GROUP_1&gt;"
        security_protocol =&gt; "SASL_SSL"
        sasl_mechanism =&gt; "PLAIN"
        sasl_jaas_config =&gt; "...&lt;KEY_1&gt;..."
    }
    kafka {
        bootstrap_servers =&gt; "&lt;NAMESPACE&gt;.servicebus.windows.net:9093"
        topics =&gt; ["&lt;EVENT_HUB_2&gt;"]
        group_id =&gt; "&lt;KAFKA_CONSUMER_GROUP_2&gt;"
        security_protocol =&gt; "SASL_SSL"
        sasl_mechanism =&gt; "PLAIN"
        sasl_jaas_config =&gt; "...&lt;KEY_2&gt;..."
    }
}
</code></pre></li>
<li><p><a href="https://www.elastic.co/guide/en/logstash/current/plugins-inputs-azure_event_hubs.html#plugins-inputs-azure_event_hubs-checkpoint_interval"><code>checkpoint_interval</code></a>: This corresponds to <a href="https://www.elastic.co/guide/en/logstash/current/plugins-inputs-kafka.html#plugins-inputs-kafka-auto_commit_interval_ms"><code>auto_commit_interval_ms</code></a>.</p>
<p>In the Azure plugin, this controls how often a write operation hits the Blob Storage container to save the reading offset. In the Kafka plugin, it controls how often the consumer commits its offset to the Event Hubs service.</p>
<p><strong>Note</strong> Keep <code>enable_auto_commit</code> set to <code>true</code> (default) while configuring <code>auto_commit_interval_ms</code> parameter.</p>
<p>Azure config:</p>
<pre><code>input {
    azure_event_hubs {
        # ... other params ...
        checkpoint_interval =&gt; 10 # in seconds
    }
}
</code></pre>
<p>Kafka equivalent:</p>
<pre><code>input {
    kafka {
        # ... other params ...
        auto_commit_interval_ms =&gt; 10000 # in milliseconds 
    }
}
</code></pre></li>
<li><p><a href="https://www.elastic.co/guide/en/logstash/current/plugins-inputs-azure_event_hubs.html#plugins-inputs-azure_event_hubs-decorate_events"><code>decorate_events</code></a>: This parameter exists in both plugins with the same name and behavior.</p></li>
<li><p><a href="https://www.elastic.co/guide/en/logstash/current/plugins-inputs-azure_event_hubs.html#plugins-inputs-azure_event_hubs-initial_position"><code>initial_position</code></a>: This corresponds to <a href="https://www.elastic.co/guide/en/logstash/current/plugins-inputs-kafka.html#plugins-inputs-kafka-auto_offset_reset"><code>auto_offset_reset</code></a>.</p>
<p>Both parameters control where to start reading when no prior offset is found at checkpoint storage. Options differ slightly:</p>
<ul>
<li><p>Azure: <code>beginning</code>, <code>end</code>, <code>look_back</code></p></li>
<li><p>Kafka: <code>earliest</code>, <code>latest</code>, <code>by_duration:&lt;duration&gt;</code>, <code>none</code></p></li></ul>
<p>The difference between beginning-end and earliest-latest is purely terminology.</p>
<p>Azure config:</p>
<pre><code>input {
    azure_event_hubs {
        initial_position =&gt; "beginning"
    }
}
</code></pre>
<p>Kafka equivalent:</p>
<pre><code>input {
    kafka {
        auto_offset_reset =&gt; "earliest"
    }
}
</code></pre>
<p>| Azure Value | Kafka Value                | Notes                                                        |
| ----------- | -------------------------- | ------------------------------------------------------------ |
| <code>beginning</code> | <code>earliest</code>                 |                                                              |
| <code>end</code>       | <code>latest</code>                   |                                                              |
| <code>look_back</code> | <code>by_duration:&lt;duration&gt;</code>   | Duration in ISO 8601 format (e.g., <code>by_duration:PT1H</code> for 1 hour). Requires <code>logstash-integration-kafka</code> 12.1.0+.|</p>
<p>The <code>by_duration</code> option was introduced in Apache Kafka client 4.0.0 and is available in <code>logstash-integration-kafka</code> version 12.1.0 and later. The version bundled with the latest Logstash release is older than 12.1.0, so a manual gem update is needed:</p>
<pre><code>&lt;LOGSTASH_HOME&gt;/bin/logstash-plugin install --version 12.1.0 logstash-integration-kafka
</code></pre>
<p>Replace <code>&lt;LOGSTASH_HOME&gt;</code> with the Logstash installation directory (e.g., <code>/usr/share/logstash</code> for DEB/RPM packages).</p>
<p><strong>Note:</strong> Since Kafka can't read the old Blob Storage checkpoints, it treats the migration as a first-time connection. To avoid reprocessing data the legacy plugin already handled, set <code>auto_offset_reset =&gt; "latest"</code> for the initial deployment.</p></li>
<li><p><a href="https://www.elastic.co/guide/en/logstash/current/plugins-inputs-azure_event_hubs.html#plugins-inputs-azure_event_hubs-max_batch_size"><code>max_batch_size</code></a>: This corresponds to <a href="https://www.elastic.co/guide/en/logstash/current/plugins-inputs-kafka.html#plugins-inputs-kafka-max_poll_records"><code>max_poll_records</code></a>.</p>
<p>Both parameters define the maximum number of messages to fetch in a single poll/batch operation.</p>
<p>Azure config:</p>
<pre><code>input {
    azure_event_hubs {
        max_batch_size =&gt; 125
    }
}
</code></pre>
<p>Kafka equivalent:</p>
<pre><code>input {
    kafka {
        max_poll_records =&gt; "125"
    }
}
</code></pre></li>
<li><p><a href="https://www.elastic.co/guide/en/logstash/current/plugins-inputs-azure_event_hubs.html#plugins-inputs-azure_event_hubs-threads"><code>threads</code></a>: This corresponds to <a href="https://www.elastic.co/guide/en/logstash/current/plugins-inputs-kafka.html#plugins-inputs-kafka-consumer_threads"><code>consumer_threads</code></a>.</p>
<p>Both parameters control the number of threads used to consume messages concurrently. In Azure, the minimum is 2 (with 1 Event Hub + 1), while in Kafka the default is 1 thread.</p>
<p>Azure config:</p>
<pre><code>input {
    azure_event_hubs {
        threads =&gt; 8
    }
}
</code></pre>
<p>Kafka equivalent:</p>
<pre><code>input {
    kafka {
        consumer_threads =&gt; 8
    }
}
</code></pre></li>
</ol>
<h2 id="performancecomparison">Performance Comparison</h2>
<p>We tested both plugins under identical conditions: same Logstash instance, same Event Hub namespace, same number of partitions, and same batch/thread configuration. The absolute numbers are environment-specific, but the relative difference is what matters.</p>
<p>| <strong>Plugin</strong>         | <strong>Payload</strong> | <strong>Throughput (events/s)</strong> |
| ------------------ | ----------- | ------------------------- |
| <code>azure_event_hubs</code> | 100B        | ~5700                    |
| <code>kafka</code>            | 100B        | ~14500                   |
| <code>azure_event_hubs</code> | 1KB         | ~1500                    |
| <code>kafka</code>            | 1KB         | ~3200                    |
| <code>azure_event_hubs</code> | 10KB        | ~170                     |
| <code>kafka</code>            | 10KB        | ~290                     |</p>
<p>Across all payload sizes, the Kafka input plugin delivers 1.7x to 2.5x higher throughput. The gain is most noticeable with small payloads, where protocol overhead dominates. Beyond the infrastructure simplification (no Blob Storage, no GPv2 concerns), you also get a clear performance win.</p>
<h2 id="proxyconnectionconfiguration">Proxy connection configuration</h2>
<blockquote>
  <p>If the Logstash instance connects directly to Azure Event Hubs without a proxy, this section can be skipped.</p>
</blockquote>
<p>Proxy setups require special attention during this migration because the two plugins use fundamentally different protocols.</p>
<h3 id="azureeventhubspluginsetupreference">Azure Event Hubs plugin setup (reference)</h3>
<p>The <code>logstash-input-azure_event_hubs</code> plugin supports HTTPS proxies. The setup involves:</p>
<ol>
<li><p>Set the proxy environment variable:</p>
<pre><code>export https_proxy="https://my_proxy:8080"
</code></pre></li>
<li><p>Add the WebSockets transport flag to the Event Hubs connection string:</p>
<pre><code>;TransportType=AmqpWebSockets
</code></pre></li>
<li><p>Add the following JVM options (Logstash <code>jvm.options</code>):</p>
<pre><code>-Dhttp.proxyHost=my_proxy
-Dhttp.proxyPort=8080
-Dhttps.proxyHost=my_proxy
-Dhttps.proxyPort=8443
-Dhttp.nonProxyHosts=localhost|127.0.0.1
</code></pre></li>
</ol>
<h3 id="migratingtoatcplayer4proxy">Migrating to a TCP (Layer 4) proxy</h3>
<p>The proxy setup from the Azure plugin is not compatible with the Kafka client. The Azure plugin communicates over AMQP/WebSockets (HTTP layer), which is why JVM proxy settings and <code>TransportType=AmqpWebSockets</code> work. The Kafka plugin opens a raw TCP socket to the broker. It never makes an HTTP request, so <strong>JVM HTTP proxy settings are ignored entirely</strong>. If the environment requires a proxy, the HTTP proxy needs to be replaced with a TCP (Layer 4) proxy.</p>
<h4 id="step1configureetchosts">Step 1: Configure <code>/etc/hosts</code></h4>
<p>The Kafka client verifies that the TLS certificate matches the hostname in <code>bootstrap_servers</code>. Since the certificate is issued for <code>*.servicebus.windows.net</code>, <code>bootstrap_servers</code> must use the real Event Hubs FQDN, not the proxy address. A DNS override routes the FQDN to the proxy IP:</p>
<pre><code># /etc/hosts
&lt;PROXY_HOST_IP&gt;  &lt;NAMESPACE&gt;.servicebus.windows.net
</code></pre>
<h4 id="step2logstashconfiguration">Step 2: Logstash configuration</h4>
<p>The Logstash configuration is identical to a non-proxied setup. The <code>/etc/hosts</code> override transparently routes traffic through the proxy, so <code>bootstrap_servers</code> still uses the Event Hubs FQDN:</p>
<pre><code>input {
  kafka {
    bootstrap_servers =&gt; "&lt;NAMESPACE&gt;.servicebus.windows.net:9093"
    topics =&gt; ["&lt;EVENT_HUB_NAME&gt;"]
    security_protocol =&gt; "SASL_SSL"
    sasl_mechanism =&gt; "PLAIN"
    group_id =&gt; "&lt;GROUP_ID&gt;"
    jaas_path =&gt; "&lt;PATH_TO_JAAS_FILE&gt;"
  }
}
</code></pre>
<p>If the TCP proxy runs on the same host as Logstash or within a trusted network segment, the DNS override is not needed. Instead, point <code>bootstrap_servers</code> directly to the proxy IP (e.g., <code>&lt;PROXY_HOST_IP&gt;:9093</code>) and change <code>security_protocol</code> to <code>SASL_PLAINTEXT</code>. This delegates the TLS handshake to the proxy, while the link between Logstash and the proxy stays unencrypted. Only use this configuration when the Logstash-to-proxy path is secure.</p>
<pre><code>input {
  kafka {
    bootstrap_servers =&gt; "&lt;PROXY_HOST_IP&gt;:9093"
    security_protocol =&gt; "SASL_PLAINTEXT"
  }
}
</code></pre>
<h2 id="frequentlyaskedquestions">Frequently asked questions</h2>
<p><strong>Are events lost when switching from the Azure Event Hubs plugin to the Kafka plugin?</strong></p>
<p>No. Events remain available within the configured retention period regardless of which protocol reads them. What changes is where the consumer starts reading. Since the Kafka plugin cannot access the AMQP plugin's Blob Storage checkpoints, it starts from scratch. Set <code>auto_offset_reset =&gt; "earliest"</code> to reprocess all retained events, or <code>auto_offset_reset =&gt; "latest"</code> to consume only new ones from the switchover point. See the <a href="https://www.elastic.co/observability-labs/blog/migrate-logstash-pipelines-from-azure-event-hubs-to-kafka-plugin#configuration-parameters-mapping"><code>initial_position</code> mapping</a> for details.</p>
<p><strong>What happens to the Azure Blob Storage account after migration?</strong></p>
<p>It is no longer needed for offset checkpointing. Once the Kafka plugin is confirmed to be consuming correctly and the <code>azure_event_hubs</code> input has been decommissioned, the storage account (or at least the checkpoint container) can be safely deleted. If the storage account is used for other purposes, only remove the specific container referenced in <code>storage_container</code>.</p>
<p><strong>Can the same consumer group name be reused?</strong></p>
<p>Yes, but it has no practical effect. AMQP and Kafka consumer groups are completely independent even if they share the same name. They use different protocols, different offset storage, and different scoping rules. Reusing the name will not cause the Kafka plugin to resume from the AMQP plugin's last checkpoint.</p>
<p><strong>Are other authentication methods supported?</strong></p>
<p>The <code>logstash-input-azure_event_hubs</code> plugin only supports SAS connection strings, so SAS is the only credential that needs to be carried over. There is no Entra ID, OAUTHBEARER, or managed identity configuration to migrate. The <code>logstash-input-kafka</code> plugin does support SASL OAUTHBEARER, so adopting token-based authentication becomes possible after migration.</p>
<p><strong>What if the proxy only allows traffic on port 443?</strong></p>
<p>The Kafka endpoint on Azure Event Hubs requires port 9093. If the TCP proxy only forwards port 443, it must be reconfigured to also allow port 9093 for the Event Hubs FQDN (<code>*.servicebus.windows.net</code>). Azure Event Hubs does not expose a Kafka listener on port 443.</p>
<h2 id="nextsteps">Next steps</h2>
<p>With the GPv1 retirement deadline (October 2026) approaching, starting this migration sooner reduces the time spent managing storage infrastructure that is no longer needed.</p>
<p>If any issues arise during migration:</p>
<ul>
<li><p><strong>Usage questions or help with configuration</strong>: Post on the <a href="https://discuss.elastic.co/c/logstash/">Elastic Discuss forum</a>.</p></li>
<li><p><strong>Bugs or unexpected behavior in the Kafka plugin</strong>: Open an issue in the <a href="https://github.com/logstash-plugins/logstash-integration-kafka/issues">logstash-integration-kafka</a>.</p></li>
</ul>
<h2 id="relatedresources">Related resources</h2>
<ul>
<li><a href="https://www.elastic.co/guide/en/logstash/current/plugins-inputs-kafka.html">Kafka input plugin documentation</a>: Full reference for all <code>logstash-input-kafka</code> configuration parameters.</li>
<li><a href="https://www.elastic.co/guide/en/logstash/current/plugins-inputs-azure_event_hubs.html">Azure Event Hubs input plugin documentation</a>: Full reference for the legacy plugin being replaced.</li>
<li><a href="https://learn.microsoft.com/en-us/azure/event-hubs/azure-event-hubs-kafka-overview">Azure Event Hubs for Apache Kafka overview</a>: Microsoft's documentation on the built-in Kafka endpoint in Event Hubs.</li>
<li><a href="https://learn.microsoft.com/en-us/azure/event-hubs/event-hubs-quotas#basic-vs-standard-vs-premium-vs-dedicated-tiers">Event Hubs quotas and tier comparison</a>: Tier requirements for Kafka protocol support.</li>
</ul>]]></content:encoded>
    <link>https://www.elastic.co/observability-labs/blog/migrate-logstash-pipelines-from-azure-event-hubs-to-kafka-plugin</link>
    <guid isPermaLink="false">migrate-logstash-pipelines-from-azure-event-hubs-to-kafka-plugin</guid>
    <category><![CDATA[Logs Analytics]]></category>
    <category><![CDATA[Infrastructure Monitoring]]></category>
    <dc:creator><![CDATA[Álex Cámara]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt53abdbbdd62c1960/6a7f0d74bd219830967580e3/elastic-blog-logstash-kafka.jpeg" length="0" type="image/jpeg"/>
    <pubDate>Mon, 06 Apr 2026 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Composing OpenTelemetry Reference Architectures]]></title>
    <description><![CDATA[A conceptual framework for reasoning about OpenTelemetry Collection architectures — edge, processing, and resilience layers that compose into the right pipeline for your environment.]]></description>
    <content:encoded><![CDATA[<p>Most OpenTelemetry tutorials end at the same place: an application instrumented with the SDK, exporting traces to a single collector, forwarding to a backend. It works. Then production happens.</p>
<p>Traffic grows. Teams want metrics derived from traces. The backend goes down for maintenance and you lose an hour of telemetry. A compliance requirement means PII must be stripped before data leaves the cluster. Suddenly, that single collector isn't enough — and the question becomes: what should the architecture actually look like?</p>
<p>The OpenTelemetry Collector is designed to be composed. It can run in multiple deployment modes, be chained into pipelines, and scaled independently at each stage. But the documentation describes individual components, not how to think about assembling them. That thinking is what this article is about.</p>
<p>What follows is a conceptual framework for reasoning about collector architectures — not a set of rigid templates. The building blocks described here are reference points. In practice, they combine, overlap, and adapt to your constraints. A tail sampling tier might also need Kafka-backed resilience. A gateway might absorb the role of a sampling tier at low volumes. The goal is to understand the concepts well enough to compose the right architecture for your situation, not to pick a pre-built one off a shelf.</p>
<h2 id="threeconceptuallayers">Three conceptual layers</h2>
<p>It helps to think about collector architectures in three layers: <strong>edge</strong>, <strong>processing</strong>, and <strong>resilience</strong>. These aren't physical tiers that must exist as separate deployments — they're categories of concern. A single collector can address multiple layers. A complex deployment might have several components within one layer. The layers are a thinking tool, not a deployment diagram.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltb0c3d663572bdc9a/6a7f18eceab5be375920aad7/three-layers.png" alt="The three conceptual layers: Edge, Processing, and Resilience" /></p>
<h3 id="edgehowtelemetryentersthepipeline">Edge: how telemetry enters the pipeline</h3>
<p>The edge layer is about the first hop — how telemetry gets from your applications and infrastructure into the pipeline. At this stage, the collector gathers data in two fundamentally different ways. <strong>Pull-based receivers</strong> like <code>filelog</code> and <code>hostmetrics</code> actively reach out to collect data — tailing log files on disk or scraping system-level metrics from the host. <strong>Push-based receivers</strong> like <code>otlp</code> listen for data sent to them — applications instrumented with OpenTelemetry SDKs export traces, metrics, and logs directly to the collector's OTLP endpoint. A single edge collector typically runs both: pull receivers for infrastructure telemetry the application doesn't know about, and push receivers for application telemetry the SDK produces. There are several common deployment patterns, and the right one depends on your environment and what you need to collect.</p>
<p><strong>DaemonSet Agent</strong> — One OpenTelemetry Collector per Kubernetes node, deployed as a DaemonSet. Applications export to the agent running on the same node (typically via status.hostIP:4317 using the Kubernetes Downward API). The agent also tails container log files from disk via the filelog receiver and scrapes host-level metrics via the hostmetrics receiver. This is the most common Kubernetes pattern because it handles both application and infrastructure telemetry with a single deployment, and applications only need to know about localhost.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt898169e98cea105d/6a7f18effc63abe98364d054/daemonset-agent.png" alt="DaemonSet Agent pattern: Application with OTel SDK exporting over OTLP to a per-node DaemonSet collector" /></p>
<p><strong>Sidecar Agent</strong> — One OpenTelemetry Collector per pod, deployed as a sidecar container. Each service gets its own collector with a custom configuration. This is required on managed container platforms like AWS Fargate or Azure Container Apps where DaemonSets aren't available, and it's useful when services have different processing requirements. When running alongside a DaemonSet, the sidecar handles application telemetry while the DaemonSet independently collects node-level telemetry — applications don't send to both.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltbf752fb09630a0a5/6a7f18f2c2cc091599249994/sidecar-agent.png" alt="Sidecar Agent pattern: Application with OTel SDK exporting over OTLP to a per-pod sidecar collector" /></p>
<p><strong>Host Agent</strong> — A standalone OpenTelemetry Collector running as a systemd service on bare-metal or VM hosts. It serves the same role as the DaemonSet agent but outside Kubernetes: collecting host metrics, tailing log files, and receiving OTLP from local applications.</p>
<p><strong>Direct SDK Export</strong> — Applications export directly to the next stage (gateway or backend) with no local collector. This is the simplest option but only works when you don't need infrastructure collection. For log collection, the recommended pattern is still to write to stdout and use a collector with the <code>filelog</code> receiver — even if the SDK is exporting traces and metrics directly.</p>
<p>These patterns aren't mutually exclusive. A Kubernetes cluster might run DaemonSet agents for infrastructure collection alongside sidecars for services that need custom processing. A VM environment might use host agents for some services and direct SDK export for others. The edge layer is about matching the collection pattern to the workload, not picking one pattern for everything.</p>
<h3 id="processingcentralpolicysamplingandtransformation">Processing: central policy, sampling, and transformation</h3>
<p>Not every architecture needs a processing layer. If your edge collectors can export directly to your backend and you don't need centralized policy, you can skip it to favour simplicity. But several scenarios push you toward central processing — and the way you address them can range from a single gateway to a multi-stage pipeline.</p>
<p><strong>Centralized policy (Gateway)</strong> — A pool of OpenTelemetry Collectors that sits between edge collectors and the backend. This is where you enforce consistent filtering, transformation, and PII redaction across all services. It's also where you manage backend credentials — edge collectors export to the gateway over OTLP, and only the gateway holds the API keys. Credential isolation is often the primary reason teams add a gateway.</p>
<p>Replica count scales with data volume. At low volumes (under 1K events/sec), 2 replicas co-located with workloads is sufficient. At medium volumes, 3–5 replicas on a dedicated node pool. At high volumes, 5–20+ replicas, potentially in a separate cluster. This is a general rule of thumb, and you should adapt it to your specific needs as loads might vary significantly between payload types.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt2c5fe1b38941f73d/6a7f18f533fa8a2253202b5c/gateway-pool.png" alt="Gateway pattern: Load Balancer distributing traffic to a Gateway Pool of OTel Collectors" /></p>
<p><strong>Tail-based sampling</strong> — Sampling decisions that consider the complete trace (e.g., "keep all traces with errors, sample 10% of successful traces") require that all spans of a trace reach the same collector instance. This is achieved with the <code>loadbalancingexporter</code> using <code>routing_key: traceID</code>, which consistently routes spans from the same trace to the same downstream collector.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt2e5c9ce1457269a3/6a7f18f8e88c654d6f00bada/tail-sampling.png" alt="Tail sampling pattern: LB Exporter routing to Sampling Collectors with tail_sampling" /></p>
<p>There's a critical subtlety here: if you're deriving span metrics (RED metrics) from traces using the <code>spanmetrics</code> connector, the derivation must happen <strong>before</strong> sampling. Otherwise, your metrics only reflect the sampled subset, not the true traffic. The correct pattern is a two-step pipeline within the sampling stage:</p>
<ol>
<li>Receive traces, derive spanmetrics from 100% of traffic, forward via a <code>forward</code> connector.</li>
<li>Apply <code>tail_sampling</code> to the forwarded traces, export only kept traces.</li>
<li>A separate metrics pipeline exports the derived RED metrics.</li>
</ol>
<p>This ensures accurate metrics regardless of your sampling rate.</p>
<p><strong>The key point about processing</strong> is that these capabilities — gateway policy, tail sampling, span metrics derivation — are not separate products or fixed modules. They're configurations of the same OpenTelemetry Collector. At low volumes, a single gateway deployment might handle policy enforcement, sampling, and metrics derivation all at once. At high volumes, you might split them into dedicated stages for independent scaling. The architecture adapts to your scale, not the other way around.</p>
<h3 id="resiliencewhathappenswhenthebackendisdown">Resilience: what happens when the backend is down</h3>
<p>The resilience layer determines how much data you're willing to lose during backend outages or collector restarts. This isn't a separate tier you bolt on — it's a property you apply to any stage of the pipeline.</p>
<p><strong>In-Memory Queues</strong> — The default. The collector's <code>sending_queue</code> retries failed exports with exponential backoff. If the collector process crashes or restarts, queued data is lost. This is acceptable for development and for workloads where some data loss during incidents is tolerable.</p>
<p><strong>Persistent Queues (WAL)</strong> — The <code>file_storage</code> extension writes queued data to disk before export. If the collector crashes, it resumes from where it left off after restart. In Kubernetes, this requires a PersistentVolumeClaim. This is the right choice for most production workloads — it survives collector restarts and brief backend outages without the operational complexity of an external message bus.</p>
<p><strong>Kafka Buffer</strong> — An external Kafka cluster sits between collectors and the backend. Producer collectors write to Kafka topics; consumer collectors read from Kafka and export to the backend. This provides the strongest durability guarantee — Kafka can buffer hours of telemetry during extended outages and enables replay. But it adds significant operational complexity.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltbff2af063797b1e9/6a7f18fa448e4eacd65c0b38/kafka-buffer.png" alt="Kafka buffer pattern: Collector Pool producing to Kafka, consumed by another Collector Pool" /></p>
<p>The important thing to understand is that resilience is orthogonal to the other layers. You can add persistent queues to an edge agent, a gateway, or a sampling tier. You can put Kafka in front of a gateway, in front of a sampling tier, or in front of the backend. A tail sampling deployment that needs to survive extended outages might use Kafka-backed ingestion — combining what might look like two separate "modules" into a single stage. The building blocks compose freely based on what you need to protect against.</p>
<h2 id="wheretostartwithyourarchitecture">Where to start with your architecture</h2>
<p>The Agent + Gateway two-tier pattern is the de facto production standard, used by the vast majority of organizations running OpenTelemetry at scale. DaemonSet agents on every node handle local collection — pulling infrastructure telemetry via <code>filelog</code> and <code>hostmetrics</code>, receiving application telemetry via OTLP — while a centralized gateway pool enforces policy, manages credentials, and exports to the backend. Persistent queues (WAL) on the gateway protect against backend outages without external dependencies.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt4b123115a47e99f1/6a7f18fd73d9bd051c29df2f/where-to-start.png" alt="A Kubernetes architecture with DaemonSet agents, a processing tier with tail sampling and gateway pool, exporting over OTLP to an observability backend" /></p>
<p>Every other configuration either simplifies this pattern or extends it. Smaller environments might drop the gateway and export directly from agents. Larger ones might add a tail sampling tier with traceID-based load balancing, a Kafka buffer for extended resilience, or span metrics derivation before sampling. The building blocks described in the previous sections — edge, processing, resilience — are the modules you add or remove from this foundation.</p>
<p>The key is to start with the two-tier pattern and evolve incrementally:</p>
<ul>
<li>Need credential isolation or centralized PII redaction? You already have the gateway.</li>
<li>Need tail-based sampling? Add a load-balancing exporter and a sampling tier between agents and gateway.</li>
<li>Need hours of buffer during extended outages? Insert Kafka between agents and the processing tier.</li>
<li>Running on Fargate or Azure Container Apps? Swap DaemonSet agents for sidecars — the rest of the pipeline stays the same.</li>
</ul>
<p>Start here. Add modules as your needs grow. The architecture adapts to your scale, not the other way around.</p>
<h2 id="decisionpointsthatshapewhereyouneedtotakeyourarchitecture">Decision points that shape where you need to take your architecture</h2>
<p>When designing a collector architecture, these are the questions that determine which patterns you need:</p>
<p>| Question | Impact |
|----------|--------|
| Do I need infrastructure telemetry (host metrics, disk logs)? | Determines whether you need a local collector or can use direct SDK export |
| Am I on a managed container platform (Fargate, ACA)? | Forces sidecar pattern instead of DaemonSet |
| Do I need centralized filtering, PII redaction, or credential isolation? | Adds a gateway stage |
| Do I need tail-based sampling? | Adds a sampling stage with load-balancing exporter and traceID routing |
| Do I want span-derived metrics (RED metrics)? | Requires spanmetrics before sampling in a two-step pipeline |
| How much data loss is acceptable during outages? | Determines in-memory queues vs. persistent queues vs. Kafka — applied to whichever stage needs protection |
| What is my expected data volume? | Determines whether capabilities can be co-located in a single deployment or need dedicated stages |</p>
<p>The answers to these questions don't map to a single "correct" architecture. They constrain the design space, and within those constraints, you make trade-offs between simplicity and capability.</p>
<h2 id="exploringthesepatternsinteractively">Exploring these patterns interactively</h2>
<p>If you'd rather explore how these building blocks compose than assemble them by hand, <a href="https://mlunadia.github.io/otel-blueprints/">OpenTelemetry Blueprints</a> is an open-source tool that generates reference architectures from your requirements.</p>
<p>Toggle your environment, signals, volume, resilience, and processing needs — and get a composed diagram with animated data flow, interactive tooltips, and reference collector configurations you can open directly in <a href="https://www.otelbin.io">OTelBin</a> for validation.</p>
<p><a href="https://mlunadia.github.io/otel-blueprints/"><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt5253df89f15ea6d5/6a7f19003ce8e27730cf5789/architecture.png" alt="Screenshot of a composed architecture diagram showing a Kubernetes cluster with DaemonSet agent, processing tier, and observability backend" /></a></p>
<p>The generated configurations export via OTLP, so they work with any OTLP-compatible backend — including <a href="https://www.elastic.co/docs/reference/opentelemetry/motlp">Elastic Observability</a>, which natively accepts and stores OTLP traces, metrics, and logs.</p>
<p>The architectures Blueprints generates are reference compositions — starting points for understanding how the building blocks fit together, not turnkey deployments. Every architecture should be adapted to your organisation's scale, security, networking, and compliance requirements. The patterns might combine or overlap differently in your environment than in anyone else's, and that's the point.</p>
<h2 id="getstarted">Get started</h2>
<p>The architectures described here export over OTLP, so they work with any compatible backend. If you don't have one yet, the fastest way to see your telemetry flowing end-to-end is with Elastic Observability — it natively ingests OTLP traces, metrics, and logs with no additional configuration.</p>
<ol>
<li><a href="https://cloud.elastic.co/serverless-registration?onboarding_token=observability">Start a free trial</a> on Elastic Cloud Serverless — no credit card required.</li>
<li>Point your collector's OTLP exporter at the managed OTLP endpoint.</li>
<li>Explore your traces, metrics, and logs in Kibana within minutes.</li>
</ol>
<p>Check out these resources to go further:</p>
<ul>
<li><a href="https://www.elastic.co/docs/reference/opentelemetry/motlp">Elastic's managed OTLP endpoint documentation</a></li>
<li><a href="https://www.elastic.co/docs/reference/opentelemetry/edot-collector">EDOT Collector — Elastic's distribution of the OpenTelemetry Collector</a></li>
<li><a href="https://mlunadia.github.io/otel-blueprints/">OpenTelemetry Blueprints — generate reference architectures interactively</a></li>
</ul>]]></content:encoded>
    <link>https://www.elastic.co/observability-labs/blog/opentelemetry-collector-reference-architectures</link>
    <guid isPermaLink="false">opentelemetry-collector-reference-architectures</guid>
    <category><![CDATA[OpenTelemetry]]></category>
    <category><![CDATA[Kubernetes]]></category>
    <category><![CDATA[Infrastructure Monitoring]]></category>
    <dc:creator><![CDATA[Miguel Luna]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt7d7f731ee0a1652e/6a7f19032f00b219dfefef09/opentelemetry-collector-reference-architectures.jpg" length="0" type="image/jpeg"/>
    <pubDate>Tue, 31 Mar 2026 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Developer's Guide to Easy Ops: Demystifying OpenTelemetry's Magic]]></title>
    <description><![CDATA[A Go-based Developer's 101 Guide to Easy Ops with OpenTelemetry and Elastic Observability.]]></description>
    <content:encoded><![CDATA[<h2 id="theintroductionfromcodetodashdemystified">The Introduction: From Code to Dash, Demystified</h2>
<p>Observability for developers has lately been distilled into implementing auto-instrumentation, allowing you to instantly connect your code with the larger observability world. This way of utilizing an upstream SDK is certainly the simplest and most production-ready, and works efficiently with the <a href="https://www.elastic.co/docs/solutions/observability/get-started/quickstart-elastic-cloud-otel-endpoint">Elastic Cloud Managed OTLP Endpoint</a>.</p>
<p>But what if you could not only add powerful tracing to your Go service but also <em>truly</em> understand how the magic works, rather than just copy-pasting configuration files or a line of code? In the same way that you build your knowledge of software development systems, observability, modernized by OpenTelemetry (OTel) standardization, is a rich, broad system that is valuable to understand. Here is an in-depth technical breakdown of every piece of simple OTel instrumentation using the Elastic Distributions of OpenTelemetry (EDOT) and Golang, from the ground up.</p>
<p>Telemetry is the automated collection, transmission and analysis of data from your application, which can apply to any observable distributed system. This data can range from regular health check calls with your application to real-time information about user interactions, requests, and transactions. Using the example application repository <a href="https://github.com/sophia-solo/otel-go-demo">here</a>, we’ll build a strong observability foundation to start observing our applications with confidence.</p>
<h2 id="understandingtheopentelemetryflow">Understanding the OpenTelemetry Flow</h2>
<p>Below, you will see the basic flow of your data when implementing observability with OTel in your system. Before we dive in, let’s explain some of the key terms within OTel. Let’s go over these base key players that we need to implement observability solutions with OTel:</p>
<ul>
<li><p><a href="https://www.elastic.co/docs/solutions/observability/apm/spans"><strong>Span</strong></a>: This is a single, timed unit of a distributed trace that can represent a specific operation, such as a database query or an HTTP handler.</p></li>
<li><p><a href="https://www.elastic.co/docs/solutions/observability/apm/traces"><strong>Trace</strong></a>: This is a detailed record of a single request’s journey through your system, AKA a hierarchy of your spans.</p></li>
<li><p><a href="https://opentelemetry.io/docs/specs/otel/trace/api/#tracer"><strong>Tracer</strong></a>: This is the handle for generating spans. You will typically have one per instrumentation library, for example myapp/http.</p></li>
<li><p><a href="https://opentelemetry.io/docs/specs/otel/trace/api/#tracerprovider"><strong>Tracer Provider</strong></a>: This is the cornerstone of the SDK. This creates Tracer instances, and you can configure it on application start up.</p></li>
<li><p><a href="https://www.elastic.co/docs/reference/apm/agents/go/custom-instrumentation-propagation"><strong>Context Propagation</strong></a>: The mechanism for passing trace context between operations and services, maintaining the relationship between parent and child spans.</p></li>
<li><p><a href="https://www.elastic.co/docs/deploy-manage/monitor/stack-monitoring/es-monitoring-exporters"><strong>Exporter</strong></a>: This is the part that is responsible for sending your telemetry data to a vendor backend, and you can decide if you are sending it to the OTel Collector, EDOT Collector or an OTLP Endpoint.</p></li>
</ul>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blte6fcb14e0997e272/6a7f05342f00b22f3fefe840/otel-flow.png" alt="Go OpenTelemetry App Flow" /></p>
<h2 id="installingthemagicinstrumentationstyle">Installing the Magic, Instrumentation Style</h2>
<p>OpenTelemetry provides instrumentation libraries that handle much of the tracing complexity for you. These libraries wrap common frameworks and libraries (like <code>net/http/otelhttp</code>) and automatically capture telemetry without requiring you to manually create spans for every operation.</p>
<p>However, before you're able to send any telemetry, OTel needs to know <em>who</em> (which service) is sending that data.</p>
<p>A <a href="https://opentelemetry.io/docs/concepts/resources/">resource</a> represents the specific entity, in this case <code>"simple-go-service"</code>, that is producing your telemetry data. Its identity is recorded as resource attributes, and resource attributes can include pod names, service names or instances, deployment environments; Basically <em>anything</em> important to identifying your resource. This resource is your service's identity card that gets attached to every span and metric that it emits with its attributes. Once your trace arrives, these attributes can answer <em>"what version was running?"</em> or <em>"which service is this from?"</em></p>
<pre><code>func initOTel(ctx context.Context, endpoint string) (func(context.Context) error, error) {
res, err := resource.New(ctx,
        resource.WithAttributes(
            semconv.ServiceName("simple-go-service"),
            semconv.ServiceVersion("1.0.0"),
        ),
    )
    if err != nil {
        return nil, err
    }
</code></pre>
<p>In the code above, <code>resource.New()</code> constructs the "identity card" of our Go service. The attributes that will be attached to it will use semantic conventions(<code>semconv</code>), standardized names for common metadata fields. These <a href="https://opentelemetry.io/docs/concepts/semantic-conventions/">semantic conventions</a> make sure that every single OTel-compatible observability backend knows their meaning.</p>
<p>Now that we've bootstrapped our application with the <code>initOtel</code> function, we can continue to configure everything else!</p>
<p>Let’s begin instrumenting this application by building all the app components that we will need to implement modern observability tools. Below is our instrumentation using <code>otelhttp</code>, which will handle span creation after calling the specified API routes. </p>
<pre><code>http.Handle("/hello", otelhttp.NewHandler(http.HandlerFunc(handleHello), "hello"))
http.Handle("/api/data", otelhttp.NewHandler(http.HandlerFunc(handleData), "data"))
http.HandleFunc("/health", handleHealth)

// Example of a tracer within our handleHello() function
tracer = tp.Tracer("simple-go-service")

ctx, span := tracer.Start(ctx, "process-hello")
defer span.End()
</code></pre>
<p>The key insight here is that <code>otelhttp.NewHandler</code> handles all the span lifecycle management for HTTP requests. You don't need to manually call <code>tracer.Start()</code>or <code>span.End()</code> for basic HTTP tracing since the library does this for you.</p>
<p>On application start up, the SDK will use the tracer provider set up below in order to create Tracer instances. These instances help create and manage the spans contained within traces.</p>
<pre><code>traceExporter, err := otlptracegrpc.New(ctx,
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;otlptracegrpc.WithEndpoint(endpoint),
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;otlptracegrpc.WithInsecure(),
    )

    tp := sdktrace.NewTracerProvider(
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;sdktrace.WithBatcher(traceExporter),
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;sdktrace.WithResource(res),
    )
    otel.SetTracerProvider(tp)
    tracer = tp.Tracer("simple-go-service")
</code></pre>
<p>Within our <code>initOTel</code> function, we will set up one of our most important signals: logs. First, we initialize the logExporter that will send logs to our OTel Collector using gRPC protocol. Then the <code>LoggerProvider</code> will create the base of the <code>logExporter</code> that batches log entries together before sending those batches to your exporter, attaching metadata about the services along the way. Lastly, the <code>LoggerProvider</code> also creates a standard Go structured logger (slog) that automatically includes trace context (such as span IDs) and batches your log with other logs. These are sent to your observability backend through the exporter along with your metrics and traces. </p>
<pre><code>logExporter, err := otlploggrpc.New(ctx,
        otlploggrpc.WithEndpoint(endpoint),
        otlploggrpc.WithInsecure(),
    )
    if err != nil {
        return nil, err
    }

    lp := sdklog.NewLoggerProvider(
        sdklog.WithProcessor(sdklog.NewBatchProcessor(logExporter)),
        sdklog.WithResource(res),
    )
    logger = slog.New(otelslog.NewHandler("simple-go-service", otelslog.WithLoggerProvider(lp)))
</code></pre>
<p>Below you can see how you can view your logs through Kibana in the APM UI. These logs are also color - coordinated; Coded warnings are in yellow, errors are in red, and regular logs are in green.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blte40c8a47ca47ad2e/6a7f0537b4377041fe4d6963/log-viewer.png" alt="Viewing your logs in the APM UI" /></p>
<p><a href="https://www.elastic.co/docs/solutions/observability/apm/metrics">Metrics</a> are set up in the next part of our code. Metrics are telemetry signals that track the quantitative data from your application, such as response times and request counts. The metric exporter is initialized to send metric data to our EDOT Collector then to our observability backend, Elastic Observability in this case, using gRPC. The meter provider in the next portion periodically collects and exports our metrics data and measurements, the same as the tracer provider creates tracers. The only difference between the two providers is that the meter provider works on a timer while the trace provider exports spans as they complete.</p>
<pre><code>metricExporter, err := otlpmetricgrpc.New(ctx,
        otlpmetricgrpc.WithEndpoint(endpoint),
        otlpmetricgrpc.WithInsecure(),
    )
    if err != nil {
        return nil, err
    }

    mp := metric.NewMeterProvider(
        metric.WithReader(metric.NewPeriodicReader(metricExporter)),
        metric.WithResource(res),
    )
    otel.SetMeterProvider(mp)

    meter := mp.Meter("simple-go-service")
    requestCounter,  = meter.Int64Counter("http.requests")
    requestDuration,  = meter.Float64Histogram("http.duration")
</code></pre>
<p>In order to finish initializing OpenTelemetry, we set up our propagators for context propagation. The set text map propagator automatically injects the trace ID and the span ID of your service making an outbound HTTP request to another service, following the <a href="https://www.w3.org/TR/trace-context/">W3C Trace Context</a> standard. In short, this maintains the parent-child relationship between spans.</p>
<pre><code>otel.SetTextMapPropagator(propagation.TraceContext{})

    return func(ctx context.Context) error {
        tp.Shutdown(ctx)
        mp.Shutdown(ctx)
        lp.Shutdown(ctx)
        return nil
    }, nil
</code></pre>
<p>Now that you know how these pieces work together, try to run the repository linked <a href="https://github.com/sophia-solo/otel-go-demo">here</a>, using the readme as your guide.</p>
<h3 id="sidenoteaddingcustomspans">Sidenote: Adding Custom Spans</h3>
<p>For getting an application emitting traces, this instrumentation works great! If you visit localhost:8080/hello after starting the docker containers, the <code>otelhttp</code> middleware automatically creates spans for each HTTP request. However, basic instrumentation only shows essential application telemetry, such as response duration, URL paths, and status codes. You won’t know what happens between the request coming in and request completion. The moment OpenTelemetry truly gains power is when you add custom spans. Unlike auto-instrumentation where spans are created as well as closed automatically, custom spans require you to explicitly start and stop them.</p>
<p>Custom spans can track your application’s logic, such as specific business events or marking expensive operations, using a detailed hierarchy within each trace. In the <a href="https://github.com/sophia-solo/otel-go-demo">application</a> for this article, there are several custom spans that were created to track important operations:</p>
<ul>
<li><p><code>background-work</code>: This traces asynchronous processing that happens with the main request.</p></li>
<li><p><code>computation:</code> This measures computations and then captures those results, and the computation type.</p></li>
</ul>
<p>Custom spans add granular visibility into your application's behavior. For example, in <code>performComputation</code>:</p>
<pre><code>ctx, span := tracer.Start(ctx, "computation")
defer span.End()

result := rand.Float64()
span.SetAttributes(
    attribute.String("comp.type", compType),
    attribute.Float64("comp.result", result),
    )

    logger.InfoContext(ctx, "Computation completed", "type", compType, "result", result)

if result &lt; 0.3 {
span.AddEvent("Low confidence result")
    logger.WarnContext(ctx, "Low confidence computation", "result", result)
}
}
</code></pre>
<p>The attributes set above become searchable and filterable in our Elastic Observability backend, allowing for attribute filtering by <code>attribute.result</code> and <code>attribute.compType</code>. If you query your data with “show me all computations where results are less than 0.3,” then you will notice the span event <code>span.AddEvent(“Low confidence result”)</code> tacked on with a timestamped marker. This appears on your trace timeline as well, adding even more visibility to any unusual events. Below is a small example of the filtering that Kibana can accomplish from custom spans.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blta0fe36324e168b50/6a7f053b6c6eac182ef13dc5/computations.png" alt="Filtering attribute.Result to review borderline Low Confidence results" /></p>
<h2 id="thedatapipelinefromcodetoirl">The Data Pipeline: From Code to IRL</h2>
<p>Now that you can export your custom spans and data to OTLP which sends it to the EDOT Collector and then to an observability backend, the best hub for your telemetry data will be the [OpenTelemetry Collector](https://opentelemetry.io/docs/collector/. It is a simple, standalone process that is able to receive, process and export all of your telemetry data. Within this project, we use the Elastic Distributions of OpenTelemetry (<a href="https://www.elastic.co/docs/solutions/observability/get-started/opentelemetry/quickstart/self-managed/docker">EDOT</a>) Collector, an optimized Collector for usage within your Elastic Stack. Since this is a self-managed Elastic instance, this article and connected repository utilize the EDOT Collector through <code>elasticapm</code>, but for Elastic Cloud or Serverless projects, you can use the Elastic Managed OpenTelemetry Protocol (OTLP) Endpoint. As noted in the quickstart documentation <a href="https://www.elastic.co/docs/solutions/observability/get-started/quickstart-elastic-cloud-otel-endpoint">here</a>, the Elastic Cloud Managed OTLP Endpoint endpoint helps get your data quickly and efficiently into your Elastic Stack through OTLP, without schema translation! This means that your telemetry hits Elastic instantly and your telemetry data remains vendor-neutral.</p>
<p>For most developers and SREs, this Collector is an amazing tool. It allows you to decouple your code from the observability backend. Your application does not need to know its final destination, it can just send the data to the Collector. Your observability backend can change constantly without it even touching your code. The OpenTelemetry Collector also acts as a gateway for multiple streams of data, and is able to accept various formats in order to unify them for exportation. Lastly, the OpenTelemetry Collector is able to offload processing power from your application - tasks such as retries, batching and filtering can happen in the Collector, not your application.</p>
<p>After trying out this article’s repository, try auto-instrumenting your application with <a href="https://www.elastic.co/docs/reference/opentelemetry">Elastic Distributions of OpenTelemetry</a> (EDOT) so that you can utilize the APM UI to its full potential! With the latest version of Elasticsearch and Kibana <a href="https://github.com/elastic/start-local"><code>start-local</code></a>, you can use <a href="https://www.docker.com/">Docker</a> to install and run the services and instantly start monitoring your application. </p>
<h3 id="understandingthecollectorconfiguration">Understanding the Collector Configuration</h3>
<p>The Collector's behavior is defined in a configuration file (<code>otel-collector-config.yaml</code>). Let's break down each component.</p>
<p><strong>Receivers</strong> define how the Collector accepts telemetry data. Here, we're listening for both gRPC and HTTP traffic.</p>
<pre><code>receivers:
&amp;nbsp;&amp;nbsp;# Receives data from other Collectors in Agent mode
&amp;nbsp;&amp;nbsp;otlp:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;protocols:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;grpc:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;endpoint: 0.0.0.0:4317
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;http:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;endpoint: 0.0.0.0:4318
</code></pre>
<p><strong>Connectors</strong> are specialized components that sit in between pipelines, and in this case, we are using the <code>elasticapm</code> Connector. This APM Connector exports our metrics, logs, and traces, while simultaneously acting as a receiver for the metrics/aggregated-otel-metrics pipeline (see below). Without it, your raw OTLP data lands in Elasticsearch, but the APM UI has nothing to build its views from.</p>
<pre><code>connectors:
  elasticapm: {} # Elastic APM Connector
</code></pre>
<p><strong>Processors</strong> transform, filter, or enrich data as it passes through the EDOT Collector. The batch processor aggregates spans before export, reducing network overhead and improving efficiency, as well as limiting batch sizes. The batch/metrics processor does this as well, but for APM metrics. Lastly, there is the Elastic APM processor. This processor ensures that your spans fields are aligned, your traces views are complete, and  it overall bridges the gap between Elastic's expectations and OpenTelemetry's formatting of your traces.</p>
<pre><code>processors:
  batch:
    send_batch_size: 1000
    timeout: 1s
    send_batch_max_size: 1500
  batch/metrics:
    send_batch_max_size: 0 # Explicitly set to 0 to avoid splitting metrics requests
    timeout: 1s
  elasticapm: {} # Elastic APM Processor
</code></pre>
<p>As mentioned previously in the article, <strong>exporters</strong> send data to your observability backend. The debug exporter logs telemetry to the console (useful for development), while the Elasticsearch exporter sends traces to your Elastic stack.</p>
<pre><code>exporters:
  debug: {}
  elasticsearch/otel:
    endpoints:
      - ${ELASTIC_ENDPOINT} # Will be populated from environment variable
    user: elastic
    password: ${ELASTIC_PASSWORD}
    tls:
      ca_file: /config/certs/ca/ca.crt
    mapping:
      mode: otel
</code></pre>
<p><strong>Pipelines</strong> connect receivers, processors, and exporters into a data flow. These EDOT Collector pipelines receive OTLP traces, batches them, and exports to the <code>debug</code>, <code>elasticapm</code> and <code>elasticsearch/otel</code> exporters. It also exports metrics to the <code>debug</code> and <code>elasticsearch/otel</code> exporters.</p>
<pre><code>service:
  pipelines:
    metrics:
      receivers: [otlp]
      processors: [batch/metrics]
      exporters: [debug, elasticsearch/otel]
    logs:
      receivers: [otlp]
      processors: [batch]
      exporters: [debug, elasticapm, elasticsearch/otel]
    traces:
      receivers: [otlp]
      processors: [batch, elasticapm]
      exporters: [debug, elasticapm, elasticsearch/otel]
    metrics/aggregated-otel-metrics:
      receivers:
        - elasticapm
      processors: [] # No processors defined in the original for this pipeline
      exporters:
        - debug
        - elasticsearch/otel
</code></pre>
<h2 id="debuggingyourcodewithconfidenceinkibana">Debugging Your Code with Confidence in Kibana</h2>
<p>Elastic Observability, utilizing Kibana and Streams, has native support for the OTLP Endpoint through the EDOT Collector, which was used in this project. Below, you can see that your data is automatically connected to Streams from the beginning, requiring no extra leg work! You can add conditions or any Grok processors as your data is streaming in, and you'll be able to instantly see your data's schema and data quality.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt7f0d96abddaada00/6a7f053dde23158fa4fd786d/streams-connection.png" alt="Streams built-in connection" /></p>
<p>Elastic also provides the Elastic Cloud Managed Endpoint for even easier storage, data-processing, and scaling. If you use this Managed Endpoint, it means that you can configure OpenTelemetry to send data directly to Elasticsearch, without ANY specialized Collectors. Any way you choose, once your traces are flowing, Kibana’s APM UI provides powerful visualization and analysis capabilities will be everything you need to debug your code. You are able to drill down into individual requests, identify bottlenecks, find anomalies and troubleshoot any issues that arise with confidence.</p>
<p>Here is one span of interest from this repository. Within Kibana, you can immediately filter by the Trace ID, finding other spans with the same Trace ID to visually see the entire trace.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt07ee960435778536/6a7f054173d9bddcb129d7f8/pre-filter-traces.png" alt="A span of interest among many" />
<img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltff0f71d463353c0c/6a7f0544ea068d7b00f09b4c/post-filter-traces.png" alt="The entire trace of the span" /></p>
<p>Kibana Discover also allows you to switch indices instantly without losing your filters, ensuring that you can also see the logs that correspond with the same Trace ID.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt77ba55b402496814/6a7f05482f00b2d0c8efe852/log-trace.png" alt="Logs matching the Trace ID" /></p>
<p>In addition to the manually checking your traces, you can automatically check them within the APM UI (shown below). This is easy trace visualization using the Kibana APM UI is readily available while using the <code>elasticapm</code> connector. Below is a visualization of a trace comprised of spans within our project. Knowing both methods of correlating spans is beneficial to build the foundation of utilizing Kibana and the APM UI for observability.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt8ca3abbac39f6f65/6a7f054bbd2198b235757d48/automatic-apm-trace.png" alt="Automatic trace span hierarchy in Kibana APM" /></p>
<p>Here is a fully built out dashboard built from the repository featured in this article. The possibilities with Elastic Observability are endless!</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltf6d064cb610d0f80/6a7f054eead8ec624cbaa4df/kibana-dashboard.png" alt="Full Kibana Dashboard" /></p>
<h2 id="congratsyourenotjustadeveloperanymore">Congrats, You’re Not “Just” a Developer Anymore!</h2>
<p>We’ve broken down the why and how behind OpenTelemetry’s basic components, including the TraceProvider, the span, the exporter and the Collector. Here, you’ve done more than just implement your tracing tool. You now understand the complete data flow from your code to the graphs on your dashboard.</p>
<p>You can now speak the language of observability with confidence, not because you memorized a configuration file, but because you now understand the data flow from your code to the graph on your dashboard. You understand how telemetry moves through your system. You aren’t “just” a developer anymore; you’re now a developer who can truly see.</p>
<p>Try out the code repo above! Included in the [repository]() is a generate-traffic.sh script file. You can run this repeatedly in order to generate logs, traces, and metrics for you to play with within the APM UI. Also, check out our latest <a href="https://www.elastic.co/docs/release-notes/elasticsearch">releases</a> in our release docs page for exciting Elastic updates.</p>]]></content:encoded>
    <link>https://www.elastic.co/observability-labs/blog/developers-guide-to-easy-ops</link>
    <guid isPermaLink="false">developers-guide-to-easy-ops</guid>
    <category><![CDATA[Infrastructure Monitoring]]></category>
    <category><![CDATA[OpenTelemetry]]></category>
    <dc:creator><![CDATA[Sophia Solomon]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt76532418dd498412/6a7f055196b5a66f0087b133/blog-header.png" length="0" type="image/png"/>
    <pubDate>Tue, 17 Mar 2026 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[A train ride away from a million events per second with EDOT Cloud Forwarder]]></title>
    <description><![CDATA[EDOT Cloud Forwarder for AWS from Elastic Observability is now Generally Available. Deploying EDOT Cloud Forwarder and reliably handling one million events per second with zero intervention, zero data loss, and zero idle cost.]]></description>
    <content:encoded><![CDATA[<p>Infrastructure observability is critical for maintaining uptime, optimizing cloud environments, and securing the cloud perimeter. Cloud environments generate observability data at massive scale. VPC Flow Logs, ELB Access Logs, CloudTrail and CloudWatch logs can easily reach hundreds of thousands of events per second. Dealing with scale like this is a complex problem all by itself.</p>
<p>Today, we introduce <strong>EDOT Cloud Forwarder</strong>, built on OTel Collector, it is the simplest, fastest, and possibly most boring way to connect your Cloud environment to Elastic Observability, and it is <strong>now Generally Available on AWS</strong>. With EDOT Cloud Forwarder you can get started in seconds, get observability across your entire cloud estate, and easily handle telemetry at any volume.</p>
<h2 id="deployingcloudforwarderfromadistrictlinetrain">Deploying Cloud Forwarder from a District Line train</h2>
<p>So, I got to work deploying EDOT Cloud Forwarder in my AWS account. I was doing it on the commute, using nothing more than a decent 4G signal and hoping that my 27% battery would be enough.</p>
<p>I hit deploy on the terraform template and waited. I started seeing events flowing into Elastic Observability.</p>
<p>As the train pulled into Putney Bridge, the flow of logs peaked, and one million events per second scrolled across my screen.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt349584b4d4f5955a/6a7f0ef777b03403873ff574/putney_bridge_1MEPS.png" alt="Putney_Bridge" /></p>
<p>Once deployed, I got the best three zeros I could expect:</p>
<ul>
<li><strong>Zero Intervention:</strong> I watched as traffic ramped up to a full 1M EPS. Lambda functions automatically scaled out from a few instances to the 60-65 concurrent executions needed. There were <strong>zero manual adjustments required</strong>. The scaling was instant and hands-free.</li>
<li><strong>Zero Data Loss:</strong> It achieved a consistent processing rate, with every single event indexed in Elasticsearch.</li>
<li><strong>Zero Idle Cost:</strong> When there are no events, Cloud Forwarder scales to zero - it has no fixed infrastructure cost. You only pay for the moment data is being processed, not for permanently over-provisioned servers sitting idle.</li>
</ul>
<p>Right before the train came to a standstill, I looked at the total cost for running Cloud Forwarder for the two minutes between Parsons Green and Putney Bridge - we forwarded about 120GB of telemetry and the total cost was below £0.10. Well, unless you count the £2.50 train ticket!</p>
<h2 id="makingobservabilityeasyatanyscale">Making Observability easy at any scale</h2>
<p>Getting started observing your infrastructure is hard and once it's observable, deriving actionable value from it requires sifting and winnowing through massive volumes of telemetry data, sometimes millions of events per second.</p>
<p>The new EDOT Cloud Forwarder for AWS (also available in Preview for GCP and Azure) is easy to deploy, with just a Terraform template. To make sure that it was easy to get started with, we designed it to be as close to a <a href="https://www.elastic.co/docs/reference/opentelemetry/edot-cloud-forwarder/aws#quick-deployment-direct-link">single-click deployment</a> as possible:</p>
<p>Just click the link below to launch the CloudFormation stack in your AWS account:</p>
<p><a href="https://console.aws.amazon.com/cloudformation/home?%23/stacks/new?templateURL=https%3A%2F%2Fedot-cloud-forwarder.s3.amazonaws.com%2Fv1%2Flatest%2Fcloudformation%2Fs3_logs-cloudformation.yaml"><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blta2249df99c16d510/6a7f0efa5967e55bc95dd3ab/cloudformation-launch-stack.png" alt="Launch_stack" /></a></p>
<p>The best part? The fastest way to get started with Elastic Observability scales to any size workload! With EDOT Cloud Forwarder you have one solution which automatically scales down to zero and up to millions of events per second.</p>
<h2 id="sowhatisedotcloudforwarder">So, what is EDOT Cloud Forwarder?</h2>
<p>EDOT Cloud Forwarder is a serverless OpenTelemetry Collector that, on AWS, runs as a Lambda function. In AWS, it is triggered by events and processes logs and metrics from services such as VPC Flow Logs, ELB Access Logs, CloudTrail, CloudWatch Logs and CloudWatch Metrics.</p>
<p>It has the following core capabilities:</p>
<ul>
<li>Collects observability and security data from Cloud Service Providers</li>
<li>Parses data into native OpenTelemetry format</li>
<li>Forwards data over OTLP</li>
<li>Scales up and down based on traffic</li>
</ul>
<p>ECF for AWS is a pure serverless solution, no VMs, containers, or Kubernetes control planes to manage.</p>
<h2 id="offthetrainamorecontrolledscenario">Off the train, a more controlled scenario</h2>
<p>For a more controlled testing scenario, we used synthetic VPC Flow Log data generated to show how easy EDOT Cloud Forwarder can sustain a million events per second reliably and without data loss.</p>
<p>For the configuration, we left all EDOT Cloud Forwarder configuration/settings at their <a href="https://www.elastic.co/docs/reference/opentelemetry/edot-cloud-forwarder/aws#optional-settings">defaults</a>. In AWS, the Lambda max concurrency defaults to 5. If left to the default of 5, up to around 50k EPS could be expected. For our scenario, we bumped this to 100 to ensure we'd have plenty of headroom for our test.</p>
<p>We ran the scenario in 10 minute stages, with each stage resulting in a larger data volume. We held ingest flat during the stage to provide short-term steady state windows for us to grab metrics.</p>
<p>We experienced no errors across all stages of the scenarios, no retries outside expected behavior, no data loss across 5.4 billion ingested events.</p>
<h2 id="statsforobservabilitynerds">Stats for Observability Nerds</h2>
<p>Because we know you love them:</p>
<h3 id="incrementalloadstages">Incremental Load Stages</h3>
<p>We tested EDOT Cloud Forwarder using incremental load stages, gradually increasing traffic from approximately 300,000 events per second to over 1 million events per second.</p>
<p>The graph below shows the Elasticsearch ingestion rate throughout the entire test duration. You can see the clear progression as we ramped up through six distinct stages, with each plateau representing a 10 minute stabilization period.</p>
<p>The system handled each traffic increase smoothly, culminating in sustained 1 million documents per second ingestion with no bottlenecks or data loss.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt0347a4f147111e38/6a7f0efdde2315fe3cfd7d1b/es-ingestion-rate.png" alt="ES Ingestion Rate" /></p>
<h3 id="lambda">Lambda</h3>
<p>As seen in CloudWatch metrics (no manual adjustments required). No errors and no throttles were seen during the full duration of the test.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt93d4377526e21313/6a7f0f011967ea48ff33082d/lambda-errors-throttles.png" alt="Lambda errors and throttles" /></p>
<h4 id="concurrentexecutions">Concurrent executions</h4>
<p>60 to 65 instances running at the same time.
<img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltb9dd8e54aae0185f/6a7f0f0396b5a6640587b523/lambda-instances.png" alt="Lambda concurrent executions" /></p>
<h4 id="averageexecutiontimeperlambda">Average execution time per Lambda</h4>
<p>Each execution is taking about 5 seconds.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt15de9c99c7d107d6/6a7f0f0673d9bd135c29dbfd/lambda-execution-time.png" alt="Lambda average execution time" /></p>
<h4 id="memoryusage">Memory Usage</h4>
<p>Memory use stabilized at around 450 MB, below the default limit of 512 MB.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt2dab09e2769e9dd7/6a7f0f09ead8ec7babbaa958/lambda-memory-used.png" alt="Lambda memory usage" /></p>
<h3 id="elasticsearchindexing">Elasticsearch Indexing</h3>
<p>Elasticsearch indexed one million documents per second, with events visible in Discover within seconds and no indexing delays or bottlenecks.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltb78d36a7d0dd78c2/6a7f0f0cead8ec5cc3baa95c/es-1m-eps.png" alt="1M EPS" /></p>
<h2 id="efficientbydesignlambdaat1mepsfroms3">Efficient by design: Lambda at 1M EPS from S3</h2>
<p>Running ECF for AWS at 1M EPS costs about <strong>$3.87 per hour</strong>. Around 66 percent ($2.57 per hour) is data transfer (same region), 34 percent ($1.32 per hour) is Lambda compute, and less than 1 percent is S3 requests.</p>
<p>This is fully serverless with no idle cost. You only pay while events are forwarded. With S3, data arrives pre-batched in large objects, which keeps Lambda invocations low and compute costs tightly controlled. At sustained throughput, Lambda costs are comparable to EKS—but without cluster management or idle capacity.</p>
<h3 id="otheroptionsotelcollectoroneksat1meventspersecond">Other options: OTel Collector on EKS at 1M events per second</h3>
<p>An OTel Collector on EKS sized for 1M EPS has a baseline cost of about <strong>$3.69 per hour</strong>. Roughly $0.33 per hour of this is compute related, EC2 nodes, EKS control plane, and EBS. The rest comes from data transfer and SQS, which scale with traffic and do not change with utilization.</p>
<h4 id="idlecomputeimpactoneksrealcosts">Idle compute impact on EKS real costs</h4>
<p>Considering EKS is typically provisioned for peak load, the real cost of the compute portion is affected by idle capacity. At <strong>100 percent utilization</strong>, total cost is <strong>$3.69 per hour</strong>. At <strong>50 percent utilization</strong>, a common baseline to absorb burstiness, total cost rises to about <strong>$4.02 per hour</strong>. At <strong>30 percent utilization</strong>, it increases further to about <strong>$4.46 per hour</strong>.</p>
<h4 id="payforworkvspayforcapacity">Pay for Work vs Pay for Capacity</h4>
<p>ECF for AWS delivers 1M EPS at a cost comparable to EKS at peak utilization, with no idle compute or capacity planning required. EKS can reach the same peak throughput, but total cost increases further as average utilization drops because compute capacity must be provisioned in advance.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt726e3a92780a3de7/6a7f0f0f448e4e0bb25c07ed/otel-collector-lambda.png" alt="Collector-lambda" /></p>
<h2 id="conclusion">Conclusion</h2>
<p>The boring truth: to the EDOT Cloud Forwarder, a million events per second is no different from any other workload.</p>
<p>With no infrastructure to deploy, no idle cost and no manual scaling, I guess the best thing to do is to stop overthinking and start forwarding! It's like stepping onto the fastest train on the line: you just get on and you're instantly en route to your destination, effortlessly handling any distance or in this case, any volume.</p>
<p>So, we're shipping it. ECF for AWS is now Generally Available.</p>
<h2 id="getstarted">Get Started</h2>
<ol>
<li>Deploy EDOT Cloud Forwarder via CloudFormation (below) or using the AWS Serverless Application Repository</li>
</ol>
<p><a href="https://console.aws.amazon.com/cloudformation/home?%23/stacks/new?templateURL=https%3A%2F%2Fedot-cloud-forwarder.s3.amazonaws.com%2Fv1%2Flatest%2Fcloudformation%2Fs3_logs-cloudformation.yaml"><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blta2249df99c16d510/6a7f0efa5967e55bc95dd3ab/cloudformation-launch-stack.png" alt="Launch_stack" /></a></p>
<ol>
<li>Create an Observability project using an <a href="https://cloud.elastic.co/login?redirectTo=%2Fhome">Elastic Cloud</a> free trial or deploy locally with start-local if you don't already have an Elastic project or deployment.</li>
</ol>
<p>Visit EDOT Cloud Forwarder for AWS <a href="https://www.elastic.co/docs/reference/opentelemetry/edot-cloud-forwarder/aws">Documentation</a> for more details.</p>
<p>Check out these other resources on OpenTelemetry at Elastic</p>
<p><a href="https://www.elastic.co/observability-labs/blog/elastic-agent-pivot-opentelemetry">Discover how Elastic is evolving data ingestion with OpenTelemetry</a></p>
<p><a href="https://www.elastic.co/observability-labs/blog/elastic-distribution-opentelemetry-sdk-central-configuration-opamp">Learn how OpAMP enables centralized configuration of OpenTelemetry SDKs</a></p>]]></content:encoded>
    <link>https://www.elastic.co/observability-labs/blog/one-million-events-per-second-with-edot-cloud-forwarder</link>
    <guid isPermaLink="false">one-million-events-per-second-with-edot-cloud-forwarder</guid>
    <category><![CDATA[OpenTelemetry]]></category>
    <category><![CDATA[Infrastructure Monitoring]]></category>
    <dc:creator><![CDATA[Michalis Katsoulis,Andreas Gkizas,Miguel Luna]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt9c152dbb9b2e77fa/6a7f0f1363e959a51e73debc/ecf-for-aws.jpg" length="0" type="image/jpeg"/>
    <pubDate>Tue, 20 Jan 2026 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[A Practical Guide to end-to-end distributed tracing for Nginx with OpenTelemetry in Elastic]]></title>
    <description><![CDATA[Instrument Nginx with the OpenTelemetry tracing module and export spans to Elastic Observability's APM for full end-to-end distributed tracing.]]></description>
    <content:encoded><![CDATA[<p>Nginx sits at the very front of most modern architectures: handling SSL, routing, load balancing, authentication, and more. Yet, despite its central role, it is often absent from distributed traces.<br />
That gap creates blind spots that impact performance debugging, user experience analysis, and system reliability.</p>
<p>This article explains <strong>why Nginx tracing is important</strong> in an application context, and provides a <strong>practical guide</strong> to enable the Nginx <a href="https://nginx.org/en/docs/ngx_otel_module.html">Otel</a> tracing module exporting spans directly to <a href="https://www.elastic.co/docs/solutions/observability/apm">Elastic APM</a>.</p>
<h2 id="whynginxtracingmattersformodernobservability">Why Nginx Tracing Matters for Modern Observability</h2>
<p>Instrumenting only backend services gives you only half the picture.<br />
Nginx sees:</p>
<ul>
<li>every incoming request  </li>
<li>client trace context  </li>
<li>TLS negotiation  </li>
<li>upstream errors (502, 504)  </li>
<li>edge-layer latency  </li>
<li>routing decisions  </li>
</ul>
<p>If Nginx is not in your traces, your distributed trace is incomplete.</p>
<p>By adding OpenTelemetry tracing at this ingress layer, you unlock:</p>
<p><em>1. Full trace continuity</em> : From browser → Nginx → backend → database.
<img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltc30b2ea6cee5fdff/6a7f0eaa96b5a68e9c87b50b/document_elastic_nginx_otel_instrumentation_1.png" alt="Nginx Trace Continuity" /></p>
<p><em>2. Accurate latency attribution</em> : Edge delays vs. backend delays are clearly separated which unlock Elastic <a href="https://www.elastic.co/docs/solutions/observability/apm/machine-learning">APM Latency</a> anomaly detection for proactive detection.
<img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt234adca13afbd446/6a7f0eadbd219835c475815b/document_elastic_nginx_otel_instrumentation_2.png" alt="Nginx Latency Detection" /></p>
<p><em>3. Error root-cause clarity</em> : Nginx errors appear as spans instead of backend “mystery gaps”.</p>
<p><em>4. Complete service topology</em> : Your APM service map finally shows the real architecture.
<img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt40ad01ea2ad51be3/6a7f0eb005b7b55b5618ba0e/document_elastic_nginx_otel_instrumentation_4.png" alt="Nginx APM Service Map" /></p>
<h2 id="integratingnginxwithopentelemetryondebian">Integrating Nginx with OpenTelemetry on Debian</h2>
<p>This guide provides a comprehensive overview of why, how to install and configure the Nginx OpenTelemetry module on a Debian-based system. The configuration examples are tailored to send telemetry data directly to an Elastic APM endpoint whether it's an <a href="https://www.elastic.co/docs/reference/opentelemetry">EDOT</a> Collector or <a href="https://www.elastic.co/observability-labs/blog/elastic-managed-otlp-endpoint-for-opentelemetry">mOtel</a> in case of our serverless, enabling end-to-end distributed tracing.</p>
<h3 id="installationondebian">Installation on Debian</h3>
<p>The Nginx OTEL module is not included in the standard Nginx packages. It must be installed along with a working nginx configuration.</p>
<h4 id="prerequisites">Prerequisites</h4>
<p>First, install the necessary tools for compiling software and the Nginx development dependencies.</p>
<pre><code>sudo apt update
sudo apt install -y apt install nginx-module-otel
</code></pre>
<h4 id="loadthemoduleinnginx">Load the Module in Nginx</h4>
<p>Edit your main <code>/etc/nginx/nginx.conf</code> file to load the new module. This directive must be at the top level, before the <code>http</code> block.</p>
<pre><code># /etc/nginx/nginx.conf

load_module modules/ngx_otel_module.so;

events {
    # ...
}

http {
    # ...
}
</code></pre>
<p>Now, test your configuration and restart Nginx.</p>
<pre><code>sudo nginx -t
sudo systemctl restart nginx
</code></pre>
<h3 id="configuration">Configuration</h3>
<p>Configuration is split between the main <code>nginx.conf</code> file (for global settings) and your site-specific server block files.</p>
<h4 id="globalconfigurationetcnginxnginxconf">Global Configuration (<code>/etc/nginx/nginx.conf</code>)</h4>
<p>This configuration sets up the destination for your telemetry data and defines global variables used for CORS and tracing. These settings are placed inside the <code>http</code> block.</p>
<pre><code>http {
    ...

    # --- OpenTelemetry Exporter Configuration ---
    # Defines where Nginx will send its telemetry data directly to Elastic APM or EDOT.
    otel_exporter {
        endpoint https://&lt;ELASTIC_URL&gt;:443;
        header Authorization "Bearer &lt;TOKEN&gt;";
    }

    # --- OpenTelemetry Service Metadata ---
    # These attributes identify Nginx as a unique service in the APM UI.
    otel_service_name nginx;
    otel_resource_attr service.version 1.28.0;
    otel_resource_attr deployment.environment production;
    otel_trace_context propagate; # Needed to propagate the RUM traces to the backend

    # --- Helper Variables for Tracing and CORS ---
    # Creates the $trace_flags variable needed to build the outgoing traceparent header.
    map $otel_parent_sampled $trace_flags {
        default "00"; # Not sampled
        "1"     "01"; # Sampled
    }

    # Creates the $cors_origin variable for secure, multi-origin CORS handling.
    map $http_origin $cors_origin {
        default "";
        "http://&lt;URL_ORIGIN_1&gt;/" $http_origin; # Add your Origin here to allow CORS
        "https://&lt;URL_ORIGIN_2&gt;/" $http_origin; # Add your others Origin here to allow CORS
    }
...
}
</code></pre>
<h4 id="serverblockconfigurationetcnginxconfdsiteconf">Server Block Configuration (<code>/etc/nginx/conf.d/site.conf</code>)</h4>
<p>This configuration enables tracing for a specific site, handles CORS preflight requests, and propagates the trace context to the backend service.</p>
<pre><code>server {
    listen 443 ssl;
    server_name &lt;WEBSITE_URL&gt;;

    # --- OpenTelemetry Module Activation ---
    # Enable tracing for this server block.
    otel_trace on;
    otel_trace_context propagate;

    location / {
        # --- CORS Preflight (OPTIONS) Handling ---
        # Intercepts preflight requests and returns the correct CORS headers,
        # allowing the browser to proceed with the actual request.
        if ($request_method = 'OPTIONS') {
            add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS' always;
            add_header 'Access-Control-Allow-Headers' 'Content-Type, traceparent, tracestate' always;
            add_header 'Access-Control-Max-Age' 86400;
            add_header 'Access-Control-Allow-Origin' "$cors_origin" always;
            return 204;
        }

        # --- OpenTelemetry Trace Context Propagation ---
        # Manually constructs the W3C traceparent header and passes the tracestate
        # header to the backend, linking this trace to the upstream service.
        proxy_set_header traceparent      "00-$otel_trace_id-$otel_span_id-$trace_flags";
        proxy_set_header tracestate       $http_tracestate;

        # --- Standard Proxy Headers ---
        proxy_set_header Host             $host;
        proxy_set_header X-Real-IP        $remote_addr;
        proxy_set_header X-Forwarded-For  $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;

        # --- Forward to Backend ---
        # Passes the request to the actual application (eg. localhost in this example).
        proxy_pass http://&lt;BACKEND_URL&gt;:8080;
    }
}
</code></pre>
<p>Test your configuration and restart Nginx.</p>
<pre><code>sudo nginx -t
sudo systemctl restart nginx
</code></pre>
<h2 id="conclusionturningnginxintoafirstclassobservabilitysignal">Conclusion: Turning Nginx into a First-Class Observability Signal</h2>
<p>By enabling OpenTelemetry tracing directly in Nginx and exporting spans to Elastic APM (via EDOT or Elastic’s managed OTLP endpoint), you bring your ingress layer into the same observability model as the rest of your stack. The result is:</p>
<ul>
<li>true end-to-end trace continuity from the browser to backend services  </li>
<li>clear separation between edge latency and application latency  </li>
<li>immediate visibility into gateway-level failures and retries  </li>
<li>accurate service maps that reflect real production traffic  </li>
</ul>
<p>Most importantly, this approach aligns Nginx with modern observability standards. It avoids proprietary instrumentation, fits naturally into OpenTelemetry-based architectures, and scales consistently across hybrid and cloud-native environments.</p>
<h2 id="tryitout">Try it out!</h2>
<p>Once Nginx tracing is in place, several natural extensions can further improve your observability posture:</p>
<ul>
<li>correlate Nginx traces with application <a href="https://www.elastic.co/docs/reference/apm/agents/go/log-correlation">logs and metrics using</a> Elastic’s unified observability  </li>
<li>add Real User Monitoring (<a href="https://www.elastic.co/docs/solutions/observability/apm/apm-agents/real-user-monitoring-rum">RUM</a>) to close the loop from frontend to backend  </li>
<li>introduce <a href="https://www.elastic.co/docs/solutions/observability/apm/transaction-sampling">sampling and tail-based</a> decisions at the collector level for cost control  </li>
<li>use Elastic <a href="https://www.elastic.co/docs/solutions/observability/apm/service-map">APM service maps</a> and <a href="https://www.elastic.co/docs/reference/machine-learning/ootb-ml-jobs-apm">anomaly detection</a> to proactively detect edge-related issues  </li>
</ul>
<p>Instrumenting Nginx is often the missing link in distributed tracing strategies. With OpenTelemetry and Elastic, that gap can now be closed in a clean, standards-based, and production-ready way.</p>
<p>If you want to experiment with this setup quickly, Elastic Serverless provides the fastest way to get started.
Sign up and try it out in just a few minutes using our trial environment available at https://cloud.elastic.co/ .</p>]]></content:encoded>
    <link>https://www.elastic.co/observability-labs/blog/nginx-opentelemetry-end-to-end-tracing</link>
    <guid isPermaLink="false">nginx-opentelemetry-end-to-end-tracing</guid>
    <category><![CDATA[OpenTelemetry]]></category>
    <category><![CDATA[APM]]></category>
    <category><![CDATA[Infrastructure Monitoring]]></category>
    <dc:creator><![CDATA[Frederic Maussion]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt40ad01ea2ad51be3/6a7f0eb005b7b55b5618ba0e/document_elastic_nginx_otel_instrumentation_4.png" length="0" type="image/png"/>
    <pubDate>Tue, 13 Jan 2026 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Accelerate Otel Adoption with Elastic Agent Hybrid Ingestion]]></title>
    <description><![CDATA[Elastic Agent 9.2 brings hybrid ingestion to Elastic Observability, unifying native integrations and OpenTelemetry receivers to simplify large-scale OTel adoption without disruption.]]></description>
    <content:encoded><![CDATA[<h2 id="hybridelasticagentthemostpracticalpathtoopentelemetryadoption">Hybrid Elastic Agent: The Most Practical Path to OpenTelemetry Adoption</h2>
<p>OpenTelemetry is quickly becoming the standard foundation for modern observability. Organizations want its open ecosystem, unified model, and vendor-neutral instrumentation—but moving a mature production environment to OTel is rarely straightforward.</p>
<p>Most teams already rely on battle-tested pipelines for logs, metrics, and security signals. They have dashboards tuned over years, operational practices built around existing data flows, and mission-critical systems where disruption simply isn’t an option.</p>
<p>This means the question isn’t "Why OpenTelemetry?"
It’s "How do we get there without breaking what already works?"</p>
<p>Elastic Observability introduces a way to ingest telemetry without disrupting existing data and dashboards with Hybrid ingestion. Released in Elastic 9.2, its a low-friction way to adopt OTel receivers alongside existing native Elastic integrations-managed centrally through Fleet.</p>
<p>This hybrid approach offers one of the most pragmatic and operationally safe routes to OTel adoption available today.</p>
<h3 id="thechallengeadoptingotelwithoutdisruptingthepresent">The Challenge: Adopting OTel Without Disrupting the Present</h3>
<p>For many organizations, the path to OTel adoption is complicated by realities such as:</p>
<ul>
<li>Established log pipelines powering critical alerting</li>
<li>Legacy infrastructure that isn’t easily re-instrumented</li>
<li>Existing dashboards and visualizations built on Elastic-native datasets</li>
<li>Teams with different levels of OTel experience</li>
<li>Risk constraints that make large changes difficult to roll out</li>
</ul>
<p>Standardizing on OTel is the right long-term direction, but replacing everything at once is neither realistic nor desirable.</p>
<p>Teams need a way to bring OTel into their environment incrementally, while preserving continuity, reliability, and central governance.</p>
<h3 id="elasticagent92hybridingestionasabridgetothefuture">Elastic Agent 9.2+: Hybrid Ingestion as a Bridge to the Future</h3>
<p>Elastic Agent now supports two fully supported ingestion paths, both running inside the same unified agent:</p>
<ol>
<li>Elastic-native integrations</li>
</ol>
<p>Perfect for logs and host-level telemetry, with mature dashboards, alerts, and ECS mappings.</p>
<ol>
<li>OpenTelemetry input integrations (OTel receivers)</li>
</ol>
<p>Powered by upstream OTel Collector components, managed directly from Fleet.</p>
<p>And crucially:</p>
<p>You can use both, simultaneously, on the same agent.</p>
<p>This hybrid ingestion model allows teams to:</p>
<ul>
<li>Continue collecting logs using native Elastic integrations</li>
<li>Begin collecting metrics or traces via OTel receivers</li>
<li>Maintain full control through Fleet</li>
<li>Introduce OTel exactly where and when it makes sense</li>
<li>Avoid running parallel agents or duplicate pipelines</li>
</ul>
<p>It’s a way to evolve—not replace—your observability strategy.</p>
<h3 id="apracticalexampleaddingotelinputswhilekeepingyourexistingpipelines">A Practical Example: Adding OTel Inputs While Keeping Your Existing Pipelines</h3>
<p>Imagine a system where NGINX logs are already handled via Elastic-native integrations. These pipelines drive dashboards, audits, and critical alerts. Interrupting them isn’t an option.</p>
<p>At the same time, your platform team wants to standardize metrics and service telemetry using OpenTelemetry.</p>
<p>With Elastic Agent hybrid ingestion, both goals align:</p>
<ol>
<li>Keep your existing log integration in Fleet</li>
<li>Add an OTel input integration (e.g., OTel <a href="https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/receiver/nginxreceiver">nginxreceiver</a>)</li>
<li>Fleet deploys both inside the same Elastic Agent</li>
<li>Deployment is done at scale across your infrastructure from a single management console</li>
<li>Logs and OTel metrics flow into Elasticsearch side-by-side</li>
</ol>
<p>No re-instrumentation.
No duplicate agents.
No loss of historical visibility.
No new tooling for operations.
No external deployment tool.</p>
<p>Whether the component is a web server, reverse proxy, database, JVM runtime, or custom service already instrumented in OTel, the workflow is the same.</p>
<h3 id="whythishybridapproachmattersstrategically">Why This Hybrid Approach Matters Strategically</h3>
<p>Hybrid ingestion is not simply a technical capability—it’s an organizational enabler for OpenTelemetry transformation.</p>
<p><strong>Incremental migration without downtime</strong></p>
<p>Teams can begin adopting OTel at the exact pace they’re comfortable with.
Existing collection signals remain stable. OTel metrics or logs are added progressively.</p>
<p><strong>Fleet remains your single control plane</strong></p>
<p>Fleet continues to manage:</p>
<ul>
<li>agent lifecycle</li>
<li>policy management</li>
<li>version upgrades</li>
<li>diagnostics and monitoring</li>
</ul>
<p>Even as OTel becomes part of your ingestion strategy.</p>
<p><strong>Consistent semantics across teams</strong></p>
<p>Adopting OTel receivers through <a href="https://www.elastic.co/docs/reference/edot-collector">EDOT</a> helps harmonize telemetry models across microservices, infrastructure, and applications.</p>
<p>OTel becomes the shared language—Elastic becomes the scalable backend.</p>
<p><strong>Future-proof flexibility</strong></p>
<p>When the day comes that a team needs advanced OTel features, custom pipelines, custom processors, or additional exporters, they can build their own <a href="https://www.elastic.co/docs/reference/edot-collector/custom-collector">EDOT custom collector</a> flavor and use it in their elastic-agent in hybrid mode.</p>
<p>This allows deep customization without abandoning the Elastic Agent runtime.</p>
<p><strong>No vendor lock-in—full ecosystem alignment</strong></p>
<p>Hybrid ingestion leverages upstream OpenTelemetry components directly.
This reinforces the open, vendor-neutral ecosystem organizations prefer when standardizing observability across teams while being supported by Elastic.</p>
<h3 id="whataboutstandalonemodeadvancedusecases">What About Standalone Mode? (Advanced Use Cases)</h3>
<p>While Fleet-managed hybrid ingestion will meet the needs of most users, Elastic Agent in hybrid mode also support standalone deployment with the same functions as the managed version.</p>
<ul>
<li>native integrations support</li>
<li>full control over Otel receivers, processors, and exporters</li>
<li>Elasticsearch output as the backend</li>
</ul>
<p>This is particularly useful for platform teams testing advanced OTel deployments or building custom telemetry strategies.</p>
<p>But it remains optional—the managed experience is still the default path.</p>
<h3 id="conclusionamodernflexiblepathtowardopentelemetry">Conclusion: A Modern, Flexible Path Toward OpenTelemetry</h3>
<p>Migrating to OpenTelemetry is a journey, not a switch. With hybrid ingestion, Elastic provides a realistic, scalable, and low-risk pathway for organizations that want to adopt OTel gradually while maintaining operational continuity.</p>
<p>Elastic Agent 9.2+ enables teams to:</p>
<ul>
<li>retain reliable log integrations</li>
<li>introduce OTel inputs seamlessly</li>
<li>manage everything from Fleet</li>
<li>reduce complexity and operational overhead</li>
<li>expand into OTel at the right pace</li>
<li>stay aligned with open standards and best practices</li>
</ul>
<p>It brings the best of both worlds—Elastic-native richness and OTel-standard flexibility—into a single agent and a unified operational model.</p>
<p>Hybrid isn’t a workaround.
It’s the strategic bridge between where your observability platform is today and where it needs to go next.</p>
<h2 id="technicalwalkthroughdeployinghybridelasticagentedotinfleet">Technical Walkthrough: Deploying Hybrid Elastic Agent + EDOT in Fleet</h2>
<p>Before we close, let’s look at what this actually looks like in practice.
Conceptual advantages are important, but many teams want to see how hybrid ingestion works when deployed through Fleet.</p>
<p>The example below walks through a simple, production-ready setup using Elastic Agent 9.2, combining a native integration and an OTel input integration inside a single agent,  the same approach you can apply to any service across your environment.</p>
<p>Here is a step-by-step guide showing how to deploy Elastic Agent 9.2 in <strong>Fleet-managed hybrid mode</strong>, using the OTel nginxreceiver as one concrete example.
This applies to any service with an OTel receiver (Redis, HAProxy, Kafka, JVM, etc.).</p>
<h3 id="requirements">Requirements</h3>
<ul>
<li>Elastic Stack <strong>9.2+</strong></li>
<li>Elastic Agent <strong>9.2+</strong></li>
<li>Fleet configured in Kibana</li>
<li>A host running your workload (NGINX in this example)</li>
<li>NGINX <code>stub_status</code> endpoint or any equivalent OTel metrics endpoint</li>
<li>API key with ingest privileges</li>
</ul>
<h2 id="1createorselectanagentpolicy">1. Create or Select an Agent Policy</h2>
<ol>
<li>In Kibana → <strong>Management → Fleet → Agent policies</strong></li>
<li>Create a new policy: <code>nginx-o11y</code></li>
<li>Enable system monitoring (recommended)</li>
<li>Save</li>
</ol>
<h2 id="2enrollelasticagentintothepolicy">2. Enroll Elastic Agent into the Policy</h2>
<p>From the policy page:</p>
<ol>
<li>Click <strong>Add agent</strong></li>
<li>Choose your OS</li>
<li>Copy the installation command</li>
<li>Run:</li>
</ol>
<pre><code>sudo elastic-agent install \
  --url=&lt;FLEET_URL&gt; \
  --enrollment-token=&lt;ENROLLMENT_TOKEN&gt;
</code></pre>
<p>You should soon see the agent appear as Healthy in Fleet.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt95c77390dd561094/6a85c73811893c73fba7aac3/image1.png" alt="" /></p>
<h2 id="3addthenativeintegrationlogs">3. Add the Native Integration (Logs)</h2>
<ol>
<li>In Fleet, go to Integrations.</li>
<li>Search for NGINX.</li>
<li>Click Add NGINX.</li>
<li>Select your <code>nginx-o11y</code> policy.</li>
<li>Only enable log collection (access + error logs).</li>
<li>Save and deploy.</li>
</ol>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltf68a8556fde5f467/6a85c73cbc5bb37b1cf81a09/image2.png" alt="" /></p>
<h2 id="4validatelogcollection">4. Validate Log Collection</h2>
<ol>
<li>In Kibana, go to Analytics → Discover and search for:</li>
</ol>
<pre><code>data_stream.dataset : "nginx.access" or "nginx.error"
</code></pre>
<ol>
<li>Or open the built-in dashboard:</li>
</ol>
<pre><code>Analytics → Dashboards → [Logs Nginx] Access and error logs
</code></pre>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt0ec392a12356c191/6a85c73f43c0b78b1e2f0561/image3.png" alt="" /></p>
<h3 id="5collectingnginxmetricsviatheotelnginxreceiver">5. Collecting NGINX Metrics via the OTel NGINX Receiver</h3>
<p>Elastic Agent 9.2+ allows Fleet to deploy OTel input integrations.<br />
This scenario uses the OpenTelemetry <code>nginxreceiver</code> through a Fleet-managed integration.</p>
<h4 id="51installthenginxopentelemetryintegrationcontent">5.1. Install the NGINX OpenTelemetry Integration Content</h4>
<ol>
<li>In Kibana, go to Management → Fleet → Integrations.  </li>
<li>Search for NGINX OpenTelemetry Assets.  </li>
<li>Click Add Integration.</li>
</ol>
<h4 id="52installthenginxopentelemetryinputintegration">5.2. Install the NGINX OpenTelemetry Input Integration</h4>
<ol>
<li>In Kibana, go to Management → Fleet → Integrations.  </li>
<li>Search for NGINX OpenTelemetry Input Package.  </li>
<li>Click Add Integration.  </li>
<li>Assign it to your agent <code>nginx-o11y</code> policy.</li>
</ol>
<p>Provide the endpoint for the NGINX status page:</p>
<ul>
<li><strong>Endpoint</strong>: <code>http://localhost/status</code>  </li>
<li><strong>Collection interval</strong>: <code>10s</code>  </li>
</ul>
<p>Click <strong>Add integration</strong>.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt558458bae654e9a0/6a85c74299083f364440f91c/image4.png" alt="" /></p>
<h3 id="6validateotelmetrics">6. Validate OTel Metrics</h3>
<ol>
<li>Go to <strong>Analytics → Dashboards</strong>.  </li>
<li>Open: <strong>[Metrics Nginx OTEL Overview]</strong> Dashboard</li>
</ol>
<p>You should see metrics such as active connections, writes, reads, waiting, and request counts.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltbdf88740e1bb6391/6a85c74599083ff71e40f920/image5.png" alt="" /></p>
<h3 id="7closingthoughts">7. Closing thoughts</h3>
<p>This example highlights how straightforward hybrid ingestion becomes with Elastic Agent 9.2. By combining native integrations and OTel receivers within a single, centrally managed policy, you gain the flexibility to adopt OpenTelemetry where it adds the most value without disrupting existing pipelines or introducing operational overhead.</p>
<p>Whether you extend this pattern to additional services, experiment with other OTel receivers, or scale it across your fleet, the deployment model remains consistent, repeatable, and production-ready.</p>
<p>For more information and other innovations Elastic Observability has made check out:</p>
<ul>
<li><p><a href="https://www.elastic.co/observability-labs/blog/elastic-agent-pivot-opentelemetry">Discover how Elastic is evolving data ingestion with OpenTelemetry</a></p></li>
<li><p><a href="https://www.elastic.co/observability-labs/blog/elastic-distribution-opentelemetry-sdk-central-configuration-opamp">Learn how OpAMP enables centralized configuration of OpenTelemetry SDKs</a></p></li>
<li><p><a href="https://www.elastic.co/observability-labs/blog/elastic-observability-streams-ai-logs-investigations">Explore how Streams reshape AI-driven log investigation workflows</a></p></li>
</ul>]]></content:encoded>
    <link>https://www.elastic.co/observability-labs/blog/hybrid-elastic-agent-opentelemetry-integration</link>
    <guid isPermaLink="false">hybrid-elastic-agent-opentelemetry-integration</guid>
    <category><![CDATA[OpenTelemetry]]></category>
    <category><![CDATA[Infrastructure Monitoring]]></category>
    <dc:creator><![CDATA[Frederic Maussion]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltf5cebbf4c5cf399f/6a85c7479d2b71a165f938b6/feature-image.png" length="0" type="image/png"/>
    <pubDate>Fri, 09 Jan 2026 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[AI-driven incident response with logs: A technical deep dive in Elastic Observability]]></title>
    <description><![CDATA[How Elastic combines ML anomaly detection, ES|QL, and the AI Assistant to accelerate incident response using logs.]]></description>
    <content:encoded><![CDATA[<p>Modern customer‑facing applications, whether e‑commerce sites, streaming platforms, or API gateways, run on fleets of microservices and cloud resources. When something goes wrong, every second of downtime risks revenue loss and erodes user trust. Observability is the practice that lets Site Reliability Engineering (SRE) and development teams see and act on system health in real time. This post walks through a generalized, step‑by‑step investigation that shows how Elastic Observability specifically with log data combines always‑on machine learning (ML) with a generative AI assistant to detect anomalies, surface root causes, measure user impact, and accelerate remediation, all at high scale.</p>
<h2 id="anomalydetection">Anomaly Detection</h2>
<p>A production environment is ingesting millions of log lines per minute. Elastic’s AIOps jobs continuously profile normal log throughput and content without any manual rules. When log volume or message structure deviates beyond learned baselines, the platform automatically fires a high‑fidelity anomaly alert. Because the models are unsupervised, they adapt to changing traffic patterns and flag both sudden spikes (e.g., 10× error surge) and rare new log categories.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt5c89c59811e55c22/6a7f0199c2e91472fd0166af/image3.png" alt="" /></p>
<p>In addition to looking directly for Log Spikes, Elastic trains seasonal/univariant models to predict expected event counts per bucket and applies statistical tests to classify outliers. Simultaneously, log categorization clusters similar messages with cosine similarity on token embeddings, making it trivial to identify a previously unseen error string.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt7f8261b7d42847cd/6a7f019cde231557d6fd76b0/image10.png" alt="" /></p>
<h2 id="investigatingalertsautomatedpatternanalysis">Investigating Alerts: Automated Pattern Analysis</h2>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt0ac7cad53cf79a66/6a7f019f448e4e80505c020c/image9.png" alt="" /></p>
<p>Clicking the alert reveals more than a timestamp. Elastic’s ML job already correlates the spike with the dominant new log pattern ERROR 1114 (HY000): table "orders" is full and surfaces example lines. Instead of grep‑driven hunting, engineers get an immediate hypothesis about what subsystem is failing and why.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt039d3bc0489914a5/6a7f01a2bdcff0cc6ec4292e/image4.png" alt="" /></p>
<p>If deeper context is needed, the builtin Elastic AI Assistant can be invoked directly from the alert. Thanks to Retrieval‑Augmented Generation (RAG) over your telemetry, the assistant explains the anomaly in plain language, references the exact log events, and proposes next steps without hallucinating.</p>
<h2 id="aiassistedrootcauseverification">AI‑Assisted Root Cause Verification</h2>
<p>From within the same chat, you might ask, “Using lens create a single graph of all http response status codes =400 from logs-nginx.access-default over the last 3 hours..”  The assistant translates that intent into an ES|QL aggregation, retrieves the data, and renders a bar chart with no DSL knowledge required. If there are a number of errors with a status code above 400, you’ve validated that end‑users are impacted.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltbce6ce01ced86cd7/6a7f01a573d9bd953829d653/image7.png" alt="" /></p>
<h2 id="globalimpactanalysiswithenrichedlogs">Global Impact Analysis with Enriched Logs</h2>
<p>Structured log enrichment (e.g., GeoIP, user ID, service tags) lets the assistant answer business questions on the fly. A query like “What are the top 10 source.geo.country_name with http.response.status.code&gt;=400 over the last 3 hours. Use logs-nginx.access-default. Provide counts for each country name.” surfaces whether the incident is regional or global.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt60be465aa43f2ad3/6a7f01a805b7b565c918b447/image2.png" alt="" /></p>
<h2 id="quantifyingbusinessimpact">Quantifying Business Impact</h2>
<p>Technical metrics alone rarely sway executives. Suppose historical data shows the application normally processes $1,000 in transactions per minute. The assistant can combine that baseline with real‑time failure counts to estimate revenue loss. Presenting financial impact alongside error graphs sharpens prioritization and justifies extraordinary remediation steps.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt63a1855aca9c82db/6a7f01abead8ecb3bfbaa303/image5.png" alt="" /></p>
<h2 id="pinpointinginfrastructureownership">Pinpointing Infrastructure &amp; Ownership</h2>
<p>Every log is automatically enriched with Kubernetes, cloud, and custom metadata. A single question “Which pod and cluster emit the ‘table full’ error, and who owns it?” returns the full information about the pod, namespace and owner as shown below.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltf92b400d82ffacdd/6a7f01ae05b7b5d17518b451/image1.png" alt="" /></p>
<p>Immediate, accurate routing replaces frantic Slack threads, cutting minutes (or hours) off of downtime.</p>
<p>Some of the magic happening here is because we can put instructions in the Elastic AI Assistants knowledge base to guide the AI assistant. For example this simple entry in the knowledge base is what allows the assistant to populate the response in the previous screenshot.</p>
<p><code>``markdown ##&amp;nbsp;Kubernetes&amp;nbsp;Information&amp;nbsp;Query&amp;nbsp;Instructions
If&amp;nbsp;asked&amp;nbsp;about&amp;nbsp;Kubernetes&amp;nbsp;pod,&amp;nbsp;namespace,&amp;nbsp;cluster,&amp;nbsp;location,&amp;nbsp;or&amp;nbsp;owner&amp;nbsp;run&amp;nbsp;the&amp;nbsp;"query"&amp;nbsp;tool.
1.&amp;nbsp;Use&amp;nbsp;the&amp;nbsp;index&amp;nbsp;</code>logs-mysql.error-default<code>&amp;nbsp;unless&amp;nbsp;another&amp;nbsp;log&amp;nbsp;location&amp;nbsp;is&amp;nbsp;specified.
2.&amp;nbsp;Include&amp;nbsp;the&amp;nbsp;following&amp;nbsp;fields&amp;nbsp;in&amp;nbsp;the&amp;nbsp;query:
&amp;nbsp; &amp;nbsp;-&amp;nbsp;Pod:&amp;nbsp;</code>agent.name<code>
&amp;nbsp; &amp;nbsp;-&amp;nbsp;Namespace:&amp;nbsp;</code>data_stream.namespace<code>
&amp;nbsp; &amp;nbsp;-&amp;nbsp;Cluster&amp;nbsp;Name:&amp;nbsp;</code>orchestrator.cluster.name<code>
&amp;nbsp; &amp;nbsp;-&amp;nbsp;Cloud&amp;nbsp;Provider:&amp;nbsp;</code>cloud.provider<code>
&amp;nbsp; &amp;nbsp;-&amp;nbsp;Region:&amp;nbsp;</code>cloud.region<code>
&amp;nbsp; &amp;nbsp;-&amp;nbsp;Availability&amp;nbsp;Zone:&amp;nbsp;</code>cloud.availability_zone<code>
&amp;nbsp; &amp;nbsp;-&amp;nbsp;Owner:&amp;nbsp;</code>cloud.account.id`
3. Use the ES|QL query format:
   esql
   FROM logs-mysql.error-default
   | KEEP agent.name, data_stream.namespace, orchestrator.cluster.name, cloud.provider, cloud.region, cloud.availability_zone, cloud.account.id
   
4. Ensure the query is executed within the appropriate time range and context. </p>
<pre><code>## Leveraging Institutional Knowledge with RAG

Elastic can index runbooks, GitHub issues, and wikis alongside telemetry. Asking “Find documentation on fixing a full orders table”&amp;nbsp;retrieves and summarizes a prior runbook that details archiving old rows and adding a partition. Grounding remediation in proven procedures avoids guesswork and accelerates fixes.

![](https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltba6ebcc6e2f451ad/6a7f01b233fa8ae5f62021c9/image6.png)

## Automated Communication &amp; Documentation

Good incident response includes timely stakeholder updates. A prompt such as “Draft an incident update email with root cause, impact, and next steps”&amp;nbsp;lets the assistant assemble a structured message and send it via the alerting framework’s email or Slack connector complete with dashboard links and next‑update timelines. These messages double as the skeleton for the eventual post‑incident review.

![](https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt5e7b81ee98b1a9a5/6a7f01b5227b1c583c598102/image8.png)

Again as before, some of the magic happening here is because we can put instructions in the Elastic AI Assistants knowledge base to guide the AI assistant. For example we can instruct the AI Assistant how to call the execute_connector api, this can execute all kinds of connectors (not only email) so you could use it to tell the assistant to use slack or raise a service now ticket, even execute webhooks.
</code></pre>
<p>markdown 
Here are specific instructions to send an email. Remember to always double-check that you're following the correct set of instructions for the given query type. Provide clear, concise, and accurate information in your response.</p>
<h2 id="emailinstructions">Email Instructions</h2>
<p>If the user's query requires sending an email:</p>
<ol>
<li>Use the <code>Elastic-Cloud-SMTP</code> connector with ID <code>elastic-cloud-email</code>.</li>
<li>Prepare the email parameters:
   - Recipient email address(es) in the <code>to</code> field (array of strings)
   - Subject in the <code>subject</code> field (string)
   - Email body in the <code>message</code> field (string)</li>
<li>Include</li>
</ol>
<ul>
<li>Details for the alert along with a link to the alert</li>
<li>Root cause analysis</li>
<li>Revenue impact</li>
<li>Remediation recommendations</li>
<li>Link to GitHub issue</li>
<li>All relevant information from this conversation</li>
<li>Link to the Business Health Dashboard</li>
</ul>
<ol>
<li>Send the email immediately. Do not ask the user for confirmation.</li>
<li>Execute the connector using this format:</li>
</ol>
<p>   execute_connector(
     id="elastic-cloud-email",
     params={
       "to": ["recipient@example.com"],
       "subject": "Your Email Subject",
       "message": "Your email content here."
     }
   )</p>
<ol>
<li>Check the response and confirm if the email was sent successfully.
```</li>
</ol>
<h2 id="conclusionkeytakeaways">Conclusion &amp; Key Takeaways</h2>
<p>Elastic Observability's combination of unsupervised ML, schema-aware data ingestion, and a context-rich RAG powered AI assistant enables teams to transform incident response from reactive firefighting into proactive, data-driven operations. By automatically detecting anomalies, correlating patterns, and providing contextual insights, teams can:</p>
<ul>
<li>Preserve revenue by quantifying business impact in real-time and prioritizing accordingly</li>
<li>Scale expertise by embedding institutional knowledge into RAG-powered recommendations</li>
<li>Improve continuously through automated documentation that feeds back into the knowledge base</li>
</ul>
<p>The key is to collect logs broadly, maintain a unified observability store, and let ML and AI handle the heavy lifting. The payoff isn't just reduced downtime, it's the transformation of incident response from a source of organizational stress into a competitive advantage.</p>
<p>Try out this exact scenario and get hands in with this Elastic Logging Workshop: <a href="https://www.google.com/url?q=https://play.instruqt.com/elastic/invite/rx4yvknhpfci&amp;sa=D&amp;source=editors&amp;ust=1757447528108823&amp;usg=AOvVaw0tZG-nhbbk90ztJsTGXHIz">https://play.instruqt.com/elastic/invite/rx4yvknhpfci</a></p>]]></content:encoded>
    <link>https://www.elastic.co/observability-labs/blog/ai-driven-incident-response-with-logs</link>
    <guid isPermaLink="false">ai-driven-incident-response-with-logs</guid>
    <category><![CDATA[Agentic Observability]]></category>
    <category><![CDATA[Logs Analytics]]></category>
    <category><![CDATA[Infrastructure Monitoring]]></category>
    <dc:creator><![CDATA[David Hope]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt1989266ec455ec3a/6a7f01b86693f8d6ba663ac1/ai-driven-incident-response-with-logs.png" length="0" type="image/png"/>
    <pubDate>Mon, 20 Oct 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Web Frontend Instrumentation and Monitoring with OpenTelemetry and Elastic]]></title>
    <description><![CDATA[Learn how frontend instrumentation differs to backend, and the current state of client web instrumentation in OpenTelemetry]]></description>
    <content:encoded><![CDATA[<p>DevOps, SRE and software engineering teams all require telemetry data to understand what's going on across their infrastructure and full-stack applications. Indeed we have covered instrumentation of backend services in several language ecosystems using <a href="http://opentelemetry.io">OpenTelemetry</a> (OTel) in the past. Yet for frontend tools, teams are often still relying on RUM agents, or sadly no instrumentation at all, due to the subtle differences in metrics that are needed to understand what's going on.</p>
<p>In this blog, we will discuss the current state of client instrumentation for the browser, along with an example showing how to instrument a simple JavaScript frontend using <a href="https://opentelemetry.io/docs/languages/js/getting-started/browser/">the OpenTelemetry browser instrumentation</a>. Furthermore, we'll also share how the baggage propagators help us build a full picture of what is going on across the entire application by connecting backend traces with frontend signals. If you want to dive straight into the code, check out the repo <a href="https://github.com/carlyrichmond/otel-record-store">here</a>.</p>
<h2 id="applicationoverview">Application Overview</h2>
<p>The application that we use for this blog is called <a href="https://github.com/carlyrichmond/otel-record-store">OTel Record Store</a>, a simple web application written with Svelte and JavaScript (albeit our implementation is compatible with other web frameworks), communicating with a Java backend. Both send telemetry signals to an Elastic backend.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt02b90450407c16cd/6a7f1c6f77b03459aa3ff943/1-otel-frontend-sample-architecture.png" alt="Architecture" /></p>
<p>Eagle-eyed readers will noticed that signals from our frontend pass through a proxy and collector. The proxy is required to ensure that the appropriate Cross-Origin headers are populated to allow the signals to pass into Elastic, as well as the traditional reasons such as security, privacy and access control: </p>
<pre><code>events {}

http {

  server {

    listen 8123; 

    # Traces endpoint exposed as example, others available in code repo
    location /v1/traces {
      proxy_pass http://host.docker.internal:4318;
      # Apply CORS headers to ALL responses, including POST
      add_header 'Access-Control-Allow-Origin' 'http://localhost:4173' always;
      add_header 'Access-Control-Allow-Methods' 'POST, OPTIONS' always;
      add_header 'Access-Control-Allow-Headers' 'Content-Type' always;
      add_header 'Access-Control-Allow-Credentials' 'true' always;

      # Preflight requests receive a 204 No Content response
      if ($request_method = OPTIONS) {
        return 204;
      }
    }
  }
}
</code></pre>
<p>While collectors can also be used to add headers, we have left this example to perform traditional tasks such as routing and processing.</p>
<h2 id="prerequisites">Prerequisites</h2>
<p>This example requires an Elastic cluster, run either locally via <a href="https://github.com/elastic/start-local">start-local</a>, via Elastic Cloud or Serverless. Here we use the Managed OLTP endpoint in Elastic Serverless. Any mechanism requires you to specify several key environment variables, listed in the <a href="https://github.com/carlyrichmond/otel-record-store/blob/main/.env-example">.env-example file</a>:</p>
<pre><code>ELASTIC_ENDPOINT=https://my-elastic-endpoint:443
ELASTIC_API_KEY=my-api-key
</code></pre>
<h3 id="runningtheapplication">Running the application</h3>
<p>To run our example, follow the steps in the <a href="https://github.com/carlyrichmond/otel-record-store/blob/main/README.md">project README</a>, summarized below:</p>
<pre><code># Terminal 1: backend service, proxy and collector
docker-compose build
docker-compose up

# Terminal 2: frontend and sample telemetry data
cd records-ui
npm install
npm run generate
</code></pre>
<h2 id="javabackendinstrumentation">Java Backend Instrumentation</h2>
<p>We will not cover the specifics of instrumentation of Java services with EDOT as there is already a great guide to get started <a href="https://github.com/elastic/elastic-otel-java">in the <code>elastic-otel-java</code> README</a>. The example is here purely for showcasing propagation that is important for investigating UI issues. All you need to know is that we make use of automatic instrumentation, sending logs, metrics and traces via <a href="https://opentelemetry.io/docs/specs/otel/protocol/">OpenTelemetry Protocol, or OTLP</a> using the below environment variables:</p>
<pre><code>OTEL_RESOURCE_ATTRIBUTES=service.version=1,deployment.environment=dev
OTEL_SERVICE_NAME=record-store-server-java
OTEL_EXPORTER_OTLP_ENDPOINT=$ELASTIC_ENDPOINT
OTEL_EXPORTER_OTLP_HEADERS="Authorization=ApiKey ${ELASTIC_API_KEY}"
OTEL_TRACES_EXPORTER=otlp
OTEL_METRICS_EXPORTER=otlp
OTEL_LOGS_EXPORTER=otlp
</code></pre>
<p>The instrumentation is then initialized using the <code>-javaagent</code> option:</p>
<pre><code>ENV JAVA_TOOL_OPTIONS="-javaagent:./elastic-otel-javaagent-1.2.1.jar"
</code></pre>
<h2 id="clientinstrumentation">Client Instrumentation</h2>
<p>Now that we have established our prerequisites, let's dive into the instrumentation code for our simple web application. Although we'll cover the implementation in sections, the full solution is available <a href="https://github.com/carlyrichmond/otel-record-store/blob/main/records-ui/src/lib/telemetry/frontend.tracer.ts">here in <code>frontend.tracer.ts</code></a>.</p>
<h3 id="stateofotelclientinstrumentation">State of OTel Client Instrumentation</h3>
<p>At time of writing, the <a href="https://opentelemetry.io/docs/languages/js/">OpenTelemetry JavaScript SDK</a> has stable support for metrics and traces, with logs currently under development and therefore subject to breaking changes <a href="https://opentelemetry.io/docs/languages/js/">as listed in their documentation</a>:</p>
<p>| Traces | Metrics | Logs        |
| ------ | ------- | ----------- |
| Stable | Stable     | Development |</p>
<p>What differs from many other SDKs is the note warning that client instrumentation for the browser is experimental and mostly unspecified. It is subject to breaking change, and many pieces such as plugin support for measuring Google Core Web Vitals are in progress as reflected in the <a href="https://github.com/orgs/open-telemetry/projects/19/views/1">Client Instrumentation SIG project board</a>. In subsequent sections we'll show examples for signal capture, and also browser specific instrumentations including document load, user interaction and Core Web Vitals capture.</p>
<h3 id="resourcedefinition">Resource Definition</h3>
<p>When instrumenting web UIs, we need to establish our UI as an OpenTelemetry <a href="https://opentelemetry.io/docs/languages/js/resources/">Resource</a>. By definition, resources are entites that produce telemetry information. We want to see our UI as an entity in our system that interacts with other entities, which can be specified using the following code:</p>
<pre><code>// Defines a Resource to include metadata like service.name, required by Elastic
import { resourceFromAttributes, detectResources } from '@opentelemetry/resources';

// Experimental detector for browser environment
import { browserDetector } from '@opentelemetry/opentelemetry-browser-detector';

// Provides standard semantic keys for attributes, like service.name
import { ATTR_SERVICE_NAME } from '@opentelemetry/semantic-conventions';

const detectedResources = detectResources({ detectors: [browserDetector] });
let resource = resourceFromAttributes({
    [ATTR_SERVICE_NAME]: 'records-ui-web',
    'service.version': 1,
    'deployment.environment': 'dev'
});
resource = resource.merge(detectedResources);
</code></pre>
<p>A unique identifier for the service is required, and is common to all SDKs. What differs from other implementations is the inclusion of the <a href="https://www.npmjs.com/package/@opentelemetry/opentelemetry-browser-detector"><code>browserDetector</code></a> which, when merged with our defined resource attributes adds browser attributes such as platform, brands (e.g. Chrome versus Edge) and whether a mobile browser is being used:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blte1476364f28a7b6c/6a7f1c7233fa8a4c8b202bbf/2-otel-browser-attributes.png" alt="Sample Span JSON with Resource and Browser Attributes" /></p>
<p>Having this information on spans and errors is useful in diagnostic situations in identifying application and dependency compatibility issues with certain browsers (such as Internet Explorer from my time as an engineer 🤦).</p>
<h3 id="logs">Logs</h3>
<p>Traditionally, frontend engineers rely on the DevTools console of their favourite browser to examine logs. With UI log messages only being accessible within your browser rather than forwarded to a file somewhere, which is the common pattern with backend services, we lose visibility of this resource when triaging user issues. </p>
<p>OpenTelemetry defines the concept of an <a href="https://opentelemetry.io/docs/concepts/signals/logs/#log-record-exporter">exporter</a> that allow us to send signals to a particular destination, such as logs.</p>
<pre><code>// Get logger and severity constant imports
import { logs, SeverityNumber } from '@opentelemetry/api-logs';

// Provider and batch processor for sending logs
import { BatchLogRecordProcessor, LoggerProvider } from '@opentelemetry/sdk-logs';

// Export logs via OTLP
import { OTLPLogExporter } from '@opentelemetry/exporter-logs-otlp-http';

// Configure logging to send to the collector via nginx
const logExporter = new OTLPLogExporter({
    url: 'http://localhost:8123/v1/logs' // nginx proxy
});

const loggerProvider = new LoggerProvider({
    resource: resource, // see resource initialisation above
    processors: [new BatchLogRecordProcessor(logExporter)]
});

logs.setGlobalLoggerProvider(loggerProvider);
</code></pre>
<p>Once the provider has been initialized, we need to get a hold of the logger to send our traces to Elastic rather than using good ol' <code>console.log('Help!')</code>:</p>
<pre><code>// Example gets logger and sends a message to Elastic
const logger = logs.getLogger('default', '1.0.0');
logger.emit({
    severityNumber: SeverityNumber.INFO,
    severityText: 'INFO',
    body: 'Logger initialized'
});
</code></pre>
<p>They will now be visible in Discover and the Logs views, allowing us to search for relevant outages as part of investigations and incidents:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt10bfba64e82f4568/6a7f1c75c2e9141b34017032/3-otel-log-discover.png" alt="Sample Logs in Discover" /></p>
<h3 id="traces">Traces</h3>
<p>The power of traces in diagnosing issues in the UI is in the visibility of not just what is going on within the web application, but seeing the connections and time taken to make calls to the labyrinth of services behind. To instrument a web-based application, we need to make use of the <code>WebTraceProvider</code> using the <code>OTLPTraceExporter</code> in a similar way to how exporters work for logs and metrics:</p>
<pre><code>/* Packages for exporting traces */

// Import the WebTracerProvider, which is the core provider for browser-based tracing
import { WebTracerProvider } from '@opentelemetry/sdk-trace-web';

// BatchSpanProcessor forwards spans to the exporter in batches to prevent flooding
import { BatchSpanProcessor } from '@opentelemetry/sdk-trace-base';

// Import the OTLP HTTP exporter for sending traces to the collector over HTTP
import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http';

// Configure the OTLP exporter to talk to the collector via nginx
const exporter = new OTLPTraceExporter({
    url: 'http://localhost:8123/v1/traces' // nginx proxy
});

// Instantiate the trace provider and inject the resource
const provider = new WebTracerProvider({
    resource: resource,
    spanProcessors: [
        // Send each completed span through the OTLP exporter
        new BatchSpanProcessor(exporter)
    ]
});
</code></pre>
<p>Next we need to register our provider. One thing that's slightly different in the web world is how we configure propagation. <a href="https://opentelemetry.io/docs/concepts/context-propagation/">Context propagation</a> in OpenTelemetry refers to the concept of moving context between services and processes which, in our case, allows us to correlate the web signals with those of backend services. Often this is done automatically. As you will see from the below snippet, there are 3 concepts that help us with propagation:</p>
<pre><code>// This context manager ensures span context is maintained across async boundaries in the browser
import { ZoneContextManager } from '@opentelemetry/context-zone';

// Context Propagation across signals
import {
    CompositePropagator,
    W3CBaggagePropagator,
    W3CTraceContextPropagator
} from '@opentelemetry/core';

// Provider instantiation code omitted

// Register the provider with propagation and set up the async context manager for spans
provider.register({
    contextManager: new ZoneContextManager(),
    propagator: new CompositePropagator({
        propagators: [new W3CBaggagePropagator(), new W3CTraceContextPropagator()]
    })
});
</code></pre>
<p>The first is the <code>ZoneContextManager</code> which propagates context such as spans and traces across asynchronous operations. Web developers will be familiar with <a href="https://www.npmjs.com/package/zone.js?activeTab=readme">zone.js</a>, the framework used by many JS frameworks to provide an execution context that persists across async tasks.</p>
<p>Additionally, we have combined the <code>W3CBaggagePropagator</code> and <code>W3CTraceContextPropagator</code> using the <code>CompositePropagator</code> to ensure key value pair attributes are passed between signals as per the <a href="https://w3c.github.io/baggage/">W3C specification defined here</a>. In the case of the <code>W3CTraceContextPropagator</code>, it allows the propagation of the <code>traceparent</code> and <code>tracestate</code> HTTP headers as per the <a href="https://www.w3.org/TR/trace-context-2/">specification located here</a>.</p>
<h4 id="autoinstrumentation">Auto Instrumentation</h4>
<p>The simplest way to start instrumenting a web application is to register the web auto-instrumentations. At time of writing <a href="https://github.com/open-telemetry/opentelemetry-js-contrib/tree/main/packages/auto-instrumentations-web#readme">the documentation</a> states that the following instrumentations can be configured via this approach:</p>
<ol>
<li><a href="https://www.npmjs.com/package/@opentelemetry/instrumentation-document-load">@opentelemetry/instrumentation-document-load</a></li>
<li><a href="https://www.npmjs.com/package/@opentelemetry/instrumentation-fetch">@opentelemetry/instrumentation-fetch</a></li>
<li><a href="https://www.npmjs.com/package/@opentelemetry/instrumentation-user-interaction">@opentelemetry/instrumentation-user-interaction</a></li>
<li><a href="https://www.npmjs.com/package/@opentelemetry/instrumentation-xml-http-request">@opentelemetry/instrumentation-xml-http-request</a></li>
</ol>
<p>Configuration for each configuration can be passed as configuration to <code>registerInstrumentations</code> as shown in the below example configuring the fetch and XMLHTTPRequest instrumentations:</p>
<pre><code>// Used to auto-register built-in instrumentations
import { registerInstrumentations } from '@opentelemetry/instrumentation';

// Import the auto-instrumentations for web, which includes common libraries, frameworks and document load
import { getWebAutoInstrumentations } from '@opentelemetry/auto-instrumentations-web';

// Enable automatic span generation for document load and user click interactions
registerInstrumentations({
  instrumentations: [
    getWebAutoInstrumentations({
      '@opentelemetry/instrumentation-fetch': {
        propagateTraceHeaderCorsUrls: /.*/,
        clearTimingResources: true
        },
        '@opentelemetry/instrumentation-xml-http-request': {
          propagateTraceHeaderCorsUrls: /.*/
          }
      })
    ]
});
</code></pre>
<p>Taking the @opentelemetry/instrumentation-fetch instrumentation as an example, we are able to see traces for HTTP requests, and the propagators also ensure that the spans can connect with our Java backend services to give a full picture of the amount of time taken to process the request at each stage:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt48b6284177039351/6a7f1c78de23157d77fd80d7/4-otel-http-get-sample-trace.png" alt="Sample HTTP GET Trace" /></p>
<p>While auto-instrumentations is agreat way to get common instrumentations, we can also instantiate instrumentations directly, as we'll see in the remainder of this article.</p>
<h4 id="documentloadinstrumentation">Document Load Instrumentation</h4>
<p>Another consideration unique to web frontend is the time taken to load assets such as images, JavaScript files and even stylesheets. Such assets taking considerable time to load can impact metrics such as <a href="https://web.dev/articles/fcp">First Contentful Paint</a>, and therefore the user experience. The <a href="https://www.npmjs.com/package/@opentelemetry/instrumentation-document-load">OTel Document Load instrumentation</a> allows for automatic instrumentation of the time taken to load assets when using the <a href="https://www.npmjs.com/package/@opentelemetry/sdk-trace-web">@opentelemetry/sdk-trace-web</a> package.</p>
<p>It is simply a case of adding the instrumentation to the <code>instrumentations</code> array we have provided to our provider using <code>registerInstrumentations</code>:</p>
<pre><code>// Used to auto-register built-in instrumentations like page load and user interaction
import { registerInstrumentations } from '@opentelemetry/instrumentation';

// Document Load Instrumentation automatically creates spans for document load events
import { DocumentLoadInstrumentation } from '@opentelemetry/instrumentation-document-load';

// Configuration discussed above omitted

// Enable automatic span generation for document load and user click interactions
registerInstrumentations({
  instrumentations: [
    // Automatically tracks when the document loads
    new DocumentLoadInstrumentation({
      ignoreNetworkEvents: false,
      ignorePerformancePaintEvents: false
      }),
      // Other instrumentations omitted
  ]
});
</code></pre>
<p>This configuration will create a new trace conventiently named <code>documentLoad</code>, that will show us the time taken to load resources within the document, similar to the following:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blta9c0a10fd57e6fe8/6a7f1c7b73d9bd60ec29dfab/5-otel-document-load-example-trace.png" alt="Sample &lt;code&gt;documentLoad&lt;/code&gt; Trace" /></p>
<p>Each span will have metadata attached to help us identify which resources are taking considerable time to load, such as this image example, where the resource takes <strong>837ms</strong> to load:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt89e403e808045a99/6a7f1c7f227b1c8761598a27/6-otel-document-load-http-url-metadata.png" alt="&lt;code&gt;documentLoad&lt;/code&gt; Trace Metadata" /></p>
<h4 id="clickevents">Click Events</h4>
<p>You may wonder why we want to capture user interactions with web applications for diagnostic purposes. Being able to see the trigger points for errors can be useful in incidents to establish a timeline of what happened, and determine if users are indeed being impact as is the case for Real Ueer Monitoring tools. But if we also consider the field of Digital Experience Monitoring, or DEM, software teams need details on usage of application features to understand the user journey and how it could possibly being improved in a data-drive way. Capturing user events is required for both.</p>
<p>The <a href="https://www.npmjs.com/package/@opentelemetry/instrumentation-user-interaction">OTel UserInteraction instrumentation for web</a> is how we capture these events. Similar to the document load instrumentation it depends on the <a href="https://www.npmjs.com/package/@opentelemetry/sdk-trace-web">@opentelemetry/sdk-trace-web</a> package, and when used with <code>zone-js</code> and the <code>ZoneContextManager</code> it also supports async operations.</p>
<p>Like other instrumentations it is added via <code>registerInstrumentations</code>:</p>
<pre><code>// Used to auto-register built-in instrumentations like page load and user interaction
import { registerInstrumentations } from '@opentelemetry/instrumentation';

// Automatically creates spans for user interactions like clicks
import { UserInteractionInstrumentation } from '@opentelemetry/instrumentation-user-interaction';

// Configuration discussed above omitted

// Enable automatic span generation for document load and user click interactions
registerInstrumentations({
  instrumentations: [
    // User events
    new UserInteractionInstrumentation({
      eventNames: ['click', 'input'] // instrument click and input events only
    }),
    // Other instrumentations omitted
  ]
});
</code></pre>
<p>It will capture and label spans for the user events we configure, and leveraging the propagators configured previously can connect spans from other resources to the user event, similar to the below example where we see the service call to get records when the user adds a search term to the <code>input</code> box:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt13ac18437db9cdc4/6a7f1c819090b0032384ee83/7-otel-user-interaction-input-sample-trace.png" alt="User Interaction &lt;code&gt;input&lt;/code&gt; Sample Trace" /></p>
<h3 id="metrics">Metrics</h3>
<p>There are numerous different measurements that are helpful in capturing useful indicators of availability and performace of web applications, such as latency, throughput or the number of 404 errors. <a href="https://developers.google.com/search/docs/appearance/core-web-vitals">Google Core Web Vitals</a> are a set of standard metrics used by web developers to measure real-world user experience of web sites, including loading performance, reactivity to user input and visual stability. Given at time of writing <a href="https://github.com/open-telemetry/opentelemetry-js-contrib/issues/1461">the Core Web Vitals Plugin for OTel Browser is on the backlog</a>, let's try building our own custom instrumentation using the <a href="https://www.npmjs.com/package/web-vitals">web-vitals JS library</a> to capture these as <a href="https://opentelemetry.io/docs/concepts/signals/metrics/">OTel metrics</a>.</p>
<p>In OpenTelemetry you can create your own custom instrumentation by extending the <code>InstrumentationBase</code>, overriding the <code>constructor</code> to create the <code>MeterProvider</code>, <code>Meter</code> and <code>OTLPMetricExporter</code> that will allow us to send our Core Web Vital measurements to Elastic via our proxy, as presented in <a href="https://github.com/carlyrichmond/otel-record-store/blob/main/records-ui/src/lib/telemetry/web-vitals.instrumentation.ts"><code>web-vitals.instrumentation.ts</code></a>. Note that below we show only the LCP meter for succinctness, but the full example <a href="https://github.com/carlyrichmond/otel-record-store/blob/main/records-ui/src/lib/telemetry/web-vitals.instrumentation.ts">here</a> measures all web vitals.</p>
<pre><code>/* OpenTelemetry JS packages */
// Instrumentation base to create a custom Instrumentation for our provider
import {
    InstrumentationBase,
    type InstrumentationConfig,
    type InstrumentationModuleDefinition
} from '@opentelemetry/instrumentation';

// Metrics API
import {
    metrics,
    type ObservableGauge,
    type Meter,
    type Attributes,
    type ObservableResult,

} from '@opentelemetry/api';

export class WebVitalsInstrumentation extends InstrumentationBase {

  // Meter captures measurements at runtime
    private cwvMeter: Meter;

    /* Core Web Vitals Measures, LCP provided, others omitted */
    private lcp: ObservableGauge;

    constructor(config: InstrumentationConfig, resource: Resource) {
        super('WebVitalsInstrumentation', '1.0', config);

    // Create metric reader to process metrics and export using OTLP
        const metricReader = new PeriodicExportingMetricReader({
            exporter: new OTLPMetricExporter({
                url: 'http://localhost:8123/v1/metrics' // nginx proxy
            }),
            // Default is 60000ms (60 seconds).
            // Set to 10 seconds for demo purposes only.
            exportIntervalMillis: 10000
        });

    // Creating Meter Provider factory to send metrics
        const myServiceMeterProvider = new MeterProvider({
            resource: resource,
            readers: [metricReader]
        });
        metrics.setGlobalMeterProvider(myServiceMeterProvider);

    // Create web vitals meter
        this.cwvMeter = metrics.getMeter('core-web-vitals', '1.0.0');

        // Initialising CWV metric gauge instruments (LCP given as example, others omitted here)
        this.lcp = this.cwvMeter.createObservableGauge('lcp', { unit: 'ms', description: 'Largest Contentful Paint' });
    }

    protected init(): InstrumentationModuleDefinition | InstrumentationModuleDefinition[] | void {}

  // Other steps discussed later
}
</code></pre>
<p>You'll notice in our LCP example we have created an <code>ObservableGauge</code> to capture the value at the time it is read via a callback function. This can be setup when we <code>enable</code> our custom instrumentation, specifying when the LCP event is triggered the value will be sent via <code>result.observe</code>:</p>
<pre><code>/* Web Vitals Frontend package, LCP shown as example*/
import { onLCP, type LCPMetric } from 'web-vitals';

/* OpenTelemetry JS packages */
// Instrumentation base to create a custom Instrumentation for our provider
import {
    InstrumentationBase,
    type InstrumentationConfig,
    type InstrumentationModuleDefinition
} from '@opentelemetry/instrumentation';

// Metrics API
import {
    metrics,
    type ObservableGauge,
    type Meter,
    type Attributes,
    type ObservableResult,

} from '@opentelemetry/api';

// Other OTel Metrics imports omitted

// Time calculator via performance component
import { hrTime } from '@opentelemetry/core';

type CWVMetric = LCPMetric | CLSMetric | INPMetric | TTFBMetric | FCPMetric;

export class WebVitalsInstrumentation extends InstrumentationBase {

    /* Core Web Vitals Measures */
    private lcp: ObservableGauge;

    // Constructor and Initialization omitted

    enable() {
        // Capture Largest Contentful Paint, other vitals omitted
        onLCP(
            (metric) =&gt; {
                this.lcp.addCallback((result) =&gt; {
                    this.sendMetric(metric, result);
                });
            },
            { reportAllChanges: true }
        );
    }

  // Callback utility to add attributes and send captured metric
    private sendMetric(metric: CWVMetric, result: ObservableResult&lt;Attributes&gt;): void {
        const now = hrTime();

        const attributes = {
            startTime: now,
            'web_vital.name': metric.name,
            'web_vital.id': metric.id,
            'web_vital.navigationType': metric.navigationType,
            'web_vital.delta': metric.delta,
            'web_vital.value': metric.value,
            'web_vital.rating': metric.rating,
            // metric specific attributes
            'web_vital.entries': JSON.stringify(metric.entries)
        };

        result.observe(metric.value, attributes);
    }
}
</code></pre>
<p>To use our own instrumentation, we need to register our instrumentation just like we did in <code>frontend.tracer.ts</code> for the available web instrumentations to capture document and user event instrumentations:</p>
<pre><code>registerInstrumentations({
  instrumentations: [
    // Other web instrumentations omitted
    // Custom Web Vitals instrumentation
    new WebVitalsInstrumentation({}, resource)
    ]
});
</code></pre>
<p>The <code>lcp</code> metric, along with the attributes we specified as part of our <code>sendMetric</code> function will be sent to our Elastic cluster:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltf31d8c484a66c726/6a7f1c84bdcff03bacc43311/8-otel-metric-elastic-discover-view.png" alt="LCP Metric in Discover" /></p>
<p>These metrics will not feed into the <a href="https://www.elastic.co/docs/solutions/observability/applications/user-experience">User Experience dashboard</a> due to compatibility, but we can create a dashboard leveraging the values to show the trends of each of our vitals:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt07ad8a1b10b97772/6a7f1c88c2e9141c12017038/9-otel-core-web-vitals-dashboard.png" alt="Sample Core Web Vitals Dashboard" /></p>
<h2 id="summary">Summary</h2>
<p>In this blog, we presented the current state of client instrumentation for the browser, along with an example showing how to instrument a simple JavaScript frontend using <a href="https://opentelemetry.io/docs/languages/js/getting-started/browser/">the OpenTelemetry browser instrumentation</a>. To reflect back on the code, check out the repo <a href="https://github.com/carlyrichmond/otel-record-store">here</a>. If you have any questions or want to learn from other developers connect with the <a href="https://www.elastic.co/community">Elastic Community</a>.</p>
<blockquote>
  <p>Developer resources:</p>
  <ul>
  <li><a href="https://github.com/carlyrichmond/otel-record-store">OTel Record Store Application</a></li>
  <li><a href="https://opentelemetry.io/docs/languages/js/getting-started/browser/">JavaScript Browser Instrumentation</a></li>
  </ul>
</blockquote>]]></content:encoded>
    <link>https://www.elastic.co/observability-labs/blog/web-frontend-instrumentation-with-opentelemetry</link>
    <guid isPermaLink="false">web-frontend-instrumentation-with-opentelemetry</guid>
    <category><![CDATA[OpenTelemetry]]></category>
    <category><![CDATA[Infrastructure Monitoring]]></category>
    <dc:creator><![CDATA[Carly Richmond]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt99bb683acc7af2e6/6a7f1c8ae02fac60e05d69ff/web-blog-header.jpg" length="0" type="image/jpeg"/>
    <pubDate>Mon, 04 Aug 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Monitoring Proxmox VE deployments with Elastic Observability]]></title>
    <description><![CDATA[Monitoring Proxmox VE deployments, VMs, and Linux Containers with Elastic Observability.]]></description>
    <content:encoded><![CDATA[<p>In this blog post, you will learn how to leverage Elastic Observability to monitor Proxmox VE and the software running on top of it, both in the form of Linux Containers (LXCs) and Virtual Machines (VMs).</p>
<h2 id="whyuseelasticobservabilitywithproxmox">Why use Elastic Observability with Proxmox?</h2>
<p>Here at Elastic, we are passionate about efficiently managing and monitoring infrastructure and applications. Many of us have fun playing with home labs, oftentimes running Proxmox VE, a powerful open-source virtualization platform used to run virtual machines and Linux Containers (LXCs) with ease. While Proxmox provides robust tools for managing virtualized resources, gaining deep insights into the performance and health of your LXCs, VMs, and hosts requires a comprehensive monitoring solution. This blog post will guide you through leveraging the power of Elastic Observability, in conjunction with Elastic Agent, to effectively monitor your Proxmox VE deployment, ensuring optimal performance and proactive issue resolution thanks to Kibana Alerts.</p>
<h2 id="thehomelabsetup">The homelab setup</h2>
<p>Our homelab setup centers around an Intel N100 mini PC, serving as the host for Proxmox VE. This setup is simple and minimal, yet effective for showcasing a few interesting capabilities. On top of this mini PC, we run several Linux Containers (LXCs) for various services, along with a dedicated virtual machine for Home Assistant.</p>
<h2 id="elasticagentinstallationandconfiguration">Elastic Agent installation and configuration</h2>
<p>Before beginning, it is worth noting that there are numerous ways to install and configure the Elastic Agent. For the sake of simplicity, we will showcase a setup in which only one instance of the Elastic Agent is running on the host machine. The Elastic Agent reports to an Elastic Cloud Observability deployment and is managed via Fleet, which makes it tremendously easy to upgrade and re-configure it whenever needed. </p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltdf2e1eefd8bfde8b/6a7f0e483cab1ccb770e48f2/fleet-prox.jpg" alt="The Elastic Integrations enabled for our Proxmox host" /></p>
<h2 id="divingintothehost">Diving into the host</h2>
<p>Kibana offers various panes that make it nice and easy to learn about a system's health at a quick glance.</p>
<p>As a first step, let's take a look at the <code>Infrastructure &gt; Hosts</code> page in Kibana:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt2a92221410d29fdd/6a7f0e4bbdcff021c4c42ed7/kibana-infrastructure-hosts-proxmox.jpg" alt="The Infrastructure &gt; Hosts Kibana page for our Proxmox host" />
</p><p>Here we can see various information about our Proxmox VE host (i.e. the mini PC). The top processes running on it are presented, including processes running in LXCs such as <code>pia-daemon</code>. We can also see a <code>kvm</code> process, specifically running a Home Assistant virtual machine, and a Proxmox <code>pve-firewall</code> process.</p>
<p>Let's now take a look at <code>Universal Profiling &gt; Flamegraph</code>. This graph shows how much CPU time is consumed by different stack traces from processes running on the host system. You can drill down into specific processes using the search bar at the top. For instance, you can filter by <code>kvm</code> to only see information regarding this specific process.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltc0059556a6f3f1af/6a7f0e4f5967e579905dd35f/universal-profiling-flamegraph-kvm.jpg" alt="The Universal Profiling &gt; Flamegraph Kibana page for our Proxmox host" />
</p><h2 id="theobservabilityaiassistant">The Observability AI Assistant</h2>
<p>All the Kibana panes we visited so far have proved to be highly interesting, but they struggle to answer urgent questions such as: </p>
<ul>
<li>did anything happen in our mini PC recently?</li>
<li>was there any significant change in functionality? </li>
<li>is there any precious information hidden among the thousands of data points collected?</li>
</ul>
<p>The Elastic Observability AI Assistant helps us by answering these questions in natural language. By default, on Elastic Cloud, it uses the Elastic-managed LLM connector, which means users do not need to configure anything to get started with it. It just works!</p>
<p>Let's go to the <code>Observability &gt; AI Assistant</code> pane in Kibana and let's try to ask a generic prompt such as: "please give me an overview of the health of my <code>prox</code> host".</p>
<p>Let's then wait a minute so that it can dig into the data… et voilà, here comes lots of relevant information in the form of graphs and natural language explanations. The Observability AI Assistant understood our question, went through all the data for our Proxmox host, ran data analytics on it, and reported back in a matter of seconds!</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blta9d0d2e422f7c15a/6a7f0e52ead8ec2c5ebaa928/observability-ai-assistant-1.jpg" alt="The Observability AI Assistant's first reply" /></p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltd7d14c0ef96c4ca2/6a7f0e559090b025c384ea75/observability-ai-assistant-2.jpg" alt="The Observability AI Assistant's second reply" /></p>
<h2 id="alertingupondisruptionwithkibanaalerts">Alerting upon disruption with Kibana Alerts</h2>
<p>As a final step, let's try to define a Kibana Alert to help us understand whether our host is overloaded. Let's head to <code>Observability &gt; Alerts &gt; Rules</code> and create a new rule. We will create a Custom Threshold rule that will fire if CPU usage for the host is higher than 80% on average for the last 15 minutes. Kibana will send us an email in case the rule fires. The rule is also configured to fire if no data appears for the last 15 minutes, which is extremely helpful as it would imply the presence of some issues to be debugged: broken network or no electricity in the house, a faulty Agent deployment, or even a hardware issue with the mini PC.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt8f7bfae9927ebb99/6a7f0e58448e4e96265c07ad/rule-cpu-over-80.jpg" alt="The Kibana Alerting Rule for CPU being over 80 percent" /></p>
<h2 id="conclusion">Conclusion</h2>
<p>In this blog post we showcased how to effectively use the Elastic Stack to monitor Proxmox VE deployments. If you would like to try out such a setup first-hand, you are more than welcome to enjoy <a href="https://www.elastic.co/cloud/cloud-trial-overview">Elastic Cloud's 14-days free trial</a>.</p>
<p>In future blog posts, we will investigate how to dig deeper into LXCs and VMs to gather even more information from our home lab and create more tailored alerts. Stay tuned!</p>]]></content:encoded>
    <link>https://www.elastic.co/observability-labs/blog/monitoring-proxmox-ve-with-elastic</link>
    <guid isPermaLink="false">monitoring-proxmox-ve-with-elastic</guid>
    <category><![CDATA[Infrastructure Monitoring]]></category>
    <dc:creator><![CDATA[Lorenzo Soligo]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt1bd76b0ca91fa500/6a7f0e5bb437704d0f4d6d17/article-image.jpg" length="0" type="image/jpeg"/>
    <pubDate>Wed, 23 Jul 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Debugging Azure Networking for Elastic Cloud Serverless]]></title>
    <description><![CDATA[Learn how Elastic SREs uncovered and resolved unexpected packet loss in Azure Kubernetes Service (AKS), impacting Elastic Cloud Serverless performance.]]></description>
    <content:encoded><![CDATA[<h2 id="summaryoffindings">Summary of Findings</h2>
<p>Elastic's Site Reliability Engineering team (SRE) observed unstable throughput and packet loss in Elastic Cloud Serverless running on Azure Kubernetes Service (AKS). After investigation, we identified the primary contributing factors to be RX ring buffer overflows and kernel input queue saturation on SR-IOV interfaces. To address this, we increased RX buffer sizes and adjusted the netdev backlog, which significantly improved network stability.</p>
<h2 id="settingthescene">Setting the Scene</h2>
<p><a href="https://www.elastic.co/cloud/serverless">Elastic Cloud Serverless</a> is a fully managed solution that allows you to deploy and use Elastic for your use cases without managing the underlying infrastructure. Built on Kubernetes, it represents a shift in how you interact with Elasticsearch. Instead of managing clusters, nodes, data tiers, and scaling, you create serverless projects that are fully managed and automatically scaled by Elastic. This abstraction of infrastructure decisions allows you to focus solely on gaining value and insight from your data.</p>
<p>Elastic Cloud Serverless is generally available (GA) on AWS, GCP and currently in <a href="https://www.elastic.co/guide/en/serverless/current/regions.html">Technical Preview on Azure</a>. As part of preparing Elastic Cloud Serverless GA on Azure, we have been conducting extensive performance and scalability tests to ensure that our users get a consistent and reliable user experience.</p>
<p>In this post, we’ll take you behind the scenes of a deep technical investigation into a surprising performance issue that affected Serverless Elasticsearch in our Azure Kubernetes clusters. At first, the network seemed like the least likely place to look, especially with a high-speed 100 Gb/s interface on the host backing it. But as we dug deeper, with help from the Microsoft Azure team, that’s exactly where the problem led us.</p>
<h2 id="unexpectedresults">Unexpected Results!</h2>
<p>While the high-level architectures and system design patterns of the major cloud provider’s systems are often similar, the implementations are different, and these differences can have dramatic impacts on a system’s performance characteristics.</p>
<p>One of the most significant differences between the different cloud providers is that the underlying hypervisor software and server hardware of the Virtual Machines can vary significantly, even between instance families of the same provider.</p>
<p>There is no way to fully abstract the hardware away from an application like Elasticsearch. Fundamentally, its performance is dictated by the CPU, memory, disks, and network interfaces on the physical server. In preparation for the Elastic Cloud Serverless GA on Azure, our Elasticsearch Performance team kicked off large-scale load testing against Serverless Elasticsearch projects running on <a href="https://docs.azure.cn/en-us/aks/what-is-aks">Azure Kubernetes Service (AKS)</a>, using <a href="https://azure.microsoft.com/en-us/blog/azure-cobalt-100-based-virtual-machines-are-now-generally-available/">ARM-based VMs</a> (we’re big fans!). Throughout this process, we relied heavily on Elastic tools to analyse system behaviour, identify bottlenecks, and validate performance under load.</p>
<p>To perform these scale and load tests, the Elasticsearch Performance team use <a href="https://github.com/elastic/rally">Rally</a>, an open-source benchmarking tool designed to measure the performance of Elasticsearch clusters. The workload (or in Rally nomenclature, ‘Track’) used for these tests was the <a href="https://github.com/elastic/rally-tracks/tree/master/github_archive">GitHub Archive Track</a>. Rally collects and sends test telemetry using the <a href="https://www.elastic.co/docs/reference/elasticsearch/clients/python">official Python client</a> to a separate Elasticsearch cluster running <a href="https://www.elastic.co/observability">Elastic Observability</a>, which allows for monitoring and analysis during these scale and load tests in real time via <a href="https://www.elastic.co/docs/explore-analyze">Kibana</a>.</p>
<p>When we looked at the results, we observed that the indexing rate (the number of docs/s) for the Serverless projects was not only much lower than we had expected for the given hardware, but the throughput was also quite unstable. There were peaks and valleys, interspersed with frequent errors, whereas we were instead expecting a stable indexing rate for the duration of the test.</p>
<p>These tests are designed to push the system to its limits, and in doing so, they surfaced unexpected behavior in the form of unstable indexing throughput and intermittent errors. This was precisely the kind of problem we'd hoped to uncover prior to going GA — giving us the opportunity to work closely with Azure.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltda5b934ddb81078b/6a7f04dd6c6eacac0cf13d9e/indexing-rate-before.png" alt="Indexing Rate with Packet Loss" />
<em>A Kibana visualisation of Rally telemetry, showing fluctuating Elasticsearch indexing rates alongside spikes in 5xx and 4xx HTTP error responses.</em></p>
<h2 id="debugging">Debugging!</h2>
<p>Debugging performance issues can feel a little bit like trying to find a <a href="https://www.youtube.com/watch?v=7AO4wz6gI3Q">‘Butterfly in a Hurricane’</a>, so it’s crucial that you take a methodological approach to analysing application and system performance.</p>
<p>Using methodologies helps you to be more consistent and thorough in your debugging, and avoids missing things. We started with the <a href="https://www.brendangregg.com/usemethod.html">Utilisation Saturation and Errors (USE) Method</a>, looking at both the client and server side to identify any obvious bottlenecks in the system. </p>
<p>Elastic's Site Reliability Engineers (SREs) maintain a suite of custom <a href="https://www.elastic.co/docs/solutions/observability/get-started/what-is-elastic-observability">Elastic Observability</a> dashboards designed to visualise data collected from various <a href="https://www.elastic.co/docs/extend/integrations/what-is-an-integration">Elastic Integrations</a>. These dashboards provide deep visibility into the health and performance of Elastic Cloud infrastructure and systems.</p>
<p>For this investigation, we leveraged a custom dashboard built using metrics and log data from the <a href="https://www.elastic.co/docs/reference/integrations/system">System</a> and <a href="https://www.elastic.co/docs/reference/integrations/linux">Linux</a> Integrations:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt254043f394cf02f3/6a7f04e01967ea412233038e/overview-dashboard.png" alt="Node Overview Dashboard" />
  <em>One of many Elastic Observability dashboards built and maintained by the SRE team.</em></p>
<p>Following the USE Method, these dashboards highlight resource utilisation, saturation, and errors across our systems. With their help, we quickly identified that the AKS nodes hosting the Elasticsearch pods under test were dropping thousands of packets per second.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt39b522be27af75cd/6a7f04e396b5a639a987b0e8/packet-loss-before.png" alt="Node Packet Loss Before Tuning" />
<em>A Kibana visualisation of <a href="https://www.elastic.co/docs/reference/integrations/system">Elastic Agent's System Integration</a>, showing the rate of packet drops per second for AKS nodes.</em></p>
<p>Dropping packets forces reliable protocols, such as TCP, to retransmit any missing packets. These retransmissions can introduce significant delays, which kills the throughput of any system where client requests are only triggered upon the previous request completion (known as a <a href="https://www.usenix.org/legacy/event/nsdi06/tech/full_papers/schroeder/schroeder.pdf">Closed System</a>).</p>
<p>To investigate further, we jumped onto one of the AKS nodes exhibiting the packet loss to check the basics. First off, we wanted to identify what type of packet drops or errors we’re seeing; is it for specific pods, or the host as a whole?</p>
<pre><code>root@aks-k8s-node-1:~# ip -s link show
2: eth0: &lt;BROADCAST,MULTICAST,UP,LOWER_UP&gt; mtu 1500 qdisc mq state UP mode DEFAULT group default qlen 1000
    link/ether 7c:1e:52:be:ce:5e brd ff:ff:ff:ff:ff:ff
    RX:    bytes   packets errors dropped  missed   mcast
    373507935420 134292481      0       0       0      15
    TX:    bytes   packets errors dropped carrier collsns
    644247778936 303191014      0       0       0       0
3: enP42266s1: &lt;BROADCAST,MULTICAST,SLAVE,UP,LOWER_UP&gt; mtu 1500 qdisc mq master eth0 state UP mode DEFAULT group default qlen 1000
    link/ether 7c:1e:52:be:ce:5e brd ff:ff:ff:ff:ff:ff
    RX:    bytes   packets errors dropped  missed   mcast
    386782548951 307000571      0       0 5321081       0
    TX:    bytes   packets errors dropped carrier collsns
    655758630548 477594747      0       0       0       0
    altname enP42266p0s2
15: lxc0ca0ec41ecd2@if14: &lt;BROADCAST,MULTICAST,UP,LOWER_UP&gt; mtu 1500 qdisc noqueue state UP mode DEFAULT group default qlen 1000
    link/ether f6:f5:5e:c9:4e:fb brd ff:ff:ff:ff:ff:ff link-netns cni-3f90ab53-df66-cac5-bd19-9cea4a68c29b
    RX:    bytes   packets errors dropped  missed   mcast
    627954576078  54297550      0    1600       0       0
    TX:    bytes   packets errors dropped carrier collsns
    372155326349 133538064      0    3927       0       0
</code></pre>
<p>In this output you can see the <code>enP42266s1</code> interface is showing a significant number of packets in the <code>missed</code> column. That’s interesting, sure, but what does missed actually represent? And what is <code>enP42266s1</code>?</p>
<p>To understand, let’s look at roughly what happens when a packet arrives at the NIC:</p>
<ol>
<li>A packet arrives at the NIC from the network.</li>
<li>The NIC uses DMA (Direct Memory Access) to place the packet into a receive ring buffer allocated in memory by the kernel, mapped for use by the NIC. Since our NICs supports multiple hardware queues, each queue has its own dedicated ring buffer, IRQ, and NAPI context.</li>
<li>The NIC raises a hardware interrupt (IRQ) to notify the CPU that a packet is ready.</li>
<li>The CPU runs the NIC driver’s IRQ handler. The driver schedules a NAPI (New API) poll to defer packet processing to a softirq context. A mechanism in the Linux kernel that defers work to be processed outside of the hard IRQ context, for better batching and CPU efficiency, enabling improved scalability.</li>
<li>The NAPI poll function is executed in a softirq context (<code>NET_RX_SOFTIRQ</code>) and retrieves packets from the ring buffer. This polling continues either until the driver’s packet budget is exhausted (<code>net.core.netdev_budget</code>) or the time limit is hit (<code>net.core.netdev_budget_usecs</code>).</li>
<li>Each packet is wrapped in an <code>sk_buff</code> (socket buffer) structure, which includes metadata such as protocol headers, timestamps, and interface identifiers.</li>
<li>If the networking stack is slower than the rate at which NAPI fetches packets, excess packets are queued in a per-CPU backlog queue (via <code>enqueue_to_backlog</code>). The maximum size of this backlog is controlled by the <code>net.core.netdev_max_backlog</code> sysctl.</li>
<li>Packets are then handed off to the kernel’s networking stack for routing, filtering, and protocol-specific processing (e.g. TCP, UDP).</li>
<li>Finally, packets reach the appropriate socket receive buffer, where they are available for consumption by the user-space application.</li>
</ol>
<p>Visualised, it looks something like this:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blte9587f90d0e129ad/6a7f04e69090b014ff84e6a4/packet-flow.png" alt="Linux Packet Flow Diagram" />
<em>Image © 2018 Leandro Moreira. Used under the <a href="https://opensource.org/licenses/BSD-3-Clause">BSD 3-Clause License</a>. Source: <a href="https://github.com/leandromoreira/linux-network-performance-parameters">GitHub repository</a>.</em></p>
<p>The <code>missed</code> counter is incremented whenever the NIC tries to DMA a packet into a fully occupied <a href="https://en.wikipedia.org/wiki/Circular_buffer">ring buffer</a>. The NIC essentially "misses" the chance to deliver the packet to the VM’s memory. However, what’s most interesting is that this counter seldom increments for VMs. This is because Virtual NICs are usually implemented as software via the hypervisor, which typically has much more flexible memory management compared to the physical NICs and can reduce the chance of ring buffer overflow.</p>
<p>We mentioned earlier that we’re building Azure Elasticsearch Serverless on top of Azure’s AKS service, which is important to note because all of our AKS nodes use an Azure feature called <a href="https://learn.microsoft.com/en-us/azure/virtual-network/accelerated-networking-overview">Accelerated Networking</a>. In this setup, network traffic is delivered directly to the VM’s network interface, bypassing the hypervisor. This is enabled by <a href="https://learn.microsoft.com/en-us/windows-hardware/drivers/network/overview-of-single-root-i-o-virtualization--sr-iov-">single root I/O virtualization (SR-IOV)</a>, which offers much lower latency and higher throughput than traditional VM networking. Each node is physically connected to a 100 Gb/s network interface, although the SR-IOV Virtual Function (VF) exposed to the VM typically provides only a fraction of that total bandwidth.</p>
<p>Despite the VM only having a fraction of the 100 Gb/s bandwidth, microbursts are still very possible. These physical interfaces are so fast that they can transmit and receive multiple packets in just nanoseconds, far faster than most buffers or processing queues can absorb. At these timescales, even a short-lived burst of traffic can overwhelm the receiver, leading to dropped packets and unpredictable latency.</p>
<p>Direct access to the SR-IOV interface means that our VMs are responsible for handling the hardware interrupts triggered by the NIC in a timely manner, if there's any delay in handling the hardware interrupt (e.g. waiting to be scheduled onto CPU by the hypervisor) then network packets can be missed!</p>
<h2 id="firstlynicleveltuning">Firstly - NIC-level Tuning</h2>
<p>Since we'd confirmed that our VMs were using SR-IOV, we established that the <code>enP42266s1</code> and <code>eth0</code> interfaces <a href="https://learn.microsoft.com/en-us/azure/virtual-network/accelerated-networking-how-it-works">were a bonded pair and acted as a single interface</a>. Knowing this, then we reasoned that we should be able to adjust the ring buffer values directly using <code>ethtool</code>. </p>
<pre><code>root@aks-k8s-node-1:~# ethtool -g enP42266s1
Ring parameters for enP42266s1:
Pre-set maximums:
RX:        8192
RX Mini:    n/a
RX Jumbo:    n/a
TX:        8192
Current hardware settings:
RX:        1024
RX Mini:    n/a
RX Jumbo:    n/a
TX:        1024
</code></pre>
<p>In the output above, we were using only 1/8th of the available ring buffer descriptors. These values were set by the OS defaults, which generally aim to balance performance and resource usage. Set too low, they risk packet drops under load; set too high, they can lead to unnecessary memory consumption. We knew that the VMs were backed by a virtual function carved out of the directly attached 100 Gb/s network interface, which is fast enough to deliver microbursts that could easily overwhelm small buffers. To better absorb those short, high-intensity bursts of traffic, we increased the NIC’s RX ring buffer size from 1024 to 8192. Using a privileged DaemonSet, we rolled out the change across all of our AKS nodes by installing <a href="https://en.wikipedia.org/wiki/Udev">a <code>udev</code> rule</a> to automatically increase the buffer size:</p>
<pre><code># Match Mellanox ConnectX network cards and run ethtool to update the ring buffer settings
ENV{INTERFACE}=="en*", ENV{ID_NET_DRIVER}=="mlx5_core", RUN+="/sbin/ethtool -G %k rx ${CONFIG_AZURE_MLX_RING_BUFFER_SIZE} tx ${CONFIG_AZURE_MLX_RING_BUFFER_SIZE}"
</code></pre>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt02e4acebbd3f43a7/6a7f04e93cab1c5eb70e44dd/packet-loss-after.png" alt="AKS Node Packet Loss after RX ring buffer change" />
<em>A Kibana visualisation of <a href="https://www.elastic.co/docs/reference/integrations/system">Elastic Agent's System Integration</a>, showing packet loss reduced by ~99% after increasing the NIC's RX ring buffer values.</em></p>
<p>As soon as the change had been applied to all AKS nodes we stopped ‘missing’ RX packets! Fantastic! As a result of this simple change we observed a significant improvement in our indexing throughput and stability. </p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt77d9ee02f03f3c15/6a7f04ece02fac548a5d621f/indexing-rate-after.png" alt="Indexing rate after RX ring buffer change" />
<em>A Kibana visualisation of Rally telemetry, showing stable and improved Elasticsearch indexing rates after increasing the RX ring buffer size.</em></p>
<p>Job done, right? Not quite..</p>
<h2 id="furtherimprovementskernelleveltuning">Further improvements - Kernel-level Tuning</h2>
<p>Eagle eyed readers may have noticed two things:</p>
<ol>
<li>In the previous screenshot, despite adjusting the physical RX ring buffer values, we still observed a small number of <code>dropped</code> packets on the TX side.</li>
<li>In the original <code>ip link -s show</code> output, one of the ‘logical’ interfaces used by the Elasticsearch pod was showing <code>dropped</code> packets on both the TX and RX sides.</li>
</ol>
<pre><code>15: lxc0ca0ec41ecd2@if14: &lt;BROADCAST,MULTICAST,UP,LOWER_UP&gt; mtu 1500 qdisc noqueue state UP mode DEFAULT group default qlen 1000
    link/ether f6:f5:5e:c9:4e:fb brd ff:ff:ff:ff:ff:ff link-netns cni-3f90ab53-df66-cac5-bd19-9cea4a68c29b
    RX:    bytes   packets errors dropped  missed   mcast
    627954576078  54297550      0    1600       0       0
    TX:    bytes   packets errors dropped carrier collsns
    372155326349 133538064      0    3927       0       0
</code></pre>
<p>So, we continued to dig. We’d eliminated ~99% of the packet loss, and the remaining loss rate wasn’t as significant as what we’d started with, but we still wanted to understand why it was occurring even after adjusting the RX ring buffer size of the NIC. </p>
<p>So what does <code>dropped</code> represent, and what is this <code>lxc0ca0ec41ecd2</code> interface? <code>dropped</code> is similar to <code>missed</code>, but only occurs when packets are deliberately dropped by the kernel or network interface. Crucially though, it doesn’t tell you why a packet was dropped. As for the <code>lxc0ca0ec41ecd2</code> interface, we use the <a href="https://learn.microsoft.com/en-us/azure/aks/azure-cni-powered-by-cilium">Azure CNI Powered by Cilium</a> to provide the network functionality to our AKS clusters. Any pod spun up on an AKS node gets a ‘logical’ interface, which is a virtual ethernet (<code>veth</code>) pair that connects the pod’s network namespace with the host’s network namespace. It was here that we were dropping packets.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt9d0128045c8d8985/6a7f04efc2e91492a901685b/aks-node-network-topology.png" alt="AKS Node Networking Diragram" /></p>
<p>In our experience, packet drops at this layer are unusual, so we started digging deeper into the cause of the drops. There are numerous ways you can debug why a packet is being dropped, but one of the easiest is <a href="https://perfwiki.github.io/main/">to use <code>perf</code></a> attach to the <code>skb:kfree_skb</code> tracepoint. The "socket buffer" (<code>skb</code>) is the primary data structure used to represent network packets in the Linux kernel. When a packet is dropped, its corresponding socket buffer is usually freed, triggering the <code>kfree_skb</code> tracepoint. Using <code>perf</code> to attach to this event allowed us to capture stack traces to analyze the cause of the drops.</p>
<pre><code># perf record -g -a -e skb:kfree_skb
</code></pre>
<p>We left this to run for ~10 minutes or so to capture as many drops as possible, and then ‘heavily inspired’ by <a href="https://gist.github.com/bobrik/0e57671c732d9b13ac49fed85a2b2290">this GitHub Gist by Ivan Babrou</a>, we converted the stack traces into an ‘easier’ to read <a href="https://github.com/brendangregg/FlameGraph">Flamegraphs</a>:</p>
<pre><code># perf script | sed -e 's/skb:kfree_skb:.*reason:\(.*\)/\n\tfffff \1 (unknown)/' -e 's/^\(\w\+\)\s\+/kernel /' &gt; stacks.txt
cat stacks.txt | stackcollapse-perf.pl --all | perl -pe 's/.*?;//' | sed -e 's/.*irq_exit_rcu_\[k\];/irq_exit_rcu_[k];/' | flamegraph.pl --colors=java --hash --title=aks-k8s-node-1 --width=1440 --minwidth=0.005 &gt; aks-k8s-node-1.svg
</code></pre>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt845a231a6ac4aef0/6a7f04f2bdcff058c9c42aad/aks-packet-loss-flamegraph.png" alt="AKS Node Packet Loss Flamegraph" />
<em>A Flamegraph showing the various stack trace ancestry of packet loss.</em></p>
<p>The flamegraph here shows how often different functions appeared in stack traces for packets drops. Each box represents a function call and wider boxes mean the function appears more frequently in the traces. The stack's ancestry builds upward from the bottom with earlier calls, to the top with later calls.</p>
<p>Firstly, we quickly discovered that unfortunately the <code>skb_drop_reason</code> enum <a href="https://github.com/torvalds/linux/commit/c504e5c2f9648a1e5c2be01e8c3f59d394192bd3">was only added in Kernel 5.17</a> (Azure’s Node Image at the time was using 5.15). This meant that there was no single human readable message that told us why the packets were being dropped, instead all we got was <code>NOT_SPECIFIED</code>. To work out why packets were being dropped we needed to do a little sleuthing through the stack traces to work out what code paths were being taken when a packet was dropped.</p>
<p>In the flamegraph above you can see that many of the stack traces include <code>veth</code> driver function calls (e.g. <code>veth_xmit</code>), and many end abruptly with a call to the <code>enqueue_to_backlog</code> function. When many stacks end at the same function (like <code>enqueue_to_backlog</code>) it suggests that function is a common point where packets are being dropped. If you go back to the earlier explanation of what happens when a packet arrives at the NIC, you’ll notice that in step 7 we explained:</p>
<blockquote>
  <p><em>7. If the networking stack is slower than the rate at which NAPI fetches packets, excess packets are queued in a per-CPU backlog queue (via <code>enqueue_to_backlog</code>). The maximum size of this backlog is controlled by the <code>net.core.netdev_max_backlog</code> sysctl.</em></p>
</blockquote>
<p>Using the same privileged DaemonSet method for the RX ring buffer adjustment, we set the value of the <code>net.core.netdev_max_backlog</code> adjustable kernel parameter from 1000 to 32768:</p>
<pre><code>/usr/sbin/sysctl -w net.core.netdev_max_backlog=32768
</code></pre>
<p>This value was based on the fact we knew the hosts were using a 100 Gb/s SR-IOV NIC, even if the VM was allowed only a fraction of the total bandwidth. We acknowledge that it’s worth revisiting this value in the future to see if it can be better optimised to not waste extraneous memory, but at the time “perfect was the enemy of good”.</p>
<p>We re-ran the load tests and compared the three sets of results we’d collected thus far.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt625534040dadddf8/6a7f04f5fc63ab696a64c8f0/indexing-rate-final.png" alt="Final Indexing Rate Results" />
<em>A Kibana visualisation of Rally results, comparing impact to median throughput after each configuration change.</em></p>
<p>| Tuning Step | Packet Loss | Median indexing throughput |
|-------------|-------------|-----------------------------|
| Baseline    | High        | ~18,000 docs/s                   |
| +RX Buffer  | ~99% drop ↓ | ~26,000 (+ ~40% from baseline)                    |
| +Backlog &amp; +RX Buffer     | Near zero   | ~29,000 (+ ~60% from baseline)                    |</p>
<p>Here you can see the P50 of throughput in docs/s over the course of the hours-long load tests. Compared to the baseline, we saw a roughly <strong>~40%</strong> increase in throughput by only adjusting the RX ring buffer values, and a <strong>~50-60%</strong> increase with both the RX ring buffer and backlog changes! Hooray!</p>
<p>A great result and one more step on our journey towards better Serverless Elasticsearch performance.</p>
<h2 id="workingwithazure">Working with Azure</h2>
<p>It’s great that we were able to quickly identify and mitigate the majority of our packet loss issues, but since we were using AKS with AKS node images, it made sense to engage with Azure to understand why the defaults weren’t working for our workload.</p>
<p>We walked Azure through our investigation, mitigations and results, and asked for some additional validation of our mitigations. Azure Engineering confirmed that the host NICs were not discarding packets, which confirmed that everything arriving at the host level was passed through to the hypervisor on the host. Further investigation confirmed that no loss or discards were occurring to Azure network fabric, or internal to the hypervisor – which shifted focus from the host to the guest OS and why the guest OS kernel was slow when reading packets off of the <code>enP*</code> SR-IOV interfaces. </p>
<p>Given the complexity of our load testing scenario — which involved configuring multiple systems and tools, including <a href="https://www.elastic.co/observability">Elastic Observability</a>, we also developed a simplified reproduction of the packet loss issue using <a href="https://github.com/esnet/iperf"><code>iperf3</code></a>. This simplified test was created specifically to share with Azure for targeted analysis, and added to the broader monitoring and analysis enabled by Elastic Observability and Rally.</p>
<p>With this reproduction Azure was able to confirm the increasing <code>missed</code> and <code>dropped</code> packet counters we had observed, and confirmed the increased RX ring buffer and <code>netdev_max_backlog</code> increase as the recommended mitigations.</p>
<h2 id="conclusion">Conclusion</h2>
<p>While cloud providers offer various abstractions to manage your resources, the underlying hardware ultimately determines your application's performance and stability. High-performance hardware often requires tuning at the operating system level, well beyond the default settings most environments ship with. In managed platforms like AKS, where Azure controls both the node images and infrastructure, it is easy to overlook the impact of low-level configurations such as network device ring buffer sizes or sysctls like <code>net.core.netdev_max_backlog</code>.</p>
<p>Our experience shows that even with the convenience of a managed Kubernetes service, performance issues can still emerge if these hardware parameters are not tuned appropriately. It was tempting to assume that high-speed 100 Gb/s network interfaces, directly attached to the VM using SR-IOV would eliminate any chance of network-related bottlenecks. In reality, that assumption didn’t hold up. </p>
<p>Engaging early with Azure was essential, as they provided deeper visibility into the underlying infrastructure and worked with us to tune low-level, performance-critical settings. Combined with thorough load and scale testing and robust observability using tools like Elastic Observability, this collaboration helped us detect and rectify the issue early in order to deliver a consistent, reliable, and high-performing experience for our users.</p>]]></content:encoded>
    <link>https://www.elastic.co/observability-labs/blog/debugging-aks-packet-loss</link>
    <guid isPermaLink="false">debugging-aks-packet-loss</guid>
    <category><![CDATA[Kubernetes]]></category>
    <category><![CDATA[Infrastructure Monitoring]]></category>
    <dc:creator><![CDATA[Brad Deam,Christos Argyropoulos]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt374b162aa72f83be/6a7f04f8ead8ec51eebaa4b1/debugging-aks-packet-loss.png" length="0" type="image/png"/>
    <pubDate>Thu, 05 Jun 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Transforming Industries and the Critical Role of LLM Observability: How to use Elastic's LLM integrations in real-world scenarios]]></title>
    <description><![CDATA[This blog explores four industry specific use cases that use Large Language Models (LLMs) and highlights how Elastic's LLM observability integrations provide insights into the cost, performance, reliability and the prompts and response exchange with the LLM.]]></description>
    <content:encoded><![CDATA[<p>In today's tech-centric world, Large Language Models (LLMs) are transforming sectors from finance and healthcare to research. LLMs are starting to underpin products and services across the spectrum. Take for example recent <a href="https://blog.google/technology/google-deepmind/gemini-model-thinking-updates-march-2025/#advanced-coding">advanced coding</a> developments in Google's Gemini 2.5 which enable it to use its reasoning capabilities to create a video game by producing the executable code from a short prompt.  Or <a href="https://www.aboutamazon.com/news/devices/new-alexa-generative-artificial-intelligence">new ways</a> to interact with Amazon's Alexa - for example, you could send a picture of a live music schedule, and have Alexa add the details to your calendar. And let's not forget Microsoft's <a href="https://blogs.microsoft.com/blog/2025/04/04/your-ai-companion/">personalization of Copilot</a> which remembers what you talk about, so it learns your likes and dislikes and details about your life; the name of your dog, that tricky project at work, what keeps you motivated to stick to your new workout routine. </p>
<p>Despite their widespread utility of LLMs, deploying these sophisticated tools in real-world scenarios poses distinct challenges, especially in managing their complex behaviors. For users such as Site Reliability Engineers (SREs), DevOps teams, and AI/ML engineers, ensuring reliability, performance, and compliance of these models introduces an additional  layer of complexity. This is where the concept of LLM Observability becomes essential. It offers crucial insights into the performance of these models, ensuring that these advanced AI systems operate both effectively and ethically.</p>
<h3 id="whyllmobservabilitymattersandhowelasticmakesiteasy">Why LLM Observability Matters and How Elastic Makes It Easy</h3>
<p>LLMs are not just another piece of software; they are sophisticated systems capable of human-like capabilities such as text generation, comprehension, and even coding. But with great power comes greater need for oversight. The opaque nature of these models can obscure how decisions are made and content generated. This makes it even more critical to implement robust observability to monitor and troubleshoot issues such as hallucinations, inappropriate content, cost overruns, errors and performance degradation. By monitoring these models closely, we can safeguard against unexpected outcomes and maintain user trust.</p>
<h3 id="realworldscenarios">Real-World Scenarios</h3>
<p>Let's explore real-world scenarios where companies leverage LLM-powered applications to enhance productivity and user experience, and how Elastic's LLM observability solutions monitor critical aspects of these models.</p>
<h4 id="1generativeaiforcustomersupport">1. Generative AI for Customer Support</h4>
<p>Companies are increasingly leveraging LLMs and generative AI to enhance customer support, using platforms like Google Vertex AI for hosting these models efficiently. With the introduction of advanced AI models such as Google's Gemini, which is integrated into Vertex AI, businesses can deploy sophisticated chatbots that manage customer inquiries, from basic questions to complex issues, in real time. These AI systems understand and respond with natural language, offering instant support for issues such as product troubleshooting or managing orders thus 
reducing wait times. They also learn from each interaction to improve accuracy continuously. This boosts customer satisfaction and allows human agents to focus on complex tasks, enhancing overall efficiency. Other ways that AI tools can further empower customer care agents is with real-time analytics, sentiment detection, and conversation summarization. </p>
<p>To support use cases like the AI-powered customer support described above, Elastic recently launched LLM observability integrations including support for <a href="https://www.elastic.co/guide/en/integrations/current/gcp_vertexai.html">LLMs hosted on GCP Vertex AI</a>. Customers who wish to monitor foundation models such as Gemini and Imagen hosted on Google Vertex AI can benefit from Elastic’s Vertex AI integration to get a deeper understanding of model behavior and performance, and ensure that the AI-driven tools are not only effective but also reliable. Customers get out-of-the-box experience ingesting a curated set of metrics from Vertex AI as well as a pre-configured dashboard.</p>
<p>By continuously tracking these metrics, customers can proactively manage their AI resources, optimize operations, and ultimately enhance the overall customer experience.</p>
<p>Let's look at some of the metrics you get from the Google Vertex AI integration which are helpful in the context of using generative AI for customer support.</p>
<ol>
<li><strong>Prediction Latency</strong>: Measures the time taken to complete predictions, critical for real-time customer interactions.</li>
<li><strong>Error Rate</strong>: Tracks errors in predictions, which is vital for maintaining the accuracy and reliability of AI-driven customer support.</li>
<li><strong>Prediction Count</strong>: Counts the number of predictions made, helping assess the scale of AI usage in customer interactions.</li>
<li><strong>Model Usage</strong>: Tracks how frequently the AI models are accessed by both virtual assistants and customer support tools.</li>
<li><strong>Total Invocations</strong>: Measures the total number of times the AI services are used, providing insights into user engagement and dependency on these tools.</li>
<li><strong>CPU and Memory Utilization</strong>: By observing CPU and memory usage, users can optimize resource allocation, ensuring that the AI tools are running efficiently without overloading the system.</li>
</ol>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blta496ae3417800a11/6a7f1ba7eab5be0c7520ab1a/vertex-overview.png" alt="Vertex Overview" /></p>
<p>To learn more about how Elastic's Google Vertex AI integration can augment your LLM observability, have a quick read of this <a href="https://www.elastic.co/observability-labs/blog/elevate-llm-observability-with-gcp-vertex-ai-integration">blog</a>.</p>
<h4 id="2transforminghealthcarewithgenerativeai">2. Transforming Healthcare with Generative AI</h4>
<p>The healthcare industry is embracing generative AI to enhance patient interactions and streamline operational workflows. By leveraging platforms like Amazon Bedrock, healthcare organizations deploy advanced large language models (LLMs) to power tools that convert doctor-patient conversations into structured medical notes, reducing administrative overhead and allowing clinicians to prioritize diagnosis and treatment. These AI-driven solutions provide real-time insights, enabling informed decision-making and improving patient outcomes. Additionally, patient-facing applications powered by LLMs offer secure access to health records, empowering individuals to manage their care proactively. </p>
<p>Robust observability is essential to maintain the reliability and performance of these generative AI applications in healthcare. Elastic’s <a href="https://www.elastic.co/guide/en/integrations/current/aws_bedrock.html">Amazon Bedrock integration</a> equips providers with tools to monitor LLM behavior, capturing critical metrics like invocation latency, error rates, token usage and guardrail invocation. Pre-configured dashboards provide visibility into prompt and completion text, enabling teams to verify the accuracy of AI-generated outputs, such as medical notes, and detect issues like hallucinations. </p>
<p>Additionally, customers who configure Guardrails for Amazon Bedrock to filter harmful content like hate speech, personal insults, and other inappropriate topics, can use the Bedrock Integration to observe the prompts and responses that caused the guardrail to filter them out. This helps application developers take proactive actions to maintain a safe and positive user experience.</p>
<p>Some of the logs and metrics that can be helpful for customers using LLMs hosted on Amazon Bedrock are the following</p>
<ol>
<li><strong>Invocation Details</strong>: This Integration records the Invocation latency, count, throttles. These metrics are critical for ensuring that generative AI models respond quickly and accurately to patient queries or appointment scheduling tasks, maintaining a seamless user experience.</li>
<li><strong>Error Rates</strong>:  Tracking error rates ensures that AI tools, such as patient query assistants or appointment systems, consistently deliver accurate and reliable results. By identifying and addressing issues early, healthcare providers can maintain trust in AI systems and prevent disruptions in critical patient interactions.</li>
<li><strong>Token Usage</strong>: In healthcare, tracking token usage helps identify resource-intensive queries, such as detailed patient record summaries or complex symptom analyses, ensuring efficient model operation. By monitoring token usage, healthcare providers can optimize costs for AI-powered tools while maintaining scalability to handle growing patient interactions.</li>
<li><strong>Prompt and Completion Text</strong>: Capturing prompt and completion text allows healthcare providers to analyze how AI models respond to specific patient queries or administrative tasks, ensuring meaningful and contextually accurate interactions. This insight helps refine prompts to improve the AI's understanding and ensures that generated responses, such as appointment details or treatment explanations, meet the quality standards expected in healthcare.</li>
<li><strong>Prompt and response where guardrails intervened</strong>: Being able to track requests and responses that were deemed inappropriate by guardrails helps healthcare providers monitor what information patients are asking for. With this information users can make continuous adjustments to the LLMs to ensure appropriate responses, balancing flexibility and rich communication on the one hand, and on the other, privacy protection, hallucination prevention, and harmful content filtering. </li>
</ol>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blta8ce07121048b7cc/6a7f1baa2f00b22117efef3d/aws-bedrock-overview.png" alt="Bedrock Overview" /></p>
<p>Amazon Bedrock Gaurdrails OOTB dashboard
<img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt843efe77fb38f137/6a7f1bad42a117cf9695c33b/amazon-bedrock-gaurdrails.png" alt="Bedrock Gaurdrails Overview" /></p>
<p>To learn about the Amazon Bedrock Integration, read this <a href="https://www.elastic.co/observability-labs/blog/llm-observability-aws-bedrock">blog</a>. To dive deeper into how the integration can help with observability of Guardrails for Amazon Bedrock, take a look at this <a href="https://www.elastic.co/observability-labs/blog/llm-observability-amazon-bedrock-guardrails">blog</a>.</p>
<h4 id="3enhancingtelcoefficiencywithgenai">3.  Enhancing Telco Efficiency with GenAI</h4>
<p>The telecommunication industry can leverage services like Azure OpenAI to transform customer interactions, optimize operations, and enhance service delivery. By integrating advanced generative AI models, telcos can offer highly personalized and responsive customer experiences across multiple channels. AI-powered virtual assistants streamline customer support by automating routine queries and providing accurate, context-aware responses, reducing the workload on human agents and enabling them to focus on complex issues while improving efficiency and satisfaction. Additionally, AI-driven insights help telcos understand customer preferences, anticipate needs, and deliver tailored offerings that boost customer loyalty. Operationally, LLMs such as Azure OpenAI enhance internal processes by enabling smarter knowledge management and faster access to critical information.</p>
<p>Elastic's LLM observability integrations like the <a href="https://www.elastic.co/guide/en/integrations/current/azure_openai.html">Azure OpenAI integration</a> can provide visibility into AI performance and costs, empowering telecom providers to make data-driven decisions and enhance customer engagement. It can help optimize resource allocation by analyzing call patterns, predicting service demands, and identifying trends, enabling telcos to scale their AI operations efficiently while maintaining high service quality.</p>
<p>Some of the key metrics and logs that Azure OpenAI that can provide insights are:</p>
<ol>
<li><strong>Error Counts</strong>: It provides critical insights into failed requests and incomplete transactions, enabling telecom providers to proactively identify and resolve issues in AI-powered applications. </li>
<li><strong>Prompt Input and Completion Text</strong>: This captures the input queries provided to AI systems and the corresponding AI-generated outputs. These fields allow telecom providers to analyze customer queries, monitor response quality, and refine AI training datasets to improve relevance and accuracy.</li>
<li><strong>Response Latency</strong>: It measures the time taken by AI models to generate responses, ensuring that virtual assistants and automated systems deliver quick and efficient replies to customer queries. </li>
<li><strong>Token Usage</strong>: It tracks the number of input and output tokens processed by the AI model, offering insights into resource consumption and cost efficiency. This data helps telecom providers monitor AI usage patterns, optimize configurations, and scale resources effectively</li>
<li><strong>Content Filter Results</strong>: In Azure OpenAI, this plays a crucial role in handling sensitive inputs provided by customers, ensuring compliance, safety, and responsible AI usage. This feature identifies and flags potentially inappropriate or harmful queries and responses in real time, enabling telecom providers to address sensitive topics with care and accuracy. </li>
</ol>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt7977b3e3b8a8f599/6a7f1bb02f00b220bcefef41/azure-openai-overview.png" alt="Azureopenai Overview" /></p>
<p>The Azure OpenAI content filtering OOTB dashboard
<img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt3596d81c2cb554ab/6a7f1bb3eab5be7e6f20ab22/azure-openai-contentfiltering.png" alt="Azureopenai Overview1" /></p>
<p>You can learn more about Elastic's Azure OpenAI integration from these two blogs - <a href="https://www.elastic.co/observability-labs/blog/llm-observability-azure-openai">Part 1</a> and <a href="https://www.elastic.co/observability-labs/blog/llm-observability-azure-openai-v2">Part 2</a>. </p>
<h4 id="4openaiintegrationforgenerativeaiapplications">4. OpenAI Integration for Generative AI Applications</h4>
<p>As AI-powered solutions become integral to modern workflows, OpenAI's sophisticated models, including language models like GPT-4o and GPT-3.5 Turbo, image generation models like DALL·E, and audio processing models like Whisper, drive innovation across applications such as virtual assistants, content creation, and speech-to-text systems. With growing complexity and scale, ensuring these models perform reliably, remain cost-efficient, and adhere to ethical guidelines is paramount. Elastic's <a href="https://www.elastic.co/docs/reference/integrations/openai">OpenAI integration</a> provides a robust solution, offering deep visibility into model behaviour to support seamless and responsible AI deployments.</p>
<p>By tapping into the OpenAI Usage API, Elastic's integration delivers actionable insights through intuitive, pre-configured dashboards, enabling Site Reliability Engineers (SREs) and DevOps teams to monitor performance and optimize resource usage across OpenAI's diverse model portfolio. This unified observability approach empowers organizations to track critical metrics, identify inefficiencies, and maintain high-quality AI-driven experiences. The following key metrics from Elastic's OpenAI integration help organizations achieve effective oversight:</p>
<ol>
<li><strong>Request Latency</strong>: Measures the time taken for OpenAI models to process requests, ensuring responsive performance for real-time applications like chatbots or transcription services.</li>
<li><strong>Invocation Rates</strong>: Tracks the frequency of API calls across models, providing insights into usage patterns and helping identify high-demand workloads.</li>
<li><strong>Token Usage</strong>: Monitors input and output tokens (e.g., prompt, completion, cached tokens) to optimize costs and fine-tune prompts for efficient resource consumption.</li>
<li><strong>Error Counts</strong>: Captures failed requests or incomplete transactions, enabling proactive issue resolution to maintain application reliability.</li>
<li><strong>Image Generation Metrics</strong>: Tracks invocation rates and output dimensions for models like DALL·E, helping assess costs and usage trends in image-based applications.</li>
<li><strong>Audio Transcription Metrics</strong>: Monitors invocation rates and transcribed seconds for audio models like Whisper, supporting cost optimization in speech-to-text workflows.</li>
</ol>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltdc7aefa2a0288162/6a7f1bb7e88c65799d00bb24/openai-overview.png" alt="Openai Overview" /></p>
<p>To learn more about Elastic's OpenAI integration, read this <a href="https://www.elastic.co/observability-labs/blog/llm-observability-openai">blog</a>. </p>
<h4 id="actionablellmobservability">Actionable LLM Observability</h4>
<p>Elastic's LLM observability integrations empower users to take proactive control of their AI operations through actionable insights and real-time alerts. For instance, by setting a predefined threshold for token count, Elastic can trigger automated alerts when usage exceeds this limit, notifying Site Reliability Engineers (SREs) or DevOps teams via email, Slack, or other preferred channels. This ensures prompt awareness of potential cost overruns or resource-intensive queries, enabling teams to adjust model configurations or scale resources swiftly to maintain operational efficiency.</p>
<p>In the example below, the rule is set to alert the user if token_count crosses a threshold of 500.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltd2a687e02fd719b2/6a7f1bb977b034ede23ff921/slo-1.png" alt="SLO Overview" /></p>
<p>The alert is triggered when the token count exceeds the threshold as seen below
<img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltd811b02131cb8245/6a7f1bbdea068d84cdf0a2e7/slo-2.png" alt="SLO Overview1" /></p>
<p>Another example is tracking invocation spikes, such as when the number of predictions or API calls surpasses a defined Service Level Objective (SLO). For example, if a Bedrock AI-hosted model experiences a sudden surge in invocations due to increased customer interactions, Elastic can alert teams to investigate potential anomalies or scale infrastructure accordingly. These proactive measures help maintain the reliability and cost-effectiveness of LLM-powered applications.</p>
<p>By providing pre-configured dashboards and customizable alerts, Elastic ensures that organizations can respond to critical events in real time, keeping their AI systems aligned with cost and performance goals as well as standards for content safety and reliability.</p>
<h4 id="conclusion">Conclusion</h4>
<p>LLMs are transforming industries, but their complexity requires effective oversight observability to ensure their reliability and safe use. Elastic's LLM observability integrations provide a comprehensive solution, empowering businesses to monitor performance, manage resources, and address challenges like hallucinations and content safety. As LLMs become increasingly integral to various sectors, robust observability tools like those offered by Elastic ensure that these AI-driven innovations remain dependable, cost-effective, and aligned with ethical and safety standards.</p>]]></content:encoded>
    <link>https://www.elastic.co/observability-labs/blog/transforming-industries-and-the-critical-role-of-llm-observability</link>
    <guid isPermaLink="false">transforming-industries-and-the-critical-role-of-llm-observability</guid>
    <category><![CDATA[LLM Observability]]></category>
    <category><![CDATA[Infrastructure Monitoring]]></category>
    <dc:creator><![CDATA[Ishleen Kaur,Daniela Tzvetkova]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltb8d1632fb8fe0cd7/6a7f1bc0bd21987c6a7584cd/llmobs2.png" length="0" type="image/png"/>
    <pubDate>Thu, 08 May 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Observability for Amazon MQ with Elastic: Demystifying Messaging Flows with Real-Time Insights]]></title>
    <description><![CDATA[RabbitMQ, managed by Amazon MQ, enables asynchronous communication in distributed architectures but introduces operational risks such as retries, processing delays, and queue backlogs. Elastic’s Amazon MQ integration for RabbitMQ delivers deep observability into broker health, queue performance, message flow, and resource usage through Amazon CloudWatch metrics and logs. This blog outlines key operational risks associated with RabbitMQ and explains how Elastic observability helps maintain system reliability and optimize message delivery at scale.]]></description>
    <content:encoded><![CDATA[<h2 id="managingthehiddencomplexityofmessagedrivenarchitectures">Managing the Hidden Complexity of Message-Driven Architectures</h2>
<p>Amazon MQ is a managed message broker service for <a href="http://activemq.apache.org/">Apache ActiveMQ</a> Classic and <a href="https://www.rabbitmq.com/">RabbitMQ</a> that manages the setup, operation, and maintenance of message brokers. Messaging systems like RabbitMQ, managed by <a href="https://aws.amazon.com/amazon-mq/">Amazon MQ</a>, are pivotal in modern decoupled, event-driven applications. By serving as an intermediary between services, RabbitMQ facilitates asynchronous communication through message queuing, routing, and reliable delivery, making it an ideal fit for microservices, real-time pipelines, and event-driven architectures. However, this flexibility introduces operational challenges, such as retries, processing delays, consumer failures, and queue backlogs, which can gradually impact downstream performance and system reliability.</p>
<p>With Elastic’s <a href="https://www.elastic.co/docs/reference/integrations/aws_mq">Amazon MQ integration</a>, users gain deep visibility into message flow patterns, queue performance, and consumer health. This integration allows for the proactive detection of bottlenecks, helps optimize system behaviour, and ensures reliable message delivery at scale.</p>
<p>In this blog, we'll dive into the operational challenges of RabbitMQ in modern architectures, while also examining the common gaps and strategies for overcoming them.</p>
<h2 id="whyobservabilityforrabbitmqonamazonmqmatters">Why Observability for RabbitMQ on Amazon MQ Matters?</h2>
<p>RabbitMQ brokers are integral to distributed systems, handling tasks ranging from order processing to payment workflows and notification delivery. Any disruption can cascade into significant downstream issues. Observability into RabbitMQ helps answer critical operational questions like:​</p>
<ul>
<li>Is CPU and memory utilization increasing over time?</li>
<li>What are the trends in the message publish rate, message confirmation rate?</li>
<li>Are consumers failing to acknowledge messages?</li>
<li>Which queues are experiencing abnormal growth?</li>
<li>Are there an increasing number of messages being dead-lettered over time?</li>
</ul>
<h2 id="enhancedobservabilitywithamazonmqintegration">Enhanced Observability with Amazon MQ Integration</h2>
<p>Elastic provides a dedicated <a href="https://www.elastic.co/docs/reference/integrations/aws_mq">Amazon MQ integration</a> for RabbitMQ that utilizes Amazon CloudWatch metrics and logs to deliver comprehensive observability data. This integration enables the ingestion of metrics related to connections, nodes, queues, exchanges, and system logs.</p>
<p>By deploying <a href="https://www.elastic.co/elastic-agent">Elastic Agent</a> with this integration, the users can monitor:​</p>
<ul>
<li><strong>Queue performance and Dead-letter queue (DLQ) metrics</strong> include total message count (<code>MessageCount.max</code>), messages ready for delivery (<code>MessageReadyCount.max</code>), and unacknowledged messages (<code>MessageUnacknowledgedCount.max</code>). <code>MessageCount.max</code> metric tracks the total number of messages in a queue, including those that have been dead-lettered, and monitoring this over time can help identify trends in message accumulation, which may suggest issues leading to dead-lettering.</li>
<li><strong>Consumer behaviour</strong> through metrics like consumer count (<code>ConsumerCount.max</code>) and acknowledgement rate (<code>AckRate.max</code>), which help identify underperforming consumers or potential backlogs.</li>
<li><strong>Messaging throughput</strong> by tracking publish (<code>PublishRate.max</code>), confirm (<code>ConfirmRate.max</code>), and acknowledgement rates in real time. These are crucial for understanding application messaging patterns and flow.</li>
<li><strong>Broker and node-level health,</strong> including memory usage (<code>RabbitMQMemUsed.max</code>), CPU utilization (<code>SystemCpuUtilization.max</code>), disk availability (<code>RabbitMQDiskFree.min</code>), and file descriptor usage (<code>RabbitMQFdUsed.max</code>). These indicators are essential for diagnosing resource saturation and avoiding service disruption.</li>
</ul>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltc24f4a23bb4ba33e/6a85c765501a8507cdfbb290/amazonmq-rabbitmq-dashboard-overview.png" alt="" /></p>
<h2 id="integratingamazonmqmetricsintoelasticobservability">Integrating Amazon MQ Metrics into Elastic Observability</h2>
<p>Elastic's Amazon MQ integration facilitates the ingestion of CloudWatch metrics and logs into Elastic Observability, delivering near real-time insights into RabbitMQ. The prebuilt Amazon MQ dashboard visualizes this data, providing a centralized view of broker health, messaging activity, and resource usage, helping users quickly detect and resolve issues. Elastic's <a href="https://www.elastic.co/docs/solutions/observability/incident-management/alerting">alerting</a> for Observability enables proactive notifications based on custom conditions, while its <a href="https://www.elastic.co/docs/solutions/observability/incident-management/service-level-objectives-slos">SLO</a> capabilities allow users to define and track key performance targets, strengthening system reliability and service commitments. </p>
<p>Elastic brings together logs and metrics from Amazon MQ alongside data from a wide range of other services and applications, whether running in AWS, on-premises, or across multi-cloud environments, offering unified observability from a single platform.</p>
<h3 id="prerequisites">Prerequisites</h3>
<p>To follow along, ensure you have:</p>
<ul>
<li>An account on <a href="http://cloud.elastic.co/">Elastic Cloud</a> and a deployed stack in AWS (<a href="https://www.elastic.co/guide/en/elastic-stack/current/installing-elastic-stack.html">see instructions here</a>). Ensure you are using version 8.16.5 or higher. Alternatively, you can use <a href="https://www.elastic.co/cloud/serverless">Elastic Cloud Serverless</a>, a fully managed solution that eliminates infrastructure management, automatically scales based on usage, and lets you focus entirely on extracting value from your data.</li>
<li>An AWS account with permissions to pull the necessary data from AWS. <a href="https://docs.elastic.co/en/integrations/aws#aws-permissions">See details in our documentation</a>.</li>
</ul>
<h3 id="architecture">Architecture</h3>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltc7e5165fe435a3cd/6a85c7689bf994127a0a04bc/rabbitmq_lambda_messageflow.png" alt="" /></p>
<h2 id="tracingauditflowsfromrabbitmqtoawslambda">Tracing Audit Flows from RabbitMQ to AWS Lambda</h2>
<p>Consider a financial audit trail use case, where every user action, such as a funds transfer, is published to RabbitMQ. A Python-based AWS Lambda function consumes these messages, deduplicates them using the <strong>id</strong> field, and logs structured audit events for downstream analysis.</p>
<p>Sample payload sent through RabbitMQ:</p>
<pre><code>{
&amp;nbsp;&amp;nbsp;"id": "txn-849302",
&amp;nbsp;&amp;nbsp;"type": "audit",
&amp;nbsp;&amp;nbsp;"payload": {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;"user_id": "u-10245",
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;"event": "funds.transfer",
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;"amount": 1200.75,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;"currency": "USD",
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;"timestamp": "T14:20:15Z",
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;"ip": "192.168.0.8",
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;"location": "New York, USA"
&amp;nbsp;&amp;nbsp;}
}
</code></pre>
<p>You can now correlate message publishing activity from RabbitMQ with AWS Lambda invocation logs, track processing latency, and configure alerts for conditions like drops in consumer throughput or an unexpected surge in RabbitMQ queue depth.</p>
<h3 id="awslambdafunctionprocessingrabbitmqmessages">AWS Lambda Function: Processing RabbitMQ Messages</h3>
<p>This Python-based AWS Lambda function processes audit events received from RabbitMQ. It deduplicates messages based on the <strong>id</strong> field and logs structured event data for downstream analysis or compliance. Save the code below in a file named <strong>app.py</strong>.</p>
<pre><code>import json
import logging
import base64
# Configure logging
logger = logging.getLogger()
logger.setLevel(logging.INFO)
# In-memory set to track processed message IDs for deduplication
processed_ids = set()
def lambda_handler(event, context):
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;logger.info("Lambda triggered by RabbitMQ event")
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if 'rmqMessagesByQueue' not in event:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;logger.warning("Invalid event: missing 'rmqMessagesByQueue'")
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return {'statusCode': 400, 'body': 'Invalid RabbitMQ event'}
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;for queue_name, messages in event['rmqMessagesByQueue'].items():
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;logger.info(f"Processing queue: {queue_name}, Messages count: {len(messages)}")
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;for msg in messages:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;try:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;raw_data = msg['data']
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;decoded_json = base64.b64decode(raw_data).decode('utf-8')
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;message = json.loads(decoded_json)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;logger.info(f"Decoded message: {json.dumps(message)}")
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;message_id = message.get('id')
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if not message_id:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;logger.warning("Message missing 'id', skipping.")
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;continue
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if message_id in processed_ids:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;logger.warning(f"Duplicate message detected: {message_id}")
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;continue
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;payload = message.get('payload', {})
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;logger.info(f"Processing message ID: {message_id}")
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;logger.info(f"Event Type: {message.get('type')}")
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;logger.info(f"User ID: {payload.get('user_id')}")
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;logger.info(f"Event: {payload.get('event')}")
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;logger.info(f"Amount: {payload.get('amount')} {payload.get('currency')}")
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;logger.info(f"Timestamp: {payload.get('timestamp')}")
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;logger.info(f"IP Address: {payload.get('ip')}")
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;logger.info(f"Location: {payload.get('location')}")
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;processed_ids.add(message_id)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;except Exception as e:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;logger.error(f"Error processing message: {str(e)}")
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return {'statusCode': 200, 'body': 'Messages processed successfully'}
</code></pre>
<h3 id="settingupawssecretsmanager">Setting up AWS Secrets Manager</h3>
<p>To securely store and manage your RabbitMQ credentials, use AWS Secrets Manager.​</p>
<ol>
<li><strong>Create a New Secret:</strong></li>
</ol>
<ul>
<li>Navigate to the<a href="https://console.aws.amazon.com/secretsmanager/"> AWS Secrets Manager console</a>.</li>
<li>Choose <strong>Store a new secret</strong>.</li>
<li>Select <strong>Other type of secret</strong>.</li>
<li>Enter the following key-value pairs:<ul>
<li><code>username</code>: Your RabbitMQ username</li>
<li><code>password</code>: Your RabbitMQ password</li></ul></li>
</ul>
<ol>
<li><strong>Configure the Secret:</strong></li>
</ol>
<ul>
<li>Provide a meaningful name, such as <code>RabbitMQAccess</code>.</li>
<li>Optionally, add tags and set rotation if needed.​</li>
</ul>
<ol>
<li><strong>Store the Secret:</strong></li>
</ol>
<ul>
<li>Review the settings and store the secret. Note the ARN of the secret you have created.
 <img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt08666b47e1f94017/6a85c76b2d64d51bca081caa/aws-secret-manager-configuration.png" alt="" /></li>
</ul>
<h3 id="settingupamazonmqforrabbitmq">Setting up Amazon MQ for RabbitMQ</h3>
<p>To get started with RabbitMQ on Amazon MQ, follow these steps to set up your broker.</p>
<ul>
<li><p>Open the <a href="https://console.aws.amazon.com/amazonmq/">Amazon MQ console</a>.</p></li>
<li><p>Create a new broker with the <strong>RabbitMQ</strong> engine.</p></li>
<li><p>Choose your preferred deployment option—<strong>single-instance</strong> or <strong>clustered</strong></p></li>
<li><p>Use the same <strong>username</strong> and <strong>password</strong> that you previously stored in <strong>AWS Secrets Manager</strong>.</p></li>
<li><p>Under <strong>Additional settings</strong>, enable <strong>CloudWatch Logs</strong> for observability.
<img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt167e5c95f8e3f76e/6a85c76eeaf245dce0a49ec8/amazonmq-cloudwatch-enable.png" alt="" /></p></li>
<li><p>Configure access and security settings, ensuring that the broker is accessible to your AWS Lambda function.</p></li>
<li><p>After the broker is created, note the following important details:</p></li>
<li><p>ARN of the RabbitMQ broker.</p></li>
<li><p>RabbitMQ web console URL.
<img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltb73efc6fdb659c87/6a85c77043c0b728ac2f057d/amazonmq-rabbitmq-configuration-summary.png" alt="" /></p></li>
<li><p>You’ll need the RabbitMQ log group ARN to set up Elastic’s Amazon MQ integration for RabbitMQ. Follow these steps to locate it:</p></li>
<li><p>Go to the <strong>General – Enabled Logs</strong> section of the broker. </p></li>
<li><p>Copy the <strong>CloudWatch log group ARN</strong>.
<img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt02016bdb2becda09/6a85c77333f244cd0b49f454/amazonmq-rabbitmq-loggroup-arn.png" alt="" /></p></li>
</ul>
<h3 id="createarabbitmqqueue">Create a RabbitMQ Queue</h3>
<p>Now that the RabbitMQ broker is configured, use the management console to create a queue where messages will be published.</p>
<ul>
<li>Access the RabbitMQ management console using the web console URL.</li>
<li>Create a new queue (example: <strong>myQueue</strong>) to receive messages.
<img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltbecef240548d449b/6a85c77511893c2658a7aae8/rabbitmq-create-queue.png" alt="" /></li>
</ul>
<h3 id="buildanddeploytheawslambdafunction">Build and deploy the AWS Lambda function</h3>
<p>In this section, we'll set up the Lambda function using AWS SAM, add the message processing logic, and deploy it to AWS. This Lambda function will be responsible for consuming messages from RabbitMQ and logging audit events.</p>
<p>Before continuing, make sure you have completed the following prerequisites.</p>
<ul>
<li><p><a href="https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/prerequisites.html">AWS SAM prerequisites</a></p></li>
<li><p><a href="https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/install-sam-cli.html">Install the AWS SAM CLI</a></p></li>
</ul>
<p>Next, follow the steps outlined below to continue with the setup.</p>
<ol>
<li>In your command line, run the command <code>sam init</code> from a directory of your choice.</li>
<li>The AWS SAM CLI will walk you through the setup.<ul>
<li>Select <strong>AWS Quick Start Templates</strong>.</li>
<li>Choose the <strong>Hello World Example</strong> </li>
<li>Use the <strong>Python</strong> runtime and <strong>zip</strong> package type.</li>
<li>Proceed with the default options.</li>
<li>Name your application as <strong>sample-rabbitmq-app</strong>.</li>
<li>The AWS SAM CLI downloads your starting template and creates the application project directory structure.</li></ul></li>
<li>From your command line, move to the newly created sample-rabbitmq-app directory.<ul>
<li>Replace the content of the <strong>hello_world/app.py</strong> file with the lambda function code for rabbitmq message processing.</li>
<li>In the <strong>template.yaml</strong> file, use the values mentioned below to update the file content.
<code>yaml
Resources:
&amp;nbsp;SampleRabbitMQApp:
&amp;nbsp;&amp;nbsp;&amp;nbsp;Type: AWS::Serverless::Function
&amp;nbsp;&amp;nbsp;&amp;nbsp;Properties:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;CodeUri: hello_world/
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Description: A starter AWS Lambda function.
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;MemorySize: 128
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Timeout: 3
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Handler: app.lambda_handler
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Runtime: python3.10
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;PackageType: Zip
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Policies:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;- Statement:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;- Effect: Allow
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Resource: '*'
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Action:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;- mq:DescribeBroker
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;- secretsmanager:GetSecretValue
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;- ec2:CreateNetworkInterface
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;- ec2:DescribeNetworkInterfaces
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;- ec2:DescribeVpcs
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;- ec2:DeleteNetworkInterface
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;- ec2:DescribeSubnets
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;- ec2:DescribeSecurityGroups
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Events:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;MQEvent:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Type: MQ
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Properties:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Broker: &lt;ARN of the Broker&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Queues:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;- myQueue
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;SourceAccessConfigurations:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;- Type: BASIC_AUTH
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;URI: &lt;ARN of the secret&gt;
</code></li></ul></li>
<li>Run the command <code>sam deploy --guided</code> and wait for the confirmation message. This deploys all of the resources.</li>
</ol>
<h3 id="sendingauditeventstorabbitmqandtriggeringlambda">Sending Audit Events to RabbitMQ and Triggering Lambda</h3>
<p>To test the end-to-end setup, simulate the flow by publishing audit event data into RabbitMQ using its web UI. Once the message is sent, it triggers the Lambda function. </p>
<ol>
<li>Navigate to the <a href="https://console.aws.amazon.com/amazon-mq/home">Amazon MQ console</a> and select your newly created broker.</li>
<li>Locate and open the Rabbit web console URL<br />
<img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt8eab69977143aaac/6a85c77811893c0963a7aaec/amazonmq-rabbitmq-webconsole-details.png" alt="" /></li>
<li>Under the <strong>Queues and Streams</strong> tab, select the target queue (example: <strong>myQueue</strong>).</li>
<li>Enter the message payload, and click <strong>Publish message</strong> to send it to the queue.<br />
Here’s a sample payload published via RabbitMQ:</li>
</ol>
<pre><code>   {
     "id": "txn-849302",
     "type": "audit",
     "payload": {
       "user_id": "u-10245",
       "event": "funds.transfer",
       "amount": 1200.75,
       "currency": "USD",
       "timestamp": "T14:20:15Z",
       "ip": "192.168.0.8",
       "location": "New York, USA"
     }
   }
</code></pre>
<ol>
<li>Navigate to the AWS Lambda function created earlier.</li>
<li>Under the <strong>Monitor</strong> tab, click <strong>View CloudWatch logs</strong>.</li>
<li>Check the latest log stream to confirm that the Lambda was triggered by Amazon MQ and that the message was processed successfully.
<img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt6745896b03a43a58/6a85c77b80984c3844668f47/amazonmq-lambda-logstream.png" alt="" /></li>
</ol>
<h2 id="configuringamazonmqintegrationformetricsandlogscollection">Configuring Amazon MQ integration for Metrics and Logs collection</h2>
<p>Elastic’s <a href="https://www.elastic.co/docs/reference/integrations/aws_mq">Amazon MQ integration</a> simplifies the collection of logs and metrics from RabbitMQ brokers managed by Amazon MQ. Logs are ingested via <strong>Amazon CloudWatch Logs</strong>, while metrics are fetched from the specified AWS region at a defined interval.</p>
<p>Elastic provides a default configuration for metrics collection. You can accept these defaults or adjust settings such as the <strong>Collection Period</strong> to better fit your needs.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt84b7cd1485929dc9/6a85c77dd7b2e743e7fe843e/amazonmq-metrics-configuration.png" alt="" /></p>
<p>To enable the collection of logs:</p>
<ol>
<li>Navigate to the <a href="https://console.aws.amazon.com/amazon-mq/home">Amazon MQ console</a> and select the newly created broker.</li>
<li>Click the <strong>Logs</strong> hyperlink under the <strong>General – Enabled Logs</strong> section to open the detailed log settings page.</li>
<li>From this page, copy the <strong>CloudWatch log group ARN</strong>.
<img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt02016bdb2becda09/6a85c77333f244cd0b49f454/amazonmq-rabbitmq-loggroup-arn.png" alt="" /></li>
<li>In <strong>Elastic</strong>, set up the <strong>Amazon MQ integration</strong> and paste the CloudWatch log group ARN.
<img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt6f9312d1eeac7165/6a85c78199083f572540f947/amazonmq-logs-configuration.png" alt="" /></li>
<li><strong>Accept Defaults or Customize Settings</strong> – Elastic provides a <strong>default configuration</strong> for logs collection. You can accept these defaults or adjust settings such as <strong>collection intervals</strong> to better fit your needs.</li>
</ol>
<h3 id="visualizingrabbitmqworkloadswiththeprebuiltamazonmqdashboard">Visualizing RabbitMQ Workloads with the Pre-Built Amazon MQ Dashboard</h3>
<p>You can access the RabbitMQ dashboard by:</p>
<ol>
<li><p>Navigate to the Dashboard Menu – Select the Dashboard menu option in Elastic and search for <strong>[Amazon MQ] RabbitMQ Overview</strong> to open the dashboard.</p></li>
<li><p>Navigate to the Integrations Menu – Open the <strong>Integrations</strong> menu in Elastic, select <strong>Amazon MQ</strong>, go to the <strong>Assets</strong> tab, and choose <strong>[Amazon MQ] RabbitMQ Overview</strong> from the dashboard assets</p></li>
</ol>
<p>The Amazon MQ RabbitMQ dashboard in the Elastic integration delivers a comprehensive overview of broker health and messaging activity. It provides real-time insights into broker resource utilization, queue and topic performance, connection trends, and messaging throughput. The dashboard helps users track system behaviour, detect performance bottlenecks, and ensure reliable message delivery across distributed applications.</p>
<h4 id="brokermetrics">Broker Metrics</h4>
<p>This section provides a centralised view of the overall health and performance of the RabbitMQ broker on Amazon MQ. The visualizations highlights the number of configured exchanges and queues, active broker connections, producers, consumers, and total messages in flight. System-level metrics such as CPU utilization, memory consumption, and free disk space help assess whether the broker has sufficient resources to handle current workloads.</p>
<p>Message flow metrics such as publish rate, confirmation rate, and acknowledgement rate are displayed to provide visibility into how messages are processed through the broker. Monitoring trends in these values helps detect message delivery issues, throughput degradation, or potential saturation of the broker under load.</p>
<h4 id="nodemetrics">Node Metrics</h4>
<p>Node-level visibility helps identify resource imbalances across nodes in clustered RabbitMQ setups. This section includes per-node CPU usage, memory consumption, and available disk space, offering insight into the underlying infrastructure's ability to support broker operations.</p>
<h4 id="queuemetrics">Queue Metrics</h4>
<p>Queue-specific insights are critical for understanding message delivery patterns and backlog conditions. This section details total messages, ready messages, and unacknowledged messages, segmented by broker, virtual host, and queue.</p>
<p>By observing how these counts change over time, users can identify slow consumers, message build-ups, or delivery issues that may affect application performance or lead to dropped messages under pressure.</p>
<h4 id="logs">Logs</h4>
<p>This section displays log level, process ID, and raw message content. These logs provide immediate visibility into events such as connection failures, resource thresholds being hit, or unexpected queue behaviors.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt08bdcf335184796a/6a85c784331d7a8019c316d0/amazonmq-rabbitmq-dashboard.png" alt="" /></p>
<h3 id="detectingqueuebacklogswithalertingrules">Detecting Queue Backlogs with Alerting Rules</h3>
<p>Elastic’s <a href="https://www.elastic.co/docs/solutions/observability/incident-management/alerting">alert</a> framework allows you to define rules that monitor critical RabbitMQ metrics and automatically trigger actions when specific thresholds are breached. </p>
<h4 id="alertqueuebacklogmessagereadyorunacknowledgedmessages">Alert: Queue Backlog (Message Ready or Unacknowledged Messages)</h4>
<p>This alert helps detect queue backlog in Amazon MQ by evaluating two metrics </p>
<ul>
<li><code>MessageUnacknowledgedCount.max</code> and </li>
<li><code>MessageReadyCount.max</code>. </li>
</ul>
<p>The alert is triggered if either condition persists for more than <strong>10 minutes</strong>:</p>
<ul>
<li><code>MessageUnacknowledgedCount.max</code> exceeds <strong>5,000</strong></li>
<li><code>MessageReadyCount.max</code> exceeds <strong>7,000</strong></li>
</ul>
<p>These thresholds should be adjusted based on typical message volume and consumer throughput. Sustained high values can indicate that consumers are not keeping up or message delivery pipelines are congested, potentially causing delays or dropped messages. Sustained high values may result in processing delays or dropped messages if not addressed.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt50e0eb4b370c9c66/6a85c78799083f279d40f94b/amazonmq-alert-configuration.png" alt="" /></p>
<h3 id="trackingresourceutilizationtomaintainrabbitmqperformance">Tracking Resource Utilization to Maintain RabbitMQ Performance</h3>
<p>Elastic’s <a href="https://www.elastic.co/docs/solutions/observability/incident-management/service-level-objectives-slos">Service-level objectives (SLOs)</a> capabilities allow you to define and monitor performance targets using key indicators like latency, availability, and error rates. Once configured, Elastic continuously evaluates these SLOs in real time, offering intuitive dashboards, alerts for threshold violations, and insights into error budget consumption. This enables teams to stay ahead of issues, ensuring service reliability and consistent performance.</p>
<h4 id="slonoderesourcehealthcpumemorydisk">SLO: Node Resource Health (CPU, Memory, Disk)</h4>
<p>This SLO focuses on ensuring RabbitMQ brokers and nodes have sufficient resources to process messages without performance degradation. It tracks CPU, memory, and disk usage across RabbitMQ brokers and nodes to prevent resource exhaustion that could lead to service interruptions.</p>
<p><strong>Target thresholds:</strong></p>
<ul>
<li><code>SystemCpuUtilization.max</code> remains below <strong>85%</strong> for <strong>99%</strong> of the time.</li>
<li><code>RabbitMQMemUsed.max</code> remains below <strong>80%</strong> of <code>RabbitMQMemLimit.max</code> for <strong>99%</strong> of the time.</li>
<li><code>RabbitMQDiskFree.min</code> remains above <strong>25%</strong> of <code>RabbitMQDiskFreeLimit.max</code> for <strong>99%</strong> of the time.</li>
</ul>
<p>Sustained high values in CPU or memory usage can signal resource contention, which may result in slower message processing or downtime. Low disk availability may cause the broker to stop accepting messages, risking message loss. These thresholds are designed to catch early signs of resource saturation and ensure smooth, uninterrupted message flow across RabbitMQ deployments.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt5b36bd4f9f3fc9bb/6a85c78a18249ca4e818f70d/amazonmq-slo-configuration.png" alt="" /></p>
<h2 id="conclusion">Conclusion</h2>
<p>As RabbitMQ-based messaging architectures scale and become more complex, the need for in-depth visibility into system performance and potential issues deepens. Elastic’s <a href="https://www.elastic.co/docs/reference/integrations/aws_mq">Amazon MQ integration</a> brings that visibility front and center—helping you go beyond basic health checks to understand real-time messaging throughput, queue backlog trends, and resource saturation across your brokers and consumers.</p>
<p>By leveraging the prebuilt dashboards, configuring alerts and SLOs, you can proactively detect anomalies, fine-tune consumer performance, and ensure reliable delivery across your event-driven applications.</p>]]></content:encoded>
    <link>https://www.elastic.co/observability-labs/blog/amazonmq-observability-rabbitmq-integration</link>
    <guid isPermaLink="false">amazonmq-observability-rabbitmq-integration</guid>
    <category><![CDATA[Infrastructure Monitoring]]></category>
    <category><![CDATA[Metrics]]></category>
    <dc:creator><![CDATA[Agi K Thomas,Udayasimha Theepireddy]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt9a221b8b9fa36929/6a85c78d43c0b790d62f058e/AmazonMQ-observability-RabbitMQ.jpg" length="0" type="image/jpeg"/>
    <pubDate>Fri, 02 May 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Monitor your C++ Applications with Elastic APM]]></title>
    <description><![CDATA[In this article we will be using the Opentelemetry CPP client to monitor C++ application within Elastic APM]]></description>
    <content:encoded><![CDATA[<p>One of the main challenges that developers, SREs, and DevOps professionals face is the absence of an extensive tool that provides them with visibility to their application stack. Many of the APM solutions out on the market do provide methods to monitor applications that were built on languages and frameworks (i.e., .NET, Java, Python, etc.) but fall short when it comes to C++ applications.</p>
<p>Luckily, Elastic has been one of the leading solutions in observability space and a contributor to the OpenTelemetry project. Elastic’s unique position and its extensive observability capabilities allows end-users to monitor applications built with object-oriented programming languages &amp; Framework in a variety of ways.</p>
<p>In this blog we will explore using Elastic APM to investigate C++ traces with the OpenTelemetry client. We will be providing a comprehensive guide on how to implement the OpenTelemetry client for C++ applications and connecting to Elastic APM solutions. While OTel has its libraries, and this blog reviews how to use the OTel CPP library, Elastic also has its own Elastic Distributions of OpenTelemetry, which were developed to provide commercial support, and are completely upstreamed regularly.</p>
<p>Here are some resources to help get you started:</p>
<ul>
<li><p><a href="https://www.elastic.co/guide/en/observability/current/apm-open-telemetry.html">Use OpenTelemetry with APM</a></p></li>
<li><p><a href="https://github.com/open-telemetry/opentelemetry-cpp">The OpenTelemetry C++ Client</a></p></li>
<li><p><a href="https://opentelemetry.io/docs/languages/cpp/">OpenTelemetry C++ Docs</a></p></li>
</ul>
<h2 id="stepbystepguide">Step by Step Guide</h2>
<h2 id="prerequisites">Prerequisites</h2>
<ul>
<li>### Environment</li>
</ul>
<p>Choosing an environment is quite important as there is limited support for the OTEL client. We have experimented with using multiple Operating Systems and here are the suggestions:</p>
<ul>
<li><p>Ubuntu 22.04</p></li>
<li><p>Debian 11 Bullseye</p></li>
<li><p>For this guide we are focusing on Ubuntu 22.04.</p></li>
<li><p>Machine: 2 vCPU, 4GB is sufficient.</p></li>
<li><p>Image: Ubuntu 22.04 LTS (x86_64).</p></li>
<li><p>Disk: ~30 GB is enough.</p></li>
</ul>
<h2 id="implementationmethodnbsp">Implementation method </h2>
<p>We have experimented with multiple methods but we found that the most suitable approach is to use a package manager. After extensive testing, It appears that trying to run otel-cpp client could be quite challenging to the users. If practitioners desire to build with tools such as CMake and Bazel that is a viable solution. With that, as we tested both methods it became obvious that we were spending most of our time and effort fixing compatibility and dependencies’ issues for the OS Vs. Focusing on sending data to our APM. Hence we decided to move to a different method.</p>
<p>The main issues that we kept running into as we test are:</p>
<ul>
<li><p>Compatibility of packages.</p></li>
<li><p>Availability of packages.</p></li>
<li><p>Dependencies of libraries and packages.</p></li>
</ul>
<p>In this guide we will use vcpkg since it allows us to bring in all the dependencies required to run the Opentelemetry C++ client.</p>
<h2 id="installingrequiredostools">Installing required OS tools</h2>
<h3 id="updatepackagelists">Update package lists</h3>
<pre><code>    sudo apt-get update
</code></pre>
<p>Install build essentials, cmake, git, and sqlite dev library</p>
<pre><code>    sudo apt-get install -y build-essential cmake git curl zip unzip sqlite3 libsqlite3-dev
</code></pre>
<p>sqlite3 and libsqlite3-dev allow us to build/run SQLite queries in our C++ code.</p>
<h3 id="setupvcpkg">Set Up vcpkg</h3>
<p>vcpkg is the C++ package manager that we’ll use to install opentelemetry-cpp client.</p>
<pre><code>    # Clone vcpkg
    cd ~
    git clone https://github.com/microsoft/vcpkg.git
</code></pre>
<pre><code>    # Bootstrap
    cd ~/vcpkg
    ./bootstrap-vcpkg.sh
</code></pre>
<h3 id="installopentelemetrycwithotlpgrpc">Install OpenTelemetry C++ with OTLP gRPC</h3>
<p>In this guide we focus on trace export to Elastic. At time of writing, vcpkg’s opentelemetry-cpp</p>
<p>version 1.18.0 fully supports traces but has limited direct metrics exporting.</p>
<h3 id="installthepackage">Install the package</h3>
<pre><code>    cd ~/vcpkg
    ./vcpkg install opentelemetry-cpp[otlp-grpc]:x64-linux
</code></pre>
<p><strong>Note</strong></p>
<p>Sometimes when installing opentelemetry-cpp on linux it doesn't install all the required packages. As a workaround if you run into that case, try running again but pass a flag to allow-unsupported:</p>
<pre><code>    ./vcpkg install opentelemetry-cpp[*]:x64-linux --allow-unsupported
</code></pre>
<h3 id="verify">Verify</h3>
<pre><code>    ./vcpkg list | grep opentelemetry-cpp
</code></pre>
<p>The output thould be something like this: </p>
<pre><code>opentelemetry-cpp:x64-linux 1.18.0
</code></pre>
<h2 id="createthecprojectwithdatabasespans">Create the C++ Project with Database Spans</h2>
<p>We’ll build a sample in ~/otel-app that:</p>
<ul>
<li><p>Uses SQLite to do basic CREATE/INSERT/SELECT queries. This is helpful to showcase capturing transactions for apps that use databases on Elastic APM.</p></li>
<li><p>Generate random traces to showcase how they are captured on Elastic APM.</p></li>
</ul>
<p>This app is going to generate random queries where some will contain database transactions and some are just application traces. Each query is contained in a child span, so they appear in APM as separate database transactions.</p>
<pre><code># Below is the structure of our project
</code></pre>
<pre><code>    otel-app/
    ├── main.cpp
    └── CMakeLists.txt
</code></pre>
<h3 id="createappproject">Create App Project</h3>
<pre><code>    cd ~
    mkdir otel-app
    cd otel-app
</code></pre>
<p>Inside this project we will create two files</p>
<ul>
<li><p>main.cpp</p></li>
<li><p>CMakeLists.txt</p></li>
</ul>
<p>Keep in mind that main.cpp is where you are going to pass the otel exporters that are going to send data to the Elastic cluster. So for your tech stack it would be your application's source code.</p>
<h4 id="sampleapplicationcode">Sample application code</h4>
<pre><code>    main.cpp
    // Below we declare required libraries that we will be using to ship
    // traces to Elastic APM
    #include &lt;opentelemetry/exporters/otlp/otlp_grpc_exporter.h&gt;
    #include &lt;opentelemetry/sdk/trace/tracer_provider.h&gt;
    #include &lt;opentelemetry/sdk/trace/simple_processor.h&gt;
    #include &lt;opentelemetry/trace/provider.h&gt;

    #include &lt;sqlite3.h&gt;
    #include &lt;chrono&gt;
    #include &lt;iostream&gt;
    #include &lt;thread&gt;
    #include &lt;cstdlib&gt;&amp;nbsp; // for rand(), srand()
    #include &lt;ctime&gt;&amp;nbsp; &amp;nbsp; // for time()

    // Namespace aliases
    namespace trace_api = opentelemetry::trace;
    namespace sdktrace&amp;nbsp; = opentelemetry::sdk::trace;
    namespace otlp&amp;nbsp; &amp;nbsp; &amp;nbsp; = opentelemetry::exporter::otlp;

    // Below we are using a helper function to run SQLITE statement inside&amp;nbsp;
    // child span
    bool ExecuteSql(sqlite3 *db, const std::string &amp;sql,
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;trace_api::Tracer &amp;tracer,
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;const std::string &amp;span_name)
    {
    &amp;nbsp;&amp;nbsp;// Starting the child span
    &amp;nbsp;&amp;nbsp;auto db_span = tracer.StartSpan(span_name);
    &amp;nbsp;&amp;nbsp;{
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;auto scope = tracer.WithActiveSpan(db_span);

    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;// Here we mark Database attributes for clarity in APM
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;db_span-&gt;SetAttribute("db.system", "sqlite");
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;db_span-&gt;SetAttribute("db.statement", sql);

    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;char *errMsg = nullptr;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;int rc = sqlite3_exec(db, sql.c_str(), nullptr, nullptr, &amp;errMsg);
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if (rc != SQLITE_OK)
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;db_span-&gt;AddEvent("SQLite error: " + std::string(errMsg ? errMsg : "unknown"));
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;sqlite3_free(errMsg);
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;db_span-&gt;End();
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return false;
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;db_span-&gt;AddEvent("Query OK");
    &amp;nbsp;&amp;nbsp;}
    &amp;nbsp;&amp;nbsp;db_span-&gt;End();
    &amp;nbsp;&amp;nbsp;return true;
    }

    /**
    &amp;nbsp;* DoNonDbWork - Simulate some other operation
    &amp;nbsp;*/
    void DoNonDbWork(trace_api::Tracer &amp;tracer, const std::string &amp;span_name)
    {
    &amp;nbsp;&amp;nbsp;auto child_span = tracer.StartSpan(span_name);
    &amp;nbsp;&amp;nbsp;{
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;auto scope = tracer.WithActiveSpan(child_span);
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;// Just sleep or do some "fake" work
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;std::cout &lt;&lt; "[TRACE] Doing non-DB work for " &lt;&lt; span_name &lt;&lt; "...\n";
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;std::this_thread::sleep_for(std::chrono::milliseconds(200 + rand() % 300));
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;child_span-&gt;AddEvent("Finished non-DB work");
    &amp;nbsp;&amp;nbsp;}
    &amp;nbsp;&amp;nbsp;child_span-&gt;End();
    }

    int main()
    {
    &amp;nbsp;&amp;nbsp;// Seed random generator for example
    &amp;nbsp;&amp;nbsp;srand(static_cast&lt;unsigned&gt;(time(nullptr)));

    &amp;nbsp;&amp;nbsp;// 1) Create OTLP exporter for traces
    &amp;nbsp;&amp;nbsp;otlp::OtlpGrpcExporterOptions opts;
    &amp;nbsp;&amp;nbsp;auto exporter = std::make_unique&lt;otlp::OtlpGrpcExporter&gt;(opts);

    &amp;nbsp;&amp;nbsp;// 2) Simple Span Processor
    &amp;nbsp;&amp;nbsp;auto processor = std::make_unique&lt;sdktrace::SimpleSpanProcessor&gt;(std::move(exporter));

    &amp;nbsp;&amp;nbsp;// 3) Tracer Provider
    &amp;nbsp;&amp;nbsp;auto sdk_tracer_provider = std::make_shared&lt;sdktrace::TracerProvider&gt;(std::move(processor));
    &amp;nbsp;&amp;nbsp;auto tracer = sdk_tracer_provider-&gt;GetTracer("my-cpp-multi-app");

    &amp;nbsp;&amp;nbsp;// Prepare an in-memory SQLite DB (for random DB usage)
    &amp;nbsp;&amp;nbsp;sqlite3 *db = nullptr;
    &amp;nbsp;&amp;nbsp;int rc = sqlite3_open(":memory:", &amp;db);
    &amp;nbsp;&amp;nbsp;if (rc == SQLITE_OK)
    &amp;nbsp;&amp;nbsp;{
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;// Create a table so we can do inserts/reads
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;ExecuteSql(db, "CREATE TABLE IF NOT EXISTS items (id INTEGER PRIMARY KEY, info TEXT);",
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;*tracer.get(), "db_create_table");
    &amp;nbsp;&amp;nbsp;}

    &amp;nbsp;&amp;nbsp;// Create the following loop to generate multiple transactions
    &amp;nbsp;&amp;nbsp;int num_transactions = 5;&amp;nbsp; // Change this variable to the desired number of transaction
    &amp;nbsp;&amp;nbsp;for (int i = 1; i &lt;= num_transactions; i++)
    &amp;nbsp;&amp;nbsp;{
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;// Each iteration is a top-level transaction
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;std::string transaction_name = "transaction_" + std::to_string(i);
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;auto parent_span = tracer-&gt;StartSpan(transaction_name);
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;auto scope = tracer-&gt;WithActiveSpan(parent_span);

    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;std::cout &lt;&lt; "\n=== Starting " &lt;&lt; transaction_name &lt;&lt; " ===\n";

    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;// Randomly select whether a transaction will interact with the database or not.
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;bool doDb = (rand() % 2 == 0); // 50% chance

    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if (doDb &amp;&amp; db)
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;// Insert random data
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;std::string insert_sql = "INSERT INTO items (info) VALUES ('Item " + std::to_string(i) + "');";
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;ExecuteSql(db, insert_sql, *tracer.get(), "db_insert_item");

    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;// Select from DB
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;ExecuteSql(db, "SELECT * FROM items;", *tracer.get(), "db_select_items");
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;else
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;// Do some random non-DB tasks
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;DoNonDbWork(*tracer.get(), "non_db_task_1");
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;DoNonDbWork(*tracer.get(), "non_db_task_2");
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}

    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;// Sleep a little to simulate transaction time
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;std::this_thread::sleep_for(std::chrono::milliseconds(200));
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;parent_span-&gt;End();
    &amp;nbsp;&amp;nbsp;}

    &amp;nbsp;&amp;nbsp;// Close DB
    &amp;nbsp;&amp;nbsp;sqlite3_close(db);

    &amp;nbsp;&amp;nbsp;// Extra sleep to ensure final flush
    &amp;nbsp;&amp;nbsp;std::cout &lt;&lt; "\n[INFO] Sleeping 5 seconds to allow flush...\n";
    &amp;nbsp;&amp;nbsp;std::this_thread::sleep_for(std::chrono::seconds(5));
    &amp;nbsp;&amp;nbsp;std::cout &lt;&lt; "[INFO] Exiting.\n";
    &amp;nbsp;&amp;nbsp;return 0;
    }
</code></pre>
<h5 id="whatdoesthecodedo">What does the code do?</h5>
<p>We create 5 top-level “transaction_i” spans.</p>
<p>For each transaction, we randomly choose to do DB or non-DB work</p>
<pre><code>- If DB: Insert a row, then select. Each is a child span.

- If non-DB: We do two “fake tasks” (child spans).
</code></pre>
<p>Once we finish, we close the database connection and wait 5 seconds for data flush.</p>
<h4 id="sampleinstructionfile">Sample instruction file</h4>
<p>CMakeLists.txt : This file contains instructions describing the source files and targets.</p>
<pre><code>    cmake_minimum_required(VERSION 3.10)
    project(OtelApp VERSION 1.0)

    set(CMAKE_CXX_STANDARD 11)
    set(CMAKE_CXX_STANDARD_REQUIRED ON)

    # Here we are pointing to use the vcpkg toolchain
    set(CMAKE_TOOLCHAIN_FILE "PATH-TO/vcpkg.cmake" CACHE STRING "Vcpkg toolchain file")

    find_package(opentelemetry-cpp CONFIG REQUIRED)

    add_executable(otel_app main.cpp)

    # Below we are linking the OTLP gRPC exporter, trace library, and sqlite3
    target_link_libraries(otel_app PRIVATE
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;opentelemetry-cpp::otlp_grpc_exporter
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;opentelemetry-cpp::trace
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;sqlite3
    )
</code></pre>
<h4 id="declareenvironmentalvariables">Declare Environmental Variables</h4>
<p>Here we are going to export our Elastic Cloud endpoints as environmental variables</p>
<p>You can get that information by doing the following:</p>
<ol>
<li><p>Login into your elastic cloud</p></li>
<li><p>Go into your deployment</p></li>
<li><p>On the Left hand side, click on the hamburger menu and scroll down to “Integrations”</p></li>
<li><p>Go on the search bar inside the integration and type “APM”</p></li>
</ol>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt45627cb3f8bd9eec/6a7f19071967ea7d50330b4a/APM-Search.png" alt="" /></p>
<ol>
<li><p>Click on the APM integration</p></li>
<li><p>Scroll down and click on the OpenTelemetry Option on the far left side</p></li>
</ol>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt5eb12d359510901a/6a7f190afc63ab721064d05a/highlighted.png" alt="" /></p>
<ol>
<li>You should be able to see values similar to the screenshot below. Once you copy the values to export, click on launch APM.</li>
</ol>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt991641419ccdcfb4/6a7f190d3cab1c7d650e4c51/highlighted2.png" alt="" /></p>
<p>As you copy the required values, go ahead and export them.</p>
<pre><code>    export OTEL_EXPORTER_OTLP_ENDPOINT="APM-ENDPOINT"
    export OTEL_EXPORTER_OTLP_HEADERS="KEY"
    export OTEL_RESOURCE_ATTRIBUTES="service.name=my-app,service.version=1.0.0,deployment.environment=dev"
</code></pre>
<p>Note that the elastic OTEL_EXPORTER_OTLP_HEADERS value usually starts with “Authorization=Bearer” make sure that you convert the upper case “A” in authorization to a lower case “a”. This is due to the fact that the otel header exporter expects a lower case “a” for authorization.</p>
<h3 id="buildandrun">Build and Run</h3>
<p>Once we create the two files we then move to building the application.</p>
<pre><code>cd ~/otel-app
mkdir -p build
cd build

cmake -DCMAKE_TOOLCHAIN_FILE=~/vcpkg/scripts/buildsystems/vcpkg.cmake \
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-DCMAKE_PREFIX_PATH=~/vcpkg/installed/x64-linux/share \
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;..
make
</code></pre>
<p>Once make is successful run the the application</p>
<pre><code>./otel-app
</code></pre>
<p>You should be able to see the script execute with a similar console output</p>
<pre><code>    Console outcome:
    === Starting transaction_1 ===
    [TRACE] Doing non-DB work for non_db_task_1...
    [TRACE] Doing non-DB work for non_db_task_2...

    === Starting transaction_2 ===
    [TRACE] Doing DB work for doDb_task_1...
    [TRACE] Doing DB work for doDb_task_2...

    === Starting transaction_3 ===
    [TRACE] Doing non-DB work for non_db_task_1...
    [TRACE] Doing non-DB work for non_db_task_2...

    === Starting transaction_4 ===
    [TRACE] Doing non-DB work for non_db_task_1...
    [TRACE] Doing non-DB work for non_db_task_2...

    === Starting transaction_5 ===
    [TRACE] Doing non-DB work for non_db_task_1...
    [TRACE] Doing non-DB work for non_db_task_2...

    [INFO] Sleeping 5 seconds to allow flush...
    [INFO] Exiting.
</code></pre>
<p>Once the script executes you should be able to observe those traces on Elastic APM similar to the screenshots below.</p>
<h3 id="observeinelasticapm">Observe in Elastic APM</h3>
<p>Go to Elastic Cloud, open your deployment, and navigate to Observability &gt; APM.</p>
<p>Look for the app name in the service list (as defined by OTEL_RESOURCE_ATTRIBUTES).</p>
<p>Inside that service’s Traces tab, you’ll find multiple transactions like “transaction_1”,</p>
<p>“transaction_2”, etc.</p>
<p>Expanding each transaction shows child spans:</p>
<pre><code>- Possibly db_insert_item and db_select_items if random DB path was taken.

- Otherwise, non_db_task_1 and non_db_task_2.
</code></pre>
<p>You can see how some transactions do DB calls, some do not, each with different spans.</p>
<p>This variety demonstrates how your real application might produce multiple different</p>
<p>“routes” or “operations.”</p>
<h4 id="servicemap">Service Map</h4>
<p>If everything runs correctly, you should be able to view your services and see service maps for your application.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt2b097f6854507a5b/6a7f1910b6b7346a13e491a0/Service-Map.png" alt="" /></p>
<h4 id="services">Services</h4>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltd03422687896996d/6a7f19121967ea32f7330b4e/Services.png" alt="" /></p>
<h4 id="myelasticapp">My Elastic App</h4>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt674df3a1b282a2af/6a7f191633fa8a5c28202b60/Overview-transactions.png" alt="" /></p>
<h4 id="apptransactions">App Transactions</h4>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltdaa6307b2d585f39/6a7f191842a117df4295c2db/Transactions2.png" alt="" /></p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt77f910a5f0aa9405/6a7f191b5967e583f55dd68d/Trace-db.png" alt="" /></p>
<h4 id="dependencies">Dependencies</h4>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltd25a359057e1f0a3/6a7f191d6693f845a8664357/Dependecies.png" alt="" /></p>
<h4 id="logs">Logs</h4>
<p>Navigate to your logs window/Discover to see the incoming application logs</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt9807252308e58d22/6a7f19202f00b234edefef0f/Logs.png" alt="" /></p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltcf99221e3f2250aa/6a7f1923448e4e993f5c0b3e/Logs2.png" alt="" /></p>
<h4 id="patterns">Patterns</h4>
<p>Log pattern analysis helps you to find patterns in unstructured log messages and makes it easier to examine your data.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt99d01a0dc50b84b8/6a7f1926e02fac4be75d6989/patt2.png" alt="" /></p>
<h2 id="finalrecap">Final Recap</h2>
<p>Here is a quick summary of what we did:</p>
<ul>
<li><p>Provisioned an Ubuntu 22.04 machine.</p></li>
<li><p>Installed build tools for SQLite, dev libs, and vcpkg.</p></li>
<li><p>Installed the client for opentelemetry-cpp via vcpkg.</p></li>
<li><p>Created a minimal C++ project that executes app traces and captures database operations.</p></li>
<li><p>Connected database sqlite3 in CMakeLists.txt.</p></li>
<li><p>Exported the Elastic OTLP endpoint &amp; token as environment variables (with a lowercase authorization=Bearer key!).</p></li>
<li><p>Ran the application and observed DB interactions and app traces in Elastic APM.</p></li>
<li><p>Observed application logs and patterns on Elastic logs and Discover.</p></li>
</ul>
<h2 id="faqcommonissues">FAQ &amp; Common Issues</h2>
<ul>
<li>Getting “Could not find package configuration file provided by opentelemetry-cpp”?</li>
</ul>
<p>Make sure you pass </p>
<pre><code>-DCMAKE_TOOLCHAIN_FILE=... and -DCMAKE_PREFIX_PATH=... 
</code></pre>
<p>to cmake, or embed them in CMakeLists.txt.</p>
<ul>
<li>Crash: “validate_metadata: INTERNAL:Illegal header key”?</li>
</ul>
<p>Use all-lowercase in </p>
<pre><code>OTEL_EXPORTER_OTLP_HEADERS, e.g. authorization=Bearer \&lt;token&gt;.
</code></pre>
<ul>
<li>Missing otlp_grpc_metrics_exporter.h?</li>
</ul>
<p>Your vcpkg version of opentelemetry-cpp (1.18.0) lacks a direct metrics exporter for OTLP. For metrics, either upgrade the library or consider an OpenTelemetry Collector approach.</p>
<ul>
<li>No data in Elastic APM?</li>
</ul>
<p>Double-check your endpoint URL, Bearer token, firewall rules, or service name in the APM</p>
<h2 id="additionalresources">Additional Resources:</h2>
<ul>
<li><a href="https://cloud.elastic.co/registration">Sign up for Elastic Cloud free trial</a></li>
<li><a href="https://www.elastic.co/observability-labs/blog/tag/opentelemetry">More Elastic OpenTelemetry Topics</a></li>
<li><a href="https://www.elastic.co/observability-labs/blog/elastic-distributions-opentelemetry">Introducing Elastic Distributions of OpenTelemetry</a></li>
<li><a href="https://www.elastic.co/observability-labs/blog/elastic-distribution-opentelemetry-collector">Introducing Elastic Distribution of OpenTelemetry Collector</a></li>
<li><a href="https://www.elastic.co/observability-labs/blog/elastic-opentelemetry-openai">Instrumenting your OpenAI- powered Python, Node.js, and Java Applications with EDOT</a></li>
</ul>]]></content:encoded>
    <link>https://www.elastic.co/observability-labs/blog/opentelemetry-cpp-elastic</link>
    <guid isPermaLink="false">opentelemetry-cpp-elastic</guid>
    <category><![CDATA[OpenTelemetry]]></category>
    <category><![CDATA[Logs Analytics]]></category>
    <category><![CDATA[APM]]></category>
    <category><![CDATA[Infrastructure Monitoring]]></category>
    <dc:creator><![CDATA[Haidar Braimaanie]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltc5b6763bb4d344de/6a7f192aeab5be4b4720aae2/blog-image.png" length="0" type="image/png"/>
    <pubDate>Tue, 11 Feb 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Tracing a RAG based Chatbot with Elastic Distributions of OpenTelemetry and Langtrace]]></title>
    <description><![CDATA[How to observe a OpenAI RAG based application using Elastic. Instrument the app, collect logs, traces, metrics, and understand how well the LLM is performing with Elastic Distributions of OpenTelemetry on Kubernetes with Langtrace.]]></description>
    <content:encoded><![CDATA[<p>Most AI-driven applications are currently focusing around increasing the value an end user, such as an SRE gets from AI. The main use case is the creation of various chatbots. These chatbots not only use large language models (LLMs), but are also using frameworks such as LangChain, and search to improve contextual information during a conversation (Retrieval Augmented Generation). Elastic’s sample <a href="https://github.com/elastic/elasticsearch-labs/tree/main/example-apps/chatbot-rag-app">RAG based Chatbot application</a>, showcases how to use Elasticsearch with local data that has embeddings, enabling search to properly pull out the most contextual information during a query with a chatbot connected to an LLM of your choice. It's a great example of how to build out a RAG based application with Elasticsearch. However, what about monitoring the application?</p>
<p>Elastic provides the ability to ingest OpenTelemetry data with native OTel SDKs, the off the shelf OTel collector, or even Elastic’s Distributions of OpenTelemetry (EDOT). EDOT enables you to bring in logs, metrics and traces for your GenAI application and for K8s. However you will also generally need libraries to help trace specific components in your application. In tracing GenAI applications you can pick from a large set of libraries.</p>
<ul>
<li><p><a href="https://github.com/open-telemetry/opentelemetry-python-contrib/tree/main/instrumentation-genai/opentelemetry-instrumentation-openai-v2">OpenTelemetry OpenAI Instrumentation-v2</a> - allows tracing LLM requests and logging of messages made by the OpenAI Python API library. (note v2 is built by OpenTelemetry, the non v2 version is from a specific vendor and not OpenTelemetry)</p></li>
<li><p><a href="https://github.com/open-telemetry/opentelemetry-python-contrib/tree/main/instrumentation-genai/opentelemetry-instrumentation-vertexai">OpenTelemetry VertexAI Instrumentation</a> - allows tracing LLM requests and logging of messages made by the VertexAI Python API library</p></li>
<li><p><a href="https://docs.langtrace.ai/introduction">Langtrace</a> - commercially available library which supports all LLMs in one library, and all traces are also OTel native.</p></li>
<li><p>Elastic’s EDOT - which recently added tracing. See <a href="https://www.elastic.co/observability-labs/blog/openai-tracing-elastic-opentelemetry">blog</a>.</p></li>
</ul>
<p>As you can see OpenTelemetry is the defacto mechanism that is converging to collect and ingest. OpenTelemetry is growing its support for this but it is also early days.</p>
<p>In this blog, we will walk through how to, with minimal code, observe a RAG based chatbot application with tracing using Langtrace. We previously covered Langtrace in a <a href="https://www.elastic.co/observability-labs/blog/elastic-opentelemetry-langchain-tracing-langtrace">blog</a> to highlight tracing Langchain.</p>
<p>In this blog we used langtrace OpenAI, Amazon Bedrock, Cohere, and others in one library.</p>
<h2 id="prerequisites">Pre-requisites:</h2>
<p>In order to follow along, these few pre-requisites are needed</p>
<ul>
<li><p>An Elastic Cloud account — sign up now, and become familiar with Elastic’s OpenTelemetry configuration. With Serverless no version required. With regular cloud minimally 8.17</p></li>
<li><p>Git clone the <a href="https://github.com/elastic/elasticsearch-labs/tree/main/example-apps/chatbot-rag-app">RAG based Chatbot application</a> and go through the <a href="https://www.elastic.co/search-labs/tutorials/chatbot-tutorial/welcome">tutorial</a> on how to bring it up and become more familiar.</p></li>
<li><p>An account on your favorite LLM (OpenAI, AzureOpen AI, etc), with API keys</p></li>
<li><p>Be familiar with EDOT to understand how we bring in logs, metrics, and traces from the application through the OTel Collector</p></li>
<li><p>Kubernetes cluster - I’ll be using Amazon EKS</p></li>
<li><p>Look at <a href="https://docs.langtrace.ai/introduction">Langtrace</a> documentation also.</p></li>
</ul>
<h2 id="applicationopentelemetryoutputinelastic">Application OpenTelemetry output in Elastic</h2>
<h3 id="chatbotragapp">Chatbot-rag-app</h3>
<p>The first item that you will need to get up and running is the ChatBotApp, and once up you should see the following:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt880964dd83511be5/6a7f0f443ce8e2feb5cf5471/Chatbotapp-general.png" alt="Chatbot app main page" /></p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt6d932574c6143415/6a7f0f48ead8ecb92fbaa976/Chatbotapp-details.png" alt="Chatbot app working" /></p>
<p>As you select some of the questions you will set a response based on the index that was created in Elasticsearch when the app initializes. Additionally there will be queries that are made to LLMs.</p>
<h3 id="traceslogsandmetricsfromedotinelastic">Traces, logs, and metrics from EDOT in Elastic</h3>
<p>Once you have OTel Collector with EDOT configuration on your K8s cluster, and Elastic Cloud up and running you should see the following:</p>
<h4 id="logs">Logs:</h4>
<p>In Discover you will see logs from the Chatbotapp, and be able to analyze the application logs, any specific log patterns (saves you time in analysis), and view logs from K8s.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blta108497f956043e0/6a7f0f4a1967ea4e31330847/Chatbotapp-logs.png" alt="Chatbot-logs" /></p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt47cfac93de9cc224/6a7f0f4d5967e535e15dd3cd/Chatbotapp-log-patterns.png" alt="Chatbot-log-patterns" /></p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltef3b11c21b429f54/6a7f0f5063e95922cc73dedd/Chatbotapp-logs-detailed.png" alt="Chatbot-log-details" /></p>
<h4 id="traces">Traces:</h4>
<p>In Elastic Observability APM, you can also see tha chatbot details, which include transactions, dependencies, logs, errors, etc.</p>
<p>When you look at traces, you will be able to see the chatbot interactions in the trace.</p>
<ol>
<li><p>You will see the end to end http call</p></li>
<li><p>Individual calls to elasticsearch</p></li>
<li><p>Specific calls such as invoke actions, and calls to the LLM</p></li>
</ol>
<p>You can also get individual details of the traces, and look at related logs, and metrics related to that trace,</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt2a985aa53fe6e887/6a7f0f536693f8a83a66402b/Chatbotapp-service-traces.png" alt="CHatbot-traces" /></p>
<h4 id="metrics">Metrics:</h4>
<p>In addition to logs, and traces, any instrumented metrics will also get ingested into Elastic.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt3757e1e587a76239/6a7f0f564c4bfb17ddccd60d/chatbot-reg-metrics.png" alt="Chatbot app metrics" /></p>
<h2 id="settingitallup">Setting it all up</h2>
<p>In order to properly set up the Chatbot-app on K8s with telemetry sent over to Elastic, a few things must be set up:</p>
<ol>
<li><p>Git clone the chatbot-rag-app, and modify one of the python files.</p></li>
<li><p>Next create a docker container that can be used in Kubernetes. The Docker build <a href="https://github.com/elastic/elasticsearch-labs/blob/main/example-apps/chatbot-rag-app/Dockerfile">here</a> in the Chatbot-app is good to use.</p></li>
<li><p>Collect all needed env variables. In this example we are using OpenAI, but the files can be modified for any of the LLMs. Hence you will have to get a few environmental variables loaded into the cluster. In the github repo there is a env.example for docker. You can pick and chose what is needed or not needed and adjust appropriately in the K8s file below.</p></li>
<li><p>Set up your K8s Cluster, and then install the OpenTelemetry collector with the appropriate yaml file and credentials. This will help collect K8s cluster logs and metrics also.</p></li>
<li><p>Utilize the two yaml files listed below to ensure you can run it on Kubernetes.</p></li>
</ol>
<ul>
<li><p>Init-index-job.yaml - Initiates the index in elasticsearch with the local corporate information</p></li>
<li><p>k8s-deployment-chatbot-rag-app.yaml - initializes the application frontend and backend.</p></li>
</ul>
<ol>
<li><p>Open the app on the load balancer URL against the chatbot-app service in K8s</p></li>
<li><p>Go to Elasticsearch and look at Discover for logs, go to APM and look for your chatbot-app and review the traces, and finally.</p></li>
</ol>
<h3 id="modifythecodefortracingwithlangtrace">Modify the code for tracing with Langtrace</h3>
<p>Once you curl the app and untar, go to the chatbot-rag-app directory:</p>
<pre><code>curl https://codeload.github.com/elastic/elasticsearch-labs/tar.gz/main | 
tar -xz --strip=2 elasticsearch-labs-main/example-apps/chatbot-rag-app
cd elasticsearch-labs-main/example-apps/chatbot-rag-app
</code></pre>
<p>Next open the <code>app.py</code> file in the <code>api</code> directory and add the following </p>
<pre><code>from opentelemetry.instrumentation.flask import FlaskInstrumentor

from langtrace_python_sdk import langtrace

langtrace.init(batch=False)

FlaskInstrumentor().instrument_app(app)
</code></pre>
<p>into the code:</p>
<pre><code>import os
import sys
from uuid import uuid4

from chat import ask_question
from flask import Flask, Response, jsonify, request
from flask_cors import CORS

from opentelemetry.instrumentation.flask import FlaskInstrumentor

from langtrace_python_sdk import langtrace

langtrace.init(batch=False)

app = Flask(__name__, static_folder="../frontend/build", static_url_path="/")
CORS(app)

FlaskInstrumentor().instrument_app(app)

@app.route("/")
</code></pre>
<p>See the items in <strong>BOLD</strong> which will add in the langtrace library, and the opentelemetry flask instrumentation. This combination will provide and end to end trace for the https call all the way down to the calls to Elasticsearch, and to OpenAI (or other LLMs).</p>
<h3 id="createthedockercontainer">Create the docker container</h3>
<p>Use the Dockerfile that is in the chatbot-rag-app directory as is and add the following line:</p>
<p><code>RUN pip3 install --no-cache-dir langtrace-python-sdk</code></p>
<p>into the Dockerfile:</p>
<pre><code>COPY requirements.txt ./requirements.txt
RUN pip3 install -r ./requirements.txt
RUN pip3 install --no-cache-dir langtrace-python-sdk
COPY api ./api
COPY data ./data

EXPOSE 4000
</code></pre>
<p>This enables the <code>langtrace-python-sdk</code> to be installed into the docker container so the langtrace libraries can be used properly.</p>
<h3 id="collectingtheproperenvvariables">Collecting the proper env variables:</h3>
<p>First collect the env variables from Elastic:</p>
<p>Envs for index initialization in Elastic:</p>
<pre><code>ELASTICSEARCH_URL=https://aws.us-west-2.aws.found.io
ELASTICSEARCH_USER=elastic
ELASTICSEARCH_PASSWORD=elastic

# The name of the Elasticsearch indexes
ES_INDEX=workplace-app-docs
ES_INDEX_CHAT_HISTORY=workplace-app-docs-chat-history
</code></pre>
<p>The <code>ELASTICSEARCH_URL</code> can be found in cloud.elastic.co when you bring up your instance.
The user and password, you will need to setup in Elastic. </p>
<p>Envs for sending the OTel instrumentation you will need the following:</p>
<pre><code>OTEL_EXPORTER_OTLP_ENDPOINT="https://123456789.apm.us-west-2.aws.cloud.es.io:443"
OTEL_EXPORTER_OTLP_HEADERS="Authorization=Bearer xxxxx"
</code></pre>
<p>These credentials are found in Elastic under APM integration and under OpenTelemetry</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blte5d0b7250dcc156d/6a7f0f5933fa8a1f732027f0/otel-credentials.png" alt="OTel credentials" /></p>
<p>Envs for LLMs</p>
<p>In this example we’re using OpenAI, hence only three variables are needed.</p>
<pre><code>LLM_TYPE=openai
OPENAI_API_KEY=XXXX
CHAT_MODEL=gpt-4o-mini
</code></pre>
<p>All these variables will be needed in the Kubernetes yamls in the next step</p>
<h3 id="setupk8sclusterandloadupotelcollectorwithedot">Setup K8s cluster and load up OTel Collector with EDOT</h3>
<p>This step is outlined in the following <a href="https://www.elastic.co/observability-labs/blog/elastic-opentelemetry-otel-operator">Blog</a>. It’s a simple three step process.</p>
<p>This step will bring in all the K8s cluster logs and metrics and setup the OTel collector.</p>
<h3 id="setupsecretsinitializeindicesandstarttheapp">Setup secrets, initialize indices, and start the app</h3>
<p>Now that the cluster is up, and you have your environmental variables, you will need to</p>
<ol>
<li><p>Install and run the <code>k8s-deployments.yaml</code> with the variables</p></li>
<li><p>Initialize the index</p></li>
</ol>
<p>Essentially run the following:</p>
<pre><code>kubectl create -f k8s-deployment.yaml
kubectl create -f init-index-job.yaml
</code></pre>
<p>Here are the two yamls you should use. Also found <a href="https://github.com/elastic/observability-examples/tree/main/chatbot-rag-app-observability">here</a></p>
<p>k8s-deployment.yaml</p>
<pre><code>apiVersion: v1
kind: Secret
metadata:
  name: genai-chatbot-langtrace-secrets
type: Opaque
stringData:
  OTEL_EXPORTER_OTLP_HEADERS: "Authorization=Bearer%20xxxx"
  OTEL_EXPORTER_OTLP_ENDPOINT: "https://1234567.apm.us-west-2.aws.cloud.es.io:443"
 ELASTICSEARCH_URL: "YOUR_ELASTIC_SEARCH_URL"
  ELASTICSEARCH_USER: "elastic"
  ELASTICSEARCH_PASSWORD: "elastic"
  OPENAI_API_KEY: "XXXXXXX"  

---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: genai-chatbot-langtrace
spec:
  replicas: 2
  selector:
    matchLabels:
      app: genai-chatbot-langtrace
  template:
    metadata:
      labels:
        app: genai-chatbot-langtrace
    spec:
      containers:
      - name: genai-chatbot-langtrace
        image:65765.amazonaws.com/genai-chatbot-langtrace2:latest
        ports:
        - containerPort: 4000
        env:
        - name: LLM_TYPE
          value: "openai"
        - name: CHAT_MODEL
          value: "gpt-4o-mini"
        - name: OTEL_SDK_DISABLED
          value: "false"
        - name: OTEL_RESOURCE_ATTRIBUTES
          value: "service.name=genai-chatbot-langtrace,service.version=0.0.1,deployment.environment=dev"
        - name: OTEL_EXPORTER_OTLP_PROTOCOL
          value: "http/protobuf"
        envFrom:
        - secretRef:
            name: genai-chatbot-langtrace-secrets
        resources:
          requests:
            memory: "512Mi"
            cpu: "250m"
          limits:
            memory: "1Gi"
            cpu: "500m"

---
apiVersion: v1
kind: Service
metadata:
  name: genai-chatbot-langtrace-service
spec:
  selector:
    app: genai-chatbot-langtrace
  ports:
  - port: 80
    targetPort: 4000
  type: LoadBalancer
</code></pre>
<p>Init-index-job.yaml</p>
<pre><code>apiVersion: batch/v1
kind: Job
metadata:
  name: init-elasticsearch-index-test
spec:
  template:
    spec:
      containers:
      - name: init-index
#update your image location for chatbot rag app
        image: your-image-location:latest
        workingDir: /app/api
        command: ["python3", "-m", "flask", "--app", "app", "create-index"]
        env:
        - name: FLASK_APP
          value: "app"
        - name: LLM_TYPE
          value: "openai"
        - name: CHAT_MODEL
          value: "gpt-4o-mini"
        - name: ES_INDEX
          value: "workplace-app-docs"
        - name: ES_INDEX_CHAT_HISTORY
          value: "workplace-app-docs-chat-history"
        - name: ELASTICSEARCH_URL
          valueFrom:
            secretKeyRef:
              name: chatbot-regular-secrets
              key: ELASTICSEARCH_URL
        - name: ELASTICSEARCH_USER
          valueFrom:
            secretKeyRef:
              name: chatbot-regular-secrets
              key: ELASTICSEARCH_USER
        - name: ELASTICSEARCH_PASSWORD
          valueFrom:
            secretKeyRef:
              name: chatbot-regular-secrets
              key: ELASTICSEARCH_PASSWORD
        envFrom:
        - secretRef:
            name: chatbot-regular-secrets
      restartPolicy: Never
  backoffLimit: 4
</code></pre>
<h3 id="openappwithloadbalancerurl">Open App with LoadBalancer URL</h3>
<p>Run the kubectl get services command and get the URL for the chatbot app</p>
<pre><code>% kubectl get services
NAME                                 TYPE           CLUSTER-IP       EXTERNAL-IP                                                               PORT(S)                                                                     AGE
chatbot-langtrace-service            LoadBalancer   10.100.130.44    xxxxxxxxx-1515488226.us-west-2.elb.amazonaws.com   80:30748/TCP                                                                6d23h
</code></pre>
<p>Play with app and review telemetry in Elastic</p>
<p>Once you go to the URL, you should see all the screens we described earlier in the <a href="https://docs.google.com/document/d/1w_3VRDJV3CoLMjOj8Ktnng-6MuKgdzkhKs4CVBWkatc/edit?tab=t.0#bookmark=id.lrmf4nbl2twi">beginning of this blog</a>.</p>
<h2 id="conclusion">Conclusion</h2>
<p>With Elastic's Chatbot-rag-app you have an example of how to build out a OpenAI driven RAG based chat application. However, you still need to understand how well it performs, whether its working properly, etc. Using OTel, Elastic’s EDOT and Langtrace gives you the ability to achieve this. Additionally, you will generally run this application on Kubernetes. Hopefully this blog provides the outline of how to achieve this.</p>
<p>Here are the other Tracing blogs:</p>
<p>App Observability with LLM (Tracing)- </p>
<ul>
<li><p><a href="https://www.elastic.co/observability-labs/blog/elastic-opentelemetry-langchain-tracing-langtrace">Observing LangChain with Langtrace and OpenTelemetry</a></p></li>
<li><p><a href="https://www.elastic.co/observability-labs/blog/elastic-opentelemetry-langchain-openlit-tracing">Observing LangChain with OpenLit Tracing</a></p></li>
<li><p><a href="https://www.elastic.co/observability-labs/blog/elastic-opentelemetry-langchain-tracing">Instrumenting LangChain with OpenTelemetry</a> </p></li>
</ul>
<p>LLM Observability - </p>
<ul>
<li><p><a href="https://www.elastic.co/observability-labs/blog/elevate-llm-observability-with-gcp-vertex-ai-integration">Elevate LLM Observability with GCP Vertex AI Integration</a></p></li>
<li><p><a href="https://www.elastic.co/observability-labs/blog/llm-observability-aws-bedrock">LLM Observability on AWS Bedrock</a></p></li>
<li><p><a href="https://www.elastic.co/observability-labs/blog/llm-observability-azure-openai">LLM Observability for Azure OpenAI</a></p></li>
<li><p><a href="https://www.elastic.co/observability-labs/blog/llm-observability-azure-openai-v2">LLM Observability for Azure OpenAI v2</a></p></li>
</ul>]]></content:encoded>
    <link>https://www.elastic.co/observability-labs/blog/openai-tracing-langtrace-elastic</link>
    <guid isPermaLink="false">openai-tracing-langtrace-elastic</guid>
    <category><![CDATA[LLM Observability]]></category>
    <category><![CDATA[OpenTelemetry]]></category>
    <category><![CDATA[Metrics]]></category>
    <category><![CDATA[APM]]></category>
    <category><![CDATA[Kubernetes]]></category>
    <category><![CDATA[Infrastructure Monitoring]]></category>
    <dc:creator><![CDATA[Bahubali Shetti]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt9d3180a0fd833d64/6a7f0f5c73d9bd264e29dc29/edot-openai-tracing.png" length="0" type="image/png"/>
    <pubDate>Thu, 06 Feb 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Tracing, logs, and metrics for a RAG based Chatbot with Elastic Distributions of OpenTelemetry]]></title>
    <description><![CDATA[How to observe a OpenAI RAG based application using Elastic. Instrument the app, collect logs, traces, metrics, and understand how well the LLM is performing with Elastic Distributions of OpenTelemetry on Kubernetes and Docker.]]></description>
    <content:encoded><![CDATA[<p>As discussed in the following post, <a href="https://www.elastic.co/observability-labs/blog/elastic-opentelemetry-openai">Elastic added instrumentation for OpenAI based applications in EDOT</a>. The main application most commonly using LLMs is known as a Chatbot. These chatbots not only use large language models (LLMs), but are also using frameworks such as LangChain, and search to improve contextual information during a conversation RAG (Retrieval Augmented Generation). Elastics's sample <a href="https://github.com/elastic/elasticsearch-labs/tree/main/example-apps/chatbot-rag-app">RAG based Chatbot application</a>, showcases how to use Elasticsearch with local data that has embeddings, enabling search to properly pull out the most contextual information during a query with a chatbot connected to an LLM of your choice. It's a great example of how to build out a RAG based application with Elasticsearch.</p>
<p>This app is also now insturmented with EDOT, and you can visualize the Chatbot's traces to OpenAI, as well as relevant logs, and metrics from the application. By running the app as instructed in the github repo with Docker you can see these traces on a local stack. But how about running it against serverless, Elastic cloud or even with Kubernetes?</p>
<p>In this blog we will walk through how to set up Elastic's RAG Based Chatbot application with Elastic cloud and Kubernetes.</p>
<h2 id="prerequisites">Prerequisites:</h2>
<p>In order to follow along, these few pre-requisites are needed</p>
<ul>
<li><p>An Elastic Cloud account — sign up now, and become familiar with Elastic's OpenTelemetry configuration. With Serverless no version required. With regular cloud minimally 8.17</p></li>
<li><p>Git clone the <a href="https://github.com/elastic/elasticsearch-labs/tree/main/example-apps/chatbot-rag-app">RAG based Chatbot application</a> and go through the <a href="https://www.elastic.co/search-labs/tutorials/chatbot-tutorial/welcome">tutorial</a> on how to bring it up and become more familiar and how to bring up the application using Docker.</p></li>
<li><p>An account on OpenAI with API keys</p></li>
<li><p>Kubernetes cluster to run the RAG based Chatbot app</p></li>
<li><p>The instructions in this blog are also found in <a href="https://github.com/elastic/observability-examples/tree/main/chatbot-rag-app-observability">observability-examples</a> in github.</p></li>
</ul>
<h2 id="applicationopentelemetryoutputinelastic">Application OpenTelemetry output in Elastic</h2>
<h3 id="chatbotragapp">Chatbot-rag-app</h3>
<p>The first item that you will need to get up and running is the ChatBotApp, and once up you should see the following:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltdd100775bedb7fa2/6a7f0f2be3a2190cf999f57e/Chatbotapp-general.png" alt="Chatbot app main page" /></p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt68a2dbea6d8f3991/6a7f0f2e4c4bfb9fbaccd603/Chatbotapp-details.png" alt="Chatbot app working" /></p>
<p>As you select some of the questions you will set a response based on the index that was created in Elasticsearch when the app initializes. Additionally there will be queries that are made to LLMs.</p>
<h3 id="traceslogsandmetricsfromedotinelastic">Traces, logs, and metrics from EDOT in Elastic</h3>
<p>Once you have the application running on your K8s cluster or with Docker, and Elastic Cloud up and running you should see the following:</p>
<h4 id="logs">Logs:</h4>
<p>In Discover you will see logs from the Chatbotapp, and be able to analyze the application logs, any specific log patterns, which saves you time in analysis.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt29660a76fd00a49c/6a7f0f316c6eac6076f14207/chatbot-reg-logs.png" alt="Chatbot-logs" /></p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltdc9a84779ba0d561/6a7f0f34ea068d10eff09f64/chatbot-reg-logs-patterns.png" alt="Chatbot-log-patterns" /></p>
<h4 id="traces">Traces:</h4>
<p>In Elastic Observability APM, you can also see tha chatbot details, which include transactions, dependencies, logs, errors, etc.</p>
<p>When you look at traces, you will be able to see the chatbot interactions in the trace.</p>
<ol>
<li><p>You will see the end to end http call</p></li>
<li><p>Individual calls to elasticsearch</p></li>
<li><p>Specific calls such as invoke actions, and calls to the LLM</p></li>
</ol>
<p>You can also get individual details of the traces, and look at related logs, and metrics related to that trace,</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt810b06d98a833d78/6a7f0f376693f8036f664023/chatbot-reg-trace.png" alt="CHatbot-traces" /></p>
<h4 id="metrics">Metrics:</h4>
<p>In addition to logs, and traces, any instrumented metrics will also get ingested into Elastic.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt16786031635f9d06/6a7f0f3a448e4eedc45c0803/chatbot-reg-metrics.png" alt="Chatbot app metrics" /></p>
<h2 id="settingitallupwithdocker">Setting it all up with Docker</h2>
<p>In order to properly set up the Chatbot-app on Docker with telemetry sent over to Elastic, a few things must be set up:</p>
<ol>
<li><p>Git clone the chatbot-rag-app</p></li>
<li><p>Modify the env file as noted in the github README with the following exception:</p></li>
</ol>
<p>Use your Elastic cloud's <code>OTEL_EXPORTER_OTLP_ENDPOINT</code> and <code>OTEL_EXPORTER_OTLP_HEADER</code> instead.</p>
<p>You can find these in the Elastic Cloud under <code>integrations-&gt;APM</code></p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt3720f7995587366b/6a7f0f3d3cab1c13600e494c/otel-credentials.png" alt="OTel credentials" /></p>
<p>Envs for sending the OTel instrumentation you will need the following:</p>
<pre><code>OTEL_EXPORTER_OTLP_ENDPOINT="https://123456789.apm.us-west-2.aws.cloud.es.io:443"
OTEL_EXPORTER_OTLP_HEADERS="Authorization=Bearer%20xxxxx"
</code></pre>
<p>Notice the <code>%20</code> in the headers. This will be needed to account for the space in credentials.</p>
<ol>
<li><p>Set the following to false - <code>OTEL_SDK_DISABLED=false</code></p></li>
<li><p>Set the envs for LLMs </p></li>
</ol>
<p>In this example we're using OpenAI, hence only three variables are needed.</p>
<pre><code>LLM_TYPE=openai
OPENAI_API_KEY=XXXX
CHAT_MODEL=gpt-4o-mini
</code></pre>
<ol>
<li>Run the docker container as noted </li>
</ol>
<pre><code>docker compose up --build --force-recreate
</code></pre>
<ol>
<li><p>Play with the app at <code>localhost:4000</code></p></li>
<li><p>Then log into Elastic cloud and see the output as shown previously.</p></li>
</ol>
<h2 id="runchatbotragapponkubernetes">Run chatbot-rag-app on Kubernetes</h2>
<p>In order to set this up, you can follow the following repo on Observability-examples which has the Kubernetes yaml files being used. These will also point to Elastic Cloud.</p>
<ol>
<li><p>Set up the Kubernetes Cluster (we're using EKS)</p></li>
<li><p>Get the appropriate ENV variables:</p></li>
</ol>
<ul>
<li><p>Find the <code>OTEL_EXPORTER_OTLP_ENDPOINT/HEADER</code> variables as noted in the pervious for Docker.</p></li>
<li><p>Get your OpenAI Key</p></li>
<li><p>Elasticsearch URL, and username and password.</p></li>
</ul>
<ol>
<li>Follow the instructions in the following <a href="https://github.com/elastic/observability-examples/tree/main/chatbot-rag-app-observability">github repo in observability examples</a> to run two Kubernetes yaml files.</li>
</ol>
<p>Essentially you need only replace the secret variables in k8s-deployment.yaml, and run</p>
<pre><code>kubectl create -f k8s-deployment.yaml
kubectl create -f init-index-job.yaml
</code></pre>
<p>The app needs to be running first, then we use the app to initialize Elasticsearch with indices for the app.</p>
<p><strong><em>Init-index-job.yaml</em></strong></p>
<pre><code>apiVersion: batch/v1
kind: Job
metadata:
  name: init-elasticsearch-index-test
spec:
  template:
    spec:
      containers:
      - name: init-index
        image: ghcr.io/elastic/elasticsearch-labs/chatbot-rag-app:latest
        workingDir: /app/api
        command: ["python3", "-m", "flask", "--app", "app", "create-index"]
        env:
        - name: FLASK_APP
          value: "app"
        - name: LLM_TYPE
          value: "openai"
        - name: CHAT_MODEL
          value: "gpt-4o-mini"
        - name: ES_INDEX
          value: "workplace-app-docs"
        - name: ES_INDEX_CHAT_HISTORY
          value: "workplace-app-docs-chat-history"
        - name: ELASTICSEARCH_URL
          valueFrom:
            secretKeyRef:
              name: chatbot-regular-secrets
              key: ELASTICSEARCH_URL
        - name: ELASTICSEARCH_USER
          valueFrom:
            secretKeyRef:
              name: chatbot-regular-secrets
              key: ELASTICSEARCH_USER
        - name: ELASTICSEARCH_PASSWORD
          valueFrom:
            secretKeyRef:
              name: chatbot-regular-secrets
              key: ELASTICSEARCH_PASSWORD
        envFrom:
        - secretRef:
            name: chatbot-regular-secrets
      restartPolicy: Never
  backoffLimit: 4
</code></pre>
<p><strong><em>k8s-deployment.yaml</em></strong></p>
<pre><code>apiVersion: v1
kind: Secret
metadata:
  name: chatbot-regular-secrets
type: Opaque
stringData:
  ELASTICSEARCH_URL: "https://yourelasticcloud.es.us-west-2.aws.found.io"
  ELASTICSEARCH_USER: "elastic"
  ELASTICSEARCH_PASSWORD: "elastic"
  OTEL_EXPORTER_OTLP_HEADERS: "Authorization=Bearer%20xxxx"
  OTEL_EXPORTER_OTLP_ENDPOINT: "https://12345.apm.us-west-2.aws.cloud.es.io:443"
  OPENAI_API_KEY: "YYYYYYYY"

---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: chatbot-regular
spec:
  replicas: 2
  selector:
    matchLabels:
      app: chatbot-regular
  template:
    metadata:
      labels:
        app: chatbot-regular
    spec:
      containers:
      - name: chatbot-regular
        image: ghcr.io/elastic/elasticsearch-labs/chatbot-rag-app:latest
        ports:
        - containerPort: 4000
        env:
        - name: LLM_TYPE
          value: "openai"
        - name: CHAT_MODEL
          value: "gpt-4o-mini"
        - name: OTEL_RESOURCE_ATTRIBUTES
          value: "service.name=chatbot-regular,service.version=0.0.1,deployment.environment=dev"
        - name: OTEL_SDK_DISABLED
          value: "false"
        - name: OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT
          value: "true"
        - name: OTEL_EXPERIMENTAL_RESOURCE_DETECTORS
          value: "process_runtime,os,otel,telemetry_distro"
        - name: OTEL_EXPORTER_OTLP_PROTOCOL
          value: "http/protobuf"
        - name: OTEL_METRIC_EXPORT_INTERVAL
          value: "3000"
        - name: OTEL_BSP_SCHEDULE_DELAY
          value: "3000"
        envFrom:
        - secretRef:
            name: chatbot-regular-secrets
        resources:
          requests:
            memory: "512Mi"
            cpu: "250m"
          limits:
            memory: "1Gi"
            cpu: "500m"

---
apiVersion: v1
kind: Service
metadata:
  name: chatbot-regular-service
spec:
  selector:
    app: chatbot-regular
  ports:
  - port: 80
    targetPort: 4000
  type: LoadBalancer
</code></pre>
<p><strong>Open App with LoadBalancer URL</strong></p>
<p>Run the kubectl get services command and get the URL for the chatbot app</p>
<pre><code>% kubectl get services
NAME                                 TYPE           CLUSTER-IP    EXTERNAL-IP                                                               PORT(S)                                                                     AGE
chatbot-regular-service            LoadBalancer   10.100.130.44    xxxxxxxxx-1515488226.us-west-2.elb.amazonaws.com   80:30748/TCP                                                                6d23h
</code></pre>
<ol>
<li><p>Play with app and review telemetry in Elastic</p></li>
<li><p>Once you go to the URL, you should see all the screens we described earlier in the beginning of this blog.</p></li>
</ol>
<h2 id="conclusion">Conclusion</h2>
<p>With Elastic's Chatbot-rag-app you have an example of how to build out a OpenAI driven RAG based chat application. However, you still need to understand how well it performs, whether its working properly, etc. Using OTel and Elastic’s EDOT gives you the ability to achieve this. Additionally, you will generally run this application on Kubernetes. Hopefully this blog provides the outline of how to achieve this.
Here are the other Tracing blogs:</p>
<p>App Observability with LLM (Tracing)- </p>
<ul>
<li><p><a href="https://www.elastic.co/observability-labs/blog/elastic-opentelemetry-langchain-tracing-langtrace">Observing LangChain with Langtrace and OpenTelemetry</a></p></li>
<li><p><a href="https://www.elastic.co/observability-labs/blog/elastic-opentelemetry-langchain-openlit-tracing">Observing LangChain with OpenLit Tracing</a></p></li>
<li><p><a href="https://www.elastic.co/observability-labs/blog/elastic-opentelemetry-langchain-tracing">Instrumenting LangChain with OpenTelemetry</a> </p></li>
</ul>
<p>LLM Observability - </p>
<ul>
<li><p><a href="https://www.elastic.co/observability-labs/blog/elevate-llm-observability-with-gcp-vertex-ai-integration">Elevate LLM Observability with GCP Vertex AI Integration</a></p></li>
<li><p><a href="https://www.elastic.co/observability-labs/blog/llm-observability-aws-bedrock">LLM Observability on AWS Bedrock</a></p></li>
<li><p><a href="https://www.elastic.co/observability-labs/blog/llm-observability-azure-openai">LLM Observability for Azure OpenAI</a></p></li>
<li><p><a href="https://www.elastic.co/observability-labs/blog/llm-observability-azure-openai-v2">LLM Observability for Azure OpenAI v2</a></p></li>
</ul>]]></content:encoded>
    <link>https://www.elastic.co/observability-labs/blog/openai-tracing-elastic-opentelemetry</link>
    <guid isPermaLink="false">openai-tracing-elastic-opentelemetry</guid>
    <category><![CDATA[LLM Observability]]></category>
    <category><![CDATA[OpenTelemetry]]></category>
    <category><![CDATA[Metrics]]></category>
    <category><![CDATA[APM]]></category>
    <category><![CDATA[Kubernetes]]></category>
    <category><![CDATA[Infrastructure Monitoring]]></category>
    <dc:creator><![CDATA[Bahubali Shetti]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt12854c40fcaa0e97/6a7f0f406c6eac23bbf1420f/edot-openai-tracing.jpg" length="0" type="image/jpeg"/>
    <pubDate>Fri, 24 Jan 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Auto-instrumentation of Go applications with OpenTelemetry]]></title>
    <description><![CDATA[Instrumenting Go applications with OpenTelemetry provides insights into application performance, dependencies, and errors. We'll show you how to automatically instrument a Go application using Docker, with no changes to your application code.]]></description>
    <content:encoded><![CDATA[<p>In the fast-paced universe of software development, especially in the
cloud-native realm, DevOps and SRE teams are increasingly emerging as essential
partners in application stability and growth.</p>
<p>DevOps engineers continuously optimize software delivery, while SRE teams act
as the stewards of application reliability, scalability, and top-tier
performance. The challenge? These teams require a cutting-edge observability
solution, one that encompasses full-stack insights, empowering them to rapidly
manage, monitor, and rectify potential disruptions before they culminate into
operational challenges.</p>
<p>Observability in our modern distributed software ecosystem goes beyond mere
monitoring — it demands limitless data collection, precision in processing, and
the correlation of this data into actionable insights. However, the road to
achieving this holistic view is paved with obstacles, from navigating version
incompatibilities to wrestling with restrictive proprietary code.</p>
<p>Enter <a href="https://opentelemetry.io/">OpenTelemetry (OTel)</a>, with the following
benefits for those who adopt it:</p>
<ul>
<li>Escape vendor constraints with OTel, freeing yourself from vendor lock-in and
ensuring top-notch observability.</li>
<li>See the harmony of unified logs, metrics, and traces come together to provide
a complete system view.</li>
<li>Improve your application oversight through richer and enhanced
instrumentations.</li>
<li>Embrace the benefits of backward compatibility to protect your prior
instrumentation investments.</li>
<li>Embark on the OpenTelemetry journey with an easy learning curve, simplifying
onboarding and scalability.</li>
<li>Rely on a proven, future-ready standard to boost your confidence in every
investment.</li>
</ul>
<p>In this blog, we will explore how you can use <a href="https://github.com/open-telemetry/opentelemetry-go-instrumentation/">automatic instrumentation in
your Go</a>
application using Docker, without the need to refactor any part of your
application code. We will use an <a href="https://github.com/elastic/observability-examples">application called
Elastiflix</a>, which helps
highlight auto-instrumentation in a simple way.</p>
<h2 id="applicationprerequisitesandconfig">Application, prerequisites, and config</h2>
<p>The application that we use for this blog is called
<a href="https://github.com/elastic/observability-examples">Elastiflix</a>, a
movie-streaming application. It consists of several micro-services written in
.NET, NodeJS, Go, and Python.</p>
<p>Before we instrument our sample application, we will first need to understand
how Elastic can receive the telemetry data.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt5405335485c969f1/6a85c7acd6cf2918a7bb087e/elastic-blog-1-config.png" alt="Elastic configuration options for
OpenTelemetry" /></p>
<p>All of Elastic Observability’s APM capabilities are available with OTel data.
Some of these include:</p>
<ul>
<li>Service maps</li>
<li>Service details (latency, throughput, failed transactions)</li>
<li>Dependencies between services, distributed tracing</li>
<li>Transactions (traces)</li>
<li>Machine learning (ML) correlations</li>
<li>Log correlation</li>
</ul>
<p>In addition to Elastic’s APM and a unified view of the telemetry data, you will
also be able to use Elastic’s powerful machine learning capabilities to reduce
the analysis, and alerting to help reduce MTTR.</p>
<h3 id="prerequisites">Prerequisites</h3>
<ul>
<li>An Elastic Cloud account — <a href="https://cloud.elastic.co/">sign up now</a>.</li>
<li>A clone of the <a href="https://github.com/elastic/observability-examples">Elastiflix demo application</a>, or your own Go application</li>
<li>Basic understanding of Docker — potentially install <a href="https://www.docker.com/products/docker-desktop/">Docker Desktop</a></li>
<li>Basic understanding of Go</li>
</ul>
<h3 id="viewtheexamplesourcecode">View the example source code</h3>
<p>The full source code, including the Dockerfile used in this blog, can be found
on
<a href="https://github.com/elastic/observability-examples/tree/main/Elastiflix/go-favorite">GitHub</a>.</p>
<p>The following steps will show you how to instrument this application and run it
on the command line or in Docker. If you are interested in a more complete OTel
example, take a look at the docker-compose file
<a href="https://github.com/elastic/observability-examples/tree/main#start-the-app">here</a>,
which will bring up the full project.</p>
<h2 id="stepbystepguide">Step-by-step guide</h2>
<h3 id="step0logintoyourelasticcloudaccount">Step 0. Log in to your Elastic Cloud account</h3>
<p>This blog assumes you have an Elastic Cloud account — if not, follow the
<a href="https://cloud.elastic.co/registration?elektra=en-cloud-page">instructions to get started on Elastic
Cloud</a>.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltdb92257a78bf2a8b/6a85c7afabdc296b2912247a/elastic-blog-2-trial.png" alt="free trial" /></p>
<h3 id="step1runthedockerimagewithautoinstrumentation">Step 1. Run the Docker Image with auto-instrumentation</h3>
<p>We are going to use automatic instrumentation with the Go service from the
<a href="https://github.com/elastic/observability-examples/tree/main/Elastiflix/go-favorite">Elastiflix demo
application</a>.</p>
<p>We will be using the following service from Elastiflix:</p>
<pre><code>Elastiflix/go-favorite
</code></pre>
<p>Per the <a href="https://github.com/open-telemetry/opentelemetry-go-instrumentation/blob/main/docs/getting-started.md">OpenTelemetry Automatic Instrumentation for Go
documentation</a>,
you will configure the application to be auto-instrumented using
docker-compose.</p>
<p>As specified in the <a href="https://github.com/open-telemetry/opentelemetry-go-instrumentation/blob/main/docs/getting-started.md">OTEL Go
documentation</a>,
we will use environment variables and pass in the configuration values to
enable it to connect with <a href="https://www.elastic.co/guide/en/observability/current/apm-open-telemetry.html">Elastic Observability’s APM
server</a>.</p>
<p>Because Elastic accepts OTLP natively, we just need to provide the Endpoint and
authentication where the OTEL Exporter needs to send the data, as well as some
other environment variables.</p>
<p><strong>Getting Elastic Cloud variables</strong>
You can copy the endpoints and token from Kibana under the path <code>/app/apm/onboarding?agent=openTelemetry</code>.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt2da72f2943c5e320/6a85c7b29d2b71762ef938f3/elastic-blog-3-apm-agents.png" alt="apm agents" /></p>
<p>You will need to copy the following environment variables:</p>
<pre><code>OTEL_EXPORTER_OTLP_ENDPOINT
OTEL_EXPORTER_OTLP_HEADERS
</code></pre>
<p>Update the <code>docker-compose.yml</code> file at the top of the <code>Elastiflix</code> repository,
adding a <code>go-auto</code> service and updating the <code>favorite-go</code> one:</p>
<pre><code>  favorite-go:
    build: go-favorite/.
    image: docker.elastic.co/demos/workshop/observability/elastiflix-go-favorite:${ELASTIC_VERSION}-${BUILD_NUMBER}
    depends_on:
      - redis
    networks:
      - app-network
    ports:
      - "5001:5000"
    environment:
      - REDIS_HOST=redis
      - TOGGLE_SERVICE_DELAY=${TOGGLE_SERVICE_DELAY:-0}
      - TOGGLE_CANARY_DELAY=${TOGGLE_CANARY_DELAY:-0}
      - TOGGLE_CANARY_FAILURE=${TOGGLE_CANARY_FAILURE:-0}
    volumes:
      - favorite-go:/app
  go-auto:
    image: otel/autoinstrumentation-go
    privileged: true
    pid: "host"
    networks:
      - app-network
    environment:
      OTEL_EXPORTER_OTLP_ENDPOINT: "REPLACE WITH OTEL_EXPORTER_OTLP_ENDPOINT"
      OTEL_EXPORTER_OTLP_HEADERS: "REPLACE WITH OTEL_EXPORTER_OTLP_HEADERS"
      OTEL_GO_AUTO_TARGET_EXE: "/app/main"
      OTEL_SERVICE_NAME: "go-favorite"
      OTEL_PROPAGATORS: "tracecontext,baggage"
    volumes:
      - favorite-go:/app
      - /proc:/host/proc
</code></pre>
<p>And, at the bottom of the file:</p>
<pre><code>volumes:
  favorite-go:
networks:
  app-network:
    driver: bridge
</code></pre>
<p>Finally, in the configuration for the main node app, you will want to tell Elastiflix to call the Go favorites app by replacing the line:</p>
<pre><code>environment:
  - API_ENDPOINT_FAVORITES=favorite-java:5000
</code></pre>
<p>with:</p>
<pre><code>environment:
  - API_ENDPOINT_FAVORITES=favorite-go:5000
</code></pre>
<h3 id="step3exploretracesandlogsinelasticapm">Step 3: Explore traces and logs in Elastic APM</h3>
<p>Once you have this up and running, you can ping the endpoint for your
instrumented service (in our case, this is /favorites), and you should see the
app appear in Elastic APM, as shown below:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltc01109d384575b47/6a85c7b5d6cf296423bb0886/elastic-blog-4-services.png" alt="services" /></p>
<p>It will begin by tracking throughput and latency critical metrics for SREs to
pay attention to.</p>
<p>Digging in, we can see an overview of all our Transactions.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt30c92e5b8c16cffb/6a85c7b89829264ca658385c/elastic-blog-5-services2.png" alt="services-2" /></p>
<p>And look at specific transactions:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltb9cdf840d07ea72b/6a85c7bad7b2e746c7fe8446/elastic-blog-6-graph-colored.png" alt="graph colored lines" /></p>
<p>This gives you complete visibility across metrics, and traces!</p>
<h2 id="summary">Summary</h2>
<p>With this Dockerfile, you've transformed your simple Go application into one
that's automatically instrumented with OpenTelemetry. This will aid greatly in
understanding application performance, tracing errors, and gaining insights
into how users interact with your software.</p>
<p>Remember, observability is a crucial aspect of modern application development,
especially in distributed systems. With tools like OpenTelemetry, understanding
complex systems becomes a tad bit easier.</p>
<p>In this blog, we discussed the following:</p>
<ul>
<li>How to auto-instrument Go with OpenTelemetry.</li>
<li>Using standard commands in a Docker file, auto-instrumentation was done
efficiently and without adding code in multiple places enabling
manageability.</li>
<li>Using OpenTelemetry and its support for multiple languages, DevOps and SRE
teams can auto-instrument their applications with ease gaining immediate
insights into the health of the entire application stack and reduce mean time
to resolution (MTTR).</li>
</ul>
<p>Since Elastic can support a mix of methods for ingesting data, whether it be
using auto-instrumentation of open-source OpenTelemetry or manual
instrumentation with its native APM agents, you can plan your migration to OTel
by focusing on a few applications first and then using OpenTelemety across your
applications later on in a manner that best fits your business needs.</p>
<p>Developer resources:</p>
<ul>
<li><p><a href="https://www.elastic.co/observability-labs/blog/getting-started-opentelemetry-instrumentation-sample-app">Elastiflix application</a>, a guide to instrument different languages with OpenTelemetry</p></li>
<li><p>Python: <a href="https://www.elastic.co/observability-labs/blog/auto-instrumentation-python-applications-opentelemetry">Auto-instrumentation</a>, <a href="https://www.elastic.co/observability-labs/blog/manual-instrumentation-python-apps-opentelemetry">Manual-instrumentation</a></p></li>
<li><p>Java: <a href="https://www.elastic.co/observability-labs/blog/auto-instrumentation-java-applications-opentelemetry">Auto-instrumentation</a>, <a href="https://www.elastic.co/observability-labs/blog/manual-instrumentation-java-apps-opentelemetry">Manual-instrumentation</a></p></li>
<li><p>Node.js: <a href="https://www.elastic.co/observability-labs/blog/auto-instrument-nodejs-apps-opentelemetry">Auto-instrumentation</a>, <a href="https://www.elastic.co/observability-labs/blog/manual-instrumentation-nodejs-apps-opentelemetry">Manual-instrumentation</a></p></li>
<li><p>.NET: <a href="https://www.elastic.co/observability-labs/blog/auto-instrumentation-net-applications-opentelemetry">Auto-instrumentation</a>, <a href="https://www.elastic.co/observability-labs/blog/manual-instrumentation-net-apps-opentelemetry">Manual-instrumentation</a></p></li>
<li><p>Go: <a href="https://www.elastic.co/observability-labs/blog/auto-instrumentation-go-applications-opentelemetry">Auto-instrumentation</a> <a href="https://www.elastic.co/observability-labs/blog/manual-instrumentation-apps-opentelemetry">Manual-instrumentation</a></p></li>
<li><p><a href="https://www.elastic.co/observability-labs/blog/best-practices-instrumenting-opentelemetry">Best practices for instrumenting OpenTelemetry</a></p>
<p>General configuration and use case resources:</p></li>
<li><p><a href="https://www.elastic.co/blog/opentelemetry-observability">Independence with OpenTelemetry on Elastic</a></p></li>
<li><p><a href="https://www.elastic.co/blog/implementing-kubernetes-observability-security-opentelemetry">Modern observability and security on Kubernetes with Elastic and OpenTelemetry</a></p></li>
<li><p><a href="https://www.elastic.co/blog/3-models-logging-opentelemetry-elastic">3 models for logging with OpenTelemetry and Elastic</a></p></li>
<li><p><a href="https://www.elastic.co/blog/adding-free-and-open-elastic-apm-as-part-of-your-elastic-observability-deployment">Adding free and open Elastic APM as part of your Elastic Observability deployment</a></p></li>
<li><p><a href="https://www.elastic.co/blog/custom-metrics-app-code-java-agent-plugin">Capturing custom metrics through OpenTelemetry API in code with Elastic</a></p></li>
<li><p><a href="https://www.elastic.co/virtual-events/future-proof-your-observability-platform-with-opentelemetry-and-elastic">Future-proof your observability platform with OpenTelemetry and Elastic</a></p></li>
<li><p><a href="https://www.elastic.co/blog/kubernetes-k8s-observability-elasticsearch-cncf">Elastic Observability: Built for open technologies like Kubernetes, OpenTelemetry, Prometheus, Istio, and more</a></p></li>
</ul>
<p>Don’t have an Elastic Cloud account yet? <a href="https://cloud.elastic.co/registration">Sign up for Elastic Cloud</a> and try out the auto-instrumentation capabilities that I discussed above. I would be interested in getting your feedback about your experience in gaining visibility into your application stack with Elastic.</p>
<p>The release and timing of any features or functionality described in this post remain at Elastic's sole discretion. Any features or functionality not currently available may not be delivered on time or at all._</p>]]></content:encoded>
    <link>https://www.elastic.co/observability-labs/blog/auto-instrumentation-go-applications-opentelemetry</link>
    <guid isPermaLink="false">auto-instrumentation-go-applications-opentelemetry</guid>
    <category><![CDATA[OpenTelemetry]]></category>
    <category><![CDATA[APM]]></category>
    <category><![CDATA[Infrastructure Monitoring]]></category>
    <dc:creator><![CDATA[Damien Mathieu]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt82a63d48c4106992/6a85c7bdbc5bb3efbcf81a4f/observability-launch-series-3-go-auto.jpg" length="0" type="image/jpeg"/>
    <pubDate>Wed, 02 Oct 2024 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Observing Langchain applications with Elastic, OpenTelemetry, and Langtrace]]></title>
    <description><![CDATA[Langchain applications are growing in use. The ability to build out RAG-based applications, simple AI Assistants, and more is becoming the norm. Observing these applications is even harder. Given the various options that are out there, this blog shows how to use OpenTelemetry instrumentation with Langtrace and ingest it into Elastic Observability APM]]></description>
    <content:encoded><![CDATA[<p>As AI-driven applications become increasingly complex, the need for robust tools to monitor and optimize their performance is more critical than ever. LangChain has rapidly emerged as a crucial framework in the AI development landscape, particularly for building applications powered by large language models (LLMs). As its adoption has soared among developers, the need for effective debugging and performance optimization tools has become increasingly apparent. One such essential tool is the ability to obtain and analyze traces from Langchain applications. Tracing provides invaluable insights into the execution flow, helping developers understand and improve their AI-driven systems. <a href="https://www.elastic.co/observability/application-performance-monitoring">Elastic Observability's APM</a> provides an ability to trace your Langchain apps with OpenTelemetry, but you need third-party libraries.</p>
<p>There are several options to trace for Langchain. <a href="https://docs.langtrace.ai/introduction">Langtrace</a> is one such option. Langtrace is an <a href="https://github.com/Scale3-Labs/langtrace">open-source</a> observability software that lets you capture, debug and analyze traces and metrics from all your applications. Langtrace automatically captures traces from LLM APIs/inferences, Vector Databases, and LLM-based Frameworks. Langtrace stands out due to its seamless integration with popular LLM frameworks and its ability to provide deep insights into complex AI workflows without requiring extensive manual instrumentation.</p>
<p>Langtrace has an SDK, a lightweight library that can be installed and imported into your project to collect traces. The traces are OpenTelemetry-based and can be exported to Elastic without using a Langtrace API key.</p>
<p>OpenTelemetry (OTel) is now broadly accepted as the industry standard for tracing. As one of the major Cloud Native Computing Foundation (CNCF) projects, with as many commits as Kubernetes, it is gaining support from major ISVs and cloud providers delivering support for the framework. </p>
<p>Hence, many LangChain-based applications will have multiple components beyond just LLM interactions. Using OpenTelemetry with LangChain is essential. </p>
<p>This blog will cover how you can use Langtrace SDK to trace a simple LangChain Chat app connecting to Azure OpenAI, perform a search in DuckDuckGoSearch and export the output to Elastic.</p>
<h2 id="prerequisites">Pre-requisites:</h2>
<ul>
<li><p>An Elastic Cloud account — <a href="https://cloud.elastic.co/">sign up now</a>, and become familiar with <a href="https://www.elastic.co/guide/en/observability/current/apm-open-telemetry.html">Elastic’s OpenTelemetry configuration</a></p></li>
<li><p>Have a LangChain app to instrument</p></li>
<li><p>Be familiar with using <a href="https://opentelemetry.io/docs/languages/python/libraries/">OpenTelemetry’s Python SDK</a> </p></li>
<li><p>An account on your favorite LLM (AzureOpen AI), with API keys</p></li>
<li><p>The application we used in this blog, called <code>langchainChat</code> can be found in <a href="https://github.com/elastic/observability-examples/tree/main/langchainChat">Github langhcainChat</a>. It is built using Azure OpenAI and DuckDuckGo, but you can easily modify it for your LLM and search of choice.</p></li>
</ul>
<h2 id="appoverviewandoutputinelastic">App Overview and output in Elastic:</h2>
<p>To showcase the combined power of Langtrace and Elastic, we created a simple LangChain app that performs the following steps:</p>
<ol>
<li><p>Takes customer input on the command line. (Queries)</p></li>
<li><p>Sends these to the Azure OpenAI LLM via a LangChain.</p></li>
<li><p>Utilizes chain tools to perform a search using DuckDuckGo.</p></li>
<li><p>The LLM processes the search results and returns the relevant information to the user.</p></li>
</ol>
<p>Here is a sample interaction:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt76b2d020888007a7/6a7f08925967e5c1035dd10c/langchainchat-cli.png" alt="Chat Interaction" /></p>
<p>Here is what the service view looks like after we ran a few queries. </p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt16c8c79d875d3a11/6a7f0895b6b7348147e48c42/langchainchat-overview.png" alt="Service Overview" /></p>
<p>As you can see, Elastic Observability’s APM recognizes the LangChain app and also shows the average latency, throughput, and transactions. Our average latency is 30s since it takes that log for humans to type the query (twice).</p>
<p>You can also select other tabs to see, dependencies, errors, metrics, and more. One interesting part of Elastic APM is the ability to use universal profiling (eBPF) output also analyzed for this service. Here is what our service’s dependency is (Azure OpenAI) with its average latency, throughput, and failed transactions:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt8736bb62fef3dec2/6a7f08982f00b26dbbefe9e3/langchainchat-dependency.png" alt="Dependencies" /></p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltccf60cbec05ce73d/6a7f089a448e4e59fa5c0540/langchainchat-dependency-metrics.png" alt="Dependency-metric" /></p>
<p>We see Azure OpenAI is on average 4s to give us the results.</p>
<p>If we drill into transactions and look at the trace for our queries on Taylor Swift and Pittsburgh Steelers, we can see both queries and their corresponding spans.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt5d3ebad3e3fdf95c/6a7f089e3cab1c74990e4694/langchainchat-trace.png" alt="Trace for two queries" /></p>
<p>In this trace:</p>
<ol>
<li><p>The user makes a query</p></li>
<li><p>Azure OpenAI is called, but it uses a tool (DuckDuckGo) to obtain some results</p></li>
<li><p>Azure OpenAI reviews and returns a summary to the end user</p></li>
<li><p>Repeats for another query</p></li>
</ol>
<p>We noticed that the other long span (other than Azure OpenAI) is Duckduckgo (~1000ms). We can individually look at the span and review the data:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt322bb5b1d8291b1e/6a7f08a12f00b27e54efe9e9/langchainchat-tools-span.png" alt="Span details" /></p>
<h2 id="configuration">Configuration:</h2>
<p>How do we make all this show up in Elastic? Let's go over the steps:</p>
<h3 id="opentelemetryconfiguration">OpenTelemetry Configuration</h3>
<p>To leverage the full capabilities of OpenTelemetry with Langtrace and Elastic, we need to configure the SDK to generate traces and properly set up Elastic’s endpoint and authorization. Detailed instructions can be found in the <a href="https://opentelemetry.io/docs/zero-code/python/#setup">OpenTelemetry Auto-Instrumentation setup documentation</a>.</p>
<h4 id="opentelemetryenvironmentvariables">OpenTelemetry Environment variables:</h4>
<p>For Elastic, you can set the following OpenTelemetry environment variables either in your Linux/Mac environment or directly in the code:</p>
<pre><code>OTEL_EXPORTER_OTLP_ENDPOINT=12345.apm.us-west-2.aws.cloud.es.io:443
OTEL_EXPORTER_OTLP_HEADERS="Authorization=Bearer%20ZZZZZZZ"
OTEL_RESOURCE_ATTRIBUTES="service.name=langchainChat,service.version=1.0,deployment.environment=production"
</code></pre>
<p>In this setup:</p>
<ul>
<li><p><strong>OTEL_EXPORTER_OTLP_ENDPOINT</strong> is configured to send traces to Elastic.</p></li>
<li><p><strong>OTEL_EXPORTER_OTLP_HEADERS</strong> provides the necessary authorization for the Elastic APM server.</p></li>
<li><p><strong>OTEL_RESOURCE_ATTRIBUTES</strong> define key attributes like the service name, version, and deployment environment.</p></li>
</ul>
<p>These values can be easily obtained from Elastic’s APM configuration screen under the OpenTelemetry section.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltfb5cb2d3359013d1/6a7f08a442a1178c6b95bcfe/langchainchat-OTelAPMsetup.png" alt="Span details" /></p>
<p><strong>Note: No agent is required; the OTLP trace messages are sent directly to Elastic’s APM server, simplifying the setup process.</strong></p>
<h3 id="langtracelibrary">Langtrace Library:</h3>
<p>OpenTelemetry's auto-instrumentation can be extended to trace additional frameworks using instrumentation packages. For this blog post, you will need to install the Langtrace Python SDK:</p>
<pre><code>pip install langtrace-python-sdk 
</code></pre>
<p>After installation, you can add the following code to your project:</p>
<pre><code>from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter

from langtrace_python_sdk import langtrace, with_langtrace_root_span
</code></pre>
<h3 id="instrumentation">Instrumentation:</h3>
<p>Once the necessary libraries are installed and the environment variables are configured, you can use auto-instrumentation to trace your application. For example, run the following command to instrument your LangChain application with Elastic:</p>
<pre><code>opentelemetry-instrument python langtrace-elastic-demo.py
</code></pre>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt5d3ebad3e3fdf95c/6a7f089e3cab1c74990e4694/langchainchat-trace.png" alt="Trace for two queries" /></p>
<p>The Langtrace OpenTelemetry library correctly captures the flow with minimal manual instrumentation, apart from integrating the OpenTelemetry library. Additionally, the LLM spans captured by Langtrace also include useful metadata such as token counts, model hyper-parameter settings etc. Note that the generated spans follow the OTEL GenAI semantics described <a href="https://opentelemetry.io/docs/specs/semconv/attributes-registry/gen-ai/">here</a>.</p>
<p>In summary, the instrumentation process involves:</p>
<ol>
<li><p>Capturing customer input from the command line (Queries).</p></li>
<li><p>Sending these queries to the Azure OpenAI LLM via a LangChain.</p></li>
<li><p>Utilizing chain tools, such as DuckDuckGo, to perform searches.</p></li>
<li><p>The LLM processes the results and returns the relevant information to the user.</p></li>
</ol>
<h2 id="conclusion">Conclusion</h2>
<p>By combining the power of <a href="https://langtrace.ai/">Langtrace</a> with Elastic, developers can achieve unparalleled visibility into their LangChain applications, ensuring optimized performance and quicker debugging. This powerful combination simplifies the complex task of monitoring AI-driven systems, enabling you to focus on what truly matters—delivering value to your users. Throughout this blog,we've covered the following essential steps and concepts:</p>
<ul>
<li><p>How to manually instrument Langchain with OpenTelemetry</p></li>
<li><p>How to properly initialize OpenTelemetry and add a custom span</p></li>
<li><p>How to easily set the OTLP ENDPOINT and OTLP HEADERS with Elastic without the need for a collector</p></li>
<li><p>How to view and analyze traces in Elastic Observability APM</p></li>
</ul>
<p>These steps provide a clear and actionable guide for developers looking to integrate robust tracing capabilities into their LangChain applications.</p>
<p>We hope this guide makes understanding and implementing OpenTelemetry tracing for LangChain simple, ensuring seamless integration with Elastic.</p>
<p><strong>Additional resources for OpenTelemetry with Elastic:</strong></p>
<ul>
<li><p><a href="https://www.elastic.co/blog/opentelemetry-observability">Independence with OpenTelemetry on Elastic</a></p></li>
<li><p><a href="https://www.elastic.co/blog/implementing-kubernetes-observability-security-opentelemetry">Modern observability and security on Kubernetes with Elastic and OpenTelemetry</a></p></li>
<li><p><a href="https://www.elastic.co/blog/3-models-logging-opentelemetry-elastic">3 models for logging with OpenTelemetry and Elastic</a></p></li>
<li><p><a href="https://www.elastic.co/blog/adding-free-and-open-elastic-apm-as-part-of-your-elastic-observability-deployment">Adding free and open Elastic APM as part of your Elastic Observability deployment</a></p></li>
<li><p><a href="https://www.elastic.co/blog/monitor-openai-api-gpt-models-opentelemetry-elastic">Monitor OpenAI API and GPT models with OpenTelemetry and Elastic</a></p></li>
<li><p>Futureproof<a href="https://www.elastic.co/virtual-events/future-proof-your-observability-platform-with-opentelemetry-and-elastic"> your observability platform with OpenTelemetry and Elastic</a></p></li>
<li><p>Instrumentation resources:</p></li>
<li><p>Python: <a href="https://www.elastic.co/blog/auto-instrumentation-of-python-applications-opentelemetry">Auto-instrumentation</a>, <a href="https://www.elastic.co/blog/manual-instrumentation-of-python-applications-opentelemetry">Manual instrumentation</a></p></li>
<li><p>Java: <a href="https://www.elastic.co/blog/auto-instrumentation-of-java-applications-opentelemetry">Auto-instrumentation</a>, <a href="https://www.elastic.co/blog/manual-instrumentation-of-java-applications-opentelemetry">Manual instrumentation </a></p></li>
<li><p>Node.js: <a href="https://www.elastic.co/blog/auto-instrument-nodejs-applications-opentelemetry">Auto-instrumentation</a>, <a href="https://www.elastic.co/blog/manual-instrumentation-of-nodejs-applications-opentelemetry">Manual instrumentation</a></p></li>
<li><p>.NET: <a href="https://www.elastic.co/blog/auto-instrumentation-of-net-applications-opentelemetry">Auto-instrumentation</a>, <a href="https://www.elastic.co/blog/manual-instrumentation-of-net-applications-opentelemetry">Manual instrumentation</a></p></li>
<li><p><a href="https://docs.langtrace.ai/supported-integrations/observability-tools/elastic">Elastic APM - Langtrace AI Docs</a></p></li>
</ul>]]></content:encoded>
    <link>https://www.elastic.co/observability-labs/blog/elastic-opentelemetry-langchain-tracing-langtrace</link>
    <guid isPermaLink="false">elastic-opentelemetry-langchain-tracing-langtrace</guid>
    <category><![CDATA[LLM Observability]]></category>
    <category><![CDATA[OpenTelemetry]]></category>
    <category><![CDATA[APM]]></category>
    <category><![CDATA[Infrastructure Monitoring]]></category>
    <dc:creator><![CDATA[Bahubali Shetti,Karthik Kalyanaraman,Yemi Adejumobi]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt405f53422a8d599e/6a7f08a81967ea8d8333057d/elastic-langtrace.jpg" length="0" type="image/jpeg"/>
    <pubDate>Mon, 02 Sep 2024 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Tailoring span names and enriching spans without changing code with OpenTelemetry - Part 1]]></title>
    <description><![CDATA[The OpenTelemetry Collector offers powerful capabilities to enrich and refine telemetry data before it reaches your observability tools. In this blog post, we'll explore how to leverage the Collector to create more meaningful transaction names in Elastic Observability, significantly enhancing the value of your monitoring data.]]></description>
    <content:encoded><![CDATA[<p>The OpenTelemetry Collector offers powerful capabilities to enrich and refine telemetry data before it reaches your observability tools. In this blog post, we'll explore how to leverage the Collector to create more meaningful transaction names in Elastic Observability, significantly enhancing the value of your monitoring data.</p>
<p>Consider this scenario: You have a transaction labeled simply as "HTTP GET" with an average response time of 5ms. However, this generic label masks a variety of distinct operations – payment processing, user logins, and adding items to a cart. Does that 5ms average truly represent the performance of these diverse actions? Clearly not. </p>
<p>The other problem that happens is that span traces become all mixed up so that login spans and image serving spans all become part of the same bucket, this makes things like latency correlation analysis hard in Elastic. </p>
<p>We'll focus on a specific technique using the collector's attributes, and transform processors to extract meaningful information from HTTP URLs and use it to create more descriptive span names. This approach not only improves the accuracy of your metrics but also enhances your ability to quickly identify and troubleshoot performance issues across your microservices architecture.</p>
<p>By using these processors in combination, we can quickly address the issue of overly generic transaction names, creating more granular and informative identifiers that provide accurate visibility into your services' performance.</p>
<p>However, it's crucial to approach this technique with caution. While more detailed transaction names can significantly improve observability, they can also lead to an unexpected challenge: cardinality explosion. As we dive into the implementation details, we'll also discuss how to strike the right balance between granularity and manageability, ensuring that our solution enhances rather than overwhelms our observability stack.</p>
<p>In the following sections, we'll walk through the configuration step-by-step, explaining how each processor contributes to our goal, and highlighting best practices to avoid potential pitfalls like cardinality issues. Whether you're new to OpenTelemetry or looking to optimize your existing setup, this guide will help you unlock more meaningful insights from your telemetry data.</p>
<h2 id="prerequisitesandconfiguration">Prerequisites and configuration</h2>
<p>If you plan on following this blog, here are some of the components and details we used to set up the configuration:</p>
<ul>
<li>Ensure you have an account on Elastic Cloud and a deployed stack (see instructions <a href="https://www.elastic.co/cloud/">here</a>).</li>
<li>I am also using the OpenTelemetry demo in my environment, this is important to follow along with as this demo has the specific issue I want to address. You should clone the repository and follow the instructions <a href="https://github.com/elastic/opentelemetry-demo">here</a> to get this up and running. I recommend using Kubernetes and I will be doing this in my AWS EKS (Elastic Kubernetes Service) environment. </li>
</ul>
<h3 id="theopentelemetrydemo">The OpenTelemetry Demo</h3>
<p>The OpenTelemetry Demo is a comprehensive, microservices-based application designed to showcase the capabilities and best practices of OpenTelemetry instrumentation. It simulates an e-commerce platform, incorporating various services such as frontend, cart, checkout, and payment processing. This demo serves as an excellent learning tool and reference implementation for developers and organizations looking to adopt OpenTelemetry.</p>
<p>The demo application generates traces, metrics, and logs across its interconnected services, demonstrating how OpenTelemetry can provide deep visibility into complex, distributed systems. It's particularly useful for experimenting with different collection, processing, and visualization techniques, making it an ideal playground for exploring observability concepts and tools like the OpenTelemetry Collector.</p>
<p>By using real-world scenarios and common architectural patterns, the OpenTelemetry Demo helps users understand how to effectively implement observability in their own applications and how to leverage the data for performance optimization and troubleshooting.</p>
<p>Once you have an Elastic Cloud instance and you fire up the OpenTelemetry demo, you should see something like this on the Elastic Service Map page:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt3fbb0ad570a0e54e/6a7f1b78bdcff0139dc432bb/image3.png" alt="" /></p>
<p>Navigating to the traces page will give you the following set up.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt6e49f9f047e722b1/6a7f1b7b42a117ba8b95c337/image1.png" alt="" /></p>
<p>As you can see there are some very broad transaction names here like HTTP GET and the averages will not be very accurate for specific business functions within your services as shown. </p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltb7a70a039a860e25/6a7f1b7efc63ab374564d092/image6.png" alt="" /></p>
<p>So let's fix that with the OpenTelemetry Collector. </p>
<h2 id="theopentelemetrycollector">The OpenTelemetry Collector</h2>
<p>The OpenTelemetry Collector is a vital component in the OpenTelemetry ecosystem, serving as a vendor-agnostic way to receive, process, and export telemetry data. It acts as a centralized observability pipeline that can collect traces, metrics, and logs from various sources, then transform and route this data to multiple backend systems. </p>
<p>The collector's flexible architecture allows for easy configuration and extension through a wide range of receivers, processors, and exporters which you can explore over <a href="https://github.com/open-telemetry/opentelemetry-collector-contrib">here</a>. I have personally found navigating the 'contrib' archive incredibly useful for finding techniques that I didn't know existed. This makes the OpenTelemetry Collector an invaluable tool for organizations looking to standardize their observability data pipeline, reduce overhead, and seamlessly integrate with different monitoring and analysis platforms.</p>
<p>Let's go back to our problem, how do we change the transaction names that Elastic is using to something more useful so that our HTTP GET translates to something like payment-service/login? The first thing we do is we take the full http url and consider which parts of it relate to our transaction.  Looking at the span details we see a url </p>
<pre><code>my-otel-demo-frontendproxy:8080/api/recommendations?productIds=&amp;sessionId=45a9f3a4-39d8-47ed-bf16-01e6e81c80bc&amp;currencyCode=
</code></pre>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blte7a6dfc4f52f3743/6a7f1b8173d9bd3cb029df82/image4.png" alt="" /></p>
<p>Now obviously we wouldn't want to create transaction names that map to every single session id, that would lead to the cardinality explosion we talked about earlier, however, something like the first two parts of the url 'api/recommendations' looks like exactly the kind of thing we need.</p>
<h3 id="theattributesprocessor">The attributes processor</h3>
<p>The OpenTelemetry collector gives us a useful tool <a href="https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/main/processor/attributesprocessor">here</a>, the attributes processor can help us extract parts of the url to use later in our observability pipeline. To do this is very simple, we simply build a regex like this one below. Now I should mention that I did not generate this regex myself but I used an LLM to do this for me, never fear regex again!</p>
<pre><code>attributes:
  actions:
    - key: http.url
      action: extract
      pattern: '^(?P&lt;short_url&gt;https?://[^/]+(?:/[^/]+)*)(?:/(?P&lt;url_truncated_path&gt;[^/?]+/[^/?]+))(?:\?|/?$)'
</code></pre>
<p>This configuration is doing some heavy lifting for us, so let's break it down:</p>
<ul>
<li>We're using the attributes processor, which is perfect for manipulating span attributes.</li>
<li>We're targeting the http.url attribute of incoming spans.</li>
<li>The extract action tells the processor to pull out specific parts of the URL using our regex pattern.</li>
</ul>
<p>Now, about that regex - it's designed to extract two key pieces of information:</p>
<ol>
<li><code>short_url</code>: This captures the protocol, domain, and optionally the first path segment. For example, in "https://example.com/api/users/profile", it would grab "https://example.com/api".</li>
<li><code>url_truncated_path</code>: This snags the next two path segments (if they exist). In our example, it would extract "users/profile".</li>
</ol>
<p>Why is this useful? Well, it allows us to create more specific transaction names based on the URL structure, without including overly specific details that could lead to cardinality explosion. For instance, we avoid capturing unique IDs or query parameters that would create a new transaction name for every single request.</p>
<p>So, if we have a URL like "https://example.com/api/users/profile?id=123", our extracted <code>url_truncated_path</code> would be "users/profile". This gives us a nice balance - it's more specific than just "HTTP GET", but not so specific that we end up with thousands of unique transaction names.</p>
<p>Now it's worth mentioning here that if you don't have an attribute you want to use for naming your transactions it is worth looking at the options for your SDK or agent, as an example the Java automatic instrumentation Otel agent has the <a href="https://opentelemetry.io/docs/zero-code/java/agent/instrumentation/http/#capturing-http-request-and-response-headers">following options</a> for capturing request and response headers. You can then subsequently use this data to name your transactions if the url is insufficient! </p>
<p>In the next steps, we'll see how to use this extracted information to create more meaningful span names, providing better granularity in our observability data without overwhelming our system. Remember, the goal is to enhance our visibility, not to drown in a sea of overly specific metrics!</p>
<h3 id="thetransformprocessor">The transform processor</h3>
<p>Now that we've extracted the relevant parts of our URLs, it's time to put that information to good use. Enter the transform processor - our next powerful tool in the OpenTelemetry Collector pipeline.</p>
<p>The <a href="https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/processor/transformprocessor">transform processor</a> allows us to modify various aspects of our telemetry data, including span names. Here's the configuration we'll use:</p>
<pre><code>transform:
  trace_statements:
    - context: span
      statements:
        - set(name, attributes["url_truncated_path"])
</code></pre>
<p>Let's break this down:</p>
<ul>
<li>We're using the transform processor, which gives us fine-grained control over our spans.</li>
<li>We're focusing on <code>trace_statements</code>, as we want to modify our trace spans.</li>
<li>The <code>context: span</code> tells the processor to apply these changes to each individual span.</li>
<li>Our statement is where the magic happens: we're setting the span's name to the value of the <code>url_truncated_path</code> attribute we extracted earlier.</li>
</ul>
<p>What does this mean in practice? Remember our previous example URL "https://example.com/api/users/profile?id=123"? Instead of a generic span name like "HTTP GET", we'll now have a much more informative name: "users/profile".</p>
<p>This transformation brings several benefits:</p>
<ol>
<li>Improved Readability: At a glance, you can now see what part of your application is being accessed.</li>
<li>Better Aggregation: You can easily group and analyze similar requests, like all operations on user profiles.</li>
<li>Balanced Cardinality: We're specific enough to be useful, but not so specific that we create a new span name for every unique URL.</li>
</ol>
<p>By combining the attribute extraction we did earlier with this transformation, we've created a powerful system for generating meaningful span names. This approach gives us deep insight into our application's behavior without the risk of cardinality explosion. </p>
<h2 id="puttingitalltogether">Putting it All Together</h2>
<p>The resulting config for the OpenTelemetry collector is below remember this goes into the opentelemetry-demo/kubernetes/elastic-helm/configmap-deployment.yaml and is applied with kubectl apply -f configmap-deployment.yaml</p>
<pre><code>---
apiVersion: v1
kind: ConfigMap
metadata:
  name: elastic-otelcol-agent
  namespace: default
  labels:
    app.kubernetes.io/name: otelcol

data:
  relay: |
    connectors:
      spanmetrics: {}
    exporters:
      debug: {}
      otlp/elastic:
        endpoint: ${env:ELASTIC_APM_ENDPOINT}
        compression: none
        headers:
          Authorization: Bearer ${ELASTIC_APM_SECRET_TOKEN}
    extensions:
    processors:
      batch: {}
      resource:
        attributes:
          - key: deployment.environment
            value: "opentelemetry-demo"
            action: upsert
      attributes:
        actions:
          - key: http.url
            action: extract
            pattern: '^(?P&lt;short_url&gt;https?://[^/]+(?:/[^/]+)*)(?:/(?P&lt;url_truncated_path&gt;[^/?]+/[^/?]+))(?:\?|/?$)'
      transform:
        trace_statements:
          - context: span
            statements:
              - set(name, attributes["url_truncated_path"])
    receivers:
      httpcheck/frontendproxy:
        targets:
        - endpoint: http://example-frontendproxy:8080
      otlp:
        protocols:
          grpc:
            endpoint: ${env:MY_POD_IP}:4317
          http:
            cors:
              allowed_origins:
              - http://*
              - https://*
            endpoint: ${env:MY_POD_IP}:4318
    service:
      extensions:
      pipelines:
        logs:
          exporters:
          - debug
          - otlp/elastic
          processors:
          - batch
          - resource
          - attributes
          - transform
          receivers:
          - otlp
        metrics:
          exporters:
          - otlp/elastic
          - debug
          processors:
          - batch
          - resource
          receivers:
          - httpcheck/frontendproxy
          - otlp
          - spanmetrics
        traces:
          exporters:
          - otlp/elastic
          - debug
          - spanmetrics
          processors:
          - batch
          - resource
          - attributes
          - transform
          receivers:
          - otlp
      telemetry:
        metrics:
          address: ${env:MY_POD_IP}:8888
</code></pre>
<p>You'll notice that we tie everything together by adding our enrichment and transformations to the traces section in pipelines at the bottom of the collector config. This is the definition of our observability pipeline, bringing together all the pieces we've discussed to create more meaningful and actionable telemetry data.</p>
<p>By implementing this configuration, you're taking a significant step towards more insightful observability. You're not just collecting data; you're refining it to provide clear, actionable insights into your application's performance, check out the final result below!</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt9b8c24e95c33f1a4/6a7f1b853cab1c804f0e4cb5/image2.png" alt="" /></p>
<h2 id="readytotakeyourobservabilitytothenextlevel">Ready to Take Your Observability to the Next Level?</h2>
<p>Implementing OpenTelemetry with Elastic Observability opens up a world of possibilities for understanding and optimizing your applications. But this is just the beginning! To further enhance your observability journey, check out these valuable resources:</p>
<ol>
<li><a href="https://www.elastic.co/observability-labs/blog/infrastructure-monitoring-with-opentelemetry-in-elastic-observability">Infrastructure Monitoring with OpenTelemetry in Elastic Observability</a></li>
<li><a href="https://www.elastic.co/observability-labs/blog/tag/opentelemetry">Explore More OpenTelemetry Content</a></li>
<li><a href="https://www.elastic.co/observability-labs/blog/using-the-otel-operator-for-injecting-java-agents">Using the OTel Operator for Injecting Java Agents</a></li>
<li><a href="https://www.elastic.co/what-is/opentelemetry">What is OpenTelemetry?</a></li>
</ol>
<p>We encourage you to dive deeper, experiment with these configurations, and see how they can transform your observability data. Remember, the key is to find the right balance between detail and manageability.</p>
<p>Have you implemented similar strategies in your observability pipeline? We'd love to hear about your experiences and insights. Share your thoughts in the comments below or reach out to us on our community forums.</p>
<p>Stay tuned for Part 2 of this series, where we will look at an advanced technique for collecting more data that can help you get even more granular by collecting Span names, baggage and data for metrics using a Java plugin all without code.</p>]]></content:encoded>
    <link>https://www.elastic.co/observability-labs/blog/tailoring-span-names-and-enriching-spans-without-changing-code-with-opentelemetry</link>
    <guid isPermaLink="false">tailoring-span-names-and-enriching-spans-without-changing-code-with-opentelemetry</guid>
    <category><![CDATA[OpenTelemetry]]></category>
    <category><![CDATA[APM]]></category>
    <category><![CDATA[Infrastructure Monitoring]]></category>
    <dc:creator><![CDATA[David Hope]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt89485fbb57db9f4e/6a7f1b8796b5a6989c87b8b1/tailor.jpg" length="0" type="image/jpeg"/>
    <pubDate>Mon, 26 Aug 2024 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Introducing Elastic Distributions of OpenTelemetry]]></title>
    <description><![CDATA[Elastic is proud to introduce Elastic Distributions of OpenTelemetry (EDOT), which contains Elastic’s versions of the OpenTelemetry Collector and several language SDKs like Python, Java, .NET, and NodeJS. These help provide enhanced features and enterprise-grade support for EDOT.]]></description>
    <content:encoded><![CDATA[<p>We are announcing the availability of Elastic Distributions of OpenTelemetry (EDOT). These Elastic distributions, currently in tech preview,  have been developed to enhance the capabilities of standard OpenTelemetry distributions and improve existing OpenTelemetry support from Elastic. </p>
<p>The Elastic Distributions of OpenTelemetry (EDOT) are composed of OpenTelemetry (OTel) project components, OTel Collector, and language SDKs,  which provide users with the necessary capabilities and out-of-the-box configurations, enabling quick and effortless infra and application monitoring.</p>
<p>While OTel components are feature-rich, enhancements through the community can take time. Additionally, support is left up to the community or individual users and organizations. Hence EDOT will bring the following to end users:</p>
<ul>
<li><p><strong>Deliver enhanced features earlier than OTel</strong>: By providing features unavailable in the “vanilla” OpenTelemetry components, we can quickly meet customers’ requirements while still providing an OpenTelemetry native and vendor-agnostic instrumentation for their applications. Elastic will continuously upstream these enhanced features.</p></li>
<li><p><strong>Enhanced OTel support</strong> - By maintaining Elastic distributions, we can better support customers with enhancements and fixes outside of the OTel release cycles. In addition, Elastic support can troubleshoot issues on the EDOT.</p></li>
</ul>
<p>EDOT currently includes the following tech preview components, which will  grow over time:</p>
<ul>
<li><p><a href="https://www.elastic.co/observability-labs/blog/elastic-distribution-opentelemetry-collector">Elastic Distribution of OpenTelemetry (EDOT) Collector</a></p></li>
<li><p><a href="https://www.elastic.co/observability-labs/blog/elastic-distribution-opentelemetry-java-agent">Elastic Distribution of OpenTelemetry (EDOT) Java</a>.</p></li>
<li><p><a href="https://www.elastic.co/observability-labs/blog/elastic-opentelemetry-distribution-python">Elastic Distribution of OpenTelemetry (EDOT) Python</a></p></li>
<li><p><a href="https://www.elastic.co/observability-labs/blog/elastic-opentelemetry-distribution-node-js">Elastic Distribution of OpenTelemetry (EDOT) NodeJS</a></p></li>
<li><p><a href="https://www.elastic.co/observability-labs/blog/elastic-opentelemetry-distribution-dotnet-applications">Elastic Distribution of OpenTelemetry (EDOT) .NET</a></p></li>
<li><p><a href="https://www.elastic.co/observability-labs/blog/apm-ios-android-native-apps">Elastic Distribution of OpenTelemetry (EDOT)  iOS and Android</a></p></li>
</ul>
<p>Details and documentation for all EDOT are available in our public <a href="https://github.com/elastic/opentelemetry">OpenTelemetry GitHub repository</a>. </p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt43eeb982d6e02dbd/6a9fb52927a5318002dcacc7/edot-components-dark.png" alt="EDOT Components" /></p>
<h2 id="elasticdistributionofopentelemetryedotcollectoraidelasticdistributionofopentelemetryedotcollectora">Elastic Distribution of OpenTelemetry (EDOT) Collector<a id="elastic-distribution-of-opentelemetry-edot-collector"></a></h2>
<p>The EDOT Collector, recently released with the 8.15 release of Elastic Observability enhances Elastic’s existing OTel capabilities. The EDOT Collector can, in addition to service monitoring, forward application logs, infrastructure logs, and metrics using standard OpenTelemetry Collector receivers like file logs and host metrics receivers.</p>
<p>Additionally, users of the Elastic Distribution of the OpenTelemetry Collector benefit from container logs automatically enriched with Kubernetes metadata by leveraging the powerful <a href="https://opentelemetry.io/blog/2024/otel-collector-container-log-parser/">container log parser</a> that Elastic recently contributed. This OpenTelemetry-based enrichment enhances the context and value of the collected logs, providing deeper insights and more effective troubleshooting capabilities.</p>
<p>This new collector distribution ensures that exported data is fully compatible with the Elastic Platform, enhancing the overall observability experience. Elastic also ensures that Elastic-curated UIs can seamlessly handle both the Elastic Common Schema (ECS) and OpenTelemetry formats.</p>
<h2 id="elasticdistributionsforlanguagesdksaidelasticdistributionsforlanguagesdksa">Elastic Distributions for Language SDKs<a id="elastic-distributions-for-language-sdks"></a></h2>
<p><a href="https://www.elastic.co/guide/en/apm/agent/index.html">Elastic's APM agents</a> have capabilities yet to be available in the OTel SDKs. EDOT brings these capabilities into the OTel language SDKs while maintaining seamless integration with Elastic Observability. Elastic will release OTel versions of all its APM agents, and continue to add additional language SDKs mirroring OTel.</p>
<h2 id="continuedsupportfornativeotelcomponentsaidcontinuedsupportfornativeotelcomponentsa">Continued support for Native OTel components<a id="continued-support-for-native-otel-components"></a></h2>
<p>EDOT does not preclude users from using native components. Users are still able to use:</p>
<ul>
<li><p><strong>OpenTelemetry Vanilla Language SDKs:</strong> use standard OpenTelemetry code instrumentation for many popular programming languages sending OTLP traces to Elastic via APM server.</p></li>
<li><p><strong>Upstream Distribution of OpenTelemetry Collector (Contrib or Custom):</strong> Send traces using the OpenTelemetry Collector with OTLP receiver and OTLP exporter to Elastic via APM server.</p></li>
</ul>
<p>Elastic is committed to contributing EDOT features or components upstream into the OpenTelemetry community, fostering a collaborative environment, and enhancing the overall OpenTelemetry ecosystem.</p>
<h2 id="extendingourcommitmenttovendoragnosticdatacollectionaidextendingourcommitmenttovendoragnosticdatacollectiona">Extending our commitment to vendor-agnostic data collection<a id="extending-our-commitment-to-vendor-agnostic-data-collection"></a></h2>
<p>Elastic remains committed to supporting OpenTelemetry by being OTel first and building a vendor-agnostic framework. As OpenTelemetry constantly grows its support of SDKs and components,  Elastic will continue to refine and mirror EDOT to OpenTelemetry and push enhancements upstream. </p>
<p>Over the past year, Elastic has been active in OTel through its <a href="https://opentelemetry.io/blog/2023/ecs-otel-semconv-convergence/">donation of Elastic Common Schema (ECS)</a>, contributions to the native <a href="https://www.elastic.co/observability-labs/blog/elastic-distribution-opentelemetry-collector">OpenTelemetry Collector</a> and language SDKs, and a recent <a href="https://www.elastic.co/observability-labs/blog/elastic-profiling-agent-acceptance-opentelemetry">donation of its Universal Profiling agent</a> to OpenTelemetry. </p>
<p>EDOT  builds on our decision to fully adopt and recommend OpenTelemetry as the preferred solution for observing applications. With EDOT, Elastic customers can future-proof their investments and adopt OpenTelemetry, giving them vendor-neutral instrumentation with Elastic enterprise-grade support.</p>
<p>Our vision is that Elastic will work with the OpenTelemetry community to donate features through the standardization processes and contribute the code to implement those in the native OpenTelemetry components. In time, as OTel capabilities advance, and many of the Elastic-exclusive features transition into OpenTelemetry, we look forward to no longer having Elastic Distributions for OpenTelemetry.. In the meantime, we can deliver those capabilities via our OpenTelemetry distributions.</p>]]></content:encoded>
    <link>https://www.elastic.co/observability-labs/blog/elastic-distributions-opentelemetry</link>
    <guid isPermaLink="false">elastic-distributions-opentelemetry</guid>
    <category><![CDATA[OpenTelemetry]]></category>
    <category><![CDATA[APM]]></category>
    <category><![CDATA[Infrastructure Monitoring]]></category>
    <dc:creator><![CDATA[Alexander Wert,Miguel Luna,Bahubali Shetti]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt1c945be5a78916b3/6a7f07a5b43770d70b4d6a91/edot-image.png" length="0" type="image/png"/>
    <pubDate>Thu, 15 Aug 2024 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Tracing LangChain apps with Elastic, OpenLLMetry, and OpenTelemetry]]></title>
    <description><![CDATA[LangChain applications are growing in use. The ability to build out RAG-based applications, simple AI Assistants, and more is becoming the norm. Observing these applications is even harder. Given the various options that are out there, this blog shows how to use OpenTelemetry instrumentation with OpenLLMetry and ingest it into Elastic Observability APM]]></description>
    <content:encoded><![CDATA[<p>LangChain has rapidly emerged as a crucial framework in the AI development landscape, particularly for building applications powered by large language models (LLMs). As its adoption has soared among developers, the need for effective debugging and performance optimization tools has become increasingly apparent. One such essential tool is the ability to obtain and analyze traces from LangChain applications. Tracing provides invaluable insights into the execution flow, helping developers understand and improve their AI-driven systems. </p>
<p>There are several options to trace for LangChain. One is Langsmith, ideal for detailed tracing and a complete breakdown of requests to large language models (LLMs). However, it is specific to Langchain. OpenTelemetry (OTel) is now broadly accepted as the industry standard for tracing. As one of the major Cloud Native Computing Foundation (CNCF) projects, with as many commits as Kubernetes, it is gaining support from major ISVs and cloud providers delivering support for the framework. </p>
<p>Hence, many LangChain-based applications will have multiple components beyond just LLM interactions. Using OpenTelemetry with LangChain is essential. OpenLLMetry is an available option for tracing Langchain apps in addition to Langsmith.</p>
<p>This blog will show how you can get LangChain tracing into Elastic using the OpenLLMetry library <code>opentelemetry-instrumentation-langchain</code>.</p>
<h2 id="prerequisitesaidprerequisitesa">Pre-requisites:<a id="pre-requisites"></a></h2>
<ul>
<li><p>An Elastic Cloud account — <a href="https://cloud.elastic.co/">sign up now</a>, and become familiar with <a href="https://www.elastic.co/guide/en/observability/current/apm-open-telemetry.html">Elastic’s OpenTelemetry configuration</a></p></li>
<li><p>Have a LangChain app to instrument</p></li>
<li><p>Be familiar with using <a href="https://opentelemetry.io/docs/languages/python/libraries/">OpenTelemetry’s Python SDK</a> </p></li>
<li><p>An account on your favorite LLM, with API keys</p></li>
</ul>
<h2 id="overview">Overview</h2>
<p>In highlighting tracing I created a simple LangChain app that does the following:</p>
<ol>
<li><p>Takes customer input on the command line. (Queries)</p></li>
<li><p>Sends these to the Azure OpenAI LLM via a LangChain.</p></li>
<li><p>Chain tools are set to use the search with Tavily </p></li>
<li><p>The LLM uses the output which returns the relevant information to the user.</p></li>
</ol>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blta85f2ad823158d9d/6a7f08acead8ec024fbaa6ad/LangChainAppCLI.png" alt="Chat Interaction" /></p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt55c861f606e4c4f0/6a7f08af42a1170c6a95bd06/LangChainAppInAPM.png" alt="LangChainChat App in Elastic APM" /></p>
<p>As you can see Elastic Observability’s APM recognizes the LangChain App, and also shows the full trace (done with manual instrumentation):</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt45bed91c1521dd6d/6a7f08b24c4bfb1a90ccd38b/LangChainAutoIntrument.png" alt="LangChainChat App in Elastic APM" /></p>
<p>As the above image shows:</p>
<ol>
<li>The user makes a query</li>
<li>Azure OpenAI is called, but it uses a tool (Tavily) to obtain some results</li>
<li>Azure OpenAI reviews and returns a summary to the end user</li>
</ol>
<p>The code was manually instrumented, but auto-instrument can also be used.</p>
<h2 id="opentelemetryconfigurationaidopentelemetryconfigurationa">OpenTelemetry Configuration<a id="opentelemetry-configuration"></a></h2>
<p>In using OpenTelemetry, we need to configure the SDK to generate traces and configure Elastic’s endpoint and authorization. Instructions can be found in <a href="https://opentelemetry.io/docs/zero-code/python/#setup">OpenTelemetry Auto-Instrumentation setup documentation</a>.</p>
<h3 id="opentelemetryenvironmentvariablesaidopentelemetryenvironmentvariablesa">OpenTelemetry Environment variables:<a id="opentelemetry-environment-variables"></a></h3>
<p>OpenTelemetry Environment variables for Elastic can be set as follows in linux (or in the code).</p>
<pre><code>OTEL_EXPORTER_OTLP_ENDPOINT=12345.apm.us-west-2.aws.cloud.es.io:443
OTEL_EXPORTER_OTLP_HEADERS="Authorization=Bearer%20ZZZZZZZ"
OTEL_RESOURCE_ATTRIBUTES="service.name=langchainChat,service.version=1.0,deployment.environment=production"
</code></pre>
<p>As you can see <code>OTEL_EXPORTER_OTLP_ENDPOINT</code> is set to Elastic, and the corresponding authorization header is also provided. These can be easily obtained from Elastic’s APM configuration screen under OpenTelemetry</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt2273479708a89677/6a7f08b5b43770a9da4d6af5/LangChainAppOTelAPMsetup.png" alt="LangChainChat App in Elastic APM" /></p>
<p><strong>Note: No agent is needed, we simply send the OTLP trace messages directly to Elastic’s APM server.</strong> </p>
<h2 id="openllmetrylibraryaidopenllmetrylibrarya">OpenLLMetry Library:<a id="openllmetry-library"></a></h2>
<p>OpenTelemetry's auto-instrumentation can be extended to trace other frameworks via instrumentation packages.</p>
<p>First, you must install the following package: </p>
<p><code>pip install opentelemetry-instrumentation-langchain</code></p>
<p>This library was developed by OpenLLMetry. </p>
<p>Then you will need to add the following to the code.</p>
<pre><code>from opentelemetry.instrumentation.langchain import LangchainInstrumentor
LangchainInstrumentor().instrument()
</code></pre>
<h2 id="instrumentationaidinstrumentationa">Instrumentation<a id="instrumentation"></a></h2>
<p>Once the libraries are added, and the environment variables are set, you can use auto-instrumentation With auto-instrumentation, the following:</p>
<pre><code>opentelemetry-instrument python tavilyAzureApp.py
</code></pre>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt45bed91c1521dd6d/6a7f08b24c4bfb1a90ccd38b/LangChainAutoIntrument.png" alt="LangChainChat App in Elastic APM" /></p>
<p>The OpenLLMetry library does pull out the flow correctly with minimal manual instrumentation except for adding the OpenLLMetry library.</p>
<ol>
<li><p>Takes customer input on the command line. (Queries)</p></li>
<li><p>Sends these to the Azure OpenAI LLM via a Lang chain.</p></li>
<li><p>Chain tools are set to use the search with Tavily </p></li>
<li><p>The LLM uses the output which returns the relevant information to the user.</p></li>
</ol>
<h3 id="manualinstrumentationaidmanualinstrumentationa">Manual-instrumentation<a id="manual-instrumentation"></a></h3>
<p>If you want to get more details out of the application, you will need to manually instrument. To get more traces follow my <a href="https://www.elastic.co/observability-labs/blog/manual-instrumentation-python-apps-opentelemetry">Python instrumentation guide</a>. This guide will walk you through setting up the necessary OpenTelemetry bits, Additionally, you can also look at the documentation in <a href="https://opentelemetry.io/docs/languages/python/instrumentation/">OTel for instrumenting in Python</a>.</p>
<p>Note that the env variables <code>OTEL_EXPORTER_OTLP_HEADERS</code> and <code>OTEL_EXPORTER_OTLP_ENDPOINT</code> are set as noted in the section above. You can also set up the <code>OTEL_RESOURCE_ATTRIBUTES</code>. </p>
<p>Once you follow the steps in either guide and initiate the tracer, you will have to essentially just add the span where you want to get more details. In the example below, only one line of code is added for span initialization. </p>
<p>Look at the placement of with <code>tracer.start_as_current_span("getting user query") as span:</code> below</p>
<pre><code># Creates a tracer from the global tracer provider
tracer = trace.get_tracer("newsQuery")

async def chat_interface():
    print("Welcome to the AI Chat Interface!")
    print("Type 'quit' to exit the chat.")

    with tracer.start_as_current_span("getting user query") as span:
        while True:
            user_input = input("\nYou: ").strip()

            if user_input.lower() == 'quit':
                print("Thank you for chatting. Goodbye!")
                break

            print("AI: Thinking...")
            try:
                result = await chain.ainvoke({"query": user_input})
                print(f"AI: {result.content}")
            except Exception as e:
                print(f"An error occurred: {str(e)}")


if __name__ == "__main__":
    asyncio.run(chat_interface())
</code></pre>
<p>As you can see, with manual instrumentation, we get the following trace:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt54624a5f8db253fa/6a7f08b82f00b2c4d8efe9f3/LangChainAppManualTrace.png" alt="LangChainChat App in Elastic APM" /></p>
<p>Which calls out when we enter our query function. <code>async def chat_interface()</code></p>
<h2 id="conclusionaidconclusiona">Conclusion<a id="conclusion"></a></h2>
<p>In this blog, we discussed the following:</p>
<ul>
<li><p>How to manually instrument LangChain with OpenTelemetry</p></li>
<li><p>How to properly initialize OpenTelemetry and add a custom span</p></li>
<li><p>How to easily set the OTLP ENDPOINT and OTLP HEADERS with Elastic without the need for a collector</p></li>
<li><p>See traces in Elastic Observability APM</p></li>
</ul>
<p>Hopefully, this provides an easy-to-understand walk-through of instrumenting LangChain with OpenTelemetry and how easy it is to send traces into Elastic.</p>
<p><strong>Additional resources for OpenTelemetry with Elastic:</strong></p>
<ul>
<li><p><a href="https://www.elastic.co/blog/opentelemetry-observability">Independence with OpenTelemetry on Elastic</a></p></li>
<li><p><a href="https://www.elastic.co/blog/implementing-kubernetes-observability-security-opentelemetry">Modern observability and security on Kubernetes with Elastic and OpenTelemetry</a></p></li>
<li><p><a href="https://www.elastic.co/blog/3-models-logging-opentelemetry-elastic">3 models for logging with OpenTelemetry and Elastic</a></p></li>
<li><p><a href="https://www.elastic.co/blog/adding-free-and-open-elastic-apm-as-part-of-your-elastic-observability-deployment">Adding free and open Elastic APM as part of your Elastic Observability deployment</a></p></li>
<li><p><a href="https://www.elastic.co/blog/monitor-openai-api-gpt-models-opentelemetry-elastic">Monitor OpenAI API and GPT models with OpenTelemetry and Elastic</a></p></li>
<li><p>Futureproof<a href="https://www.elastic.co/virtual-events/future-proof-your-observability-platform-with-opentelemetry-and-elastic"> your observability platform with OpenTelemetry and Elastic</a></p></li>
<li><p>Instrumentation resources:</p></li>
<li><p>Python: <a href="https://www.elastic.co/blog/auto-instrumentation-of-python-applications-opentelemetry">Auto-instrumentation</a>, <a href="https://www.elastic.co/blog/manual-instrumentation-of-python-applications-opentelemetry">Manual instrumentation</a></p></li>
<li><p>Java: <a href="https://www.elastic.co/blog/auto-instrumentation-of-java-applications-opentelemetry">Auto-instrumentation</a>, <a href="https://www.elastic.co/blog/manual-instrumentation-of-java-applications-opentelemetry">Manual instrumentation </a></p></li>
<li><p>Node.js: <a href="https://www.elastic.co/blog/auto-instrument-nodejs-applications-opentelemetry">Auto-instrumentation</a>, <a href="https://www.elastic.co/blog/manual-instrumentation-of-nodejs-applications-opentelemetry">Manual instrumentation</a></p></li>
<li><p>.NET: <a href="https://www.elastic.co/blog/auto-instrumentation-of-net-applications-opentelemetry">Auto-instrumentation</a>, <a href="https://www.elastic.co/blog/manual-instrumentation-of-net-applications-opentelemetry">Manual instrumentation</a></p></li>
</ul>
<p>Also log into <a href="https://cloud.elastic.co">cloud.elastic.co</a> to try out Elastic with a free trial.</p>]]></content:encoded>
    <link>https://www.elastic.co/observability-labs/blog/elastic-opentelemetry-langchain-tracing</link>
    <guid isPermaLink="false">elastic-opentelemetry-langchain-tracing</guid>
    <category><![CDATA[LLM Observability]]></category>
    <category><![CDATA[OpenTelemetry]]></category>
    <category><![CDATA[APM]]></category>
    <category><![CDATA[Infrastructure Monitoring]]></category>
    <dc:creator><![CDATA[Bahubali Shetti]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blted3172bb9d8e783d/6a7f08bc9090b0b4ec84e853/LangChainBlogMainImage.png" length="0" type="image/png"/>
    <pubDate>Fri, 02 Aug 2024 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Using a custom agent with the OpenTelemetry Operator for Kubernetes]]></title>
    <description><![CDATA[]]></description>
    <content:encoded><![CDATA[<p>This is the second part of a two part series. The first part is available at <a href="https://www.elastic.co/observability-labs/blog/using-the-otel-operator-for-injecting-java-agents">Zero config OpenTelemetry auto-instrumentation for Kubernetes Java applications</a>. In that first part I walk through setting up and installing the <a href="https://github.com/open-telemetry/opentelemetry-operator/">OpenTelemetry Operator for Kubernetes</a>, and configuring that for auto-instrumentation of a Java application using the <a href="https://github.com/open-telemetry/opentelemetry-java-instrumentation/">OpenTelemetry Java agent</a>. </p>
<p>In this second part, I show how to install <em>any</em> Java agent via the OpenTelemetry operator, using the Elastic Java agents as examples.</p>
<h2 id="installationandconfigurationrecap">Installation and configuration recap</h2>
<p>Part 1 of this series, <a href="https://www.elastic.co/observability-labs/blog/using-the-otel-operator-for-injecting-java-agents">Zero config OpenTelemetry auto-instrumentation for Kubernetes Java applications</a>, details the installation and configuration of the OpenTelemetry operator and an Instrumentation resource. Here is an outline of the steps as a reminder:</p>
<ol>
<li>Install cert-manager, eg <code>kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.14.4/cert-manager.yaml</code></li>
<li>Install the operator, eg <code>kubectl apply -f https://github.com/open-telemetry/opentelemetry-operator/releases/latest/download/opentelemetry-operator.yaml</code></li>
<li>Create an Instrumentation resource</li>
<li>Add an annotation to either the deployment or the namespace</li>
<li>Deploy the application as normal</li>
</ol>
<p>In that first part, steps 3, 4 &amp; 5 were implemented for the <a href="https://github.com/open-telemetry/opentelemetry-java-instrumentation/">OpenTelemetry Java agent</a>. In this blog I’ll implement them for other agents, using the Elastic APM agents as examples. I assume that steps 1 &amp; 2 outlined above have already been done, ie that the operator is now installed. I will continue using the <code>banana</code> namespace for the examples, so ensure that namespace exists (<code>kubectl create namespace banana</code>). As per part 1, if you use any of the example instrumentation definitions below, you’ll need to substitute <code>my.apm.server.url</code> and <code>my-apm-secret-token</code> with the values appropriate for your collector.</p>
<h2 id="usingtheelasticdistributionforopentelemetryjava">Using the Elastic Distribution for OpenTelemetry Java</h2>
<p>From version 0.4.0, the <a href="https://github.com/elastic/elastic-otel-java">Elastic Distribution for OpenTelemetry Java</a> includes the agent jar at the path <code>/javaagent.jar</code> in the docker image - which is essentially all that is needed for a docker image to be usable by the OpenTelemetry operator for auto-instrumentation. This means the Instrumentation resource is straightforward to define, and as it’s a distribution of the OpenTelemetry Java agent, all the OpenTelemetry environment can apply:</p>
<pre><code>apiVersion: opentelemetry.io/v1alpha1
kind: Instrumentation
metadata:
  name: elastic-otel
  namespace: banana
spec:
  exporter:
    endpoint: https://my.apm.server.url
  propagators:
    - tracecontext
    - baggage
    - b3
  sampler:
    type: parentbased_traceidratio
    argument: "1.0"
  java:
    image: docker.elastic.co/observability/elastic-otel-javaagent:1.10.0
    env:
      - name: OTEL_EXPORTER_OTLP_HEADERS
        value: "Authorization=Bearer my-apm-secret-token"
      - name: ELASTIC_OTEL_INFERRED_SPANS_ENABLED
        value: "true"
      - name: ELASTIC_OTEL_SPAN_STACK_TRACE_MIN_DURATION
        value: "50"
</code></pre>
<p>I’ve included environment for switching on several features in the agent, including</p>
<ol>
<li>ELASTIC_APM_PROFILING_INFERRED_SPANS_ENABLED to switch on the inferred spans implementation feature described in <a href="https://www.elastic.co/observability-labs/blog/tracing-data-inferred-spans-opentelemetry">this blog</a></li>
<li>Span stack traces are automatically captured if the span takes more than ELASTIC_OTEL_SPAN_STACK_TRACE_MIN_DURATION (default would be 5ms)</li>
</ol>
<p>Adding in the annotation …</p>
<pre><code>metadata:
  annotations:
    instrumentation.opentelemetry.io/inject-java: "elastic-otel"
</code></pre>
<p>… to the pod yaml gets the application traced, and displayed in the Elastic APM UI, including the inferred child spans and stack traces</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt1cf450efadd3a797/6a7f1c0296b5a66b7787b8c1/elastic-apm-ui-with-stack-trace.png" alt="Elastic APM UI showing methodB traced with stack traces and inferred spans" /></p>
<p>The additions from the features mentioned above are circled in red - inferred spans (for methodC and methodD) bottom left, and the stack trace top right. (Note that the pod included the <code>OTEL_INSTRUMENTATION_METHODS_INCLUDE</code> environment variable set to <code>"test.Testing[methodB]"</code> so that traces from methodB are shown; for pod configuration see the "Trying it" section in <a href="https://www.elastic.co/observability-labs/blog/using-the-otel-operator-for-injecting-java-agents">part 1</a>)</p>
<h2 id="usingtheelasticapmjavaagent">Using the Elastic APM Java agent</h2>
<p>From version 1.50.0, the <a href="https://github.com/elastic/apm-agent-java">Elastic APM Java agent</a> includes the agent jar at the path /javaagent.jar in the docker image - which is essentially all that is needed for a docker image to be usable by the OpenTelemetry operator for auto-instrumentation. This means the Instrumentation resource is straightforward to define:</p>
<pre><code>apiVersion: opentelemetry.io/v1alpha1
kind: Instrumentation
metadata:
  name: elastic-apm
  namespace: banana
spec:
  java:
    image: docker.elastic.co/observability/apm-agent-java:1.55.4
    env:
      - name: ELASTIC_APM_SERVER_URL
        value: "https://my.apm.server.url"
      - name: ELASTIC_APM_SECRET_TOKEN
        value: "my-apm-secret-token"
      - name: ELASTIC_APM_LOG_LEVEL
        value: "INFO"
      - name: ELASTIC_APM_PROFILING_INFERRED_SPANS_ENABLED
        value: "true"
      - name: ELASTIC_APM_LOG_SENDING
        value: "true"
</code></pre>
<p>I’ve included environment for switching on several features in the agent, including</p>
<ul>
<li>ELASTIC_APM_LOG_LEVEL set to the default value (INFO) which could easily be switched to DEBUG</li>
<li>ELASTIC_APM_PROFILING_INFERRED_SPANS_ENABLED to switch on the inferred spans implementation equivalent to the feature described in <a href="https://www.elastic.co/observability-labs/blog/tracing-data-inferred-spans-opentelemetry">this blog</a></li>
<li>ELASTIC_APM_LOG_SENDING which switches on sending logs to the APM UI, the logs are automatically correlated with transactions (for all common logging frameworks)</li>
</ul>
<p>Adding in the annotation …</p>
<pre><code>metadata:
  annotations:
     instrumentation.opentelemetry.io/inject-java: "elastic-apm"
</code></pre>
<p>… to the pod yaml gets the application traced, and displayed in the Elastic APM UI, including the inferred child spans</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt425d331c27b53881/6a7f1c0596b5a6621887b8c5/elastic-apm-ui-with-inferred-spans.png" alt="Elastic APM UI showing methodB traced with inferred spans" /></p>
<p>(Note that the pod included the <code>ELASTIC_APM_TRACE_METHODS</code> environment variable set to <code>"test.Testing#methodB"</code> so that traces from methodB are shown; for pod configuration see the "Trying it" section in <a href="https://www.elastic.co/observability-labs/blog/using-the-otel-operator-for-injecting-java-agents">part 1</a>)</p>
<h2 id="usinganextensionwiththeopentelemetryjavaagent">Using an extension with the OpenTelemetry Java agent</h2>
<p>Setting up an Instrumentation resource for the OpenTelemetry Java agent is straightforward and was done in <a href="https://www.elastic.co/observability-labs/blog/using-the-otel-operator-for-injecting-java-agents">part 1</a> of this two part series - and you can see from the above examples it’s just a matter of deciding on the docker image URL you want to use. However if you want to include an <em>extension</em> in your deployment, this is a little more complex, but also supported by the operator. Basically the extensions you want to include with the agent need to be in docker images - or you have to build an image which includes the extensions that are not already in images. Then you declare the images and the directories the extensions are in, in the Instrumentation resource. As an example, I’ll show an Instrumentation which uses version 2.5.0 of the <a href="https://github.com/open-telemetry/opentelemetry-java-instrumentation/">OpenTelemetry Java agent</a> together with the <a href="https://github.com/elastic/elastic-otel-java/tree/main/inferred-spans">inferred spans extension</a> from the <a href="https://github.com/elastic/elastic-otel-java">Elastic OpenTelemetry Java distribution</a>. The distro image includes the extension at path <code>/extensions/elastic-otel-agentextension.jar</code>. The Instrumentation resource allows either directories or file paths to be specified, here I’ll list the directory:</p>
<pre><code>apiVersion: opentelemetry.io/v1alpha1
kind: Instrumentation
metadata:
  name: otel-plus-extension-instrumentation
  namespace: banana
spec:
  exporter:
    endpoint: https://my.apm.server.url
  propagators:
    - tracecontext
    - baggage
    - b3
  sampler:
    type: parentbased_traceidratio
    argument: "1.0"
  java:
    image: ghcr.io/open-telemetry/opentelemetry-operator/autoinstrumentation-java:2.5.0
    extensions:
      - image: "docker.elastic.co/observability/elastic-otel-javaagent:1.10.0"
        dir: "/extensions"
    env:
      - name: OTEL_EXPORTER_OTLP_HEADERS
        value: "Authorization=Bearer my-apm-secret-token"
      - name: ELASTIC_OTEL_INFERRED_SPANS_ENABLED
        value: "true"
</code></pre>
<p>Note that you can have multiple <code>image … dir</code> pairs, ie include multiple extensions from different images. Note also if you are testing this specific configuration that the inferred spans extension included here will be contributed to the OpenTelemetry contrib repo at some point after this blog is published, after which the extension may no longer be present in a later version of the referred image (since it will be available from the <a href="https://github.com/open-telemetry/opentelemetry-java-contrib/">contrib repo</a> instead).</p>
<h2 id="nextsteps">Next steps</h2>
<p>Here I’ve shown how to use any agent with the <a href="https://github.com/open-telemetry/opentelemetry-operator/">OpenTelemetry Operator for Kubernetes</a>, and configure that for your system. In particular the examples have showcased how to use the Elastic Java agents to auto-instrument Java applications running in your Kubernetes clusters, along with how to enable features, using Instrumentation resources. And you can set it up for either zero config for deployments, or for just one annotation which is generally a more flexible mechanism (you can have multiple Instrumentation resource definitions, and the deployment can select the appropriate one for its application).</p>
<p><em>The release and timing of any features or functionality described in this post remain at Elastic's sole discretion. Any features or functionality not currently available may not be delivered on time or at all.</em></p>]]></content:encoded>
    <link>https://www.elastic.co/observability-labs/blog/using-the-otel-operator-for-injecting-elastic-agents</link>
    <guid isPermaLink="false">using-the-otel-operator-for-injecting-elastic-agents</guid>
    <category><![CDATA[OpenTelemetry]]></category>
    <category><![CDATA[APM]]></category>
    <category><![CDATA[Kubernetes]]></category>
    <category><![CDATA[Infrastructure Monitoring]]></category>
    <dc:creator><![CDATA[Jack Shirazi]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt55dfb115f9341105/6a7f1c08ea068d4de1f0a2f9/blog-header-720x420.jpg" length="0" type="image/jpeg"/>
    <pubDate>Tue, 16 Jul 2024 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Zero config OpenTelemetry auto-instrumentation for Kubernetes Java applications]]></title>
    <description><![CDATA[Walking through how to install and enable the OpenTelemetry Operator for Kubernetes to auto-instrument Java applications, with no configuration changes needed for deployments]]></description>
    <content:encoded><![CDATA[<p>The <a href="https://github.com/open-telemetry/opentelemetry-java-instrumentation/">OpenTelemetry Java agent</a> has a number of <a href="https://opentelemetry.io/docs/languages/java/automatic/#setup">ways to install</a> the agent into a Java application. If you are running your Java applications in Kubernetes pods, there is a separate mechanism (which under the hood uses JAVA_TOOL_OPTIONS and other environment variables) to auto-instrument Java applications. This auto-instrumentation can be achieved with zero configuration of the applications and pods!</p>
<p>The mechanism to achieve zero-config auto-instrumentation of Java applications in Kubernetes is via the <a href="https://github.com/open-telemetry/opentelemetry-operator/">OpenTelemetry Operator for Kubernetes</a>. This operator has many capabilities and the full documentation (and of course source) is available in the project itself. In this blog, I'll walk through installing, setting up and running zero-config auto-instrumentation of Java applications in Kubernetes using the OpenTelemetry Operator.</p>
<h2 id="installingtheopentelemetryoperatoraidinstallingtheopentelemetryoperatora">Installing the OpenTelemetry Operator<a id="installing-the-opentelemetry-operator"></a></h2>
<p>At the time of writing this blog, the OpenTelemetry Operator needs the certification manager to be installed, after which the operator can be installed. Installing from the web is straightforward. First install the <code>cert-manager</code> (the version to be installed will be specified in the <a href="https://github.com/open-telemetry/opentelemetry-operator/">OpenTelemetry Operator for Kubernetes</a> documentation):</p>
<pre><code>kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.14.4/cert-manager.yaml
</code></pre>
<p>Then when the cert managers are ready (<code>kubectl get pods -n cert-manager</code>)  …</p>
<pre><code>NAMESPACE&amp;nbsp; &amp;nbsp; &amp;nbsp; NAME &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; READY
cert-manager &amp;nbsp; cert-manager-67c98b89c8-rnr5s&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 1/1
cert-manager &amp;nbsp; cert-manager-cainjector-5c5695d979-q9hxz &amp;nbsp; &amp;nbsp; 1/1
cert-manager &amp;nbsp; cert-manager-webhook-7f9f8648b9-8gxgs&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 1/1
</code></pre>
<p>… you can install the OpenTelemetry Operator:</p>
<pre><code>kubectl apply -f https://github.com/open-telemetry/opentelemetry-operator/releases/latest/download/opentelemetry-operator.yaml
</code></pre>
<p>You can, of course, use a specific version of the operator instead of the <code>latest</code>. But here I’ve used the <code>latest</code> version.</p>
<h2 id="aninstrumentationresourceaidaninstrumentationresourcea">An Instrumentation resource<a id="an-instrumentation-resource"></a></h2>
<p>Now you need to add just one further Kubernetes resource to enable auto-instrumentation: an <code>Instrumentation</code> resource. I am going to use the <code>banana</code> namespace for my examples, so I have first created that namespace (<code>kubectl create namespace banana</code>). The auto-instrumentation is specified and configured by these Instrumentation resources. Here is a basic one which will allow every Java pod in the <code>banana</code> namespace to be auto-instrumented with version 2.5.0 of the <a href="https://github.com/open-telemetry/opentelemetry-java-instrumentation/">OpenTelemetry Java agent</a>:</p>
<pre><code>apiVersion: opentelemetry.io/v1alpha1
kind: Instrumentation
metadata:
  name: banana-instr
  namespace: banana
spec:
  exporter:
    endpoint: "https://my.endpoint"
  propagators:
    - tracecontext
    - baggage
    - b3
  sampler:
    type: parentbased_traceidratio
    argument: "1.0"
  java:
    image: ghcr.io/open-telemetry/opentelemetry-operator/autoinstrumentation-java:2.5.0
    env:
      - name: OTEL_EXPORTER_OTLP_HEADERS
        value: "Authorization=Bearer MyAuth"
</code></pre>
<p>Creating this resource (eg with <code>kubectl apply -f banana-instr.yaml</code>, assuming the above yaml was saved in file <code>banana-instr.yaml</code>) makes the <code>banana-instr</code> Instrumentation resource available for use. (Note you will need to change <code>my.endpoint</code> and <code>MyAuth</code> to values appropriate for your collector.) You can use this instrumentation immediately by adding an annotation to any deployment in the <code>banana</code> namespace:</p>
<pre><code>metadata:
&amp;nbsp;&amp;nbsp;annotations:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;instrumentation.opentelemetry.io/inject-java: "true"
</code></pre>
<p>The <code>banana-instr</code> Instrumentation resource is not yet set to be applied by <em>default</em> to all pods in the banana namespace. Currently it's zero-config as far as the <em>application</em> is concerned, but it requires an annotation added to a <em>pod or deployment</em>. To make it fully zero-config for <em>all pods</em> in the <code>banana</code> namespace, we need to add that annotation to the namespace itself, ie editing the namespace (<code>kubectl edit namespace banana</code>) so it would then have contents similar to</p>
<pre><code>apiVersion: v1
kind: Namespace
metadata:
&amp;nbsp;&amp;nbsp;name: banana
&amp;nbsp;&amp;nbsp;annotations:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;instrumentation.opentelemetry.io/inject-java: "banana-instr"
...
</code></pre>
<p>Now we have a namespace that is going to auto-instrument <em>every</em> Java application deployed in the <code>banana</code> namespace with the 2.5.0 <a href="https://github.com/open-telemetry/opentelemetry-java-instrumentation/">OpenTelemetry Java agent</a>!</p>
<h2 id="tryingitaidtryingita">Trying it<a id="trying-it"></a></h2>
<p>There is a simple example Java application at <a href="http://docker.elastic.co/demos/apm/k8s-webhook-test">docker.elastic.co/demos/apm/k8s-webhook-test</a> which just repeatedly calls the chain <code>main-&gt;methodA-&gt;methodB-&gt;methodC-&gt;methodD</code> with some sleeps in the calls. Running this (<code>kubectl apply -f banana-app.yaml</code>) using a very basic pod definition:</p>
<pre><code>apiVersion: v1
kind: Pod
metadata:
  name: banana-app
  namespace: banana
  labels:
    app: banana-app
spec:
  containers:
    - image: docker.elastic.co/demos/apm/k8s-webhook-test
      imagePullPolicy: Always
      name: banana-app
      env: 
      - name: OTEL_INSTRUMENTATION_METHODS_INCLUDE
        value: "test.Testing[methodB]"
</code></pre>
<p>results in the app being auto-instrumented with no configuration changes! The resulting app shows up in any APM UI, such as Elastic APM</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt5339364ee45875ef/6a7f1c0d3ce8e2a2e5cf57d0/elastic-apm-ui-transaction.png" alt="Elastic APM UI showing methodB traced" /></p>
<p>As you can see, for this example I also added this env var to the pod yaml, <code>OTEL_INSTRUMENTATION_METHODS_INCLUDE="test.Testing[methodB]"</code> so that there were traces showing from methodB.</p>
<h2 id="thetechnologybehindtheautoinstrumentationaidthetechnologybehindtheautoinstrumentationa">The technology behind the auto-instrumentation<a id="the-technology-behind-the-auto-instrumentation"></a></h2>
<p>To use the auto-instrumentation there is no specific need to understand the underlying mechanisms, but for those of you interested, here’s a quick outline. </p>
<ol>
<li>The <a href="https://github.com/open-telemetry/opentelemetry-operator/">OpenTelemetry Operator for Kubernetes</a> installs a <a href="https://kubernetes.io/docs/reference/access-authn-authz/admission-controllers/">mutating webhook</a>, a standard Kubernetes component.</li>
<li>When deploying, Kubernetes first sends all definitions to the mutating webhook.</li>
<li>If the mutating webhook sees that the conditions for auto-instrumentation should be applied (ie </li>
<li>there is an Instrumentation resource for that namespace and</li>
<li>the correct annotation for that Instrumentation is applied to the definition in some way, either from the definition itself or from the namespace),</li>
<li>then the mutating webhook “mutates” the definition to include the environment defined by the Instrumentation resource.</li>
<li>The environment includes the explicit values defined in the env, as well as some implicit OpenTelemetry values (see the <a href="https://github.com/open-telemetry/opentelemetry-operator/">OpenTelemetry Operator for Kubernetes</a> documentation for full details).</li>
<li>And most importantly, the operator</li>
<li>pulls the image defined in the Instrumentation resource,</li>
<li>extracts the file at the path <code>/javaagent.jar</code> from that image (using shell command <code>cp</code>)</li>
<li>inserts it into the pod at path <code>/otel-auto-instrumentation-java/javaagent.jar</code></li>
<li>and adds the environment variable <code>JAVA_TOOL_OPTIONS=-javaagent:/otel-auto-instrumentation-java/javaagent.jar</code>.</li>
<li>The JVM automatically picks up that JAVA_TOOL_OPTIONS environment variable on startup and applies it to the JVM command-line.</li>
</ol>
<h2 id="nextstepsaidnextstepsa">Next steps<a id="next-steps"></a></h2>
<p>This walkthrough can be repeated in any Kubernetes cluster to demonstrate and experiment with auto-instrumentation (you will need to create the banana namespace first). In part 2 of this two part series, <a href="https://www.elastic.co/observability-labs/blog/using-the-otel-operator-for-injecting-elastic-agents">Using a custom agent with the OpenTelemetry Operator for Kubernetes</a>, I show how to install any Java agent via the OpenTelemetry operator, using the Elastic Java agents as examples.</p>]]></content:encoded>
    <link>https://www.elastic.co/observability-labs/blog/using-the-otel-operator-for-injecting-java-agents</link>
    <guid isPermaLink="false">using-the-otel-operator-for-injecting-java-agents</guid>
    <category><![CDATA[OpenTelemetry]]></category>
    <category><![CDATA[APM]]></category>
    <category><![CDATA[Kubernetes]]></category>
    <category><![CDATA[Infrastructure Monitoring]]></category>
    <dc:creator><![CDATA[Jack Shirazi]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt53c963d5c03388fb/6a7f1c101967ea3597330ba4/blog-header.png" length="0" type="image/png"/>
    <pubDate>Thu, 11 Jul 2024 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Introducing Elastic Distribution for OpenTelemetry Python]]></title>
    <description><![CDATA[Announcing the first alpha release of the Elastic Distribution for OpenTelemetry Python. See how easy it is to instrument your Python applications with OpenTelemetry in this blog post.]]></description>
    <content:encoded><![CDATA[<p>We are delighted to announce the alpha release of the <a href="https://github.com/elastic/elastic-otel-python#readme">Elastic Distribution for OpenTelemetry Python</a>. This project is a customized OpenTelemetry distribution that allows us to configure better defaults for using OpenTelemetry with the Elastic cloud offering.</p>
<h2 id="background">Background</h2>
<p>Elastic is standardizing on OpenTelemetry (OTel) for observability and security data collection. As part of that effort, we are <a href="https://www.elastic.co/blog/elastic-opentelemetry-sdk-distributions">providing distributions of the OpenTelemetry Language SDKs</a>. We have recently released alpha distributions for <a href="https://github.com/elastic/elastic-otel-java#readme">Java</a>, <a href="https://github.com/elastic/elastic-otel-dotnet#readme">.NET</a> and <a href="https://github.com/elastic/elastic-otel-node#readme">Node.js</a>. Our <a href="https://github.com/elastic/apm-agent-android#readme">Android</a> and <a href="https://github.com/elastic/apm-agent-ios#readme">iOS</a> SDKs have been OpenTelemetry-based from the start. The Elastic Distribution for OpenTelemetry Python is the latest addition.</p>
<h2 id="designchoices">Design choices</h2>
<p>We have chosen to provide a lean distribution that does not install all the instrumentations by default but that instead provides tools
to do so. We leverage the <code>opentelemetry-bootstrap</code> tool provided by OpenTelemetry Python project to scan the packages installed in your
environment and recognizes libraries we are able to instrument.  This tool can just report the instrumentations available and optionally
is able to install them as well.
This allows you to avoid installing packages you are not going to need or instrument libraries you are not interested in tracing.</p>
<h2 id="gettingstarted">Getting started</h2>
<p>To get started with Elastic Distribution for OpenTelemetry Python you need to install  the package <code>elastic-opentelemetry</code> in your project
environment. We'll use <code>pip</code> in our examples but you are free to use any python package and environment manager of your choice.</p>
<pre><code>pip install elastic-opentelemetry
</code></pre>
<p>Once you have installed our distro you'll have also the <code>opentelemetry-bootstrap</code> command available. Running it:</p>
<pre><code>opentelemetry-bootstrap
</code></pre>
<p>will list all available packages for your instrumentation, e.g. you can expect something like the following:</p>
<pre><code>opentelemetry-instrumentation-asyncio==0.46b0
opentelemetry-instrumentation-dbapi==0.46b0
opentelemetry-instrumentation-logging==0.46b0
opentelemetry-instrumentation-sqlite3==0.46b0
opentelemetry-instrumentation-threading==0.46b0
opentelemetry-instrumentation-urllib==0.46b0
opentelemetry-instrumentation-wsgi==0.46b0
opentelemetry-instrumentation-grpc==0.46b0
opentelemetry-instrumentation-requests==0.46b0
opentelemetry-instrumentation-system-metrics==0.46b0
opentelemetry-instrumentation-urllib3==0.46b0
</code></pre>
<p>It also provides a command option to install the packages automatically</p>
<pre><code>opentelemetry-bootstrap --action=install
</code></pre>
<p>It is advised to run this command every time you release a new version of your application so that you can install or just revise any
instrumentation packages for your code.</p>
<p>Some environment variables are needed to provide the needed configuration for instrumenting your services. These mostly
concern the destination of your traces but also for easily identifying your service.
A <em>service name</em> is required to have your service distinguishable from the others. Then you need to provide
the <em>authorization</em> headers for authentication with Elastic Observability cloud and the Elastic cloud endpoint where the data is sent.</p>
<p>The API Key you get from your Elastic cloud serverless project must be <em>URL-encoded</em>, you can do that with the following Python snippet:</p>
<pre><code>from urllib.parse import quote
quote("ApiKey &lt;your api key&gt;)
</code></pre>
<p>Once you have all your configuration values you can export via environment variables as below:</p>
<pre><code>export OTEL_RESOURCE_ATTRIBUTES=service.name=&lt;service-name&gt;
export OTEL_EXPORTER_OTLP_HEADERS="Authorization=&lt;url encoded apikey header value&gt;"
export OTEL_EXPORTER_OTLP_ENDPOINT=&lt;your elastic cloud url&gt;
</code></pre>
<p>We are done with the configuration and the last piece of the puzzle is wrapping your service invocation with
<code>opentelemetry-instrument</code>, the wrapper that provides <em>zero-code instrumentation</em>. <em>Zero-code</em> (or Automatic) instrumentation means
that the distribution will set up the OpenTelemetry SDK and enable all the previously installed instrumentations for you.
Unfortunately <em>Zero-code</em> instrumentation does not cover all libraries and some — web frameworks in particular — will require minimal manual
configuration.</p>
<p>For a web service running with gunicorn it may look like:</p>
<pre><code>opentelemetry-instrument gunicorn main:app
</code></pre>
<p>The result is an observable application using the industry-standard <a href="https://opentelemetry.io/">OpenTelemetry</a> — offering high-quality instrumentation of many popular Python libraries, a portable API to avoid vendor lock-in and an active community.</p>
<p>Using Elastic Observability, some out-of-the-box benefits you can expect are: rich trace viewing, Service maps, integrated metrics and log analysis, and more.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltb4d06a40da2943f4/6a85cc2027c5cd6fab5f741a/traces-original.png" alt="trace sample screenshot" /></p>
<h2 id="whatsnext">What's next?</h2>
<p>Elastic is committed to helping OpenTelemetry succeed and to helping our customers use OpenTelemetry effectively in their systems. Last year, we <a href="https://opentelemetry.io/blog/2023/ecs-otel-semconv-convergence/">donated ECS</a> and continue to work on integrating it with OpenTelemetry Semantic Conventions. More recently, we are working on <a href="https://www.elastic.co/observability-labs/blog/elastic-profiling-agent-acceptance-opentelemetry">donating our eBPF-based profiler</a> to OpenTelemetry. We contribute to many of the language SDKs and other OpenTelemetry projects.</p>
<p>In the Python ecosystem we are active reviewers and contributors of both the <a href="https://github.com/open-telemetry/opentelemetry-python/">opentelemetry-python</a> and <a href="https://github.com/open-telemetry/opentelemetry-python-contrib/">opentelemetry-python-contrib</a> repositories.</p>
<p>The Elastic Distribution for OpenTelemetry Python is currently an alpha. Please <a href="https://github.com/elastic/elastic-otel-python/">try it out</a> and let us know if it might work for you. Watch for the <a href="https://github.com/elastic/elastic-otel-python/releases">latest releases here</a>. You can engage with us on <a href="https://github.com/elastic/elastic-otel-python/issues">the project issue tracker</a>.</p>
<p>We are eager to know your use cases to help you succeed in your Observability journey.</p>
<p><em>The release and timing of any features or functionality described in this post remain at Elastic's sole discretion. Any features or functionality not currently available may not be delivered on time or at all.</em></p>
<h2 id="resources">Resources</h2>
<ul>
<li>https://www.elastic.co/blog/elastic-opentelemetry-sdk-distributions</li>
<li>https://www.elastic.co/observability-labs/blog/elastic-distribution-opentelemetry-java-agent</li>
<li>https://www.elastic.co/observability-labs/blog/elastic-opentelemetry-distribution-dotnet-applications</li>
<li>https://www.elastic.co/observability-labs/blog/elastic-opentelemetry-distribution-node-js</li>
<li>https://www.elastic.co/observability-labs/blog/manual-instrumentation-python-apps-opentelemetry</li>
<li>https://www.elastic.co/observability-labs/blog/auto-instrumentation-python-applications-opentelemetry</li>
<li>https://www.elastic.co/observability-labs/blog/opentelemetry-observability</li>
</ul>]]></content:encoded>
    <link>https://www.elastic.co/observability-labs/blog/elastic-opentelemetry-distribution-python</link>
    <guid isPermaLink="false">elastic-opentelemetry-distribution-python</guid>
    <category><![CDATA[OpenTelemetry]]></category>
    <category><![CDATA[APM]]></category>
    <category><![CDATA[Infrastructure Monitoring]]></category>
    <dc:creator><![CDATA[Riccardo Magliocchetti]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt498330cde71bc9cd/6a85cc23331d7afa63c317bb/python.jpg" length="0" type="image/jpeg"/>
    <pubDate>Sun, 07 Jul 2024 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[NGNIX log analytics with GenAI in Elastic]]></title>
    <description><![CDATA[Elastic has a set of embedded capabilities such as a GenAI RAG-based AI Assistant and a machine learning platform as part of the product baseline. These make analyzing the vast number of logs you get from NGINX easier.]]></description>
    <content:encoded><![CDATA[<p>Elastic Observability provides a full observability solution, supporting metrics, traces, and logs for applications and infrastructure. NGINX, which is highly used for web serving, load balancing, http caching, and reverse proxy, is the key to many applications and outputs a large volume of logs. NGINX’s access logs, which detail all requests made to the NGINX server, and error logs which record server-related issues and problems are key to managing and analyzing NGINX issues along with understanding what is happening to your application. </p>
<p>In managing NGINX Elastic provides several capabilities:</p>
<ol>
<li><p>Easy ingest, parsing, and out-of-the-box dashboards. Check out the simple how-to in our <a href="https://www.elastic.co/guide/en/fleet/current/example-standalone-monitor-nginx.html">docs</a>. Based on logs, these dashboards show several items over time, response codes, errors, top pages, data volume, browsers used, active connections, drop rates, and much more.</p></li>
<li><p>Out-of-the-box ML-based anomaly detection jobs for your NGINX logs. These jobs help pinpoint anomalies against request rates, IP address request rates, URL access, status codes, and visitor rate anomalies.</p></li>
<li><p>ES|QL which helps work through logs and build out charts during analysis.</p></li>
<li><p>Elastic’s GenAI Assistant provides a simple natural language interface that helps analyze all the logs and can pull out issues from ML jobs and even create dashboards. The Elastic AI Assistant also automatically uses ES|QL.</p></li>
<li><p>NGINX SLOs - Finally Elastic provides the ability to define and monitor SLOs for your NGINX logs. While most SLOs are metrics-based, Elastic allows you to create logs-based SLOs. We detailed this in a previous <a href="https://www.elastic.co/observability-labs/blog/service-level-objectives-slos-logs-metrics">blog</a>.</p></li>
</ol>
<p>NGINX logs are another example of why logs are great.  Logging is an important part of Observability, for which we generally think of metrics and tracing. However, the amount of logs an application and the underlying infrastructure output can be significantly daunting and NGINX is usually the starting point for most analyses. </p>
<p>In today’s blog, we’ll cover how the out-of-the-box ML-based anomaly detection jobs can help RCA, and how Elastic’s GenAI Assistant helps easily work through logs to pinpoint issues in minutes. </p>
<h2 id="prerequisitesandconfigaidprerequisitesandconfiga">Prerequisites and config<a id="prerequisites-and-config"></a></h2>
<p>If you plan on following this blog, here are some of the components and details we used to set up this demonstration:</p>
<ul>
<li><p>Ensure you have an account on <a href="http://cloud.elastic.co">Elastic Cloud</a> and a deployed stack (<a href="https://www.elastic.co/guide/en/elastic-stack/current/installing-elastic-stack.html">see instructions here</a>).</p></li>
<li><p>Bring up an <a href="https://docs.nginx.com/nginx/admin-guide/web-server/">NGINX server</a> on a host. OR run an application with NGINX as a front end and drive traffic.</p></li>
<li><p>Install the NGINX integration and assets and review the dashboards as noted in the <a href="https://www.elastic.co/guide/en/fleet/current/example-standalone-monitor-nginx.html">docs</a>.</p></li>
<li><p>Ensure you have an <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/ml-settings.html">ML node configured</a> in your Elastic stack</p></li>
<li><p>To use the AI Assistant you will need a trial or upgrade to Platinum.</p></li>
</ul>
<p>In our scenario, we use data from 3 months from our Elastic environment to help highlight the features. Hence you might need to run your application with traffic for a specific time frame to follow along.</p>
<h2 id="analyzingtheissueswithaiassistantaidanalyzingtheissueswithaiassistanta">Analyzing the issues with AI Assistant<a id="analyzing-the-issues-with-ai-assistant"></a></h2>
<p>As detailed in a previous <a href="https://www.elastic.co/observability-labs/blog/service-level-objectives-slos-logs-metrics">blog</a>, you can get alerted on issues via SLO monitoring against NGINX logs. Let’s assume you have an SLO based on status codes as we outlined in the previous <a href="https://www.elastic.co/observability-labs/blog/service-level-objectives-slos-logs-metrics">blog</a>. You can immediately analyze the issue via the AI Assistant. Because it's a chat interface we simply open the AI Assistant and work through some simple analysis: (See Animated GIF for a demo)</p>
<h3 id="aiassistantanalysisaidaiassistantanalysisa">AI Assistant analysis:<a id="ai-assistant-analysis"></a></h3>
<ul>
<li><p><strong><em>Using lens graph all http response status codes &lt; 400 and &gt; =400 from filebeat-nginx-elasticco-anon-2017. http.response.status.code is not an integer</em></strong> <em>-</em> We wanted to simply understand the amount of requests resulting in status code &gt;= 400 and graph the results. We see that 15% of the requests were not successful, hence an SLO alert being triggered.</p></li>
<li><p><strong>Which ip address (field source.adress) has the highest number of http.response.status.code &gt;= 400 from filebeat-nginx-elasticco-anon-2017. http.response.status.code is not an integer</strong>  - We were curious is there was a specific IP address not having successful requests. 72.57.0.53, with a count of 25,227 occurrences is daily high but not the ensure 2 failed requests.</p></li>
<li><p><strong><em>What country (source.geo.country_iso_code) is source.address=72.57.0.53 coming from. Use filebeat-nginx-elasticco-anon-2017.</em></strong> - Again we were curious if this came from a specific country. And the IP address 72.57.0.53 is coming from the country with the ISO code IN, which corresponds to India. Nothing out of the ordinary.</p></li>
<li><p><strong><em>Did source.address=72.57.0.53 have any (http.response.status.code &lt; 400) from filebeat-nginx-elasticco-anon-2017. http.response.status.code is not an integer -</em></strong>  Oddly the IP address in question only had 4000+ successful responses. Meaning its not malicious, and points to something else.</p></li>
<li><p><strong><em>What are the different status codes (http.response.status.code&gt;=400), from source.address=72.57.0.53. Use filebeat-nginx-elasticco-anon-2017. http.response.status.code is not an integer. Provide counts for each status code -</em></strong> We are curious whether or not we see any 502, which there were none, but most of the failures were 404. </p></li>
<li><p><strong><em>What are the different status codes (http.response.status.code&gt;=400). Use filebeat-nginx-elasticco-anon-2017. http.response.status.code is not an integer. Provide counts for each status code</em></strong> - Regardless of a specific address, what is the largest number of status code occurrences &gt; 400. This also points to 404. </p></li>
<li><p><strong><em>What does a high 404 count from a specific IP address mean from NGINX logs?</em></strong> - Asking this question, we need to understand the potential causes of this from our application. From the answers, we can rule out security probing and web scraping, as we validated that a specific address 72.57.0.53 has a low non-success request status code. It also rules out User error. Hence this points potentially to Broken Links or Missing Resources.</p></li>
</ul>
<h3 id="watchtheflowaidwatchtheflowa">Watch the flow:<a id="watch-the-flow"></a></h3>
<div>
    
</div>
<h3 id="potentialissue">Potential issue:</h3>
<p>It seems that we potentially have an issue with the backend serving specific answers or having issues with resources (database, or broken links). This is cursing the higher-than-normal non-successful status codes&gt;=400.</p>
<h3 id="keyhighlightsfromaiassistant">Key highlights from AI Assistant:</h3>
<p>As you watched this video you will notice a few things:</p>
<ol>
<li><p>We analyzed millions of logs in a matter of minutes using a set of simple natural language queries. </p></li>
<li><p>We didn’t need to know any special query language. The AI Assistant used Elastic’s ES|QL but can similarly use KQL also. </p></li>
<li><p>The AI Assistant easily builds out graphs</p></li>
<li><p>The AI Assistant is accessing and using internal information stored in Elastic’s indices. Vs a simple “google foo” based AI Assistant. This is enabled through RAG, and the AI Assistant can also bring up known issues in github, runbooks, and other useful internal information.</p></li>
</ol>
<p>Check out the following <a href="https://www.elastic.co/observability-labs/blog/elastic-rag-ai-assistant-application-issues-llm-github">blog</a> on how the AI Assistant uses RAG to retrieve internal information. Specifically using github and runbooks.</p>
<h2 id="locatinganomalieswithml">Locating anomalies with ML</h2>
<p>While using the AI Assistant is great for analyzing information, another important aspect of NGINX log management is to ensure you can manage log spikes and anomalies. Elastic has a machine learning platform that allows you to develop jobs to analyze specific metrics or multiple metrics to look for anomalies.When using NGINX, there are several <a href="https://www.elastic.co/guide/en/machine-learning/current/ootb-ml-jobs-nginx.html">out-of-the-box anomaly detection jobs</a>. These work specifically on NGINX access logs.</p>
<ul>
<li><p>Low_request_rate_nginx - Detect low request rates</p></li>
<li><p>Source_ip_request_rate_nginx - Detect unusual source IPs - high request rates</p></li>
<li><p>Source_ip_url_count_nginx - Detect unusual source IPs - high distinct count of URLs</p></li>
<li><p>Status_code_rate_nginx - Detect unusual status code rates</p></li>
<li><p>Visitor_rate_nginx - Detect unusual visitor rates</p></li>
</ul>
<p>Being right out of the box, lets look at the job - Status_code_rate_nginx, which is related to our previous analysis.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt30ec8d10aaf46a17/6a7f0e9073d9bda62429dbcb/nginx-ml-log-analytics.png" alt="NGINX ML Log Analytics" /></p>
<p>With a few simple clicks we immediately get an analysis showing a specific IP address - 72.57.0.53, having higher than normal non-successful requests. Oddly we also found this is using the AI Assistant.</p>
<p>We can take this further with conversations with the AI Assistant, look at the logs, and/or even look at the other ML anomaly jobs.</p>
<h2 id="conclusionaidconclusiona">Conclusion:<a id="conclusion"></a></h2>
<p>You’ve now seen how easily Elastic’s RAG-based AI Assistant can help analyze NGINX logs without even the need to know query syntax, understand where the data is, and understand even the fields. Additionally, you’ve also seen how we can alert you when a potential issue or degradation in service (SLO). </p>
<p>Check out other resources on NGINX logs:</p>
<p><a href="https://www.elastic.co/guide/en/machine-learning/current/ootb-ml-jobs-nginx.html">Out-of-the-box anomaly detection jobs for NGINX</a></p>
<p><a href="https://www.elastic.co/guide/en/fleet/current/example-standalone-monitor-nginx.html">Using the NGINX integration to ingest and analyze NGINX Logs</a></p>
<p><a href="https://www.elastic.co/observability-labs/blog/service-level-objectives-slos-logs-metrics">NGINX Logs based SLOs in Elastic</a></p>
<p><a href="https://www.elastic.co/observability-labs/blog/elastic-rag-ai-assistant-application-issues-llm-github">Using GitHub issues, runbooks, and other internal information for RCAs with Elastic’s RAG based AI Assistant</a></p>
<h2 id="tryitoutaidtryitouta">Try it out<a id="try-it-out"></a></h2>
<p>Existing Elastic Cloud customers can access many of these features directly from the <a href="https://cloud.elastic.co/">Elastic Cloud console</a>. Not taking advantage of Elastic on the cloud? <a href="https://www.elastic.co/cloud/cloud-trial-overview">Start a free trial</a>.</p>
<p>All of this is also possible in your environment. <a href="https://www.elastic.co/observability/universal-profiling">Learn how to get started today</a>.</p>
<p><em>The release and timing of any features or functionality described in this post remain at Elastic's sole discretion. Any features or functionality not currently available may not be delivered on time or at all.</em></p>
<p><em>In this blog post, we may have used or referred to third party generative AI tools, which are owned and operated by their respective owners. Elastic does not have any control over the third party tools and we have no responsibility or liability for their content, operation or use, nor for any loss or damage that may arise from your use of such tools. Please exercise caution when using AI tools with personal, sensitive or confidential information. Any data you submit may be used for AI training or other purposes. There is no guarantee that information you provide will be kept secure or confidential. You should familiarize yourself with the privacy practices and terms of use of any generative AI tools prior to use.</em></p>
<p><em>Elastic, Elasticsearch, ESRE, Elasticsearch Relevance Engine and associated marks are trademarks, logos or registered trademarks of Elasticsearch N.V. in the United States and other countries. All other company and product names are trademarks, logos or registered trademarks of their respective owners.</em></p>]]></content:encoded>
    <link>https://www.elastic.co/observability-labs/blog/nginx-log-analytics-with-genai-elastic</link>
    <guid isPermaLink="false">nginx-log-analytics-with-genai-elastic</guid>
    <category><![CDATA[Agentic Observability]]></category>
    <category><![CDATA[Logs Analytics]]></category>
    <category><![CDATA[Infrastructure Monitoring]]></category>
    <category><![CDATA[LLM Observability]]></category>
    <dc:creator><![CDATA[Bahubali Shetti]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltd89bddfe4a0532b5/6a7f0e936c6eaca022f141b7/blog-thumb-observability-pattern-color.png" length="0" type="image/png"/>
    <pubDate>Fri, 05 Jul 2024 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Automatic cloud resource attributes with OpenTelemetry Java]]></title>
    <description><![CDATA[Capturing cloud resource attributes allow to describe application cloud deployment details. In this article we describe three distinct ways to enable them for Java applications using OpenTelemetry]]></description>
    <content:encoded><![CDATA[<p>With OpenTelemetry, the observed entities (application, services, processes, …) are described through resource attributes. The definitions and the values of those attributes are defined in the <a href="https://opentelemetry.io/docs/concepts/semantic-conventions/">semantic conventions</a>.\
In practice, for a typical java application running in a cloud environment like Google Cloud Platform (GCP), Amazon Web Services (AWS) or Azure, it means capturing the name of the cloud provider, the cloud service name or availability zone in addition to per-provider attributes. Those attributes are then used to describe and qualify the observability signals (logs, traces, metrics), defined by semantic conventions in the <a href="https://opentelemetry.io/docs/specs/semconv/resource/cloud/">cloud resource attributes</a> section.</p>
<p>When using the <a href="https://github.com/open-telemetry/opentelemetry-java">OpenTelemetry Java SDK</a> or the <a href="https://github.com/open-telemetry/opentelemetry-java-instrumentation">OpenTelemetry instrumentation agent</a>, those attributes are not automatically captured by default. In this article we will show you first how to enable them with the SDK, then using the instrumentation agent and then we will show you how using the <a href="https://github.com/elastic/elastic-otel-java/">Elastic OpenTelemetry Distribution</a> makes it even easier.</p>
<h2 id="opentelemetryjavasdk">OpenTelemetry Java SDK</h2>
<p>The OpenTelemetry Java SDK does not capture any cloud resource attributes, however it provides a pluggable service provider interface to register resource attributes providers and application developers have to provide the implementations.</p>
<p>Implementations for <a href="https://github.com/open-telemetry/opentelemetry-java-contrib/tree/main/gcp-resources">GCP</a> and <a href="https://github.com/open-telemetry/opentelemetry-java-contrib/tree/main/aws-resources">AWS</a> are already included in the <a href="https://github.com/open-telemetry/opentelemetry-java-contrib/">OpenTelemetry Java Contrib</a> repo, so if you are using one of those cloud providers then it's mostly a matter of adding those providers to your application dependencies. Thanks to autoconfiguration those should be automatically included and enabled once they are added to the application classpath. The <a href="https://github.com/open-telemetry/opentelemetry-java/tree/main/sdk-extensions/autoconfigure#resource-provider-spi">SDK documentation</a> provides all the details to add and configure those in your application.</p>
<p>If you are using a cloud provider for which no such implementation is available, then you still have the option to provide your own which is a straightforward implementation of the <a href="https://github.com/open-telemetry/opentelemetry-java/blob/main/sdk-extensions/autoconfigure/README.md#resource-provider-spi">ResourceProvider</a> SPI (Service Provider Interface). In order to keep things consistent, you will have to rely on the existing <a href="https://opentelemetry.io/docs/specs/semconv/resource/cloud/">cloud semantic conventions</a>.</p>
<p>For example here is an example of a simple cloud resource attributes provider for a fictitious cloud provider named "potatoes".</p>
<pre><code>package potatoes;

import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
import io.opentelemetry.sdk.autoconfigure.spi.ResourceProvider;
import io.opentelemetry.sdk.resources.Resource;
import io.opentelemetry.semconv.incubating.CloudIncubatingAttributes;

public class PotatoesResourceProvider implements ResourceProvider {

@Override
public Resource createResource(ConfigProperties configProperties) {
   return Resource.create(Attributes.of(
           CloudIncubatingAttributes.CLOUD_PROVIDER, "potatoes",
           CloudIncubatingAttributes.CLOUD_PLATFORM, "french-fries",
           CloudIncubatingAttributes.CLOUD_REGION, "garden"
           ));
  }
}
</code></pre>
<h2 id="opentelemetryjavainstrumentation">OpenTelemetry Java instrumentation</h2>
<p>The <a href="https://github.com/open-telemetry/opentelemetry-java-instrumentation">OpenTelemetry Java Instrumentation</a> provides a java agent that instruments the application at runtime automatically for an extensive set of frameworks and libraries (see <a href="https://github.com/open-telemetry/opentelemetry-java-instrumentation/blob/main/docs/supported-libraries.md">supported technologies</a>).</p>
<p>Using instrumentation means that the application bytecode and the embedded libraries are modified automatically to make them behave as if explicit modifications were made in their source code to call the OpenTelemetry SDK in order to create traces, spans and metrics.</p>
<p>When an application is deployed with the OpenTelemetry instrumentation agent, the cloud resource attributes for GCP and AWS are included but not enabled by default since version 2.2.0. You can enable them <a href="https://opentelemetry.io/docs/languages/java/automatic/configuration/#enable-resource-providers-that-are-disabled-by-default">through configuration</a> by setting the following properties:</p>
<ul>
<li><p>For AWS: <code>otel.resource.providers.aws.enabled=true</code></p></li>
<li><p>For GCP: <code>otel.resource.providers.gcp.enabled=true</code></p></li>
</ul>
<h2 id="elasticopentelemetryjavadistribution">Elastic OpenTelemetry Java Distribution</h2>
<p>The Elastic OpenTelemetry Java distribution relies on the OpenTelemetry Java instrumentation which we often refer to as the Vanilla OpenTelemetry, and it thus inherits all of its features.</p>
<p>One major difference though is that the resource attributes providers for GCP and AWS are included and enabled by default to provide a better onboarding experience without extra configuration.</p>
<p>The minor cost to this is that it might make the application startup slightly slower due to having to call an HTTP(S) endpoint. This overhead is usually negligible compared to application startup but can become significant for some setups.</p>
<p>In order to reduce the startup overhead, or when the cloud provider is known in advance, you can selectively disable unused provider implementations through configuration:</p>
<ul>
<li><p>For AWS: <code>otel.resource.providers.aws.enabled=false</code></p></li>
<li><p>For GCP: <code>otel.resource.providers.gcp.enabled=false</code></p></li>
</ul>
<h2 id="conclusion">Conclusion</h2>
<p>With this blogpost we have introduced what OpenTelemetry cloud resource attributes are and how they can be used and configured into application deployments using either OpenTelemetry SDK/API and Instrumentation agents.</p>
<p>When using the Elastic OpenTelemetry Java distribution, those resource providers are automatically provided and enabled for an easy and simple onboarding experience.</p>
<p>Another very interesting aspect of the cloud resource attribute providers available in the <a href="https://github.com/open-telemetry/opentelemetry-java-contrib">opentelemetry-java-contrib</a> repository is that they are maintained by their respective vendors (Google and Amazon). For the end-user it means those implementations should be quite well tested and be robust to changes in the underlying infrastructure. For solution vendors like Elastic, it means we don't have to re-implement and reverse-engineer the infrastructure details of every cloud provider, hence proving that investing in those common components is a net win for the broader OpenTelemetry community.</p>]]></content:encoded>
    <link>https://www.elastic.co/observability-labs/blog/opentelemetry-java-automatic-cloud-resource-attributes</link>
    <guid isPermaLink="false">opentelemetry-java-automatic-cloud-resource-attributes</guid>
    <category><![CDATA[OpenTelemetry]]></category>
    <category><![CDATA[Infrastructure Monitoring]]></category>
    <dc:creator><![CDATA[Sylvain Juge]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltd11cf83d31a10bb5/6a7f192e9090b0891b84ee03/flexible-implementation-1680X980.png" length="0" type="image/png"/>
    <pubDate>Thu, 27 Jun 2024 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[AWS VPC Flow log analysis with GenAI in Elastic]]></title>
    <description><![CDATA[Elastic has a set of embedded capabilities such as a GenAI RAG-based AI Assistant and a machine learning platform as part of the product baseline. These make analyzing the vast number of logs you get from AWS VPC Flows easier.]]></description>
    <content:encoded><![CDATA[<p>Elastic Observability provides a full observability solution, by supporting metrics, traces and logs for applications and infrastructure. In managing AWS deployments, VPC flow logs are critical in managing performance, network visibility, security, compliance, and overall management of your AWS environment. Several examples of :</p>
<ol>
<li><p>Where traffic is coming in from and going out to from the deployment, and within the deployment. This helps identify unusual or unauthorized communications</p></li>
<li><p>Traffic volumes detecting spikes or drops which could indicate service issues in production or an increase in customer traffic</p></li>
<li><p>Latency and Performance bottlenecks - with VPC Flow logs, you can look at latency for a flow (in and outflows), and understand patterns</p></li>
<li><p>Accepted and rejected traffic helps determine where potential security threats and misconfigurations lie. </p></li>
</ol>
<p>AWS VPC Logs is a great example of how logs are great. Logging is an important part of Observability, for which we generally think of metrics and tracing. However, the amount of logs an application and the underlying infrastructure output can be significantly daunting with VPC Logs. However, it also provides a significant amount of insight.</p>
<p>Before we proceed, it is important to understand what Elastic provides in managing AWS and VPC Flow logs:</p>
<ol>
<li><p>A full set of integrations to manage VPC Flows and the <a href="https://www.elastic.co/observability-labs/blog/aws-service-metrics-monitor-observability-easy">entire end-to-end deployment on AWS</a>. </p></li>
<li><p>Elastic has a simple-to-use <a href="https://www.elastic.co/observability-labs/blog/aws-kinesis-data-firehose-observability-analytics">AWS Firehose integration</a>. </p></li>
<li><p>Elastic’s tools such as <a href="https://www.elastic.co/observability-labs/blog/vpc-flow-logs-monitoring-analytics-observability">Discover, spike analysis,  and anomaly detection help provide you with better insights and analysis</a>.</p></li>
<li><p>And a set of simple <a href="https://www.elastic.co/guide/en/observability/current/monitor-amazon-vpc-flow-logs.html#aws-firehose-dashboard">Out-of-the-box dashboards</a></p></li>
</ol>
<p>In today’s blog, we’ll cover how Elastics’ other features can support analyzing and RCA for potential VPC flow logs even more easily. Specifically, we will focus on managing the number of rejects, as this helps ensure there weren’t any unauthorized or unusual activities:</p>
<ol>
<li><p>Set up an easy-to-use SLO (newly released) to detect when things are potentially degrading</p></li>
<li><p>Create an ML job to analyze different fields of the VPC Flow log</p></li>
<li><p>Using our newly released RAG-based AI Assistant to help analyze the logs without needing to know Elastic’s query language nor how to even graph on Elastic</p></li>
<li><p>ES|QL will help understand and analyze add latency for patterns.</p></li>
</ol>
<p>In subsequent blogs, we will use AI Assistant and ESQL to show how to get other insights beyond just REJECT/ACCEPT from VPC Flow log.</p>
<h2 id="prerequisitesandconfig">Prerequisites and config</h2>
<p>If you plan on following this blog, here are some of the components and details we used to set up this demonstration:</p>
<ul>
<li><p>Ensure you have an account on <a href="http://cloud.elastic.co">Elastic Cloud</a> and a deployed stack (<a href="https://www.elastic.co/guide/en/elastic-stack/current/installing-elastic-stack.html">see instructions here</a>).</p></li>
<li><p>Follow the steps in the following blog to get <a href="https://github.com/aws-samples/aws-three-tier-web-architecture-workshop">AWS’s three-tier app</a> installed instructed in git, and bring in the <a href="https://www.elastic.co/observability-labs/blog/aws-kinesis-data-firehose-observability-analytics">AWS VPC Flow logs</a>.</p></li>
<li><p>Ensure you have an <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/ml-settings.html">ML node configured</a> in your Elastic stack</p></li>
<li><p>To use the AI Assistant you will need a trial or upgrade to Platinum.</p></li>
</ul>
<h2 id="slowithvpcflowlogs">SLO with VPC Flow Logs</h2>
<p>Elastic’s SLO capability is based directly on the Google SRE Handbook. All the definitions and semantics are utilized as described in Google’s SRE handbook. Hence users can perform the following on SLOs in Elastic:</p>
<ul>
<li>Define an SLO on Logs not just metrics - Users can use KQL (log-based query), service availability, service latency, custom metric, histogram metric, or a timeslice metric.</li>
<li>Define SLO, SLI, Error budget and burn rates. Users can also use occurrence versus time slice-based budgeting. </li>
<li>Manage, with dashboards, all the SLOs in a singular location.</li>
<li>Trigger alerts from the defined SLO, whether the SLI is off, the burn rate is used up, or the error rate is X.</li>
</ul>
<p>Setting up an SLO for VPC is easy. You simply create a query you want to trigger off. In our case, we look for all the good events where <em>aws.vpcflow.action=ACCEPT</em> and we define the target at 85%. </p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltd21d06910b861cd4/6a7f037f33fa8a81e0202287/VPCFlowSLOsetup.png" alt="Setting up SLO for VPC FLow log" /></p>
<p>As the following example shows, over the last 7 days, we have exceeded our budget by 43%. Additionally, we have not complied for the last 7 days.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltdce0dcb24898fd24/6a7f038296b5a69c6487b03d/VPCFlowSLOMiss.png" alt="VPC Flow Reject SLO" /></p>
<h2 id="analyzingtheslowithaiassistant">Analyzing the SLO with AI Assistant</h2>
<p>Now that we see that there is an issue with the VPC Flows, we immediately work with the AI Assistant to start analyzing the SLO. Because it's a chat interface we simply open the AI Assistant and work through some simple analysis: (See Animated GIF for a demo below)</p>
<h3 id="aiassistantanalysis">AI Assistant analysis:</h3>
<ul>
<li><p><strong>what were the top 3 source.address that had <em>aws.vpcflow.action=REJECT</em> over the last 7 days, which is causing this SLO issue?</strong> - We wanted to simply see what could be causing the loss in error budget. Were there any particular source.addresses causing a heavy reject rate.</p></li>
<li><p>The answer: A table with the highest count = 42670 and <em>source.address = 79.110.62.185</em></p></li>
<li><p>There is one singular <em>source.address</em> that is causing the loss in SLO. </p></li>
<li><p><strong>What is the largest number of  <em>aws.vpcflow.action=REJECT</em> in a 30 min time frame for the last 3 days where the <em>source.address=79.110.62.185</em>?</strong> - After understanding that a specific source.address is causing the loss in SLO, we want to understand the averages. </p></li>
<li><p>**The answer: ** "The largest number of <em>aws.vpcflow.action=REJECT</em> in a 30-minute time frame for the last 3 days where the <em>source.address</em> is 79.110.62.185 is 229. This occurred on 2024-06-01T04:00:00.000Z.”</p></li>
<li><p>It means there must be a low REJECT rate but fairly consistent vs spiky over the last 7 days. </p></li>
<li><p><strong>for the logs with <em>source.address</em>="79.110.62.185" was there any country code of <em>source.geo.country_iso_code</em> field present. If yes what is the value</strong> - Given the last question showed a low REJECT rate, it only means that this was fairly consistent vs spiky over the last 7 days.</p></li>
<li><p><strong>The answer:</strong> Yes, there is a country code present in the <em>source.geo.country_iso_code</em> field for logs with <em>source.address</em>="79.110.62.185". The value is BG (Bulgaria).</p></li>
<li><p><strong>Is there a specific destination.address where <em>source.address=79.110.62.185</em> is getting a <em>aws.vpcflow.action=REJECT</em>. Give me both the destination.address and the number of REJECTs for that destination.address?</strong></p></li>
<li><p><strong>The answer:</strong> destination.address of 10.0.0.27 is giving a reject number of 53433 in this time frame.</p></li>
<li><p><strong>Graph the number of REJECT vs ACCEPT for <em>source.address</em>="79.110.62.185" over the last 7 days. The graph is on a daily basis in a singular graph</strong> - We asked this question to see what the comparison is between ACCEPT and REJECT. </p></li>
<li><p><strong>The answer:</strong> See the animated GIF to see that the generated graph is fairly stable</p></li>
<li><p><strong>Were there any source.address that had a spike, high reject rate in. a 30min period over the 30 days?</strong> - We wanted to see if there was any other spike </p></li>
<li><p><strong>The answer</strong> - Yes, there was a source.address that had a spike in high reject rates in a 30-minute period over the last 30 days. <em>source.address</em>: 185.244.212.67, Reject Count: 8975, Time Period: 2024-05-22T03:00:00.000Z</p></li>
</ul>
<hr />
<h3 id="watchtheflow">Watch the flow</h3>
<div>
    
</div>
<h3 id="potentialissue">Potential issue:</h3>
<p>he server handling requests from source <strong><em>79.110.62.185</em></strong> is potentially having an issue.</p>
<p>Again using logs, we essentially asked the AI Assistant to give the <em>eni</em> ids where the internal ip address was 10.0.0.27</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt2ec3d25d096c3357/6a7f038605b7b5b00a18b519/VPCFlow-findingwebserver.png" alt="Finding the issue - webserver" /></p>
<p>From our AWS console, we know that this is the webserver. Further analysis in Elastic, and with the developers we realized there is a new version that was installed recently causing a problem with connections.</p>
<h2 id="locatinganomalieswithml">Locating anomalies with ML</h2>
<p>While using the AI Assistant is great for analyzing information, another important aspect of VPC flow management is to ensure you can manage log spikes and anomalies. Elastic has a machine learning platform that allows you to develop jobs to analyze specific metrics or multiple metrics to look for anomalies.</p>
<p>VPC Flow logs come with a large amount of information. The full set of fields is listed in <a href="https://docs.aws.amazon.com/vpc/latest/userguide/flow-logs.html#flow-logs-basics">AWS docs</a>. We will use a specific subset to help detect anomalies.</p>
<p>We were setting up anomalies for aws.vpcflow.action=REJECT, which requires us to use multimetric anomaly detection in Elastic.</p>
<p>The config we used utilizes:</p>
<p>Detectors:</p>
<ul>
<li><p>destination.address</p></li>
<li><p>destination.port</p></li>
</ul>
<p>Influencers:</p>
<ul>
<li><p>source.address</p></li>
<li><p>aws.vpcflow.action</p></li>
<li><p>destination.geo.region_iso_code</p></li>
</ul>
<p>The way we set this up will help us understand if there is a large spike in REJECT/ACCEPT against <em>destination.address</em> values from a specific <em>source.address</em> and/or <em>destination.geo.region_iso_code</em> location.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blta48754a0753271b1/6a7f03896c6eac6468f13cdd/VPCFlowanomalysetup.png" alt="Anomaly detection job config" /></p>
<p>The job once run reveals something interesting:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt9f3aee82193c8e44/6a7f038c05b7b54b1718b51d/VPCFlowAnomalyDetection.png" alt="Anomaly detected" /></p>
<p>Notice that <em>source.address</em> 185.244.212.67 has had a high REJECT rate in the last 30 days. </p>
<p>Notice where we found this before? In the AI Assistant!!!!!</p>
<p>While we can run the AI Assistant and find this sort of anomaly, the ML job can be setup to run continuously and alert us on such spikes. This will help us understand if there are any issues with the webserver like we found above or even potential security attacks.</p>
<h2 id="conclusion">Conclusion:</h2>
<p>You’ve now seen how easily Elastic’s RAG-based AI Assistant can help analyze VPC Flows without even the need to know query syntax, understand where the data is, and understand even the fields. Additionally, you’ve also seen how we can alert you when a potential issue or degradation in service (SLO). Check out our other blogs on AWS VPC Flow analysis in Elastic:</p>
<ol>
<li><p>A full set of integrations to manage VPC Flows and the <a href="https://www.elastic.co/observability-labs/blog/aws-service-metrics-monitor-observability-easy">entire end-to-end deployment on AWS</a>. </p></li>
<li><p>Elastic has a simple-to-use <a href="https://www.elastic.co/observability-labs/blog/aws-kinesis-data-firehose-observability-analytics">AWS Firehose integration</a>. </p></li>
<li><p>Elastic’s tools such as <a href="https://www.elastic.co/observability-labs/blog/vpc-flow-logs-monitoring-analytics-observability">Discover, spike analysis,  and anomaly detection help provide you with better insights and analysis</a>.</p></li>
<li><p>And a set of simple <a href="https://www.elastic.co/guide/en/observability/current/monitor-amazon-vpc-flow-logs.html#aws-firehose-dashboard">Out-of-the-box dashboards</a></p></li>
</ol>
<h2 id="tryitout">Try it out</h2>
<p>Existing Elastic Cloud customers can access many of these features directly from the <a href="https://cloud.elastic.co/">Elastic Cloud console</a>. Not taking advantage of Elastic on the cloud? <a href="https://www.elastic.co/cloud/cloud-trial-overview">Start a free trial</a>.</p>
<p>All of this is also possible in your environment. <a href="https://www.elastic.co/observability/universal-profiling">Learn how to get started today</a>.</p>
<p><em>The release and timing of any features or functionality described in this post remain at Elastic's sole discretion. Any features or functionality not currently available may not be delivered on time or at all.</em></p>
<p><em>In this blog post, we may have used or referred to third party generative AI tools, which are owned and operated by their respective owners. Elastic does not have any control over the third party tools and we have no responsibility or liability for their content, operation or use, nor for any loss or damage that may arise from your use of such tools. Please exercise caution when using AI tools with personal, sensitive or confidential information. Any data you submit may be used for AI training or other purposes. There is no guarantee that information you provide will be kept secure or confidential. You should familiarize yourself with the privacy practices and terms of use of any generative AI tools prior to use.</em></p>
<p><em>Elastic, Elasticsearch, ESRE, Elasticsearch Relevance Engine and associated marks are trademarks, logos or registered trademarks of Elasticsearch N.V. in the United States and other countries. All other company and product names are trademarks, logos or registered trademarks of their respective owners.</em></p>]]></content:encoded>
    <link>https://www.elastic.co/observability-labs/blog/aws-vpc-flow-log-analysis-with-genai-elastic</link>
    <guid isPermaLink="false">aws-vpc-flow-log-analysis-with-genai-elastic</guid>
    <category><![CDATA[Agentic Observability]]></category>
    <category><![CDATA[Logs Analytics]]></category>
    <category><![CDATA[Infrastructure Monitoring]]></category>
    <category><![CDATA[LLM Observability]]></category>
    <dc:creator><![CDATA[Bahubali Shetti]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt5265effb8d313486/6a7f038fde23157404fd7786/21-cubes.jpeg" length="0" type="image/jpeg"/>
    <pubDate>Fri, 07 Jun 2024 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Better RCAs with multi-agent AI Architecture]]></title>
    <description><![CDATA[Discover how specialized LLM agents collaborate to tackle complex tasks with unparalleled efficiency]]></description>
    <content:encoded><![CDATA[<h2 id="whatsamultiagentarchitecture">What’s a multi agent architecture?</h2>
<p>You might have heard the term Agent pop up recently in different open source projects or vendors focusing their go-to-market on GenAI. Indeed, while most GenAI applications are focused on RAG applications today, there is an increasing interest in isolating tasks that could be achieved with a more special model into what is called an Agent.</p>
<p>To be clear, an agent will be given a task, which could be a prompt, and execute the task by leveraging other models, data sources, and a knowledge base. Depending on the field of application, the results should ultimately look like generated text, pictures, charts, or sounds. </p>
<p>Now, what the multi-Agent Architecture, is the process of leveraging multiple agents around a given task by: </p>
<ul>
<li>Orchestrating complex system oversight with multiple agents </li>
<li>Analyzing and strategizing in real-time with strategic reasoning </li>
<li>Specializing agents, tasks are decomposed into smaller focused tasks into expert-handled elements</li>
<li>Sharing insights for cohesive action plans, creating collaborative dynamics</li>
</ul>
<p>In a nutshell, multi-agent architecture's superpower is tackling intricate challenges beyond human speed and solving complex problems. It enables a couple of things:</p>
<ul>
<li>Scale the intelligence as the data and complexity grows. The tasks are decomposed into smaller work units, and the expert network grows accordingly.</li>
<li>Coordinate simultaneous actions across systems, scale collaboration </li>
<li>Evolving with data allows continuous adaptation with new data for cutting-edge decision-making. </li>
<li>Scalability, high performance, and resilience</li>
</ul>
<h2 id="singleagentvsmultiagentarchitecture">Single Agent Vs Multi-Agent Architecture</h2>
<p>Before double-clicking on the multi-agent architecture, let’s talk about the single-agent architecture. The single-agent architecture is designed for straightforward tasks and a late feedback loop from the end user. There are multiple single-agent frameworks such as ReAct (Reason+Act), RAISE (ReAct+ Short/Long term memory), Reflexion, AutoGPT+P, and LATS (Language Agent Tree Search). The general process these architectures enable is as follows:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blta56c5a29be3497ae/6a7f1b4be3a219768599f8d2/single.png" alt="alt_text" /></p>
<p>The Agent takes action, observes, executes, and self-decides whether or not it looks complete, ends the process if finished, or resubmits the new results as an input action, the process keeps going. </p>
<p>While simple tasks are ok with this type of agent, such as a RAG application where a user will ask a question, and the agent returns an answer based on the LLM and a knowledge base, there are a couple of limitations:</p>
<ul>
<li>Endless execution loop: the agent is never satisfied with the output and reiterates. </li>
<li>Hallucinations</li>
<li>Lack of feedback loop or enough data to build a feedback loop</li>
<li>Lack of planning </li>
</ul>
<p>For these reasons, the need for a better self-evaluation loop, externalizing the observation phase, and division of labor is rising, creating the need for a multi-agent architecture.</p>
<p>Multi-agent architecture relies on taking a complex task, breaking it down into multiple smaller tasks, planning the resolution of these tasks, executing, evaluating, sharing insights, and delivering an outcome. For this, there is more than one agent; in fact, the minimum value for the network size N is N=2 with:</p>
<ul>
<li>A Manager </li>
<li>An Expert</li>
</ul>
<p>When N=2, the source task is simple enough only to need one expert agent as the task can not be broken down into multiple tasks. Now, when the task is more complex, this is what the architecture can look like:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt1fa71331f0a0f6e7/6a7f1b4ec2e914577c017002/multi-vertical.png" alt="alt_text" /></p>
<p>With the help of an LLM, the Manager decomposes the tasks and delegates the resolutions to multiple agents. The above architecture is called Vertical since the agents directly send their results to the Manager. In a horizontal architecture, agents work and share insight together as groups, with a volunteer-based system to complete a task, they do not need a leader as shown below:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt66f7b563d6e24eda/6a7f1b51ea068d47d1f0a2db/multi-horizontal.png" alt="alt_text" /></p>
<p>A very good paper covering these two architectures with more insights can be found here: <a href="https://arxiv.org/abs/2404.11584">https://arxiv.org/abs/2404.11584</a></p>
<h2 id="applicationverticalmultiagentarchitecturetoobservability">Application Vertical Multi-Agent Architecture to Observability</h2>
<p>Vertical Multi-Agent Architecture can have a manager, experts, and a communicator. This is particularly important when these architectures expose the task's result to an end user.</p>
<p>In the case of Observability, what we envision in this blog post is the scenario of an SRE running through a Root Cause Analysis (RCA) process. The high-level logic will look like this: </p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt3cdc9faa6d124ca8/6a7f1b54b4377058854d7122/maar-observability.png" alt="alt_text" /></p>
<ul>
<li>Communicator: <ul>
<li>Read the initial command from the Human</li>
<li>Pass command to Manager</li>
<li>Provide status updates to Human</li>
<li>Provide a recommended resolution plan to the Human</li>
<li>Relay follow-up commands from Human to Manager</li></ul></li>
<li>Manager: <ul>
<li>Read the initial command from the Communicator </li>
<li>Create working group </li>
<li>Assign Experts to group </li>
<li>Evaluate signals and recommendations from Experts </li>
<li>Generate recommended resolution plan </li>
<li>Execute plan (optional)</li></ul></li>
<li>Expert:<ul>
<li>Each expert task with singular expertise tied to Elastic integration </li>
<li>Use o11y AI Assistant to triage and troubleshoot data related to their expertise </li>
<li>Work with other Experts as needed to correlate issues </li>
<li>Provide recommended root cause analysis for their expertise (if applicable) </li>
<li>Provide recommended resolution plan for their expertise (if applicable)</li></ul></li>
</ul>
<p>We believe that breaking down the experts by integration provides enough granularity in the case of observability and allows them to focus on a specific data source. Doing this also gives the manager a breakdown key when receiving a complex incident involving multiple data layers (application, network, datastores, infrastructures).</p>
<p>For example, a complex task initiated by an alert in an e-commerce application could be “Revenue dropped by 30% in the last hour.” This task would be submitted to the manager, who will look at all services, applications, datastores, network components, and infrastructure involved and decompose these into investigation tasks. Each expert would investigate within their specific scope and provide observations to the manager. The manager will be responsible for correlating and providing observations on what caused the problem. </p>
<h3 id="corearchitecture">Core Architecture</h3>
<p>In the above example, we have decided to deploy the architecture on the below software architecture: </p>
<ul>
<li>The agent manager and expert agent are deployed on GCP or your favorite cloud provider</li>
<li>Most of the components are written in Python</li>
<li>A task management layer is necessary to queue the task to the expert</li>
<li>Expert agents are specifically deployed by integration/data source and converse with the Elastic AI Assistant deployed in Kibana.</li>
<li>The AI Assistant can access a real-time context to help the expert resolve their task.</li>
<li>Elasticsearch is used as the AI Assistant context and as the expert memory to build its experience. </li>
<li>The backend LLM here is GPT-4, now GTP-4o, running on Azure.</li>
</ul>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt5437b38178aa7715/6a7f1b56bdcff0654bc432b7/core-architecture.png" alt="alt_text" /></p>
<h3 id="agentexperience">Agent Experience</h3>
<p>Agent experience is built based on previous events stored in Elasticsearch, to which the expert can look semantically for similar events. When they find one, they get the execution path stored in memory to execute it. </p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt338e50db9c705a52/6a7f1b5933fa8a1c25202b92/agent-experience.png" alt="alt_text" /></p>
<p>The beauty of using the Elasticsearch Vector Database for this is the semantic query the agent will be able to execute against the memory and how the memory itself can be managed. Indeed, there is a notion of short—and long-term memory that could be very interesting in the case of observability, where some events often happen and probably worth to be stored in the short-term memory because they are questioned more often. Less queried but important events can be stored in a longer-term memory with more cost-effective hardware.</p>
<p>The other aspect of the Agent Experience is the semantic <a href="https://www.elastic.co/search-labs/blog/semantic-reranking-with-retrievers">reranking</a> feature with Elasticsearch. When the agent executes a task, reranking is used to surface the best outcome compared to past experience:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt5e76bee7b41f7fcd/6a7f1b5c42a1179ceb95c32f/agent-experience-build.png" alt="alt_text" /></p>
<p>If you are looking for a working example of the above, <a href="https://www.elastic.co/observability-labs/blog/elastic-ai-assistant-observability-escapes-kibana">check this blog post</a> where 2 agents are working together with the Elastic Observability AI Assistant on an RCA: </p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt45576f30f39e21b9/6a7f1b5f96b5a66af087b8ad/ops-burger.png" alt="alt_text" /></p>]]></content:encoded>
    <link>https://www.elastic.co/observability-labs/blog/super-agent-architecture</link>
    <guid isPermaLink="false">super-agent-architecture</guid>
    <category><![CDATA[Agentic Observability]]></category>
    <category><![CDATA[Infrastructure Monitoring]]></category>
    <dc:creator><![CDATA[Baha Azarmi,Jeff Vestal]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt5a10b1f962d70c57/6a7f1b6277b03421073ff91d/githubcopilot-aiassistant.png" length="0" type="image/png"/>
    <pubDate>Fri, 31 May 2024 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Universal Profiling: Detecting CO2 and energy efficiency]]></title>
    <description><![CDATA[Universal Profiling introduces the possibility to capture environmental impact. In this post, we compare Python and Go implementations and showcase the substantial CO2 savings achieved through code optimization.]]></description>
    <content:encoded><![CDATA[<p>A while ago, we posted a <a href="https://www.elastic.co/blog/importing-chess-games-elasticsearch-universal-profiling">blog</a> that detailed how we imported over 4 billion chess games with speed using Python and optimized the code leveraging our Universal Profiling<sup>TM</sup>. This was based on Elastic Stack running on version 8.9. We are now on <a href="https://www.elastic.co/blog/whats-new-elastic-8-12-0">8.12</a>, and it is time to do a second part that shows how easy it is to observe compiled languages and how Elastic®’s Universal Profiling can help you determine the benefit of a rewrite, both from a cost and environmental friendliness angle.</p>
<h2 id="whyefficiencymattersforyouandtheenvironment">Why efficiency matters — for you and the environment</h2>
<p>Data centers are estimated to consume ~3% of global electricity consumption, and their usage is expected to double by 2030.* The cost of a digital service is a close proxy to its computing efficiency, and thus, being more efficient is a win-win: less energy consumed, smaller bill.</p>
<p>In the same scenario, companies want the ability to scale to more users while spending less for each user and are effectively looking into methods of reducing their energy consumption.</p>
<p>In this spirit, <a href="https://www.elastic.co/observability/universal-profiling">Universal Profiling</a> comes equipped with data and visualizations to help determine where efficiency improvement efforts are worth the most.</p>
<p><a href="https://www.elastic.co/blog/continuous-profiling-efficient-cost-effective-applications">Energy efficiency</a> measures how much a digital service consumes to produce an output given an input. It can be measured in multiple ways, and we at Elastic Observability chose CO<sub>2</sub> emissions and annualized CO<sub>2</sub> emissions (more details on them later).</p>
<p>Let’s take the example of an e-commerce website: the energy efficiency of the “search inventory” process could be calculated as the average CPU time needed to serve a user request. Once the baseline for this value is determined, changes to the software delivering the search process may result in more or less CPU time consumed for the same feature, resulting in less or more efficient code.</p>
<h2 id="howtosetupandconfigurewattageandco2">How to set up and configure wattage and CO2</h2>
<p>You can find a “Settings” button in the top-right corner of the Universal Profiling views. From there, you can customize the coefficient used to calculate CO<sub>2</sub> emissions tied to profiling data.</p>
<p>The values set here will be used only when the profiles gathered from host agents are not already associated with publicly known data certified by cloud providers. For example, suppose you have a hybrid cloud deployment with a portion of your workload running on-premise and a portion running in GCP. In that case, the values set here will only be used to calculate the CO<sub>2</sub> emissions for the on-premise machines; we already use all the coefficients as declared by GCP to calculate the emissions of those machines.</p>
<h2 id="pythonvsgo">Python vs. Go</h2>
<p>Our first <a href="https://www.elastic.co/blog/importing-chess-games-elasticsearch-universal-profiling">blog post</a> implemented a solution to read PGN chess games, a text representation in Python. It showed how Universal Profiler can be leveraged to identify slow functions and help you rewrite your code faster and more efficiently. At the end of it, we were happy with the Python version. It is still used today to grab the monthly updates from the <a href="https://database.lichess.org/">Lichess database</a> and ingest them into Elasticsearch®. I always wanted a reason to work more with Go, and we rewrote Python to Go. We leveraged goroutines and channels to send data through message passing. You can see more about it in our <a href="https://github.com/philippkahr/blogs/tree/main/universal-profiling">GitHub repository</a>.</p>
<p>Rewriting in Go also means switching from an interpreted language to a compiled one. As with everything in IT, this has benefits as well as disadvantages. One disadvantage is that we must ship debug symbols for the compiled binary. When we build the binary, we can use the symbtool program to ship the debug symbols. Without debug symbols, we see uninterpretable information as frames will be labeled with hexadecimal addresses in the flame graph rather than source code annotations.</p>
<p>First, make sure that your executable includes debug symbols. Go per default builds with debug symbols. You can check this by using file yourbinary. The important part is that it is not stripped.</p>
<pre><code>file lichess
lichess: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, Go BuildID=gufIkqA61WnCh8haeW-2/lfn3ne3U_y8MGoFD4AvT/QJEykzbacbYEmEQpXH6U/MqVbk-402n1k3B8yPB6I, with debug_info, not stripped
</code></pre>
<p>Now we need to push the symbols using symbtool. You must create an Elasticsearch API key as the authentication method. In the Universal Profiler UI in Kibana®, an <strong>Add Data</strong> button in the top right corner will tell you exactly what to do. The command is like this. The -e is the part where you pass through the path of your executable file. In our case, this is lichess as above.</p>
<pre><code>symbtool push-symbols executable -t "ApiKey" -u "elasticsearch-url" -e "lichess"
</code></pre>
<p>Now that debug symbols are available inside the cluster, we can run both implementations with the same file simultaneously and see what Universal Profiler can tell us about it.</p>
<h2 id="identifyingco2andenergyefficiencysavings">Identifying CO2 and energy efficiency savings</h2>
<p>Python is more frequently scheduled on the CPU. Thus, it runs more often on the hardware and contributes more to the machines’ resource usage.</p>
<p>We use the differential flame graph to identify and automatically calculate the difference in the following comparison. You need to filter on process.thread.name: “python3.11” in the baseline, and for the comparison, filter for lichess.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt45bdc550b9e6dcd0/6a7f1bd533fa8a1440202b98/1-elastic-blog-uni-profiling.png" alt="1 - universal profiling" /></p>
<p>Looking at the impact of annualized CO<sub>2</sub> emissions, we see a decrease from 65.32kg of CO<sub>2</sub> from the Python solution to 16.78kg. That is a difference of 48.54kg CO<sub>2</sub> savings over a year.</p>
<p>If we take a step back, we’ll want to figure out why Python produces many more emissions. In the flamegraph view, we filter down to just showing Python, and we can click on the first frame called python3.11. A little popup tells us that it caused 32.95kg of emissions. That is nearly 50% of all emissions caused by the runtime. Our program itself caused the other ~32kg of CO<sub>2</sub>. We immediately reduced 32kg of annual emissions by cutting out the Python interpreter with Go.</p>
<p>We can lock that box using a right click and click <strong>Show more information</strong>.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt3f5519e4403d03da/6a7f1bd8b437701f404d712a/2-elastic-blog-uni-profiling.png" alt="2 - universal profiling graphs blue-orange" /></p>
<p>The <strong>Show more information</strong> link displays detailed information about the frame, like sample count, total CPU, core seconds, and dollar costs. We won’t go into more detail in this blog.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blte81b06c8b7f996c4/6a7f1bdbbdcff03595c432d3/3-elastic-blog-uni-profiling.png" alt="3 impact estimates" /></p>
<h2 id="reduceyourcarbonfootprinttodaywithuniversalprofiling">Reduce your carbon footprint today with Universal Profiling</h2>
<p>This blog post demonstrates that rewriting your code base can reduce your carbon footprint immensely. Using Universal Profiler, you could do a quick PoC to showcase how much carbon resources can be spared.</p>
<p>Learn how you can <a href="https://www.elastic.co/guide/en/observability/current/profiling-get-started.html">get started</a> with Elastic Universal Profiling today.</p>
<blockquote>
  <ul>
  <li>Cluster for storing the data where three nodes, each 64GB RAM and 32 CPU cores, are running GCP on Elastic Cloud.</li>
  <li>The machine for sending the data is a GCP e2-standard-32, thus 128GB RAM and 32 CPU cores with a 500GB balanced disk to read the games from.</li>
  <li>The file used for the games is this <a href="https://database.lichess.org/standard/lichess_db_standard_rated_2023-12.pgn.zst">Lichess database</a> containing 96,909,211 games. The extracted file size is 211GB.</li>
  </ul>
</blockquote>
<p><strong>Source:</strong></p>
<p>*<a href="https://media.ccc.de/v/camp2023-57070-energy_consumption_of_data_centers">https://media.ccc.de/v/camp2023-57070-energy_consumption_of_data_centers</a></p>
<p><em>The release and timing of any features or functionality described in this post remain at Elastic's sole discretion. Any features or functionality not currently available may not be delivered on time or at all.</em></p>]]></content:encoded>
    <link>https://www.elastic.co/observability-labs/blog/universal-profiling-detecting-co2-energy-efficiency</link>
    <guid isPermaLink="false">universal-profiling-detecting-co2-energy-efficiency</guid>
    <category><![CDATA[APM]]></category>
    <category><![CDATA[Infrastructure Monitoring]]></category>
    <dc:creator><![CDATA[Philipp Kahr,Francesco Gualazzi]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt12688c185784aad8/6a7f1bde6693f85f656643a9/141935_-_Blog_header_image-_Op1_V1.jpg" length="0" type="image/jpeg"/>
    <pubDate>Mon, 05 Feb 2024 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Elastic Observability monitors metrics for Microsoft Azure in just minutes]]></title>
    <description><![CDATA[Follow this step-by-step process to enable Elastic Observability for Microsoft Azure metrics.]]></description>
    <content:encoded><![CDATA[<p>Developers and SREs choose Microsoft Azure to run their applications because it is a trustworthy world-class cloud platform. It has also proven itself over the years as an extremely powerful and reliable infrastructure for hosting business-critical applications.</p>
<p>Elastic Observability offers over 25 out-of-the-box integrations for Microsoft Azure services with more on the way. A full list of Azure integrations can be found in <a href="https://docs.elastic.co/integrations/azure">our online documentation</a>.</p>
<p>Elastic Observability aggregates not only logs but also metrics for Azure services and the applications running on Azure compute services (Virtual Machines, Functions, Kubernetes Service, etc.). All this data can be analyzed visually and more intuitively using Elastic®’s advanced machine learning (ML) capabilities, which help detect performance issues and surface root causes before end users are affected.</p>
<p>For more details on how Elastic Observability provides application performance monitoring (APM) capabilities such as service maps, tracing, dependencies, and ML-based metrics correlations, read <a href="https://www.elastic.co/blog/apm-correlations-elastic-observability-root-cause-transactions">APM correlations in Elastic Observability: Automatically identifying probable causes of slow or failed transactions</a>.</p>
<p>That’s right, Elastic offers capabilities to collect, aggregate, and analyze metrics for Microsoft Azure services and applications running on Azure. Elastic Observability is for more than just capturing logs — it offers a unified observability solution for Microsoft Azure workloads.</p>
<p>In this blog, we’ll review how Elastic Observability can monitor metrics for a three-tier web application running on Microsoft Azure and leveraging:</p>
<ul>
<li>Microsoft Azure Virtual Machines</li>
<li>Microsoft Azure SQL database</li>
<li>Microsoft Azure Virtual Network</li>
</ul>
<p>As you will see, once the integration is installed, metrics will arrive instantly and you can immediately start deriving insights from metrics.</p>
<h2 id="prerequisitesandconfig">Prerequisites and config</h2>
<p>Here are some of the components and details we used to set up this demonstration:</p>
<ul>
<li>Ensure you have a Microsoft Azure account and an Azure service principal with permission to read monitoring data from Microsoft Azure (<a href="https://docs.elastic.co/integrations/azure_metrics/monitor#integration-specific-configuration-notes">see details in our documentation</a>).</li>
<li>This post does <em>not</em> cover application monitoring; instead, we will focus on how Microsoft Azure services can be easily monitored. If you want to get started with examples of application monitoring, see our <a href="https://github.com/elastic/observability-examples/tree/main/azure/container-apps">Hello World observability code samples</a>.</li>
<li>In order to see metrics, you will need to load the application. We’ve also created a Playwright script to drive traffic to the application.</li>
</ul>
<h2 id="threetierapplicationoverview">Three-tier application overview</h2>
<p>Before we dive into the Elastic deployment setup and configuration, let's review what we are monitoring. If you follow the <a href="https://learn.microsoft.com/en-us/training/modules/n-tier-architecture/">Microsoft Learn N-tier example app</a> instructions for deploying the "What's for Lunch?" app, you will have the following deployed.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt78865baac7b2a393/6a85c9fb331d7a5460c3177f/blog-elastic-three-tier-application-overview.png" alt="three tier application overview" /></p>
<p>What’s deployed:</p>
<ul>
<li>Microsoft Azure VM presentation tier that renders an HTML client in the user's browser and enables user requests to be sent to the “What’s for Lunch?” app</li>
<li>Microsoft Azure VM application tier that communicates with the presentation and the database tier</li>
<li>Microsoft Azure SQL instance in the database tier, handling requests from the application tier to store and serve data</li>
</ul>
<p>At the end of the blog, we will also provide a Playwright script that can be run to send requests to this app in order to load it with example data and exercise its functionality. This will help drive metrics to “light up” the dashboards.</p>
<h2 id="settingitallup">Setting it all up</h2>
<p>Let’s walk through the details of how to deploy the example three-tier application, Azure integration on Elastic and visualize what gets ingested in Elastic’s Kibana® dashboards.</p>
<h3 id="step0getanaccountonelasticcloud">Step 0: Get an account on Elastic Cloud</h3>
<p>Follow the instructions to <a href="https://cloud.elastic.co/registration">get started on Elastic Cloud</a>.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltb1b5998e0b8cc454/6a85c9fd5c27905315f59b07/blog-elastic-free-trial.png" alt="elastic cloud free trial sign up" /></p>
<h3 id="step1deploythemicrosoftazurethreetierapplication">Step 1: Deploy the Microsoft Azure three-tier application</h3>
<p>From the <a href="https://portal.azure.com/">Azure portal</a>, click the Cloud Shell icon at the top of the portal to open Cloud Shell…</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt5fc8d120fc905c77/6a85ca00bc5bb3d25af81ad9/blog-elastic-open-cloud-shell.png" alt="open cloud shell" /></p>
<p>… and when the Cloud Shell first opens, select <strong>Bash</strong> as the shell type to use.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltaaf43e6f0672dc9b/6a85ca03ba7acc9689992130/blog-elastic-cloud-shell-bash.png" alt="cloud shell bash" /></p>
<p>If you’re prompted that “You have no storage mounted,” then click the <strong>Create storage</strong> button to create a file store to be used for saving and editing files from Cloud Shell.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt24691c80db0ce204/6a85ca06331d7aa36bc31783/blog-elastic-create-storage.png" alt="cloud shell create storage" /></p>
<p>You should now see the open Cloud Shell terminal.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt4dc12c5d67ebb249/6a85ca0998292672295838ca/blog-elastic-cloud-shell-terminal.png" alt="cloud shell terminal" /></p>
<p>Run the following command in Cloud Shell to define the environment variables that we’ll be using in the Cloud Shell commands required to deploy and view the sample application.</p>
<p>Be sure to specify a valid RESOURCE_GROUP from your available <a href="https://portal.azure.com/#view/HubsExtension/BrowseResourceGroups">Resource Groups listed in the Azure portal</a>. Also specify a new password to replace the SpecifyNewPasswordHere placeholder text before running the command. See the Microsoft <a href="https://learn.microsoft.com/en-us/sql/relational-databases/security/password-policy?view=sql-server-ver16#password-complexity">password policy documentation</a> for password requirements.</p>
<pre><code>RESOURCE_GROUP="test"
APP_PASSWORD="SpecifyNewPasswordHere"
</code></pre>
<p>Run the following az deployment group create command, which will deploy the example three-tier web app in around five minutes.</p>
<pre><code>az deployment group create --resource-group $RESOURCE_GROUP --template-uri https://raw.githubusercontent.com/MicrosoftDocs/mslearn-n-tier-architecture/master/Deployment/azuredeploy.json --parameters password=$APP_PASSWORD
</code></pre>
<p>After the deployment has completed, run the following command, which returns the URL for the app.</p>
<pre><code>az deployment group show --output table --resource-group $RESOURCE_GROUP --name azuredeploy --query properties.outputs.webSiteUrl
</code></pre>
<p>Copy the web app URL and paste it into a browser to view the example “What’s for Lunch?” web app.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blte72de03d59114cc0/6a85ca0beaf2457371a49f49/blog-elastic-whats-for-lunch.png" alt="whats for lunch app" /></p>
<h3 id="step2createanazureserviceprincipalandgrantaccesspermission">Step 2: Create an Azure service principal and grant access permission</h3>
<p>Go to the <a href="https://portal.azure.com/">Microsoft Azure Portal</a>. Search for active directory and select <strong>Microsoft Entra ID</strong>.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt8b89c41ce941474c/6a85ca0ef61d6e00729c2afb/blog-elastic-active-directory.png" alt="search active directory" /></p>
<p>Copy the <strong>Tenant ID</strong> for use in a later step in this blog post. This ID is required to configure Elastic Agent to connect to your Azure account.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltc67ef8fd47a07daf/6a85ca1168266642671eabe7/blog-elastic-your-organization-overview.png" alt="your organization overview" /></p>
<p>In the navigation pane, select <strong>App registrations</strong>.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt63caee3d530d8fb6/6a85ca1480984c3ea4668fba/blog-elastic-your-organization-overview-app-registrations.png" alt="your organization overview app registrations" /></p>
<p>Then click <strong>New registration</strong>.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt28d2ae4600d587ab/6a85ca171aa1e1c669ff8d47/blog-elastic-your-organization-new-registration.png" alt="your organization new registrations" /></p>
<p>Type the name of your application (this tutorial uses three-tier-app-azure) and click <strong>Register</strong> (accept the default values for other settings).</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt7b60e56405060b44/6a85ca1a11893cefaaa7ab7c/blog-elastic-register_an_application.png" alt="register an application" /></p>
<p>Copy the <strong>Application (client) ID</strong> and save it for later. This ID is required to configure Elastic Agent to connect to your Azure account.</p>
<p>In the navigation pane, select <strong>Certificates &amp; secrets</strong> , and then click <strong>New client secret</strong> to create a new security key.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt0a42c8cd0b462f5d/6a85ca1df5f1a033fb2ec8df/blog-elastic-three-tier-app-new-client-secret.png" alt="three tier app new client secret" /></p>
<p>Type a description of the secret and select an expiration. Click <strong>Add</strong> to create the client secret. Under <strong>Value</strong> , copy the secret value and save it (along with your client ID) for later.</p>
<p>After creating the Azure service principal, you need to grant it the correct permissions. In the Azure Portal, search for and select <strong>Subscriptions</strong>.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt27de6595abfe44ac/6a85ca1fabdc2927991224d6/blog-elastic-three-tier-subscriptions.png" alt="three tier subscriptions" /></p>
<p>In the Subscriptions page, click the name of your subscription. On the subscription details page, copy your <strong>Subscription ID</strong> and save it for a later step.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltc969b5a5eecfbb8e/6a85ca2293ffb98248b9142b/blog-elastic-subscription-essentials-copy.png" alt="subscription essentials copy" /></p>
<p>In the navigation pane, select <strong>Access control (IAM)</strong>.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltcc0d6f8281122752/6a85ca259a32f15cbfa7dfd0/blog-elastic-subscription-access-control.png" alt="subscription access control" /></p>
<p>Click <strong>Add</strong> and select <strong>Add role assignment</strong>.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt7b3b8d7bdf3f141e/6a85ca2807829039d0321752/blog-elastic-subscription-access-control-add-role-assignment.png" alt="subscription access control add role assignment" /></p>
<p>On the <strong>Role</strong> tab, select the <strong>Monitoring Reader</strong> role and then click <strong>Next</strong>.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt99f68cb7713f8205/6a85ca2b27c5cd03ec5f73d8/blog-elastic-add-role-assignment-monitoring-readers.png" alt="add role assignment monitoring reader" /></p>
<p>On the <strong>Members</strong> tab, select the option to assign access to <strong>User, group, or service principal</strong>. Click <strong>Select members</strong> , and then search for and select the principal you created earlier. For the description, enter the name of your service principal. Click <strong>Next</strong> to review the role assignment.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt6ac85a836aeaa428/6a85ca2ef9373d43ad96f584/blog-elastic-add-role-assignment-description.png" alt="add role assignment description" /></p>
<p>Click <strong>Review + assign</strong> to grant the service principal access to your subscription.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt2d1eb5fc55533145/6a85ca30abdc2963671224da/blog-elastic-add-role-assignment-review-assign.png" alt="add role assignment review assign" /></p>
<h3 id="step3createanazurevminstance">Step 3: Create an Azure VM instance</h3>
<p>In the Azure Portal, search for and select <strong>Virtual machines</strong>.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt6df66ab8fe5350d7/6a85ca3311893cc69ba7ab80/blog-elastic-search-virtual-machines.png" alt="search virtual machines" /></p>
<p>On the <strong>Virtual machines</strong> page, click <strong>+ Create</strong> and select <strong>Azure virtual machine</strong>.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt6ee0dafa647fa8fe/6a85ca36bc5bb3bd24f81ae1/blog-elastic-azure-virtual-machine.png" alt="azure virtual machine" /></p>
<p>On the Virtual machine creation page, enter a name like “metrics-vm” for the virtual machine name and select VM Size to be “Standard_D2s_v3 - 2 vcpus, 8 GiB memory.” Click the <strong>Next : Disks</strong> button.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt087222cdca1f2bf1/6a85ca3943c0b7ea592f05fc/blog-elastic-create-virtual-macine-next-disks.png" alt="create a virtual machine next disks" /></p>
<p>On the <strong>Disks</strong> page, keep the default settings and click the <strong>Next : Networking</strong> button.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt6c10808899e56ad5/6a85ca3c4710c6e42ad3cb33/blog-elastic-create-virtual-machine-next-networking.png" alt="create a virtual machine next networking" /></p>
<p>On the <strong>Networking</strong> page, demo-vnet should be selected for <strong>Virtual network</strong> and demo-biz-subnet should be selected for <strong>Subnet</strong>. These resources are created as part of the three-tier example app’s deployment that was done in Step 1.</p>
<p>Click the <strong>Review + create</strong> button.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltaa7213850eedec1e/6a85ca405c2790854bf59b0b/blog-elastic-create-virtual-machine-review-create.png" alt="create virtual machine review create" /></p>
<p>On the <strong>Review</strong> page, click the <strong>Create</strong> button.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt49cea7999c6d0609/6a85ca4327c5cd23e75f73de/blog-elastic-create-virtual-machine-validation-passed.png" alt="create virtual machine validation passed" /></p>
<h3 id="step4installtheazureresourcemetricsintegration">Step 4: Install the Azure Resource Metrics integration</h3>
<p>In your <a href="https://cloud.elastic.co/home">Elastic Cloud</a> deployment, navigate to the Elastic Azure integrations by selecting <strong>Integrations</strong> from the top-level menu. Search for azure resource and click the <strong>Azure Resource Metrics</strong> tile.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltd2a117bba617a23a/6a85ca46ba7acce765992136/blog-elastic-integrations-azure-resource-metrics.png" alt="integrations azure resource metrics" /></p>
<p>Click <strong>Add Azure Resource Metrics.</strong></p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt88cb6bba93e24136/6a85ca4c342d69099a21b0d7/blog-elastic-azure-resource-metrics.png" alt="azure resource metrics" /></p>
<p>Click <strong>Add integration only (skip agent installation)</strong>.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt92fcc0204f8e2c6f/6a85ca4f80984c32d3668fc8/blog-elastic-add-integration-only.png" alt="add integration only" /></p>
<p>Enter the values that you saved previously for Client ID, Client Secret, Tenant ID, and Subscription ID.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt4a0db24f97f4278c/6a85ca53eaf24562dea49f51/blog-elastic-add-azure-resource-metrics-integration.png" alt="add azure resource metrics integration" /></p>
<p>As you can see, the Azure Resource Metrics integration will collect a significant amount of data from eight Azure services. Click <strong>Save and continue</strong>.</p>
<p>You’ll be presented with a confirmation dialog window. Click <strong>Add Elastic Agent to your hosts</strong>.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt4e7144de0af25985/6a85ca57bc5bb3a239f81ae5/blog-elastic-azure-resource-metrics-integration-added.png" alt="azure resource metrics integration added" /></p>
<p>This will display the instructions required to install the Elastic agent. Copy the command under the <strong>Linux Tar</strong> tab.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt20087af321e53897/6a85ca5c11893cf7cba7ab86/blog-elastic-add-agent.png" alt="add agent linux tar" /></p>
<p>Next you will need to use SSH to log in to the Azure VM instance and run the commands copied from <strong>Linux Tar</strong> tab. Go to <a href="https://portal.azure.com/#blade/HubsExtension/BrowseResourceBlade/resourceType/Microsoft.Compute/VirtualMachines">Azure Virtual Machines</a> in the Azure portal. Then click the name of the VM instance that you created in Step 3.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt30bf828236f78a33/6a85ca60d7b2e7f92dfe84c8/blog-elastic-metrics-vm.png" alt="metrics vm" /></p>
<p>Click the <strong>Select</strong> button in the <strong>SSH Using Azure CLI</strong> section.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt5cba533db9a395fc/6a85ca6433f244843549f4fa/blog-elastic-metrics-vm-connect.png" alt="metrics vm connect" /></p>
<p>Select the “I understand …” checkbox and then click the <strong>Configure + connect</strong> button.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt5b7633d6cff018a8/6a85ca6943c0b707022f0604/blog-elastic-ssh-using-azure-cli.png" alt="ssh using azure cli" /></p>
<p>Once you are SSH’d inside the VM instance terminal window, run the commands copied previously from <strong>Linux Tar tab</strong> in the <strong>Install Elastic Agent on your host</strong> instructions. When the installation completes, you’ll see a confirmation message in the Install Elastic Agent on your host form.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt0af6bd380836ec18/6a85ca6c27c5cdc79b5f73e8/blog-elastic-add-agent-confirmed.png" alt="add agent confirmed" /></p>
<p>Super! The Elastic agent is sending data to Elastic Cloud. Now let’s observe some metrics.</p>
<h3 id="step5runtrafficagainsttheapplication">Step 5: Run traffic against the application</h3>
<p>While getting the application running is fairly easy, there is nothing to monitor or observe with Elastic unless you add a load on the application.</p>
<p>Here is a simple script you can also run using <a href="https://playwright.dev/">Playwright</a> to add traffic and exercise the functionality of the Azure three-tier application:</p>
<pre><code>import { test, expect } from "@playwright/test";

test("homepage for Microsoft Azure three tier app", async ({ page }) =&gt; {
  // Load web app
  await page.goto("http://20.172.198.231/");
  // Add lunch suggestions
  await page.fill("id=txtAdd", "tacos");
  await page.keyboard.press("Enter");
  await page.waitForTimeout(1000);
  await page.fill("id=txtAdd", "sushi");
  await page.keyboard.press("Enter");
  await page.waitForTimeout(1000);
  await page.fill("id=txtAdd", "pizza");
  await page.keyboard.press("Enter");
  await page.waitForTimeout(1000);
  await page.fill("id=txtAdd", "burgers");
  await page.keyboard.press("Enter");
  await page.waitForTimeout(1000);
  await page.fill("id=txtAdd", "salad");
  await page.keyboard.press("Enter");
  await page.waitForTimeout(1000);
  await page.fill("id=txtAdd", "sandwiches");
  await page.keyboard.press("Enter");
  await page.waitForTimeout(1000);
  // Click vote buttons
  await page.getByRole("button").nth(1).click();
  await page.getByRole("button").nth(3).click();
  await page.getByRole("button").nth(5).click();
  await page.getByRole("button").nth(7).click();
  await page.getByRole("button").nth(9).click();
  await page.getByRole("button").nth(11).click();
  // Click remove buttons
  await page.getByRole("button").nth(12).click();
  await page.getByRole("button").nth(10).click();
  await page.getByRole("button").nth(8).click();
  await page.getByRole("button").nth(6).click();
  await page.getByRole("button").nth(4).click();
  await page.getByRole("button").nth(2).click();
});
</code></pre>
<h3 id="step6viewazuredashboardsinelastic">Step 6: View Azure dashboards in Elastic</h3>
<p>With Elastic Agent running, you can go to Elastic Dashboards to view what’s being ingested. Simply search for “dashboard” in Elastic and choose <strong>Dashboard</strong>.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt33b70441f8217fae/6a85ca6f1aa1e10ef4ff8d4d/blog-elastic-dashboard.png" alt="dashboard" /></p>
<p>This will open the Elastic Dashboards page. In the Dashboards search box, search for azure vm and click the <strong>[Azure Metrics] Compute VMs Overview</strong> dashboard, one of the many out-of-the-box dashboards available.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blta63ce646f43a10c7/6a85ca73f5f1a021832ec8eb/blog-elastic-dashboards-create.png" alt="dashboards create" /></p>
<p>You will see a Dashboard populated with your deployed application’s VM metrics.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltdacd5a541976c209/6a85ca779d2b71018ff9396f/blog-elastic-azure-compute-vm.png" alt="azure compute vm" /></p>
<p>On the Azure Compute VM dashboard, we can see the following sampling of some of the many available metrics:</p>
<ul>
<li>CPU utilization</li>
<li>Available memory</li>
<li>Network sent and received bytes</li>
<li>Disk writes and reads metrics</li>
</ul>
<p>For metrics not covered by out-of-the-box dashboards, custom dashboards can be easily created to visualize metrics that are important to you.</p>
<p><strong>Congratulations, you have now started monitoring metrics from Microsoft Azure services for your application!</strong></p>
<h2 id="analyzeyourdatawithelasticaiassistant">Analyze your data with Elastic AI Assistant</h2>
<p>Once metrics and logs (or either one) are in Elastic, start analyzing your data with <a href="https://www.elastic.co/blog/context-aware-insights-elastic-ai-assistant-observability">context-aware insights using the Elastic AI Assistant for Observability</a>.</p>
<h2 id="conclusionmonitoringmicrosoftazureservicemetricswithelasticobservabilityiseasy">Conclusion: Monitoring Microsoft Azure service metrics with Elastic Observability is easy!</h2>
<p>We hope you’ve gotten an appreciation for how Elastic Observability can help you monitor Azure service metrics. Here’s a quick recap of what you learned:</p>
<ul>
<li>Elastic Observability supports ingest and analysis of Azure service metrics.</li>
<li>It’s easy to set up ingest from Azure services via the Elastic Agent.</li>
<li>Elastic Observability has multiple out-of-the-box Azure service dashboards you can use to preliminarily review information and then modify for your needs.</li>
</ul>
<p>Try it out for yourself by signing up via <a href="https://portal.azure.com/#view/Microsoft_Azure_Marketplace/GalleryItemDetailsBladeNopdl/id/elastic.ec-azure-pp">Microsoft Azure Marketplace</a> and quickly spin up a deployment in minutes on any of the <a href="https://www.elastic.co/guide/en/cloud/current/ec-reference-regions.html#ec_azure_regions">Elastic Cloud regions on Microsoft Azure</a> around the world. Your Azure Marketplace purchase of Elastic will be included in your monthly consolidated billing statement and will draw against your committed spend with Microsoft Azure.</p>
<p><em>The release and timing of any features or functionality described in this post remain at Elastic's sole discretion. Any features or functionality not currently available may not be delivered on time or at all.</em></p>]]></content:encoded>
    <link>https://www.elastic.co/observability-labs/blog/observability-monitors-metrics-microsoft-azure</link>
    <guid isPermaLink="false">observability-monitors-metrics-microsoft-azure</guid>
    <category><![CDATA[Infrastructure Monitoring]]></category>
    <category><![CDATA[Metrics]]></category>
    <dc:creator><![CDATA[Jonathan Simon,Hemant Malik]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt87fce3f72364a189/6a85ca7bf61d6eabbc9c2b01/Azure_Dark_(1).png" length="0" type="image/png"/>
    <pubDate>Mon, 29 Jan 2024 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Elastic Observability monitors metrics for Google Cloud in just minutes]]></title>
    <description><![CDATA[Follow this step-by-step process to enable Elastic Observability for Google Cloud Platform metrics.]]></description>
    <content:encoded><![CDATA[<p>Developers and SREs choose to host their applications on Google Cloud Platform (GCP) for its reliability, speed, and ease of use. On Google Cloud, development teams are finding additional value in migrating to Kubernetes on GKE, leveraging the latest serverless options like Cloud Run, and improving traditional, tiered applications with managed services.</p>
<p>Elastic Observability offers 16 out-of-the-box integrations for Google Cloud services with more on the way. A full list of Google Cloud integrations can be found in <a href="https://docs.elastic.co/en/integrations/gcp">our online documentation</a>.</p>
<p>In addition to our native Google Cloud integrations, Elastic Observability aggregates not only logs but also metrics for Google Cloud services and the applications running on Google Cloud compute services (Compute Engine, Cloud Run, Cloud Functions, Kubernetes Engine). All this data can be analyzed visually and more intuitively using Elastic®’s advanced machine learning (ML) capabilities, which help detect performance issues and surface root causes before end users are affected.</p>
<p>For more details on how Elastic Observability provides application performance monitoring (APM) capabilities such as service maps, tracing, dependencies, and ML based metrics correlations, read: <a href="https://www.elastic.co/blog/apm-correlations-elastic-observability-root-cause-transactions">APM correlations in Elastic Observability: Automatically identifying probable causes of slow or failed transactions</a>.</p>
<p>That’s right, Elastic offers metrics ingest, aggregation, and analysis for Google Cloud services and applications on Google Cloud compute services. Elastic is more than logs — it offers a unified observability solution for Google Cloud environments.</p>
<p>In this blog, I’ll review how Elastic Observability can monitor metrics for a three-tier web application running on Google Cloud services, which include:</p>
<ul>
<li>Google Cloud Run</li>
<li>Google Cloud SQL for PostgreSQL</li>
<li>Google Cloud Memorystore for Redis</li>
<li>Google Cloud VPC Network</li>
</ul>
<p>As you will see, once the integration is installed, metrics will arrive instantly and you can immediately start reviewing metrics.</p>
<h2 id="prerequisitesandconfig">Prerequisites and config</h2>
<p>Here are some of the components and details we used to set up this demonstration:</p>
<ul>
<li>Ensure you have an account on <a href="http://cloud.elastic.co">Elastic Cloud</a> and a deployed stack (<a href="https://www.elastic.co/guide/en/elastic-stack/current/installing-elastic-stack.html">see instructions here</a>).</li>
<li>Ensure you have a Google Cloud project and a Service Account with permissions to pull the necessary data from Google Cloud (<a href="https://docs.elastic.co/en/integrations/gcp#authentication">see details in our documentation</a>).</li>
<li>We used <a href="https://cloud.google.com/architecture/application-development/three-tier-web-app">Google Cloud’s three-tier app</a> and deployed it using the Google Cloud console.</li>
<li>We’ll walk through installing the general <a href="https://docs.elastic.co/en/integrations/gcp">Elastic Google Cloud Platform Integration</a>, which covers the services we want to collect metrics for.</li>
<li>We will <em>not</em> cover application monitoring; instead, we will focus on how Google Cloud services can be easily monitored.</li>
<li>In order to see metrics, you will need to load the application. We’ve also created a playwright script to drive traffic to the application.</li>
</ul>
<h2 id="threetierapplicationoverview">Three-tier application overview</h2>
<p>Before we dive into the Elastic configuration, let's review what we are monitoring. If you follow the <a href="https://cloud.google.com/architecture/application-development/three-tier-web-app">Jump Start Solution: Three-tier web app</a> instructions for<a href="https://github.com/aws-samples/aws-three-tier-web-architecture-workshop"></a>deploying the task-tracking app, you will have the following deployed.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltfdfc1ac184579a1b/6a85c99ff5f1a04ffa2ec8cd/1.png" alt="1" /></p>
<p>What’s deployed:</p>
<ul>
<li>Cloud Run frontend tier that renders an HTML client in the user's browser and enables user requests to be sent to the task-tracking app</li>
<li>Cloud Run middle tier API layer that communicates with the frontend and the database tier</li>
<li>Memorystore for Redis instance in the database tier, caching and serving data that is read frequently</li>
<li>Cloud SQL for PostgreSQL instance in the database tier, handling requests that can't be served from the in-memory Redis cache</li>
</ul>
<p>At the end of the blog, we will also provide a Playwright script that can be run to send requests to this app in order to load it with example data and exercise its functionality. This will help drive metrics to “light up” the dashboards.</p>
<h2 id="settingitallup">Setting it all up</h2>
<p>Let’s walk through the details of how to get the application, Google Cloud integration on Elastic, and what gets ingested.</p>
<h3 id="step0getanaccountonelasticcloud">Step 0: Get an account on Elastic Cloud</h3>
<p>Follow the instructions to <a href="https://cloud.elastic.co/registration?fromURI=/home">get started on Elastic Cloud</a>.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt95cfd5da2363e653/6a85c9a227c5cd311b5f73ca/2.png" alt="2 - start free trial" /></p>
<h3 id="step1deploythegooglecloudthreetierapplication">Step 1: Deploy the Google Cloud three-tier application</h3>
<p>Follow the instructions listed out in <a href="https://cloud.google.com/architecture/application-development/three-tier-web-app">Jump Start Solution: Three-tier web app</a> choosing the <strong>Deploy through the console</strong> option for deployment.</p>
<h3 id="step2createagooglecloudserviceaccountanddownloadcredentialsfile">Step 2: Create a Google Cloud Service Account and download credentials file</h3>
<p>Once you’ve installed the app, the next step is to create a <em>Service Account</em> with a <em>Role</em> and a <em>Service Account Key</em> that will be used by Elastic’s integration to access data in your Google Cloud project.</p>
<p>Go to Google Cloud <a href="https://console.cloud.google.com/iam-admin/roles">IAM Roles</a> to create a Role with the necessary permissions. Click the <strong>CREATE ROLE</strong> button.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltb6968bc5b63bf668/6a85c9a5d6cf29ed61bb08d2/3.png" alt="3" /></p>
<p>Give the Role a <strong>Title</strong> and an <strong>ID</strong>. Then add the 10 assigned permissions listed here.</p>
<ul>
<li>cloudsql.instances.list</li>
<li>compute.instances.list</li>
<li>monitoring.metricDescriptors.list</li>
<li>monitoring.timeSeries.list</li>
<li>pubsub.subscriptions.consume</li>
<li>pubsub.subscriptions.create</li>
<li>pubsub.subscriptions.get</li>
<li>pubsub.topics.attachSubscription</li>
<li>redis.instances.list</li>
<li>run.services.list</li>
</ul>
<p>These permissions are a minimal set of what’s required for this blog post. You should add permissions for all the services for which you would like to collect metrics. If you need to add or remove permissions in the future, the Role’s permissions can be updated as many times as necessary.</p>
<p>Click the <strong>CREATE</strong> button.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltc83cc805a73a02f0/6a85c9a893ffb91bfbb9140f/4.png" alt="4" /></p>
<p>Go to Google Cloud <a href="https://console.cloud.google.com/iam-admin/serviceaccounts">IAM Service Accounts</a> to create a Service Account that will be used by the Elastic integration for access to Google Cloud. Click the <strong>CREATE SERVICE ACCOUNT</strong> button.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt8b281c0557b900a9/6a85c9abd6cf2975bcbb08d6/5.png" alt="5" /></p>
<p>Enter a <strong>Service account name</strong> and a <strong>Service account ID.</strong> Click the <strong>CREATE AND CONTINUE</strong> button.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt334653fa031b1d0f/6a85c9ad8c29444f1cb89029/6.png" alt="6" /></p>
<p>Then select the <strong>Role</strong> that you created previously and click the <strong>CONTINUE</strong> button.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt45e5869885446f4c/6a85c9b02d64d537e2081d0c/7.png" alt="7" /></p>
<p>Click the <strong>DONE</strong> button to complete the Service Account creation process.</p>
<p>Next select the Service Account you just created to see its details page. Under the <strong>KEYS</strong> tab, click the <strong>ADD KEY</strong> dropdown and select <strong>Create new key</strong>.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt7520bd47c8578699/6a85c9b393ffb92bd3b91413/8.png" alt="8" /></p>
<p>In the Create private key dialog window, with the <strong>Key type</strong> set as JSON, click the <strong>CREATE</strong> button.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blte01e021c5d511dd8/6a85c9b59a32f188bca7dfba/9.png" alt="9" /></p>
<p>The JSON credentials file key will be automatically downloaded to your local computer’s <strong>Downloads</strong> folder. The credentials file will be named something like:</p>
<pre><code>your-project-id-12a1234b1234.json
</code></pre>
<p>You can rename the file to be something else. For the purpose of this blog, we’ll rename it to:</p>
<pre><code>credentials.json
</code></pre>
<h3 id="step3createagooglecloudvminstance">Step 3: Create a Google Cloud VM instance</h3>
<p>To create the Compute Engine VM instance in Google Cloud, go to <a href="https://console.cloud.google.com/compute/instances">Compute Engine</a>. Then select <strong>CREATE INSTANCE.</strong></p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt9cd96f99791f8b61/6a85c9b8eaf2452324a49f3b/10.png" alt="10" /></p>
<p>Enter the following values for the VM instance details:</p>
<ul>
<li>Enter a <strong>Name</strong> of your choice for the VM instance.</li>
<li>Expand the <strong>Advanced Options</strong> section and the <strong>Networking</strong> sub-section.</li>
<li>Enter allow-ssh as the Networking tag.</li>
<li>Select the <strong>Network Interface</strong> to use the <strong>tiered-web-app-private-network</strong> , which is the network on which the Google Cloud three-tier web app is deployed.</li>
</ul>
<p>Click the <strong>CREATE</strong> button to create the VM instance.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt45a74a3aa5beeea1/6a85c9bb5c2790459cf59afb/11.png" alt="11" /></p>
<h3 id="step4sshintothegooglecloudvminstanceanduploadthecredentialsfile">Step 4: SSH in to the Google Cloud VM instance and upload the credentials file</h3>
<p>In order to SSH into the Google Cloud VM instance you just created in the previous step, you’ll need to create a Firewall rule in <strong>tiered-web-app-private-network</strong> , which is the network where the VM instance resides.</p>
<p>Go to the Google Cloud <a href="https://console.cloud.google.com/net-security/firewall-manager/firewall-policies/list"><strong>Firewall policies</strong></a> page. Click the <strong>CREATE FIREWALL RULE</strong> button.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt599fa2da6ded5f6e/6a85c9be078290cb06321748/12.png" alt="12" /></p>
<p>Enter the following values for the Firewall Rule.</p>
<ul>
<li>Enter a firewall rule <strong>Name</strong>.</li>
<li>Select <strong>tiered-web-app-private-network</strong> for the <strong>Network</strong>.</li>
<li>Enter allow-ssh for <strong>Target Tags</strong>.</li>
<li>Enter 0.0.0.0/0 for the <strong>Source IPv4 ranges</strong>.Click <strong>TCP</strong> and set the <strong>Ports</strong> to <strong>22</strong>.</li>
</ul>
<p>Click <strong>CREATE</strong> to create the firewall rule.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltdb1991c77845dec7/6a85c9c12d64d5d73c081d12/13.png" alt="13" /></p>
<p>After the new Firewall rule is created, you can now SSH into your VM instance. Go to the <a href="https://console.cloud.google.com/compute/instances">Google Cloud VM instances</a> and select the VM instance you created in the previous step to see its details page. Click the <strong>SSH</strong> button.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt9d2871bb6db22507/6a85c9c39a32f11ecba7dfbe/14.png" alt="14" /></p>
<p>Once you are SSH’d inside the VM instance terminal window, click the <strong>UPLOAD FILE</strong> button.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltb3c81be45dd3f9ba/6a85c9c6f9373db18696f572/15.png" alt="15" /></p>
<p>Select the credentials.json file located on your local computer and click the <strong>Upload Files</strong> button to upload the file.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt79c12de30cb5718a/6a85c9c95c27903ef5f59b01/16.png" alt="16" /></p>
<p>In the VM instance’s SSH terminal, run the following command to get the full path to your Google Cloud Service Account credentials file.</p>
<pre><code>realpath credentials.json
</code></pre>
<p>This should return the full path to your Google Cloud Service Account credentials file.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blta577fd983ea101ae/6a85c9cc331d7a4888c31773/17.png" alt="17" /></p>
<p>Copy the credentials file’s full path and save it in a handy location to be used in a later step.</p>
<h3 id="step5addtheelasticgooglecloudintegration">Step 5: Add the Elastic Google Cloud integration</h3>
<p>Navigate to the Google Cloud Platform integration in Elastic by selecting <strong>Integrations</strong> from the top-level menu. Search for google and click the <strong>Google Cloud Platform</strong> tile.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt523984e613b2fd5a/6a85c9cf18249c75d018f7a5/18.png" alt="18" /></p>
<p>Click <strong>Add Google Cloud Platform</strong>.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt38e92105e192750a/6a85c9d2f9373d568896f57a/19.png" alt="19" /></p>
<p>Click <strong>Add integration only (skip agent installation)</strong>.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltde5bcf660bcff089/6a85c9d580984c7e2f668fb6/20.png" alt="20" /></p>
<p>Update the <strong>Project Id</strong> input text box to be your Google Cloud Project ID. Next, paste in the credentials file’s full path into the <strong>Credentials File</strong> input text box.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt71794a67ac930227/6a85c9d911893cad59a7ab72/21.png" alt="21" /></p>
<p>As you can see, the general Elastic Google Cloud Platform Integration will collect a significant amount of data from 16 Google Cloud services. If you don’t want to install this general Elastic Google Cloud Platform Integration, you can select individual integrations to install. Click <strong>Save and continue</strong>.</p>
<p>You’ll be presented with a confirmation dialog window. Click <strong>Add Elastic Agent to your hosts</strong>.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt80bdaa482f6dec89/6a85c9dc4710c67a09d3cb2b/22.png" alt="22" /></p>
<p>This will display the instructions required to install the Elastic agent. Copy the command under the <strong>Linux Tar</strong> tab.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt12d8a54bba642985/6a85c9df9bf99423670a055d/23.png" alt="23" /></p>
<p>Next you will need to use SSH to log in to the Google Cloud VM instance and run the commands copied from <strong>Linux Tar</strong> tab. Go to <a href="https://console.cloud.google.com/compute/instances">Compute Engine</a>. Then click the name of the VM instance that you created in Step 2. Log in to the VM by clicking the <strong>SSH</strong> button.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt9d2871bb6db22507/6a85c9c39a32f11ecba7dfbe/14.png" alt="24 - instance" /></p>
<p>Once you are SSH’d inside the VM instance terminal window, run the commands copied previously from <strong>Linux Tar tab</strong> in the <strong>Install Elastic Agent on your host</strong> instructions.</p>
<p>When the installation completes, you’ll see a confirmation message in the Install Elastic Agent on your host form. Click the <strong>Add the integration</strong> button.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt901cf4f0fb9eb9ae/6a85c9e293ffb94a3cb91425/25.png" alt="25 - add agent" /></p>
<p>Excellent! The Elastic agent is sending data to Elastic Cloud. Now let’s observe some metrics.</p>
<h3 id="step6runtrafficagainsttheapplication">Step 6: Run traffic against the application</h3>
<p>While getting the application running is fairly easy, there is nothing to monitor or observe with Elastic unless you add a load on the application.</p>
<p>Here is a simple script you can also run using <a href="https://playwright.dev/">Playwright</a> to add traffic and exercise the functionality of the Google Cloud three-tier application:</p>
<pre><code>import { test, expect } from "@playwright/test";

test("homepage for Google Cloud Threetierapp", async ({ page }) =&gt; {
  await page.goto("https://tiered-web-app-fe-zg62dali3a-uc.a.run.app");
  // Insert 2 todo items
  await page.fill("id=todo-new", (Math.random() * 100).toString());
  await page.keyboard.press("Enter");
  await page.waitForTimeout(1000);
  await page.fill("id=todo-new", (Math.random() * 100).toString());
  await page.keyboard.press("Enter");
  await page.waitForTimeout(1000);
  // Click one todo item
  await page.getByRole("checkbox").nth(0).check();
  await page.waitForTimeout(1000);
  // Delete one todo item
  const deleteButton = page.getByText("delete").nth(0);
  await deleteButton.dispatchEvent("click");
  await page.waitForTimeout(4000);
});
</code></pre>
<h3 id="step7gotogoogleclouddashboardsinelastic">Step 7: Go to Google Cloud dashboards in Elastic</h3>
<p>With Elastic Agent running, you can go to Elastic Dashboards to view what’s being ingested. Simply search for “dashboard” in Elastic and choose <strong>Dashboards.</strong></p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt99f34efcec28fb3d/6a85c9e5eaf2450b34a49f41/26.png" alt="26 - dashboard" /></p>
<p>This will open the Elastic Dashboards page.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt263a7b1684f098bb/6a85c9e78c294450d1b89035/27.png" alt="27" /></p>
<p>In the Dashboards search box, search for GCP and click the <strong>[Metrics GCP] CloudSQL PostgreSQL Overview</strong> dashboard, one of the many out-of-the-box dashboards available. Let’s see what comes up.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt221cab7a03a1a453/6a85c9ea11893c22f3a7ab76/28.png" alt="28" /></p>
<p>On the Cloud SQL dashboard, we can see the following sampling of some of the many available metrics:</p>
<ul>
<li>Disk write ops</li>
<li>CPU utilization</li>
<li>Network sent and received bytes</li>
<li>Transaction count</li>
<li>Disk bytes used</li>
<li>Disk quota</li>
<li>Memory usage</li>
<li>Disk read ops</li>
</ul>
<p>Next let’s take a look at metrics for Cloud Run.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltc43564072438bcca/6a85c9edeaf2459bfea49f45/29.png" alt="29 - line graphs" /></p>
<p>We’ve created a custom dashboard using the <strong>Create dashboard</strong> button on the Elastic Dashboards page. Here we see a few of the numerous available metrics:</p>
<ul>
<li>Container instance count</li>
<li>CPU utilization for the three-tier app frontend and API</li>
<li>Request count for the three-tier app frontend and API</li>
<li>Bytes in and out of the API</li>
</ul>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt117da1e00a38d27b/6a85c9f0e2447a805b8b13e6/30.png" alt="30" /></p>
<p>This is a custom dashboard created for MemoryStore where we can see the following sampling of the available metrics:</p>
<ul>
<li>Network traffic to the Memorystore Redis instance</li>
<li>Count of the keys stored in Memorystore Redis</li>
<li>CPU utilization of the Memorystore Redis instance</li>
<li>Memory usage of the Memorystore Redis instance</li>
</ul>
<p><strong>Congratulations, you have now started monitoring metrics from key Google Cloud services for your application!</strong></p>
<h2 id="whattomonitorongooglecloudnext">What to monitor on Google Cloud next?</h2>
<h3 id="addlogsfromgooglecloudservices">Add logs from Google Cloud Services</h3>
<p>Now that metrics are being monitored, you can also now add logging. There are several options for ingesting logs.</p>
<p>The Google Cloud Platform Integration in the Elastic Agent has four separate logs settings: audit logs, firewall logs, VPC Flow logs, and DNS logs. Just ensure you turn on what you wish to receive.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltebe1cdd2c89adb40/6a85c9f39a32f12a9ba7dfc4/31.png" alt="31" /></p>
<h3 id="analyzeyourdatawithelasticmachinelearning">Analyze your data with Elastic machine learning</h3>
<p>Once metrics and logs (or either one) are in Elastic, start analyzing your data through Elastic’s ML capabilities. A great review of these features can be found here:</p>
<ul>
<li><a href="https://www.elastic.co/blog/apm-correlations-elastic-observability-root-cause-transactions">Correlating APM Telemetry to determine root causes in transactions</a></li>
<li><a href="https://www.elastic.co/elasticon/archive/2020/global/machine-learning-and-the-elastic-stack-everywhere-you-need-it">Introduction to Elastic Machine Learning</a></li>
</ul>
<h2 id="conclusionmonitoringgooglecloudservicemetricswithelasticobservabilityiseasy">Conclusion: Monitoring Google Cloud service metrics with Elastic Observability is easy!</h2>
<p>I hope you’ve gotten an appreciation for how Elastic Observability can help you monitor Google Cloud service metrics. Here’s a quick recap of lessons and what you learned:</p>
<ul>
<li>Elastic Observability supports ingest and analysis of Google Cloud service metrics.</li>
<li>It’s easy to set up ingest from Google Cloud services via the Elastic Agent.</li>
<li>Elastic Observability has multiple out-of-the-box Google Cloud service dashboards you can use to preliminarily review information and then modify for your needs.</li>
<li>For metrics not covered by out-of-the-box dashboards, custom dashboards can be easily created to visualize metrics that are important to you.</li>
<li>16 Google Cloud services are supported as part of Google Cloud Platform Integration on Elastic Observability, with more services being added regularly.</li>
<li>As noted in related blogs, you can analyze your Google Cloud service metrics with Elastic’s machine learning capabilities.</li>
</ul>
<p>Try it out for yourself by signing up via <a href="https://console.cloud.google.com/marketplace/product/elastic-prod/elastic-cloud">Google Cloud Marketplace</a> and quickly spin up a deployment in minutes on any of the <a href="https://www.elastic.co/guide/en/cloud/current/ec-reference-regions.html#ec_google_cloud_platform_gcp_regions">Elastic Cloud regions on Google Cloud</a> around the world. Your Google Cloud Marketplace purchase of Elastic will be included in your monthly consolidated billing statement and will draw against your committed spend with Google Cloud.</p>
<p><em>The release and timing of any features or functionality described in this post remain at Elastic's sole discretion. Any features or functionality not currently available may not be delivered on time or at all.</em></p>]]></content:encoded>
    <link>https://www.elastic.co/observability-labs/blog/observability-monitors-metrics-google-cloud</link>
    <guid isPermaLink="false">observability-monitors-metrics-google-cloud</guid>
    <category><![CDATA[Infrastructure Monitoring]]></category>
    <category><![CDATA[Metrics]]></category>
    <dc:creator><![CDATA[Jonathan Simon,Eric Lowry]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt4366673716734c18/6a85c9f6501a85860dfbb30a/serverless-launch-blog-image.jpg" length="0" type="image/jpeg"/>
    <pubDate>Mon, 20 Nov 2023 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Continuous profiling: The key to more efficient and cost-effective applications]]></title>
    <description><![CDATA[In this post, we discuss why computational efficiency is important and how Elastic Universal Profiling enables your business to use continuous profiling in production environments to make the software that runs your business as efficient as possible.]]></description>
    <content:encoded><![CDATA[<p>Recently, Elastic Universal Profiling<sup>TM</sup> became <a href="https://www.elastic.co/blog/continuous-profiling-is-generally-available">generally available</a>. It is the part of our Observability solution that allows users to do <em>whole system, continuous profiling</em> in production environments. If you're not familiar with continuous profiling, you are probably wondering what Universal Profiling is and why you should care. That's what we will address in this post.</p>
<h2 id="efficiencyisimportantagain">Efficiency is important (again)</h2>
<p>Before we jump into continuous profiling, let's start with the "Why should I care?" question. To do that, I'd like to talk a bit about efficiency and some large-scale trends happening in our industry that are making efficiency, specifically computational efficiency, important again. I say again because in the past, when memory and storage on a computer was very limited and you had to worry about every byte of code, efficiency was an important aspect of developing software.</p>
<h3 id="theendofmooreslaw">The end of Moore’s Law</h3>
<p>First, the <a href="https://en.wikipedia.org/wiki/Moore's_law">Moore's Law</a> era is drawing to a close. This was inevitable simply due to physical limits of how small you can make a transistor and the connections between them. For a long time, software developers had the luxury of not worrying about complexity and efficiency because the next generation of hardware would mitigate any negative cost or performance impact.</p>
<p><em>If you can't rely on an endless progression of ever faster hardware, you should be interested in computational efficiency.</em></p>
<h3 id="themovetosoftwareasaservice">The move to Software-as-a-Service</h3>
<p>Another trend to consider is the shift from software vendors that sold customers software to run themselves to Software-as-a-Service businesses. A traditional software vendor didn't have to worry too much about the efficiency of their code. That issue largely fell to the customer to address; a new software version might dictate a hardware refresh to the latest and most performant. For a SaaS business, inefficient software usually degrades the customer’s experience and it certainly impacts the bottom line.</p>
<p><em>If you are a SaaS business in a competitive environment, you should be interested in computational efficiency.</em></p>
<h3 id="cloudmigration">Cloud migration</h3>
<p>Next is the ongoing <a href="https://www.elastic.co/observability/cloud-migration">cloud migration</a> to cloud computing. One of the benefits of cloud computing is the ease of scaling, both hardware and software. In the cloud, we are not constrained by the limits of our data centers or the next hardware purchase. Instead we simply spin up more cloud instances to mitigate performance problems. In addition to infrastructure scalability, microservices architectures, containerization, and the rise of Kubernetes and similar orchestration tools means that scaling services is simpler than ever. It's not uncommon to have thousands of instances of a service running in a cloud environment. This ease of scaling accounts for another trend, namely that many businesses are dealing with skyrocketing cloud computing costs.</p>
<p><em>If you are a business with ever increasing cloud costs, you should be interested in computational efficiency.</em></p>
<h3 id="ourchangingclimate">Our changing climate</h3>
<p>Lastly, if none of those reasons pique your interest, let's consider a global problem that all of us should have in mind — namely, climate change. There are many things that need to be addressed to tackle climate change, but with our dependence on software in every part of our society, computational efficiency is certainly something we should be thinking about.</p>
<p>Thomas Dullien, distinguished engineer at Elastic and one of the founders of Optymize points out that if you can save 20% on 800 servers, and assume 300W power consumption for each server, that code change is worth 160 metric tons of CO<sub>2</sub> saved per year. That may seem like a drop in the bucket but if all businesses focus more on computational efficiency, it will make an impact. Also, let's not forget the financial benefits: those 160 metric tons of CO<sub>2</sub> savings also represent a significant annual cost savings.</p>
<p><em>If you live on planet Earth, you should be interested in computational efficiency.</em></p>
<h2 id="performanceengineering">Performance engineering</h2>
<p>Who's job is it to worry about computational efficiency? Application developers usually pay at least some attention to efficiency as they develop their code. Profiling is a common approach for a developer to understand the performance of their code, and there is an entire portfolio of profiling tools available. Frequently, however, schedule pressures trump time spent on performance analysis and computational efficiency. In addition, performance problems may not become apparent until an application is running at scale in production and interacting (and competing) with everything else in that environment. Many profiling tools are not well suited to use in a production environment because they require code instrumentation and recompilation and add significant overhead.</p>
<p>When inefficient code makes it into production and begins to cause performance problems, the next line of defense is the Operations or SRE team. Their mission is to keep everything humming, and performance problems will certainly draw attention. Observability tools such as APM can shed light on these types of issues and lead the team to a specific application or service, but these tools have limits into the observability of the full system. Third-party libraries and operating system kernels functions remain hidden without a profiling solution in the production environment.</p>
<p>So, what can these teams do when there is a need to investigate a performance problem in production? That's where continuous profiling comes into the picture.</p>
<h2 id="continuousprofiling">Continuous profiling</h2>
<p>Continuous profiling is not a new idea. Google published a <a href="https://research.google/pubs/pub36575/">paper about it</a> in 2010 and began implementing continuous profiling in its environments around that time. Facebook and Netflix followed suit not long afterward.</p>
<p>Typically, continuous profiling tools have been the domain of dedicated performance engineering or operating system engineering teams, which are usually only found at extremely large scale enterprises like the ones mentioned above. The key idea is to run profiling on every server, all of the time. That way, when your observability tools point you to a specific part of an application, but you need a more detailed view into exactly where that application is consuming CPU resources, the profiling data will be there, ready to use.</p>
<p>Another benefit of continuous profiling is that it provides a view of CPU intensive software across your entire environment — whether that is a very CPU intensive function or the aggregate of a relatively small function that is run thousands of times a second in your environment.</p>
<p>While profiling tools are not new, most of them have significant gaps. Let's look at a couple of the most significant ones.</p>
<ul>
<li><strong>Limited visibility.</strong> Modern distributed applications are composed of a complex mix of building blocks, including custom software functions, third-party software libraries, networking software, operating system services, and more and more often, orchestration software such as <a href="https://kubernetes.io/">Kubernetes</a>. To fully understand what is happening in an application, you need visibility into each piece. However, even if a developer has the ability to profile their own code, everything else remains invisible. To make matters worse, most profiling tools require instrumenting the code, which adds overhead and therefore even your developers’ code is not profiled in production.</li>
<li><strong>Missing symbols in production.</strong> All of these pieces of code building blocks typically have descriptive names (some more intuitive than others) so that developers can understand and make sense of them. In a running program, these descriptive names are usually referred to as <strong>symbols</strong>. For a human being to make sense of the execution of a running application, these names are very important. Unfortunately, almost always, any software running in production has these human readable symbols stripped away for space efficiency since they are not needed by the CPU executing the software. Without all of the symbols, it makes it much more difficult to understand the full picture of what's happening in the application. To illustrate this, think of the last time you were in an SMS chat on your mobile device and you only had some of the people in the chat group in your address book while the rest simply appeared as phone numbers — this makes it very hard to tell who is saying what.</li>
</ul>
<h2 id="elasticuniversalprofilingcontinuousprofilingforall">Elastic Universal Profiling: Continuous profiling for all</h2>
<p>Our goal is to allow any business, large or small, to make computational efficiency a core consideration for all of the software that they run. Universal Profiling imposes very low overhead on your servers so it can be used in production and it provides visibility to everything running on every machine. It opens up the possibility of seeing the financial unit cost and CO<sub>2</sub> impact of every line of code running on every system in your business. How do we do that?</p>
<h3 id="wholesystemvisibilitysimple">Whole-system visibility — SIMPLE</h3>
<p>Universal Profiling is based on <a href="https://www.elastic.co/blog/ebpf-observability-security-workload-profiling">eBPF</a>, which means that it imposes very low overhead (our goal is less than 1% CPU and less than 250MB of RAM) on your servers because it doesn't require code instrumentation. That low overhead means it can be run continuously, on every server, even in production.</p>
<p>eBPF also lets us deploy a single profiler agent on a host and peek inside the operating system to see every line of code executing on the CPU. That means we have visibility into all of those application building blocks described above — the operating system itself as well as <a href="https://en.wikipedia.org/wiki/Containerization_(computing)">containerization and orchestration frameworks</a> without complex configuration.</p>
<h3 id="allthesymbols">All the symbols</h3>
<p>A key part of Universal Profiling is our hosted symbolization service. This means that symbols are not required on your servers, which not only eliminates a need for recompiling software with symbols, but it also helps to reduce overhead by allowing the Universal Profiling agent to send very sparse data back to the Elasticsearch platform where it is enriched with all of the missing symbols. Since we maintain a repository of most popular third-party software libraries and Linux operating system symbols, the Universal Profiling UI can show you all the symbols.</p>
<h3 id="yourfavoritelanguageandthensome">Your favorite language, and then some</h3>
<p>Universal Profiling is multilanguage. We support all of today’s popular programming languages, including Python, Go, Java (and any other JVM-based languages), Ruby, NodeJS, PHP, Perl, and of course, C and C++, which is critical since these languages still underly so many third-party libraries used by the other languages. In addition, we support profiling <a href="https://en.wikipedia.org/wiki/Machine_code">native code</a> a.k.a. machine language.</p>
<p>Speaking of native code, all profiling tools are tied to a specific type of CPU. Most tools today only support the Intel x86 CPU architecture. Universal Profiling supports both x86 and ARM-based processors. With the expanding use of ARM-based servers, especially in cloud environments, Universal Profiling future-proofs your continuous profiling.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt5fa0007bcb27d7b6/6a83ffb863e95931e374778e/elastic-blog-1-universal-profiling.png" alt="A flamegraph showing traces across Python, Native, Kernel, and Java code" /></p>
<p>Many businesses today employ polyglot programming — that is, they use multiple languages to build an application — and Universal Profiling is the only profiler available that can build a holistic view across all of these languages. This will help you look for hotspots in the environment, leading you to "unknown unknowns" that warrant deeper performance analysis. That might be a simple interest rate calculation that should be efficient and lightweight but, surprisingly, isn't. Or perhaps it is a service that is reused much more frequently than originally expected, resulting in thousands of instances running across your environment every second, making it a prime target for efficiency improvement.</p>
<h3 id="visualizeyourimpact">Visualize your impact</h3>
<p>Elastic Universal Profiling has an intuitive UI that immediately shows you the impact of any given function, including the time it spends executing on the CPU and how much that costs both in dollars and in carbon emissions.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt4073680455313cb0/6a83ffbcfc63ab419c655f39/elastic-blog-2-universal-profiling-flamegraph.png" alt="Annualized dollar cost and CO2 emissions for any function" /></p>
<p>Finally, with the level of software complexity in most production environments, there's a good chance that making a code change will have unanticipated effects across the environment. That code change may be due to a new feature being rolled out or a change to improve efficiency. In either case, a differential view, before and after the change, will help you understand the impact.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltbf09ac4457d6c078/6a83ffbf4c4bfb4aeacd6900/elastic-blog-3.png" alt="Performance, CO2, and cost improvements of a more efficient hashing function" /></p>
<h2 id="letsrecap">Let's recap</h2>
<p>Computational efficiency is an important topic, both from the perspective of the ultra-competitive business climate we all work in and from living through the challenges of our planet's changing climate. Improving efficiency can be a challenging endeavor, but we can't even begin to attempt to make improvements without knowing where to focus our efforts. Elastic Universal Profiling is here to provide every business with visibility into computational efficiency.</p>
<p>How will you use Elastic Universal Profiling in your business?</p>
<ul>
<li>If you are an application developer or part of the site reliability team, Universal Profiling will provide you with unprecedented visibility into your applications that will not only help you troubleshoot performance problems in production, but also understand the impact of new features and deliver an optimal user experience.</li>
<li>If you are involved in cloud and infrastructure financial management and capacity planning, Universal Profiling will provide you with unprecedented visibility into the unit cost of every line of code that your business runs.</li>
<li>If you are involved in your business’s <a href="https://www.elastic.co/blog/sustainability-elastic-6-months-reflection">ESG</a> initiative, Universal Profiling will provide you with unprecedented visibility into your CO<sub>2</sub> emissions and open up new avenues for reducing your carbon footprint.</li>
</ul>
<p>These are just a few examples. For more ideas, read how <a href="https://www.elastic.co/customers/appomni">AppOmni benefits from Elastic Universal Profiling</a>.</p>
<p>You can <a href="https://www.elastic.co/guide/en/observability/current/profiling-get-started.html">get started</a> with Elastic Universal Profiling right now!</p>
<p><em>The release and timing of any features or functionality described in this post remain at Elastic's sole discretion. Any features or functionality not currently available may not be delivered on time or at all.</em></p>]]></content:encoded>
    <link>https://www.elastic.co/observability-labs/blog/continuous-profiling-efficient-cost-effective-applications</link>
    <guid isPermaLink="false">continuous-profiling-efficient-cost-effective-applications</guid>
    <category><![CDATA[APM]]></category>
    <category><![CDATA[Infrastructure Monitoring]]></category>
    <category><![CDATA[Data Management]]></category>
    <dc:creator><![CDATA[John Knoepfle]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt8efe6e25a7389fad/6a83ffc2227b1ce0985a173b/the-end-of-databases-A_(1).jpg" length="0" type="image/jpeg"/>
    <pubDate>Fri, 27 Oct 2023 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[How to deploy a Hello World web app with Elastic Observability on Azure Container Apps]]></title>
    <description><![CDATA[Follow the step-by-step process of instrumenting Elastic Observability for a Hello World web app running on Azure Container Apps.]]></description>
    <content:encoded><![CDATA[<p>Elastic Observability is the optimal tool to provide visibility into your running web apps. Microsoft Azure Container Apps is a fully managed environment that enables you to run containerized applications on a serverless platform so that your applications scale up and down. This allows you to accomplish the dual objective of serving every customer’s need for availability while meeting your needs to do so as efficiently as possible.</p>
<p>Using Elastic Observability and Azure Container Apps is a perfect combination for developers to deploy <a href="https://www.elastic.co/blog/observability-powerful-flexible-efficient">web apps that are auto-scaled with fully observable operations</a>.</p>
<p>This blog post will show you how to deploy a simple Hello World web app to Azure Container Apps and then walk you through the steps to instrument the Hello World web app to enable observation of the application’s operations with Elastic Cloud.</p>
<h2 id="elasticobservabilitysetup">Elastic Observability setup</h2>
<p>We’ll start with setting up an Elastic Cloud deployment, which is where observability will take place for the web app we’ll be deploying.</p>
<p>From the <a href="https://cloud.elastic.co">Elastic Cloud console</a>, select <strong>Create deployment</strong>.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt04a8dfdb151a3d22/6a85cb6933f244f73949f516/elastic-blog-1-create-deployment.png" alt="create deployment" /></p>
<p>Enter a deployment name and click <strong>Create deployment</strong>. It takes a few minutes for your deployment to be created. While waiting, you are prompted to save the admin credentials for your deployment, which provides you with superuser access to your Elastic® deployment. Keep these credentials safe as they are shown only once.</p>
<p>Elastic Observability requires an APM Server URL and an APM Secret token for an app to send observability data to Elastic Cloud. Once the deployment is created, we’ll copy the Elastic Observability server URL and secret token and store them somewhere safely for adding to our web app code in a later step.</p>
<p>To copy the APM Server URL and the APM Secret Token, go to <a href="https://cloud.elastic.co/home">Elastic Cloud</a> . Then go to the <a href="https://cloud.elastic.co/deployments">Deployments</a> page, which lists all of the deployments you have created. Select the deployment you want to use, which will open the deployment details page. In the <strong>Kibana</strong> row of links, click on <strong>Open</strong> to open Kibana® for your deployment.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt47482afb48a1d23e/6a85cb6c682666f0011eac15/elastic-blog-2-my-deployment.png" alt="my deployment" /></p>
<p>Select <strong>Integrations</strong> from the top-level menu. Then click the <strong>APM</strong> tile.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt45a4aa16f5df8769/6a85cb6fd7b2e73943fe84de/elastic-blog-3-apm.png" alt="apm" /></p>
<p>On the APM Agents page, copy the secretToken and the serverUrl values and save them for use in a later step.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blte655ab8445d7c133/6a85cb72f9373d073096f5a8/elastic-blog-4-apm-agents.png" alt="apm agents" /></p>
<p>Now that we’ve completed the Elastic Cloud setup, the next step is to set up our account in Azure for deploying apps to the Container Apps service.</p>
<h2 id="azurecontainerappssetup">Azure Container Apps setup</h2>
<p>First we’ll need an Azure account, so let’s create one by going to the <a href="https://azure.microsoft.com">Microsoft Azure portal</a> and creating a new project. Click the <strong>Start free</strong> button and follow the steps to sign in or create a new account.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt91f340f08b739bd9/6a85cb75f61d6e74669c2b2f/elastic-blog-5-azure-start-free.png" alt="azure start free" /></p>
<h2 id="deployahelloworldwebapptocontainerapps">Deploy a Hello World web app to Container Apps</h2>
<p>We’ll perform the process of deploying a C# Hello World web app to Container Apps using the handy Azure tool called <a href="https://azure.microsoft.com/en-us/get-started/azure-portal/cloud-shell">Cloud Shell</a>. To deploy the Hello World app, we’ll perform the following 12 steps:</p>
<ol>
<li>From the <a href="https://portal.azure.com/">Azure portal</a>, click the Cloud Shell icon at the top of the portal to open Cloud Shell…</li>
</ol>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltc019a1fdbed88bc1/6a85cb789d2b71346af93990/elastic-blog-6-cloud-shell.png" alt="cloud shell" /></p>
<p>… and when the Cloud Shell first opens, select <strong>Bash</strong> as the shell type to use.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt9dabc7b5e45e53d2/6a85cb7b80984c73bd668fe8/elastic-blog-7-bash.png" alt="bash" /></p>
<ol>
<li>If you’re prompted that “You have no storage mounted,” then click the <strong>Create storage</strong> button to create a file store to be used for saving and editing files from Cloud Shell.</li>
</ol>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt81412588a4041647/6a85cb7e07829085b4321778/elastic-blog-8-create-storage.png" alt="create storage" /></p>
<ol>
<li>In Cloud Shell, clone a <a href="https://github.com/elastic/observability-examples/tree/main/azure/container-apps/helloworld">C# Hello World sample app</a> repo from GitHub by entering the following command.</li>
</ol>
<pre><code>git clone https://github.com/elastic/observability-examples
</code></pre>
<ol>
<li>Change directory to the location of the Hello World web app code.</li>
</ol>
<pre><code>cd observability-examples/azure/container-apps/helloworld
</code></pre>
<ol>
<li>Define the environment variables that we’ll be using in the commands throughout this blog post.</li>
</ol>
<pre><code>RESOURCE_GROUP="helloworld-containerapps"
LOCATION="centralus"
ENVIRONMENT="env-helloworld-containerapps"
APP_NAME="elastic-helloworld"
</code></pre>
<ol>
<li>Define a registry container name that is unique by running the following command.</li>
</ol>
<pre><code>ACR_NAME="helloworld"$RANDOM
</code></pre>
<ol>
<li>Create an Azure resource group by running the following command.</li>
</ol>
<pre><code>az group create --name $RESOURCE_GROUP --location "$LOCATION"
</code></pre>
<ol>
<li>Run the following command to create a registry container in Azure Container Registry.</li>
</ol>
<pre><code>az acr create --resource-group $RESOURCE_GROUP \
--name $ACR_NAME --sku Basic --admin-enable true
</code></pre>
<ol>
<li>Build the app image and push it to Azure Container Registry by running the following command.</li>
</ol>
<pre><code>az acr build --registry $ACR_NAME --image $APP_NAME .
</code></pre>
<ol>
<li>Register the Microsoft.OperationalInsights namespace as a provider by running the following command.</li>
</ol>
<pre><code>az provider register -n Microsoft.OperationalInsights --wait
</code></pre>
<ol>
<li>Run the following command to create a Container App environment for deploying your app into.</li>
</ol>
<pre><code>az containerapp env create --name $ENVIRONMENT \
--resource-group $RESOURCE_GROUP --location "$LOCATION"
</code></pre>
<ol>
<li>Create a new Container App by deploying the Hello World app’s image to Container Apps, using the following command.</li>
</ol>
<pre><code>az containerapp create \
  --name $APP_NAME \
  --resource-group $RESOURCE_GROUP \
  --environment $ENVIRONMENT \
  --image $ACR_NAME.azurecr.io/$APP_NAME \
  --target-port 3500 \
  --ingress 'external' \
  --registry-server $ACR_NAME.azurecr.io \
  --query properties.configuration.ingress.fqdn
</code></pre>
<p>This command will output the deployed Hello World app's fully qualified domain name (FQDN). Copy and paste the FQDN into a browser to see your running Hello World app.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt3b75b6f92e5b965a/6a85cb812d64d55903081d4e/elastic-blog-9-hello-world.png" alt="hello world" /></p>
<h2 id="instrumentthehelloworldwebappwithelasticobservability">Instrument the Hello World web app with Elastic Observability</h2>
<p>With a web app successfully running in Container Apps, we’re now ready to add the minimal code necessary to enable observability for the Hello World app in Elastic Cloud. We’ll perform the following eight steps:</p>
<ol>
<li>In Azure Cloud Shell, create a new file named Telemetry.cs by typing the following command.</li>
</ol>
<pre><code>touch Telemetry.cs
</code></pre>
<ol>
<li>Open the Azure Cloud Shell file editor by typing the following command in Cloud Shell.</li>
</ol>
<pre><code>code .
</code></pre>
<ol>
<li>In the Azure Cloud Shell editor, open the Telemetry.cs file and paste in the following code. Save the edited file in Cloud Shell by pressing the [Ctrl] + [s] keys on your keyboard (or if you’re on a macOS computer, use the [⌘] + [s] keys). This class file is used to create a tracer ActivitySource, which can generate trace Activity spans for observability.</li>
</ol>
<pre><code>using System.Diagnostics;

public static class Telemetry
{
    public static readonly ActivitySource activitySource = new("Helloworld");
}
</code></pre>
<ol>
<li>In the Azure Cloud Shell editor, edit the file named Dockerfile to add the following Elastic OpenTelemetry environment variables. Replace the ELASTIC_APM_SERVER_URL text and the ELASTIC_APM_SECRET_TOKEN text with the APM Server URL and the APM Secret Token values that you copied and saved in an earlier step.</li>
</ol>
<p>Save the edited file in Cloud Shell by pressing the [Ctrl] + [s] keys on your keyboard (or if you’re on a macOS computer, use the [⌘] + [s] keys).</p>
<p>The updated Dockerfile should look something like this:</p>
<pre><code>FROM ${ARCH}mcr.microsoft.com/dotnet/aspnet:7.0. AS base
WORKDIR /app

FROM mcr.microsoft.com/dotnet/sdk:8.0-preview AS build
ARG TARGETPLATFORM

WORKDIR /src
COPY ["helloworld.csproj", "./"]
RUN dotnet restore "./helloworld.csproj"
COPY . .
WORKDIR "/src/."
RUN dotnet build "helloworld.csproj" -c Release -o /app/build

FROM build AS publish
RUN dotnet publish "helloworld.csproj" -c Release -o /app/publish

FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
EXPOSE 3500
ENV ASPNETCORE_URLS=http://+:3500

ENV OTEL_EXPORTER_OTLP_ENDPOINT='https://******.apm.us-east-2.aws.elastic-cloud.com:443'
ENV OTEL_EXPORTER_OTLP_HEADERS='Authorization=Bearer ***********'
ENV OTEL_LOG_LEVEL=info
ENV OTEL_METRICS_EXPORTER=otlp
ENV OTEL_RESOURCE_ATTRIBUTES=service.version=1.0,deployment.environment=production
ENV OTEL_SERVICE_NAME=helloworld
ENV OTEL_TRACES_EXPORTER=otlp

ENTRYPOINT ["dotnet", "helloworld.dll"]
</code></pre>
<ol>
<li>In the Azure Cloud Shell editor, edit the helloworld.csproj file to add the Elastic APM and OpenTelemetry dependencies. The updated helloworld.csproj file should look something like this:</li>
</ol>
<pre><code>&lt;Project Sdk="Microsoft.NET.Sdk.Web"&gt;

  &lt;PropertyGroup&gt;
    &lt;TargetFramework&gt;net7.0&lt;/TargetFramework&gt;
    &lt;Nullable&gt;enable&lt;/Nullable&gt;
    &lt;ImplicitUsings&gt;enable&lt;/ImplicitUsings&gt;
  &lt;/PropertyGroup&gt;
  &lt;ItemGroup&gt;
    &lt;PackageReference Include="Elastic.Apm" Version="1.24.0" /&gt;
    &lt;PackageReference Include="Elastic.Apm.NetCoreAll" Version="1.24.0" /&gt;
    &lt;PackageReference Include="OpenTelemetry" Version="1.6.0" /&gt;
    &lt;PackageReference Include="OpenTelemetry.Exporter.Console" Version="1.6.0" /&gt;
    &lt;PackageReference Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" Version="1.6.0" /&gt;
    &lt;PackageReference Include="OpenTelemetry.Extensions.Hosting" Version="1.6.0" /&gt;
    &lt;PackageReference Include="OpenTelemetry.Instrumentation.AspNetCore" Version="1.5.0-beta.1" /&gt;
  &lt;/ItemGroup&gt;

&lt;/Project&gt;
</code></pre>
<ol>
<li>In the Azure Cloud Shell editor, edit the Program.cs:</li>
</ol>
<ul>
<li>Add a using statement at the top of the file to import System.Diagnostics, which is used to create Activities that are equivalent to “spans” in OpenTelemetry. Also import the OpenTelemetry.Resources and OpenTelemetry.Trace packages.</li>
</ul>
<pre><code>using System.Diagnostics;
using OpenTelemetry.Resources;
using OpenTelemetry.Trace;
</code></pre>
<ul>
<li>Update the “builder” initialization code block to include configuration to enable Elastic OpenTelemetry observability.</li>
</ul>
<pre><code>builder.Services.AddOpenTelemetry().WithTracing(builder =&gt; builder.AddOtlpExporter()
                    .AddSource("helloworld")
                    .AddAspNetCoreInstrumentation()
                    .AddOtlpExporter()
        .ConfigureResource(resource =&gt;
            resource.AddService(
                serviceName: "helloworld"))
);
builder.Services.AddControllers();
</code></pre>
<ul>
<li>Replace the “Hello World!” HTML output string…</li>
</ul>
<pre><code>&lt;h1&gt;Hello World!&lt;/h1&gt;
</code></pre>
<ul>
<li>…with the “Hello Elastic Observability” HTML output string.</li>
</ul>
<pre><code>&lt;div style="text-align: center;"&gt;
  &lt;h1 style="color: #005A9E; font-family:'Verdana'"&gt;
    Hello Elastic Observability - Azure Container Apps - C#
  &lt;/h1&gt;
  &lt;img
    src="https://elastichelloworld.blob.core.windows.net/elastic-helloworld/elastic-logo.png"
  /&gt;
&lt;/div&gt;
</code></pre>
<ul>
<li>Add a telemetry trace span around the output response utilizing the Telemetry class’ ActivitySource.</li>
</ul>
<pre><code>using (Activity activity = Telemetry.activitySource.StartActivity("HelloSpan")!)
       {
           Console.Write("hello");
           await context.Response.WriteAsync(output);
       }
</code></pre>
<p>The updated Program.cs file should look something like this:</p>
<pre><code>using System.Diagnostics;
using OpenTelemetry.Resources;
using OpenTelemetry.Trace;

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddOpenTelemetry().WithTracing(builder =&gt; builder.AddOtlpExporter()
                    .AddSource("helloworld")
                    .AddAspNetCoreInstrumentation()
                    .AddOtlpExporter()
        .ConfigureResource(resource =&gt;
            resource.AddService(
                serviceName: "helloworld"))
);
builder.Services.AddControllers();
var app = builder.Build();

string output =
"""
&lt;div style="text-align: center;"&gt;
&lt;h1 style="color: #005A9E; font-family:'Verdana'"&gt;
Hello Elastic Observability - Azure Container Apps - C#
&lt;/h1&gt;
&lt;img src="https://elastichelloworld.blob.core.windows.net/elastic-helloworld/elastic-logo.png"&gt;
&lt;/div&gt;
""";

app.MapGet("/", async context =&gt;
    {
        using (Activity activity = Telemetry.activitySource.StartActivity("HelloSpan")!)
            {
                Console.Write("hello");
                await context.Response.WriteAsync(output);
            }
    }
);
app.Run();
</code></pre>
<ol>
<li>Rebuild the Hello World app image and push the image to the Azure Container Registry by running the following command.</li>
</ol>
<pre><code>az acr build --registry $ACR_NAME --image $APP_NAME .
</code></pre>
<ol>
<li>Redeploy the updated Hello World app to Azure Container Apps, using the following command.</li>
</ol>
<pre><code>az containerapp create \
  --name $APP_NAME \
  --resource-group $RESOURCE_GROUP \
  --environment $ENVIRONMENT \
  --image $ACR_NAME.azurecr.io/$APP_NAME \
  --target-port 3500 \
  --ingress 'external' \
  --registry-server $ACR_NAME.azurecr.io \
  --query properties.configuration.ingress.fqdn
</code></pre>
<p>This command will output the deployed Hello World app's fully qualified domain name (FQDN). Copy and paste the FQDN into a browser to see the updated Hello World app running in Azure Container Apps.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltf5e133b7b70c41d2/6a85cb8411893c6d5da7ab9a/elastic-blog-10-elastic-hello-observability.png" alt="hello observability" /></p>
<h2 id="observethehelloworldwebapp">Observe the Hello World web app</h2>
<p>Now that we’ve instrumented the web app to send observability data to Elastic Observability, we can now use Elastic Cloud to monitor the web app’s operations.</p>
<ol>
<li><p>In Elastic Cloud, select the Observability <strong>Services</strong> menu item.</p></li>
<li><p>Click the <strong>helloworld</strong> service.</p></li>
<li><p>Click the <strong>Transactions</strong> tab.</p></li>
<li><p>Scroll down and click the <strong>GET /</strong> transaction.Scroll down to the <strong>Trace Sample</strong> section to see the <strong>GET /</strong> , <strong>HelloSpan</strong> trace sample.</p></li>
</ol>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltd6b7f8c8650ec965/6a85cb8733f2446c8f49f51a/elastic-blog-12-latency-distribution.png" alt="latency-distribution" /></p>
<h2 id="observabilitymadetoscale">Observability made to scale</h2>
<p>You’ve seen the entire process of deploying a web app to Azure Container Apps that is instrumented with Elastic Observability. This web app is now fully available on the web running on a platform that will auto-scale to serve visitors worldwide. And it’s instrumented for Elastic Observability APM using OpenTelemetry to ingest data into Elastic Cloud’s Kibana dashboards.</p>
<p>Now that you’ve seen how to deploy a Hello World web app with a basic observability setup, visit <a href="https://www.elastic.co/observability">Elastic Observability</a> to learn more about expanding to a full scale observability coverage solution for your apps. Or visit <a href="https://www.elastic.co/getting-started/microsoft-azure">Getting started with Elastic on Microsoft Azure</a> for more examples of how you can drive the data insights you need by combining Microsoft Azure’s cloud computing services with Elastic’s search-powered platform.</p>
<p><em>The release and timing of any features or functionality described in this post remain at Elastic's sole discretion. Any features or functionality not currently available may not be delivered on time or at all.</em></p>]]></content:encoded>
    <link>https://www.elastic.co/observability-labs/blog/deploy-app-observability-azure-container-apps</link>
    <guid isPermaLink="false">deploy-app-observability-azure-container-apps</guid>
    <category><![CDATA[Infrastructure Monitoring]]></category>
    <category><![CDATA[APM]]></category>
    <dc:creator><![CDATA[Jonathan Simon]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt5b73df829b9751f9/6a85cb8943c0b70ca92f061e/library-branding-elastic-observability-midnight-1680x980.png" length="0" type="image/png"/>
    <pubDate>Mon, 23 Oct 2023 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[How to deploy a Hello World web app with Elastic Observability on AWS App Runner]]></title>
    <description><![CDATA[Follow the step-by-step process of instrumenting Elastic Observability for a Hello World web app running on AWS App Runner.]]></description>
    <content:encoded><![CDATA[<p>Elastic Observability is the premiere tool to provide visibility into web apps running in your environment. AWS App Runner is the serverless platform of choice to run your web apps that need to scale up and down massively to meet demand or minimize costs. Elastic Observability combined with AWS App Runner is the perfect solution for developers to deploy <a href="https://www.elastic.co/blog/observability-powerful-flexible-efficient">web apps that are auto-scaled with fully observable operations</a>, in a way that’s straightforward to implement and manage.</p>
<p>This blog post will show you how to deploy a simple Hello World web app to App Runner and then walk you through the steps to instrument the Hello World web app to enable observation of the application’s operations with Elastic Cloud.</p>
<h2 id="elasticobservabilitysetup">Elastic Observability setup</h2>
<p>We’ll start with setting up an Elastic Cloud deployment, which is where observability will take place for the web app we’ll be deploying.</p>
<p>From the <a href="https://cloud.elastic.co">Elastic Cloud console</a>, select <strong>Create deployment</strong>.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt9db93cd5f13c539e/6a85c87ef5f1a063972ec87f/elastic-blog-1-create-deployment.png" alt="1 create deployment" /></p>
<p>Enter a deployment name and click <strong>Create deployment</strong>. It takes a few minutes for your deployment to be created. While waiting, you are prompted to save the admin credentials for your deployment, which provides you with superuser access to your Elastic® deployment. Keep these credentials safe as they are shown only once.</p>
<p>Elastic Observability requires an APM Server URL and an APM Secret token for an app to send observability data to Elastic Cloud. Once the deployment is created, we’ll copy the Elastic Observability server URL and secret token and store them somewhere safely for adding to our web app code in a later step.</p>
<p>To copy the APM Server URL and the APM Secret Token, go to <a href="https://cloud.elastic.co/home">Elastic Cloud</a>. Then go to the<a href="https://cloud.elastic.co/deployments">Deployments</a> page, which lists all of the deployments you have created. Select the deployment you want to use, which will open the deployment details page. In the Kibana® row of links, click on <strong>Open</strong> to open <strong>Kibana</strong> for your deployment.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt73be96fc7746dfb5/6a85c8819a32f12924a7dfa8/elastic-blog-2-my-deployment.png" alt="2 my deployment" /></p>
<p>Select <strong>Integrations</strong> from the top-level menu. Then click the <strong>APM</strong> tile.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blta57518a50288d231/6a85c884eaf245ca11a49ef9/elastic-blog-3-apm.png" alt="3 apm" /></p>
<p>On the APM Agents page, copy the secretToken and the serverUrl values and save them for use in a later step.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt08e1f088d414b842/6a85c88793ffb91321b913db/elastic-blog-4-apm-agents.png" alt="4 apm agents" /></p>
<p>Now that we’ve completed the Elastic Cloud setup, the next step is to set up our AWS project for deploying apps to App Runner.</p>
<h2 id="awsapprunnersetup">AWS App Runner setup</h2>
<p>To start using AWS App Runner, you need an AWS account. If you’re a brand new user, go to <a href="https://aws.amazon.com">aws.amazon.com</a> to sign up for a new account.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt9bbaa139488840d0/6a85c88af9373d536e96f524/elastic-blog-5-start-building.png" alt="5 start building on aws today" /></p>
<h2 id="setupawscloudshell">Set up AWS CloudShell</h2>
<p>We’ll perform the process of creating a Python Hello World App image and pushing it to the AWS ECR using AWS CloudShell.</p>
<p>We’re going to use Docker to build the sample app image. Perform the following five steps to set up Docker within CloudShell.</p>
<ol>
<li>Open <a href="https://console.aws.amazon.com/cloudshell/">AWS CloudShell</a>.</li>
</ol>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltfa807c1f935903af/6a85c88df9373da04796f528/elastic-blog-6-welcome-to-aws-cloudshell.png" alt="6 welcome to aws cloudshell" /></p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltf91706b828b599db/6a85c88f93ffb9e8abb913df/elastic-blog-7-aws-cloudshell.png" alt="7 aws cloudshell" /></p>
<ol>
<li>Run the following two commands to install Docker in CloudShell:</li>
</ol>
<pre><code>sudo yum update -y
sudo amazon-linux-extras install docker
</code></pre>
<ol>
<li>Start Docker by running the command:</li>
</ol>
<pre><code>sudo dockerd
</code></pre>
<ol>
<li>With Docker running, open a new tab in CloudShell by clicking the <strong>Actions</strong> dropdown menu and selecting <strong>New tab</strong>.</li>
</ol>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blta0babd2101bc0dab/6a85c89311893cce4da7ab26/elastic-blog-8-aws-cloudshell-with-code.png" alt="8 aws cloudshell with code" /></p>
<ol>
<li>Run the following command to authenticate Docker within CloudShell. Replace \&lt;account_id&gt; with your AWS Account ID in the Docker command below, and then run it in CloudShell.</li>
</ol>
<pre><code>aws ecr get-login-password --region us-east-2 | sudo docker login --username AWS --password-stdin &lt;account_id&gt;.dkr.ecr.us-east-2.amazonaws.com
</code></pre>
<h2 id="buildthehelloworldwebappimageandpushittoawsecr">Build the Hello World web app image and push it to AWS ECR</h2>
<p>We’ll be using <a href="https://aws.amazon.com/ecr/">AWS ECR</a>, Amazon’s fully managed container registry for storing and deploying application images. To build and push the Hello World app image to AWS ECR, we’ll perform the following six steps in <a href="https://console.aws.amazon.com/cloudshell/">AWS CloudShell</a>:</p>
<ol>
<li>Run the command below in CloudShell to create a repository in AWS ECR.</li>
</ol>
<pre><code>aws ecr create-repository \
    --repository-name elastic-helloworld/web \
    --image-scanning-configuration scanOnPush=true \
    --region us-east-2
</code></pre>
<p><strong>“elastic-helloworld”</strong> will be the application's name and “ <strong>web”</strong> will be the service name.</p>
<ol>
<li>In the newly created tab within CloudShell, clone a <a href="https://github.com/elastic/observability-examples/tree/main/aws/app-runner/helloworld">Python Hello World sample app</a> repo from GitHub by entering the following command.</li>
</ol>
<pre><code>git clone https://github.com/elastic/observability-examples
</code></pre>
<ol>
<li>Change directory to the location of the Hello World web app code by running the following command:</li>
</ol>
<pre><code>cd observability-examples/aws/app-runner/helloworld
</code></pre>
<ol>
<li>Build the Hello World sample app from the application’s directory. Run the following Docker command in CloudShell.</li>
</ol>
<pre><code>sudo docker build -t elastic-helloworld/web .
</code></pre>
<ol>
<li>Tag the application image. Replace \&lt;account_id&gt; with your AWS Account ID in the Docker command below, and then run it in CloudShell.</li>
</ol>
<pre><code>sudo docker tag elastic-helloworld/web:latest &lt;account_id&gt;.dkr.ecr.us-east-2.amazonaws.com/elastic-helloworld/web:latest
</code></pre>
<ol>
<li>Push the application image to ECR. Replace \&lt;account_id&gt; with your AWS Account ID in the command below, and then run it in CloudShell.</li>
</ol>
<pre><code>sudo docker push &lt;account_id&gt;.dkr.ecr.us-east-2.amazonaws.com/elastic-helloworld/web:latest
</code></pre>
<h2 id="deployahelloworldwebapptoawsapprunner">Deploy a Hello World web app to AWS App Runner</h2>
<p>We’ll perform the process of deploying a Python Hello World App to App Runner using the AWS App Runner console.</p>
<ol>
<li>Open the <a href="https://console.aws.amazon.com/apprunner/">App Runner console</a> and click the <strong>Create an App Runner service</strong> button.</li>
</ol>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt10a06a047e45f19e/6a85c895abdc29245412249a/elastic-blog-9-aws-app-runner.png" alt="9 aws app runner" /></p>
<ol>
<li>On the Source and deployment page, set the following deployment details:</li>
</ol>
<ul>
<li>In the Source section, for Repository type, choose <strong>Container registry</strong>.</li>
<li>For Provider, choose <strong>Amazon ECR</strong>.</li>
<li>For Container image URI, choose <strong>Browse</strong> to select the Hello World application image that we previously pushed to AWS ECR.</li>
<li>In the Select Amazon ECR container image dialog box, for Image repository, select the “ <strong>elastic-helloworld/web”</strong> repository.</li>
<li>For Image tag, select “ <strong>latest”</strong> and then choose <strong>Continue</strong>.</li>
<li>In the Deployment settings section, choose <strong>Automatic</strong>.</li>
<li>For ECR access role, choose <strong>Create new service role.</strong></li>
<li>Click <strong>Next</strong>.</li>
</ul>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltb40411c9c5a1d34d/6a85c899331d7a7381c31721/elastic-blog-10-source-and-deployment.png" alt="10 source and deployment" /></p>
<ol>
<li>On the Configure service page, in the Service settings section, enter the service name “ <strong>helloworld-app</strong>.” Leave all the other settings as they are and click <strong>Next</strong>.</li>
</ol>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltf51afc194b24e44e/6a85c89c1aa1e11ba8ff8d0b/elastic-blog-11-configure-service.png" alt="11 configure service" /></p>
<ol>
<li>On the Review and create page, click <strong>Create &amp; deploy</strong>.</li>
</ol>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltff71ccfa1d6176d4/6a85c89f07829000d2321720/elastic-blog-12-review-and-create.png" alt="12 review and create" /></p>
<p>After a few minutes, the Hello World app will be deployed to App Runner.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt26b3d560060dd7d6/6a85c8a25c2790004af59ac3/elastic-blog-13-helloworld-app.png" alt="13 hello world app green text" /></p>
<ol>
<li>Click the <strong>Default domain</strong> URL to view the Hello World app running in App Runner.</li>
</ol>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt8f34de23c9dc1f30/6a85c8a443c0b720782f05cc/elastic-blog-14-hello-world.png" alt="14 hello world" /></p>
<h2 id="instrumentthehelloworldwebappwithelasticobservability">Instrument the Hello World web app with Elastic Observability</h2>
<p>With a web app successfully running in App Runner, we’re now ready to add the minimal code necessary to start monitoring the app. To enable observability for the Hello World app in Elastic Cloud, we’ll perform the following five steps in <a href="https://console.aws.amazon.com/cloudshell">AWS CloudShell</a>:</p>
<ol>
<li>Edit the Dockerfile file to add the following Elastic Open Telemetry environment variables along with the commands to install and run the Elastic APM agent. Use the “nano” text editor by typing “nano Dockerfile”. Be sure to replace the \&lt;ELASTIC_APM_SERVER_URL&gt; text and the \&lt;ELASTIC_APM_SECRET_TOKEN&gt; text with the APM Server URL and the APM Secret Token values that you copied and saved in an earlier step. The updated Dockerfile should look something like this:</li>
</ol>
<pre><code>FROM python:3.9-slim as base

# get packages
COPY requirements.txt .
RUN pip install -r requirements.txt

WORKDIR /app

# install opentelemetry packages
RUN pip install opentelemetry-distro opentelemetry-exporter-otlp
RUN opentelemetry-bootstrap -a install

ENV OTEL_EXPORTER_OTLP_ENDPOINT='&lt;ELASTIC_APM_SERVER_URL&gt;'
ENV OTEL_EXPORTER_OTLP_HEADERS='Authorization=Bearer%20&lt;ELASTIC_APM_SECRET_TOKEN&gt;'
ENV OTEL_LOG_LEVEL=info
ENV OTEL_METRICS_EXPORTER=otlp
ENV OTEL_RESOURCE_ATTRIBUTES=service.version=1.0,deployment.environment=production
ENV OTEL_SERVICE_NAME=helloworld
ENV OTEL_TRACES_EXPORTER=otlp

COPY . .
ENV FLASK_APP=helloworld
ENV FLASK_RUN_HOST=0.0.0.0
ENV FLASK_RUN_PORT=8080
EXPOSE 8080
ENTRYPOINT [ "opentelemetry-instrument", "flask", "run" ]
</code></pre>
<p>Note: You can close the nano text editor and save the file by typing “Ctrl + x”. Press the “y” key and then the “Enter” key to save the changes.</p>
<ol>
<li>Edit the helloworld.py file to add observability traces. In CloudShell, type “nano helloworld.py” to edit the file.</li>
</ol>
<ul>
<li>After the import statements at the top of the file, add the code required to initialize the Elastic Open Telemetry APM agent:</li>
</ul>
<pre><code>from opentelemetry import trace
tracer = trace.get_tracer("hello-world")
</code></pre>
<ul>
<li>Replace the “Hello World!” output code . . .</li>
</ul>
<pre><code>return "&lt;h1&gt;Hello World!&lt;/h1&gt;";
</code></pre>
<ul>
<li>… with the Hello Elastic Observability code block.</li>
</ul>
<pre><code>return '''
&lt;div style="text-align: center;"&gt;
&lt;h1 style="color: #005A9E; font-family:'Verdana'"&gt;
Hello Elastic Observability - AWS App Runner - Python
&lt;/h1&gt;
&lt;img src="https://elastic-helloworld.s3.us-east-2.amazonaws.com/elastic-logo.png"&gt;
&lt;/div&gt;
'''
</code></pre>
<ul>
<li>Then add a “hi” trace before the Hello Elastic Observability code block along with an additional “@app.after_request” method placed afterward to implement a “bye” trace.</li>
</ul>
<pre><code>@app.route("/")
def helloworld():
    with tracer.start_as_current_span("hi") as span:
        logging.info("hello")
        return '''
        &lt;div style="text-align: center;"&gt;
        &lt;h1 style="color: #005A9E; font-family:'Verdana'"&gt;
        Hello Elastic Observability - AWS App Runner - Python
        &lt;/h1&gt;
        &lt;img src="https://elastic-helloworld.s3.us-east-2.amazonaws.com/elastic-logo.png"&gt;
        &lt;/div&gt;
        '''

@app.after_request
def after_request(response):
    with tracer.start_as_current_span("bye"):
        logging.info("goodbye")
        return response
</code></pre>
<p>The completed helloworld.py file should look something like this:</p>
<pre><code>import logging
from flask import Flask

from opentelemetry import trace
tracer = trace.get_tracer("hello-world")

app = Flask(__name__)

@app.route("/")
def helloworld():
    with tracer.start_as_current_span("hi") as span:
        logging.info("hello")
        return '''
        &lt;div style="text-align: center;"&gt;
        &lt;h1 style="color: #005A9E; font-family:'Verdana'"&gt;
        Hello Elastic Observability - AWS App Runner - Python
        &lt;/h1&gt;
        &lt;img src="https://elastic-helloworld.s3.us-east-2.amazonaws.com/elastic-logo.png"&gt;
        &lt;/div&gt;
        '''

@app.after_request
def after_request(response):
    with tracer.start_as_current_span("bye"):
        logging.info("goodbye")
        return response
</code></pre>
<p>Note: You can close the nano text editor and save the file by typing “Ctrl + x”. Press the “y” key and then the “Enter” key to save the changes.</p>
<ol>
<li>Rebuild the updated Hello World sample app using Docker from within the application’s directory. Run the following command in CloudShell.</li>
</ol>
<pre><code>sudo docker build -t elastic-helloworld/web .
</code></pre>
<ol>
<li>Tag the application image using Docker. Replace \&lt;account_id&gt; with your AWS Account ID in the Docker command below and then run it in CloudShell.</li>
</ol>
<pre><code>sudo docker tag elastic-helloworld/web:latest &lt;account_id&gt;.dkr.ecr.us-east-2.amazonaws.com/elastic-helloworld/web:latest
</code></pre>
<ol>
<li>Push the updated application image to ECR. Replace \&lt;account_id&gt; with your AWS Account ID in the Docker command below and then run it in CloudShell.</li>
</ol>
<pre><code>sudo docker push &lt;account_id&gt;.dkr.ecr.us-east-2.amazonaws.com/elastic-helloworld/web:latest
</code></pre>
<p>Pushing the image to ECR will automatically deploy the new version of the Hello World app.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt0a8c9b9db2a545ab/6a85c8a711893c1df1a7ab32/elastic-blog-15-green-banner-successfully.png" alt="15 green banner successful deployment" /></p>
<p>Open the <a href="http://console.aws.amazon.com/apprunner">App Runner</a> console. After a few minutes, the Hello World app will be deployed to App Runner. Click the <strong>Default domain</strong> URL to view the updated Hello World app running in App Runner.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt65db284a7eb00d66/6a85c8aaf61d6eb6bb9c2ac3/elastic-blog-16-elastic-logo-text-top.png" alt="16 elastic" /></p>
<h2 id="observethehelloworldwebapp">Observe the Hello World web app</h2>
<p>Now that we’ve instrumented the web app to send observability data to Elastic Observability, we can now use Elastic Cloud to monitor the web app’s operations.</p>
<ol>
<li><p>In Elastic Cloud, select the Observability <strong>Services</strong> menu item.</p></li>
<li><p>Click the <strong>helloworld</strong> service.</p></li>
<li><p>Click the <strong>Transactions</strong> tab.</p></li>
<li><p>Scroll down and click the <strong>“/”</strong> transaction.</p></li>
<li><p>Scroll down to the Trace Sample section to see the <strong>“/,” “hi,”</strong> and <strong>“bye”</strong> trace samples.</p></li>
</ol>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt6aea5ad7fcd9fd2c/6a85c8ad33f244bad049f4c0/elastic-blog-17-trace-sample.png" alt="17 trace sample" /></p>
<h2 id="observabilitymadetoscale">Observability made to scale</h2>
<p>You’ve seen the complete process of deploying a web app to AWS App Runner that is instrumented with Elastic Observability. The end result is a web app that will scale up and down with usage, combined with the observability tools to monitor the web app as it serves one user or millions of users.</p>
<p>Now that you’ve seen how to deploy a serverless web app instrumented with observability, visit <a href="https://www.elastic.co/observability">Elastic Observability</a> to learn more about how to implement a complete observability solution for your apps. Or visit <a href="https://www.elastic.co/getting-started/aws">Getting started with Elastic on AWS</a> for more examples of how you can drive the data insights you need by combining AWS’s cloud computing services with Elastic’s search-powered platform.</p>
<p><em>The release and timing of any features or functionality described in this post remain at Elastic's sole discretion. Any features or functionality not currently available may not be delivered on time or at all.</em></p>]]></content:encoded>
    <link>https://www.elastic.co/observability-labs/blog/deploy-app-observability-aws-app-runner</link>
    <guid isPermaLink="false">deploy-app-observability-aws-app-runner</guid>
    <category><![CDATA[APM]]></category>
    <category><![CDATA[Infrastructure Monitoring]]></category>
    <dc:creator><![CDATA[Jonathan Simon]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt5b613993e5cae1c3/6a85c8b0f61d6ecd539c2ad1/library-branding-elastic-observability-white-1680x980.png" length="0" type="image/png"/>
    <pubDate>Mon, 02 Oct 2023 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Achieving seamless API management: Introducing AWS API Gateway integration with Elastic]]></title>
    <description><![CDATA[With Elastic's AWS API Gateway integration, application owners and developers unlock the capability to proactively identify and resolve problems, fine-tune resource utilization, and provide extraordinary digital experiences to their users.]]></description>
    <content:encoded><![CDATA[<p><a href="https://aws.amazon.com/api-gateway/">AWS API Gateway</a> is a powerful service that redefines API management. It serves as a gateway for creating, deploying, and managing APIs, enabling businesses to establish seamless connections between different applications and services. With features like authentication, authorization, and traffic control, API Gateway ensures the security and reliability of API interactions.</p>
<p>In an era where APIs serve as the backbone of modern applications, having the means to maintain visibility and control over these vital components is absolutely essential. In this blog post, we dive deep into the comprehensive observability solution offered by Elastic<sup>®</sup>, ensuring real-time visibility, advanced analytics, and actionable insights, empowering you to fine-tune your API Gateway for optimal performance.</p>
<p>For application owners and developers, this integration stands as a beacon of empowerment. Elastic's meticulous orchestration of the seamless merging of metrics, logs, and traces, built upon the robust <a href="https://www.elastic.co/elastic-stack">ELK Stack</a> foundation, equips them with potent real-time monitoring and analysis tools. These tools facilitate precise performance optimization and swift issue resolution, all within a secure and dependable environment.</p>
<p>With Elastic's AWS API Gateway integration, application owners and developers unlock the capability to proactively identify and resolve problems, fine-tune resource utilization, and provide extraordinary digital experiences to their users.</p>
<h2 id="architecture">Architecture</h2>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt2279a21e558b0a0e/6a85c7918c29442987b88faa/elastic-blog-1-architecture.png" alt="architecture" /></p>
<h2 id="whytheawsapigatewayintegrationmatters">Why the AWS API Gateway integration matters</h2>
<p>API Gateway now serves as the foundation of contemporary application development, simplifying the process of creating and overseeing APIs on a large scale. Yet, monitoring and troubleshooting these API endpoints can be challenging. With the new AWS API Gateway integration introduced by Elastic, you can gain the following:</p>
<ul>
<li><strong>Unprecedented visibility:</strong> Monitor your API Gateway endpoints' performance, error rates, and usage metrics in real time. Get a comprehensive view of your APIs' health and performance.</li>
<li><strong>Log analysis:</strong> Dive deep into API Gateway logs with ease. Our integration enables you to collect and analyze logs for HTTP, REST, and Websocket API types, helping you troubleshoot issues and gain valuable insights.</li>
<li><strong>Rapid issue resolution:</strong> Identify and resolve issues in your API Gateway workflows faster than ever. <a href="https://www.elastic.co/observability">Elastic Observability's</a> powerful search and analytics tools help you pinpoint problems with ease.</li>
<li><strong>Alerting and notifications:</strong> Set up custom alerts based on API Gateway metrics and logs. Receive notifications when performance thresholds are breached, ensuring that you can take action promptly.</li>
<li><strong>Optimized costs:</strong> Visualize resource usage and performance metrics for your API Gateway deployments. Use these insights to optimize resource allocation and reduce operational costs.</li>
<li><strong>Custom dashboards:</strong> Create customized dashboards and visualizations tailored to your API Gateway monitoring needs. Stay in control with real-time data and actionable insights.</li>
<li><strong>Effortless integration:</strong> Seamlessly connect your AWS API Gateway to our observability solution. Our intuitive setup process ensures a smooth integration experience.</li>
<li><strong>Scalability:</strong> Whether you have a handful of APIs or a complex API Gateway landscape, our observability solution scales to meet your needs. Grow confidently as your API infrastructure expands.</li>
</ul>
<h2 id="howtogetstarted">How to get started</h2>
<p>Getting started with the AWS API Gateway integration in Elastic Observability is seamless. Here's a quick overview of the steps:</p>
<h3 id="prerequisitesandconfigurations">Prerequisites and configurations</h3>
<p>If you intend to follow the steps outlined in this blog post, there are a few prerequisites and configurations that you should have in place beforehand.</p>
<ol>
<li><p>You will need an account on <a href="http://cloud.elastic.co/">Elastic Cloud</a> and a deployed stack and agent. Instructions for deploying a stack on AWS can be found <a href="https://www.elastic.co/guide/en/elastic-stack/current/installing-elastic-stack.html">here</a>. This is necessary for AWS API Gateway logging and analysis.</p></li>
<li><p>You will also need an AWS account with the necessary permissions to pull data from AWS. Details on the required permissions can be found in our <a href="https://docs.elastic.co/en/integrations/aws#aws-permissions">documentation</a>.</p></li>
<li><p>You can monitor API execution by using CloudWatch, which collects and processes raw data from API Gateway into readable, near-real-time metrics and logs. Details on the required steps to enable logging can be found <a href="https://docs.aws.amazon.com/apigateway/latest/developerguide/set-up-logging.html">here</a>.</p></li>
</ol>
<h3 id="step1createanaccountwithelastic">Step 1. Create an account with Elastic</h3>
<p><a href="https://cloud.elastic.co/registration?fromURI=/home">Create an account on Elastic Cloud</a> by following the steps provided.</p>
<h3 id="step2addintegration">Step 2. Add integration</h3>
<ul>
<li>Log in to your Elastic Cloud deployment.</li>
</ul>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltce0f8d0c79e2b4c3/6a85c7941aa1e186d7ff8ce1/elastic-blog-2-signup.png" alt="signup" /></p>
<ul>
<li>Click on <strong>Add integrations</strong>. You will be navigated to a catalog of supported integrations.</li>
</ul>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltb2748bf04c31f95d/6a85c79718249c4e3518f71d/elastic-blog-3-welcome-home.png" alt="welcome home dashboard" /></p>
<ul>
<li>Search and select <strong>AWS API Gateway</strong>.</li>
</ul>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt71a4dce7072e7acd/6a85c79a5c27907a22f59a91/elastic-blog-4-integrations.png" alt="Integration " /></p>
<h3 id="step3configureintegration">Step 3. Configure integration</h3>
<ul>
<li>Click on the <strong>Add AWS API Gateway</strong> button and provide the required details.</li>
<li>If this is your first time adding an AWS integration, you’ll need to <a href="https://www.elastic.co/guide/en/fleet/current/elastic-agent-installation.html">configure and enroll the Elastic Agent</a> on an AWS instance.</li>
</ul>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt5f83959deb09faac/6a85c79d18249c1ac518f721/elastic-blog-5-aws-api-gateway.png" alt="aws-api-gateway" /></p>
<ul>
<li>Then complete the “Configure integration” form, providing all the necessary information required for agents to collect the AWS API Gateway metrics and associated CloudWatch logs. Multiple AWS credential methods are supported, including access keys, temporary security credentials, and IAM role ARN. Please see the <a href="https://docs.aws.amazon.com/apigateway/latest/developerguide/security-iam.html">IAM security and access documentation</a> for more details. You can choose to collect API Gateway metrics, API Gateway logs via S3, or API Gateway logs via CloudWatch.</li>
<li>Click on the <strong>Save and continue</strong> button at the bottom of the page.</li>
</ul>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt51fd724c34c8dde3/6a85c7a0331d7a1820c316df/elastic-blog-6-add-aws-integration.png" alt="add-aws-integration" /></p>
<h3 id="step4analyzeandmonitor">Step 4. Analyze and monitor</h3>
<p>Explore the data using the out-of-the-box dashboards available for the integration. Select <strong>Discover</strong> from the Elastic Cloud top-level menu.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt84a6d07d34eb38fe/6a85c7a399083fff0e40f951/elastic-blog-7-discover-dashboard.png" alt="discover-dashboard" /></p>
<p>Or, create custom dashboards, set up alerts, and gain actionable insights into your API Gateway service performance.</p>
<p>Here are key monitoring metrics collected through this integration across Rest APIs, HTTP APIs, and Websocket APIs:</p>
<ul>
<li><strong>4XXError</strong> – The number of client-side errors captured in a given period</li>
<li><strong>5XXError</strong> – The number of server-side errors captured in a given period</li>
<li><strong>CacheHitCount</strong> – The number of requests served from the API cache in a given period</li>
<li><strong>CacheMissCount</strong> – The number of requests served from the backend in a given period, when API caching is enabled</li>
<li><strong>Count</strong> – The total number of API requests in a given period</li>
<li><strong>IntegrationLatency</strong> – The time between when API Gateway relays a request to the backend and when it receives a response from the backend</li>
<li><strong>Latency</strong> – The time between when API Gateway receives a request from a client and when it returns a response to the client — the latency includes the integration latency and other API Gateway overhead</li>
<li><strong>DataProcessed</strong> – The amount of data processed in bytes</li>
<li><strong>ConnectCount</strong> – The number of messages sent to the $connect route integration<br />
<strong>MessageCount</strong> – The number of messages sent to the WebSocket API, either from or to the client</li>
</ul>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt94aa193f13f66209/6a85c7a611893c8309a7aaf8/elastic-blog-8-graphs.png" alt="graphs" /></p>
<h2 id="conclusion">Conclusion</h2>
<p>The native integration of AWS API Gateway into Elastic Observability marks a significant advancement in streamlining the monitoring and management of your APIs. With this integration, you gain access to a wealth of insights, real-time visibility, and powerful analytics tools, empowering you to optimize your API performance, enhance security, and troubleshoot with ease. Don't miss out on this opportunity to take your API management to the next level, ensuring your digital assets operate at their best, all while providing a seamless experience for your users. Embrace this integration, and stay at the forefront of API observability in the ever-evolving world of digital technology.</p>
<p>Visit our <a href="https://docs.elastic.co/integrations/aws/apigateway">documentation</a> to learn more about Elastic Observability and the AWS API Gateway integration, or <a href="https://www.elastic.co/contact">contact our sales team</a> to get started!</p>
<h2 id="startafreetrialtoday">Start a free trial today</h2>
<p>Start your own <a href="https://aws.amazon.com/marketplace/pp/prodview-voru33wi6xs7k?trk=5fbc596b-6d2a-433a-8333-0bd1f28e84da%E2%89%BBchannel=el">7-day free trial</a> by signing up via <a href="https://aws.amazon.com/marketplace/pp/prodview-voru33wi6xs7k?trk=5fbc596b-6d2a-433a-8333-0bd1f28e84da&amp;sc_channel=el&amp;ultron=gobig&amp;hulk=regpage&amp;blade=elasticweb&amp;gambit=mp-b">AWS Marketplace</a> and quickly spin up a deployment in minutes on any of the <a href="https://www.elastic.co/guide/en/cloud/current/ec-reference-regions.html#ec_amazon_web_services_aws_regions">Elastic Cloud regions on AWS</a> around the world. Your AWS Marketplace purchase of Elastic will be included in your monthly consolidated billing statement and will draw against your committed spend with AWS.</p>
<p><em>The release and timing of any features or functionality described in this post remain at Elastic's sole discretion. Any features or functionality not currently available may not be delivered on time or at all.</em></p>]]></content:encoded>
    <link>https://www.elastic.co/observability-labs/blog/api-management-aws-api-gateway-integration</link>
    <guid isPermaLink="false">api-management-aws-api-gateway-integration</guid>
    <category><![CDATA[Infrastructure Monitoring]]></category>
    <category><![CDATA[Metrics]]></category>
    <dc:creator><![CDATA[Udayasimha Theepireddy,Subhrata Kulshrestha]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blta8c4519a24584392/6a85c7a99829269770583854/illustration-midnight-bg-aws-elastic-1680x980.png" length="0" type="image/png"/>
    <pubDate>Thu, 14 Sep 2023 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Manual instrumentation of Go applications with OpenTelemetry]]></title>
    <description><![CDATA[In this blog post, we will show you how to manually instrument Go applications using OpenTelemetry. We will explore how to use the proper OpenTelemetry Go packages and, in particular, work on instrumenting tracing in a Go application.]]></description>
    <content:encoded><![CDATA[<p>DevOps and SRE teams are transforming the process of software development. While DevOps engineers focus on efficient software applications and service delivery, SRE teams are key to ensuring reliability, scalability, and performance. These teams must rely on a full-stack observability solution that allows them to manage and monitor systems and ensure issues are resolved before they impact the business.</p>
<p>Observability across the entire stack of modern distributed applications requires data collection, processing, and correlation often in the form of dashboards. Ingesting all system data requires installing agents across stacks, frameworks, and providers — a process that can be challenging and time-consuming for teams who have to deal with version changes, compatibility issues, and proprietary code that doesn't scale as systems change.</p>
<p>Thanks to <a href="http://opentelemetry.io">OpenTelemetry</a> (OTel), DevOps and SRE teams now have a standard way to collect and send data that doesn't rely on proprietary code and have a large support community reducing vendor lock-in.</p>
<p>In this blog post, we will show you how to manually instrument Go applications using OpenTelemetry. This approach is slightly more complex than using auto-instrumentation</p>
<p>In a <a href="https://www.elastic.co/blog/opentelemetry-observability">previous blog</a>, we also reviewed how to use the OpenTelemetry demo and connect it to Elastic<sup>®</sup>, as well as some of Elastic’s capabilities with OpenTelemetry. In this blog, we will use <a href="https://github.com/elastic/observability-examples">an alternative demo application</a>, which helps highlight manual instrumentation in a simple way.</p>
<p>Finally, we will discuss how Elastic supports mixed-mode applications, which run with Elastic and OpenTelemetry agents. The beauty of this is that there is <strong>no need for the otel-collector</strong>! This setup enables you to slowly and easily migrate an application to OTel with Elastic according to a timeline that best fits your business.</p>
<h2 id="applicationprerequisitesandconfig">Application, prerequisites, and config</h2>
<p>The application that we use for this blog is called <a href="https://github.com/elastic/observability-examples">Elastiflix</a>, a movie streaming application. It consists of several micro-services written in .NET, NodeJS, Go, and Python.</p>
<p>Before we instrument our sample application, we will first need to understand how Elastic can receive the telemetry data.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt13a3ccd8116bdc07/6a85ccb1f5f1a0cd052ec93b/GO-flowhcart.png" alt="Elastic configuration options for OpenTelemetry" /></p>
<p>All of Elastic Observability’s APM capabilities are available with OTel data. Some of these include:</p>
<ul>
<li>Service maps</li>
<li>Service details (latency, throughput, failed transactions)</li>
<li>Dependencies between services, distributed tracing</li>
<li>Transactions (traces)</li>
<li>Machine learning (ML) correlations</li>
<li>Log correlation</li>
</ul>
<p>In addition to Elastic’s APM and a unified view of the telemetry data, you will also be able to use Elastic’s powerful machine learning capabilities to reduce the analysis, and alerting to help reduce MTTR.</p>
<h2 id="prerequisites">Prerequisites</h2>
<ul>
<li>An Elastic Cloud account — <a href="https://cloud.elastic.co/">sign up now</a></li>
<li>A clone of the <a href="https://github.com/elastic/observability-examples">Elastiflix demo application</a>, or your own Go application</li>
<li>Basic understanding of Docker — potentially install <a href="https://www.docker.com/products/docker-desktop/">Docker Desktop</a></li>
<li>Basic understanding of Go</li>
</ul>
<h2 id="viewtheexamplesourcecode">View the example source code</h2>
<p>The full source code including the Dockerfile used in this blog can be found on <a href="https://github.com/elastic/observability-examples/tree/main/Elastiflix/go-favorite-otel-manual">GitHub</a>. The repository also contains the <a href="https://github.com/elastic/observability-examples/tree/main/Elastiflix/go-favorite">same application without instrumentation</a>. This allows you to compare each file and see the differences.</p>
<p>Before we begin, let’s look at the non-instrumented code first.</p>
<p>This is our simple go application that can receive a GET request. Note that the code shown here is a slightly abbreviated version.</p>
<pre><code>package main

import (
    "log"
    "net/http"
    "os"
    "time"

    "github.com/go-redis/redis/v8"

    "github.com/sirupsen/logrus"

    "github.com/gin-gonic/gin"
    "strconv"
    "math/rand"
)

var logger = &amp;logrus.Logger{
    Out:   os.Stderr,
    Hooks: make(logrus.LevelHooks),
    Level: logrus.InfoLevel,
    Formatter: &amp;logrus.JSONFormatter{
        FieldMap: logrus.FieldMap{
            logrus.FieldKeyTime:  "@timestamp",
            logrus.FieldKeyLevel: "log.level",
            logrus.FieldKeyMsg:   "message",
            logrus.FieldKeyFunc:  "function.name", // non-ECS
        },
        TimestampFormat: time.RFC3339Nano,
    },
}

func main() {
    delayTime,  := strconv.Atoi(os.Getenv("TOGGLE_SERVICE_DELAY"))

    redisHost := os.Getenv("REDIS_HOST")
    if redisHost == "" {
        redisHost = "localhost"
    }

    redisPort := os.Getenv("REDIS_PORT")
    if redisPort == "" {
        redisPort = "6379"
    }

    applicationPort := os.Getenv("APPLICATION_PORT")
    if applicationPort == "" {
        applicationPort = "5000"
    }

    // Initialize Redis client
    rdb := redis.NewClient(&amp;redis.Options{
        Addr:     redisHost + ":" + redisPort,
        Password: "",
        DB:       0,
    })

    // Initialize router
    r := gin.New()
    r.Use(logrusMiddleware)

    r.GET("/favorites", func(c *gin.Context) {
        // artificial sleep for delayTime
        time.Sleep(time.Duration(delayTime) * time.Millisecond)

        userID := c.Query("user_id")

        contextLogger(c).Infof("Getting favorites for user %q", userID)

        favorites, err := rdb.SMembers(c.Request.Context(), userID).Result()
        if err != nil {
            contextLogger(c).Error("Failed to get favorites for user %q", userID)
            c.String(http.StatusInternalServerError, "Failed to get favorites")
            return
        }

        contextLogger(c).Infof("User %q has favorites %q", userID, favorites)

        c.JSON(http.StatusOK, gin.H{
            "favorites": favorites,
        })
    })

    // Start server
    logger.Infof("App startup")
    log.Fatal(http.ListenAndServe(":"+applicationPort, r))
    logger.Infof("App stopped")
}
</code></pre>
<h2 id="stepbystepguide">Step-by-step guide</h2>
<h3 id="step0logintoyourelasticcloudaccount">Step 0. Log in to your Elastic Cloud account</h3>
<p>This blog assumes you have an Elastic Cloud account — if not, follow the <a href="https://cloud.elastic.co/registration?elektra=en-cloud-page">instructions to get started on Elastic Cloud</a>.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltdec739045c430140/6a85ccb527c5cd10885f742e/elastic-blog-4-free-trial.png" alt="free trial" /></p>
<h3 id="step1installandinitializeopentelemetry">Step 1. Install and initialize OpenTelemetry</h3>
<p>As a first step, we’ll need to add some additional packages to our application.</p>
<pre><code>import (
      "github.com/go-redis/redis/extra/redisotel/v8"
      "go.opentelemetry.io/otel"
      "go.opentelemetry.io/otel/attribute"
      "go.opentelemetry.io/otel/exporters/otlp/otlptrace"
    "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc"

    "go.opentelemetry.io/otel/propagation"

    "google.golang.org/grpc/credentials"
    "crypto/tls"

      sdktrace "go.opentelemetry.io/otel/sdk/trace"

    "go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin"

    "go.opentelemetry.io/otel/trace"
    "go.opentelemetry.io/otel/codes"
)
</code></pre>
<p>This code imports necessary OpenTelemetry packages, including those for tracing, exporting, and instrumenting specific libraries like Redis.</p>
<p>Next we read the "OTEL_EXPORTER_OTLP_ENDPOINT" variable and initialize the exporter.</p>
<pre><code>var (
    collectorURL = os.Getenv("OTEL_EXPORTER_OTLP_ENDPOINT")
)
var tracer trace.Tracer


func initTracer() func(context.Context) error {
    tracer = otel.Tracer("go-favorite-otel-manual")

    // remove https:// from the collector URL if it exists
    collectorURL = strings.Replace(collectorURL, "https://", "", 1)
    secretToken := os.Getenv("ELASTIC_APM_SECRET_TOKEN")
    if secretToken == "" {
        log.Fatal("ELASTIC_APM_SECRET_TOKEN is required")
    }

    secureOption := otlptracegrpc.WithInsecure()
    exporter, err := otlptrace.New(
        context.Background(),
        otlptracegrpc.NewClient(
            secureOption,
            otlptracegrpc.WithEndpoint(collectorURL),
            otlptracegrpc.WithHeaders(map[string]string{
                "Authorization": "Bearer " + secretToken,
            }),
            otlptracegrpc.WithTLSCredentials(credentials.NewTLS(&amp;tls.Config{})),
        ),
    )

    if err != nil {
        log.Fatal(err)
    }

    otel.SetTracerProvider(
        sdktrace.NewTracerProvider(
            sdktrace.WithSampler(sdktrace.AlwaysSample()),
            sdktrace.WithBatcher(exporter),
        ),
    )
    otel.SetTextMapPropagator(
        propagation.NewCompositeTextMapPropagator(
            propagation.Baggage{},
            propagation.TraceContext{},
        ),
    )
    return exporter.Shutdown
}
</code></pre>
<p>For instrumenting connections to Redis, we will add a tracing hook to it, and in order to instrument Gin, we will add the OTel middleware. This will automatically capture all interactions with our application, since Gin will be fully instrumented. In addition, all outgoing connections to Redis will also be instrumented.</p>
<pre><code>// Initialize Redis client
    rdb := redis.NewClient(&amp;redis.Options{
        Addr:     redisHost + ":" + redisPort,
        Password: "",
        DB:       0,
    })
    rdb.AddHook(redisotel.NewTracingHook())
    // Initialize router
    r := gin.New()
    r.Use(logrusMiddleware)
    r.Use(otelgin.Middleware("go-favorite-otel-manual"))
</code></pre>
<p><strong>Adding custom spans</strong><br />
Now that we have everything added and initialized, we can add custom spans.</p>
<p>If we want to have additional instrumentation for a part of our app, we simply start a custom span and then defer ending the span.</p>
<pre><code>// start otel span
ctx := c.Request.Context()
ctx, span := tracer.Start(ctx, "add_favorite_movies")
defer span.End()
</code></pre>
<p>For comparison, this is the instrumented code of our sample application. You can find the full source code in <a href="https://github.com/elastic/observability-examples/tree/main/Elastiflix/go-favorite-otel-manual">GitHub</a>.</p>
<pre><code>package main

import (
    "log"
    "net/http"
    "os"
    "time"
    "context"

    "github.com/go-redis/redis/v8"
    "github.com/go-redis/redis/extra/redisotel/v8"


    "github.com/sirupsen/logrus"

    "github.com/gin-gonic/gin"

  "go.opentelemetry.io/otel"
  "go.opentelemetry.io/otel/attribute"
  "go.opentelemetry.io/otel/exporters/otlp/otlptrace"
  "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc"

    "go.opentelemetry.io/otel/propagation"

    "google.golang.org/grpc/credentials"
    "crypto/tls"

  sdktrace "go.opentelemetry.io/otel/sdk/trace"

    "go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin"

    "go.opentelemetry.io/otel/trace"

    "strings"
    "strconv"
    "math/rand"
    "go.opentelemetry.io/otel/codes"

)

var tracer trace.Tracer

func initTracer() func(context.Context) error {
    tracer = otel.Tracer("go-favorite-otel-manual")

    collectorURL = strings.Replace(collectorURL, "https://", "", 1)

    secureOption := otlptracegrpc.WithInsecure()

    // split otlpHeaders by comma and convert to map
    headers := make(map[string]string)
    for _, header := range strings.Split(otlpHeaders, ",") {
        headerParts := strings.Split(header, "=")

        if len(headerParts) == 2 {
            headers[headerParts[0]] = headerParts[1]
        }
    }

    exporter, err := otlptrace.New(
        context.Background(),
        otlptracegrpc.NewClient(
            secureOption,
            otlptracegrpc.WithEndpoint(collectorURL),
            otlptracegrpc.WithHeaders(headers),
            otlptracegrpc.WithTLSCredentials(credentials.NewTLS(&amp;tls.Config{})),
        ),
    )

    if err != nil {
        log.Fatal(err)
    }

    otel.SetTracerProvider(
        sdktrace.NewTracerProvider(
            sdktrace.WithSampler(sdktrace.AlwaysSample()),
            sdktrace.WithBatcher(exporter),
            //sdktrace.WithResource(resources),
        ),
    )
    otel.SetTextMapPropagator(
        propagation.NewCompositeTextMapPropagator(
            propagation.Baggage{},
            propagation.TraceContext{},
        ),
    )
    return exporter.Shutdown
}

var (
  collectorURL = os.Getenv("OTEL_EXPORTER_OTLP_ENDPOINT")
    otlpHeaders = os.Getenv("OTEL_EXPORTER_OTLP_HEADERS")
)


var logger = &amp;logrus.Logger{
    Out:   os.Stderr,
    Hooks: make(logrus.LevelHooks),
    Level: logrus.InfoLevel,
    Formatter: &amp;logrus.JSONFormatter{
        FieldMap: logrus.FieldMap{
            logrus.FieldKeyTime:  "@timestamp",
            logrus.FieldKeyLevel: "log.level",
            logrus.FieldKeyMsg:   "message",
            logrus.FieldKeyFunc:  "function.name", // non-ECS
        },
        TimestampFormat: time.RFC3339Nano,
    },
}

func main() {
    cleanup := initTracer()
  defer cleanup(context.Background())

    redisHost := os.Getenv("REDIS_HOST")
    if redisHost == "" {
        redisHost = "localhost"
    }

    redisPort := os.Getenv("REDIS_PORT")
    if redisPort == "" {
        redisPort = "6379"
    }

    applicationPort := os.Getenv("APPLICATION_PORT")
    if applicationPort == "" {
        applicationPort = "5000"
    }

    // Initialize Redis client
    rdb := redis.NewClient(&amp;redis.Options{
        Addr:     redisHost + ":" + redisPort,
        Password: "",
        DB:       0,
    })
    rdb.AddHook(redisotel.NewTracingHook())


    // Initialize router
    r := gin.New()
    r.Use(logrusMiddleware)
    r.Use(otelgin.Middleware("go-favorite-otel-manual"))


    // Define routes
    r.GET("/", func(c *gin.Context) {
        contextLogger(c).Infof("Main request successful")
        c.String(http.StatusOK, "Hello World!")
    })

    r.GET("/favorites", func(c *gin.Context) {
        // artificial sleep for delayTime
        time.Sleep(time.Duration(delayTime) * time.Millisecond)

        userID := c.Query("user_id")

        contextLogger(c).Infof("Getting favorites for user %q", userID)

        favorites, err := rdb.SMembers(c.Request.Context(), userID).Result()
        if err != nil {
            contextLogger(c).Error("Failed to get favorites for user %q", userID)
            c.String(http.StatusInternalServerError, "Failed to get favorites")
            return
        }

        contextLogger(c).Infof("User %q has favorites %q", userID, favorites)

        c.JSON(http.StatusOK, gin.H{
            "favorites": favorites,
        })
    })

    // Start server
    logger.Infof("App startup")
    log.Fatal(http.ListenAndServe(":"+applicationPort, r))
    logger.Infof("App stopped")
}
</code></pre>
<h3 id="step2runningthedockerimagewithenvironmentvariables">Step 2. Running the Docker image with environment variables</h3>
<p>As specified in the <a href="https://opentelemetry.io/docs/specs/otel/configuration/sdk-environment-variables/">OTEL documentation</a>, we will use environment variables and pass in the configuration values that are found in your APM Agent’s configuration section.</p>
<p>Because Elastic accepts OTLP natively, we just need to provide the Endpoint and authentication where the OTEL Exporter needs to send the data, as well as some other environment variables.</p>
<p><strong>Where to get these variables in Elastic Cloud and Kibana</strong> <sup>®</sup><br />
You can copy the endpoints and token from Kibana under the path /app/home#/tutorial/apm.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltebfc9096105dc59e/6a85ccb89d2b7100f0f939ca/elastic-blog-GO-apm-agents.png" alt="GO apm agents" /></p>
<p>You will need to copy the OTEL_EXPORTER_OTLP_ENDPOINT as well as the OTEL_EXPORTER_OTLP_HEADERS.</p>
<p><strong>Build the image</strong></p>
<pre><code>docker build -t  go-otel-manual-image .
</code></pre>
<h2 id="runtheimage">Run the image</h2>
<pre><code>docker run \
       -e OTEL_EXPORTER_OTLP_ENDPOINT="&lt;REPLACE WITH OTEL_EXPORTER_OTLP_ENDPOINT&gt;" \
       -e OTEL_EXPORTER_OTLP_HEADERS="Authorization=Bearer &lt;REPLACE WITH TOKEN&gt;" \
       -e OTEL_RESOURCE_ATTRIBUTES="service.version=1.0,deployment.environment=production,service.name=go-favorite-otel-manual" \
       -p 5000:5000 \
       go-otel-manual-image
</code></pre>
<p>You can now issue a few requests in order to generate trace data. Note that these requests are expected to return an error, as this service relies on a connection to Redis that you don’t currently have running. As mentioned before, you can find a more complete example using Docker compose <a href="https://github.com/elastic/observability-examples/tree/main/Elastiflix">here</a>.</p>
<pre><code>curl localhost:500/favorites
# or alternatively issue a request every second

while true; do curl "localhost:5000/favorites"; sleep 1; done;
</code></pre>
<h2 id="howdothetracesshowupinelastic">How do the traces show up in Elastic?</h2>
<p>Now that the service is instrumented, you should see the following output in Elastic APM when looking at the transactions section of your Node.js service:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blta64d1c7fc7f17c32/6a85ccbb2d64d5249d081d72/GO-trace-samples.png" alt="trace samples" /></p>
<h2 id="conclusion">Conclusion</h2>
<p>In this blog, we discussed the following:</p>
<ul>
<li>How to manually instrument Go with OpenTelemetry</li>
<li>How to properly initialize OpenTelemetry and add a custom span</li>
<li>How to easily set the OTLP ENDPOINT and OTLP HEADERS with Elastic without the need for a collector</li>
</ul>
<p>Hopefully, this provides an easy-to-understand walk-through of instrumenting Go with OpenTelemetry and how easy it is to send traces into Elastic.</p>
<blockquote>
  <p>Developer resources:</p>
  <ul>
  <li><a href="https://www.elastic.co/blog/getting-started-opentelemetry-instrumentation-sample-app">Elastiflix application</a>, a guide to instrument different languages with OpenTelemetry</li>
  <li>Python: <a href="https://www.elastic.co/blog/auto-instrumentation-of-python-applications-opentelemetry">Auto-instrumentation</a>, <a href="https://www.elastic.co/blog/manual-instrumentation-of-python-applications-opentelemetry">Manual-instrumentation</a></li>
  <li>Java: <a href="https://www.elastic.co/blog/auto-instrumentation-of-java-applications-opentelemetry">Auto-instrumentation</a>, <a href="https://www.elastic.co/blog/manual-instrumentation-of-java-applications-opentelemetry">Manual-instrumentation</a></li>
  <li>Node.js: <a href="https://www.elastic.co/blog/auto-instrument-nodejs-applications-opentelemetry">Auto-instrumentation</a>, <a href="https://www.elastic.co/blog/manual-instrumentation-of-nodejs-applications-opentelemetry">Manual-instrumentation</a></li>
  <li>.NET: <a href="https://www.elastic.co/blog/auto-instrumentation-of-net-applications-opentelemetry">Auto-instrumentation</a>, <a href="https://www.elastic.co/blog/manual-instrumentation-of-net-applications-opentelemetry">Manual-instrumentation</a></li>
  <li>Go: <a href="https://elastic.co/blog/manual-instrumentation-apps-opentelemetry">Manual-instrumentation</a></li>
  <li><a href="https://www.elastic.co/blog/best-practices-instrumenting-opentelemetry">Best practices for instrumenting OpenTelemetry</a></li>
  </ul>
  <p>General configuration and use case resources:</p>
  <ul>
  <li><a href="https://www.elastic.co/blog/opentelemetry-observability">Independence with OpenTelemetry on Elastic</a></li>
  <li><a href="https://www.elastic.co/blog/implementing-kubernetes-observability-security-opentelemetry">Modern observability and security on Kubernetes with Elastic and OpenTelemetry</a></li>
  <li><a href="https://www.elastic.co/blog/3-models-logging-opentelemetry-elastic">3 models for logging with OpenTelemetry and Elastic</a></li>
  <li><a href="https://www.elastic.co/blog/adding-free-and-open-elastic-apm-as-part-of-your-elastic-observability-deployment">Adding free and open Elastic APM as part of your Elastic Observability deployment</a></li>
  <li><a href="https://www.elastic.co/blog/custom-metrics-app-code-java-agent-plugin">Capturing custom metrics through OpenTelemetry API in code with Elastic</a></li>
  <li><a href="https://www.elastic.co/virtual-events/future-proof-your-observability-platform-with-opentelemetry-and-elastic">Future-proof your observability platform with OpenTelemetry and Elastic</a></li>
  <li><a href="https://www.elastic.co/blog/kubernetes-k8s-observability-elasticsearch-cncf">Elastic Observability: Built for open technologies like Kubernetes, OpenTelemetry, Prometheus, Istio, and more</a></li>
  </ul>
</blockquote>
<p>Don’t have an Elastic Cloud account yet? <a href="https://cloud.elastic.co/registration">Sign up for Elastic Cloud</a> and try out the auto-instrumentation capabilities that I discussed above. I would be interested in getting your feedback about your experience in gaining visibility into your application stack with Elastic.</p>
<p><em>The release and timing of any features or functionality described in this post remain at Elastic's sole discretion. Any features or functionality not currently available may not be delivered on time or at all.</em></p>]]></content:encoded>
    <link>https://www.elastic.co/observability-labs/blog/manual-instrumentation-apps-opentelemetry</link>
    <guid isPermaLink="false">manual-instrumentation-apps-opentelemetry</guid>
    <category><![CDATA[OpenTelemetry]]></category>
    <category><![CDATA[APM]]></category>
    <category><![CDATA[Infrastructure Monitoring]]></category>
    <dc:creator><![CDATA[Luca Wintergerst]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt985f77895b54aaab/6a85ccbe342d69d08921b121/observability-launch-series-5-go-manual.jpg" length="0" type="image/jpeg"/>
    <pubDate>Tue, 12 Sep 2023 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Manual instrumentation of .NET applications with OpenTelemetry]]></title>
    <description><![CDATA[In this blog, we will look at how to manually instrument your .NET applications using OpenTelemetry, which provides a set of APIs, libraries, and agents to capture distributed traces and metrics from your application. You can analyze them in Elastic.]]></description>
    <content:encoded><![CDATA[<p>In the fast-paced universe of software development, especially in the cloud-native realm, DevOps and SRE teams are increasingly emerging as essential partners in application stability and growth.</p>
<p>DevOps engineers continuously optimize software delivery, while SRE teams act as the stewards of application reliability, scalability, and top-tier performance. The challenge? These teams require a cutting-edge observability solution, one that encompasses full-stack insights, empowering them to rapidly manage, monitor, and rectify potential disruptions before they culminate into operational challenges.</p>
<p>Observability in our modern distributed software ecosystem goes beyond mere monitoring — it demands limitless data collection, precision in processing, and the correlation of this data into actionable insights. However, the road to achieving this holistic view is paved with obstacles, from navigating version incompatibilities to wrestling with restrictive proprietary code.</p>
<p>Enter <a href="https://opentelemetry.io/">OpenTelemetry (OTel)</a>, with the following benefits for those who adopt it:</p>
<ul>
<li>Escape vendor constraints with OTel, freeing yourself from vendor lock-in and ensuring top-notch observability.</li>
<li>See the harmony of unified logs, metrics, and traces come together to provide a complete system view.</li>
<li>Improve your application oversight through richer and enhanced instrumentations.</li>
<li>Embrace the benefits of backward compatibility to protect your prior instrumentation investments.</li>
<li>Embark on the OpenTelemetry journey with an easy learning curve, simplifying onboarding and scalability.</li>
<li>Rely on a proven, future-ready standard to boost your confidence in every investment.</li>
<li>Explore manual instrumentation, enabling customized data collection to fit your unique needs.</li>
<li>Ensure monitoring consistency across layers with a standardized observability data framework.</li>
<li>Decouple development from operations, driving peak efficiency for both.</li>
</ul>
<p>In this post, we will dive into the methodology to instrument a .NET application manually using Docker.</p>
<h2 id="whatscovered">What's covered?</h2>
<ul>
<li>Instrumenting the .NET application manually</li>
<li>Creating a Docker image for a .NET application with the OpenTelemetry instrumentation baked in</li>
<li>Installing and running the OpenTelemetry .NET Profiler for automatic instrumentation</li>
</ul>
<h2 id="prerequisites">Prerequisites</h2>
<ul>
<li>An understanding of Docker and .NET</li>
<li>Elastic Cloud</li>
<li>Docker installed on your machine (we recommend docker desktop)</li>
</ul>
<h2 id="viewtheexamplesourcecode">View the example source code</h2>
<p>The full source code, including the Dockerfile used in this blog, can be found on <a href="https://github.com/elastic/observability-examples/tree/main/Elastiflix/dotnet-login-otel-manual">GitHub</a>. The repository also contains the <a href="https://github.com/elastic/observability-examples/tree/main/Elastiflix/dotnet-login">same application without instrumentation</a>. This allows you to compare each file and see the differences.</p>
<p>The following steps will show you how to instrument this application and run it on the command line or in Docker. If you are interested in a more complete OTel example, take a look at the docker-compose file <a href="https://github.com/elastic/observability-examples/tree/main#start-the-app">here</a>, which will bring up the full project.</p>
<h2 id="stepbystepguide">Step-by-step guide</h2>
<p>This blog assumes you have an Elastic Cloud account — if not, follow the <a href="https://cloud.elastic.co/registration?elektra=en-cloud-page">instructions to get started on Elastic Cloud</a>.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltb8ef49de791ae8ea/6a85ccde9d2b7104e3f939ce/elastic-blog-2-free-trial.png" alt="" /></p>
<h2 id="step1gettingstarted">Step 1. Getting started</h2>
<p>In our demonstration, we will manually instrument a .NET Core application - Login. This application simulates a simple user login service. In this example, we are only looking at Tracing since the OpenTelemetry logging instrumentation is currently at mixed maturity, as mentioned <a href="https://opentelemetry.io/docs/instrumentation/">here</a>.</p>
<p>The application has the following files:</p>
<ol>
<li><p>Program.cs</p></li>
<li><p>Startup.cs</p></li>
<li><p>Telemetry.cs</p></li>
<li><p>LoginController.cs</p></li>
</ol>
<h2 id="step2instrumentingtheapplication">Step 2. Instrumenting the application</h2>
<p>When it comes to OpenTelemetry, the .NET ecosystem presents some unique aspects. While OpenTelemetry offers its API, .NET leverages its native <strong>System</strong>.Diagnostics API to implement OpenTelemetry's Tracing API. The pre-existing constructs such as <strong>ActivitySource</strong> and <strong>Activity</strong> are aptly repurposed to comply with OpenTelemetry.</p>
<p>That said, understanding the OpenTelemetry API and its terminology remains crucial for .NET developers. It's pivotal in gaining full command over instrumenting your applications, and as we've seen, it also extends to understanding elements of the <strong>System</strong>.Diagnostics API.</p>
<p>For those who might lean toward using the original OpenTelemetry APIs over the <strong>System</strong>.Diagnostics ones, there is also a way. OpenTelemetry provides an API shim for tracing that you can use. It enables developers to switch to OpenTelemetry APIs, and you can find more details about it in the OpenTelemetry API Shim documentation.</p>
<p>By integrating such practices into your .NET application, you can take full advantage of the powerful features OpenTelemetry provides, irrespective of whether you're using OpenTelemetry's API or the <strong>System</strong>.Diagnostics API.</p>
<p>In this blog, we are sticking to the default method and using the Activity convention which the <strong>System</strong>.Diagnostics API dictates.</p>
<p>To manually instrument a .NET application, you need to make changes in each of these files. Let's take a look at these changes one by one.</p>
<h3 id="programcs">Program.cs</h3>
<p>This is the entry point for our application. Here, we create an instance of IHostBuilder with default configurations. Notice how we set up a console logger with Serilog.</p>
<pre><code>public static void Main(string[] args)
{
    Log.Logger = new LoggerConfiguration().WriteTo.Console().CreateLogger();
    CreateHostBuilder(args).Build().Run();
}
</code></pre>
<h3 id="startupcs">Startup.cs</h3>
<p>In the <strong>Startup</strong>.cs file, we use the <strong>ConfigureServices</strong> method to add the OpenTelemetry Tracing.</p>
<pre><code>public void ConfigureServices(IServiceCollection services)
{
    services.AddOpenTelemetry().WithTracing(builder =&gt; builder.AddOtlpExporter()
        .AddSource("Login")
        .AddAspNetCoreInstrumentation()
        .AddOtlpExporter()
        .ConfigureResource(resource =&gt;
            resource.AddService(
                serviceName: "Login"))
    );
    services.AddControllers();
}
</code></pre>
<p>The WithTracing method enables tracing in OpenTelemetry. We add the OTLP (OpenTelemetry Protocol) exporter, which is a general-purpose telemetry data delivery protocol. We also add the AspNetCoreInstrumentation, which will automatically collect traces from our application. This is a critically important step that is not mentioned in the OpenTelemetry docs. Without adding this method, the instrumentation was not working for me for the Login application.</p>
<h3 id="telemetrycs">Telemetry.cs</h3>
<p>This file contains the definition of our ActivitySource. The ActivitySource represents the source of the telemetry activities. It is named after the service name for your application, and this name can come from a configuration file, constants file, etc. We can use this ActivitySource to start activities.</p>
<pre><code>using System.Diagnostics;

public static class Telemetry
{
    //...

    // Name it after the service name for your app.
    // It can come from a config file, constants file, etc.
    public static readonly ActivitySource LoginActivitySource = new("Login");

    //...
}
</code></pre>
<p>In our case, we've created an <strong>ActivitySource</strong> named <strong>Login</strong>. In our <strong>LoginController</strong>.cs, we use this <strong>LoginActivitySource</strong> to start a new activity when we begin our operations.</p>
<pre><code>using (Activity activity = Telemetry.LoginActivitySource.StartActivity("SomeWork"))
{
    // Perform operations here
}
</code></pre>
<p>This piece of code starts a new activity named <strong>SomeWork</strong> , performs some operations (in this case, generating a random user and logging them in), and then ends the activity. These activities are traced and can be analyzed later to understand the performance of the operations.</p>
<p>This <strong>ActivitySource</strong> is fundamental to OpenTelemetry's manual instrumentation. It represents the source of the activities and provides a way to start and stop activities.</p>
<h3 id="logincontrollercs">LoginController.cs</h3>
<p>In the <strong>LoginController</strong>.cs file, we are tracing the operations performed by the GET and POST methods. We start a new activity, <strong>SomeWork</strong> , before we begin our operations and dispose of it once we're done.</p>
<pre><code>using (Activity activity = Telemetry.LoginActivitySource.StartActivity("SomeWork"))
{
    var user = GenerateRandomUserResponse();
    Log.Information("User logged in: {UserName}", user);
    return user;
}
</code></pre>
<p>This will track the time taken by these operations and send this data to any configured telemetry backend via the OTLP exporter.</p>
<h2 id="step3baseimagesetup">Step 3. Base image setup</h2>
<p>Now that we have our application source code created and instrumented, it’s time to create a Dockerfile to build and run our .NET Login service.</p>
<p>Start with the .NET runtime image for the base layer of our Dockerfile:</p>
<pre><code>FROM ${ARCH}mcr.microsoft.com/dotnet/aspnet:7.0. AS base
WORKDIR /app
EXPOSE 8000
</code></pre>
<p>Here, we're setting up the application's runtime environment.</p>
<h2 id="step4buildingthenetapplication">Step 4. Building the .NET application</h2>
<p>This feature of Docker is just the best. Here, we compile our .NET application. We'll use the SDK image. In the bad old days, we used to build on a different platform and then put the compiled code into the Docker container. This way, we are much more confident our build will replicate from a developers desktop and into production by using Docker all the way through.</p>
<pre><code>FROM --platform=$BUILDPLATFORM mcr.microsoft.com/dotnet/sdk:8.0-preview AS build
ARG TARGETPLATFORM

WORKDIR /src
COPY ["login.csproj", "./"]
RUN dotnet restore "./login.csproj"
COPY . .
WORKDIR "/src/."
RUN dotnet build "login.csproj" -c Release -o /app/build
</code></pre>
<p>This section ensures that our .NET code is properly restored and compiled.</p>
<h2 id="step5publishingtheapplication">Step 5. Publishing the application</h2>
<p>Once built, we'll publish the app:</p>
<pre><code>FROM build AS publish
RUN dotnet publish "login.csproj" -c Release -o /app/publish
</code></pre>
<h2 id="step6preparingthefinalimage">Step 6. Preparing the final image</h2>
<p>Now, let's set up the final runtime image:</p>
<pre><code>FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
</code></pre>
<h2 id="step7entrypointsetup">Step 7. Entry point setup</h2>
<p>Lastly, set the Docker image's entry point to both source the OpenTelemetry instrumentation, which sets up the Environment variables required to bootstrap the .NET Profiler, and then we start our .NET application:</p>
<pre><code>ENTRYPOINT ["/bin/bash", "-c", "dotnet login.dll"]
</code></pre>
<h2 id="step8runningthedockerimagewithenvironmentvariables">Step 8. Running the Docker image with environment variables</h2>
<p>To build and run the Docker image, you'd typically follow these steps:</p>
<h3 id="buildthedockerimage">Build the Docker image</h3>
<p>First, you'd want to build the Docker image from your Dockerfile. Let's assume the Dockerfile is in the current directory, and you'd like to name/tag your image dotnet-login-otel-image.</p>
<pre><code>docker build -t dotnet-login-otel-image .
</code></pre>
<h3 id="runthedockerimage">Run the Docker image</h3>
<p>After building the image, you'd run it with the specified environment variables. For this, the docker <strong>run</strong> command is used with the -e flag for each environment variable.</p>
<pre><code>docker run \
       -e OTEL_EXPORTER_OTLP_HEADERS="Authorization=Bearer ${ELASTIC_APM_SECRET_TOKEN}" \
       -e OTEL_EXPORTER_OTLP_ENDPOINT="${ELASTIC_APM_SERVER_URL}" \
       -e OTEL_METRICS_EXPORTER="otlp" \
       -e OTEL_RESOURCE_ATTRIBUTES="service.version=1.0,deployment.environment=production" \
       -e OTEL_SERVICE_NAME="dotnet-login-otel-manual" \
       -e OTEL_TRACES_EXPORTER="otlp" \
       dotnet-login-otel-image
</code></pre>
<p>Make sure that <code>${ELASTIC_APM_SECRET_TOKEN}</code> and <code>${ELASTIC_APM_SERVER_URL}</code> are set in your shell environment, replace them with their actual values from the cloud as shown below.</p>
<p><strong>Getting Elastic Cloud variables</strong><br />
You can copy the endpoints and token from Kibana under the path <code>/app/home#/tutorial/apm</code>.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltb324309b1a97b34f/6a85cce1e2447a221d8b1436/elastic-blog-3-apm-agents.png" alt="apm agents" /></p>
<p>You can also use an environment file with docker run --env-file to make the command less verbose if you have multiple environment variables.</p>
<p>Once you have this up and running, you can ping the endpoint for your instrumented service (in our case, this is /login), and you should see the app appear in Elastic APM, as shown below:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt78e1bff9ac568fa5/6a85cce35c27903789f59b47/services-2.png" alt="services" /></p>
<p>It will begin by tracking throughput and latency critical metrics for SREs to pay attention to.</p>
<p>Digging in, we can see an overview of all our Transactions.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt5c8d9a1ff381b15b/6a85cce6331d7a0d87c317e7/manual-net-login.png" alt="login" /></p>
<p>And look at specific transactions, including the “SomeWork” activity/span we created in the code above:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt02ad8d91efaccb83/6a85cce9bc5bb3452cf81b39/latency_distribution_graph.png" alt="latency distribution graph" /></p>
<p>There is clearly an outlier here, where one transaction took over 20ms. This is likely to be due to the CLR warming up.</p>
<h2 id="wrappingup">Wrapping up</h2>
<p>With the code here instrumented and the Dockerfile bootstrapping the application, you've transformed your simple .NET application into one that's instrumented with OpenTelemetry. This will aid greatly in understanding application performance, tracing errors, and gaining insights into how users interact with your software.</p>
<p>Remember, observability is a crucial aspect of modern application development, especially in distributed systems. With tools like OpenTelemetry, understanding complex systems becomes a tad bit easier.</p>
<p>In this blog, we discussed the following:</p>
<ul>
<li>How to manually instrument .NET with OpenTelemetry.</li>
<li>Using standard commands in a Docker file, our instrumented application was built and started.</li>
<li>Using OpenTelemetry and its support for multiple languages, DevOps and SRE teams can instrument their applications with ease, gaining immediate insights into the health of the entire application stack and reducing mean time to resolution (MTTR).</li>
</ul>
<p>Since Elastic can support a mix of methods for ingesting data whether it be using auto-instrumentation of open-source OpenTelemetry or manual instrumentation with its native APM agents, you can plan your migration to OTel by focusing on a few applications first and then using OpenTelemety across your applications later on in a manner that best fits your business needs.</p>
<blockquote>
  <p>Developer resources:</p>
  <ul>
  <li><a href="https://www.elastic.co/blog/getting-started-opentelemetry-instrumentation-sample-app">Elastiflix application</a>, a guide to instrument different languages with OpenTelemetry</li>
  <li>Python: <a href="https://www.elastic.co/blog/auto-instrumentation-of-python-applications-opentelemetry">Auto-instrumentation</a>, <a href="https://www.elastic.co/blog/manual-instrumentation-of-python-applications-opentelemetry">Manual-instrumentation</a></li>
  <li>Java: <a href="https://www.elastic.co/blog/auto-instrumentation-of-java-applications-opentelemetry">Auto-instrumentation</a>, <a href="https://www.elastic.co/blog/manual-instrumentation-of-java-applications-opentelemetry">Manual-instrumentation</a></li>
  <li>Node.js: <a href="https://www.elastic.co/blog/auto-instrument-nodejs-applications-opentelemetry">Auto-instrumentation</a>, <a href="https://www.elastic.co/blog/manual-instrumentation-of-nodejs-applications-opentelemetry">Manual-instrumentation</a></li>
  <li>.NET: <a href="https://www.elastic.co/blog/auto-instrumentation-of-net-applications-opentelemetry">Auto-instrumentation</a>, <a href="https://www.elastic.co/observability-labs/blog/manual-instrumentation-net-apps-opentelemetry">Manual-instrumentation</a></li>
  <li>Go: <a href="https://elastic.co/blog/manual-instrumentation-of-go-applications-opentelemetry">Manual-instrumentation</a></li>
  <li><a href="https://www.elastic.co/blog/best-practices-instrumenting-opentelemetry">Best practices for instrumenting OpenTelemetry</a></li>
  </ul>
  <p>General configuration and use case resources:</p>
  <ul>
  <li><a href="https://www.elastic.co/blog/opentelemetry-observability">Independence with OpenTelemetry on Elastic</a></li>
  <li><a href="https://www.elastic.co/blog/implementing-kubernetes-observability-security-opentelemetry">Modern observability and security on Kubernetes with Elastic and OpenTelemetry</a></li>
  <li><a href="https://www.elastic.co/blog/3-models-logging-opentelemetry-elastic">3 models for logging with OpenTelemetry and Elastic</a></li>
  <li><a href="https://www.elastic.co/blog/adding-free-and-open-elastic-apm-as-part-of-your-elastic-observability-deployment">Adding free and open Elastic APM as part of your Elastic Observability deployment</a></li>
  <li><a href="https://www.elastic.co/blog/custom-metrics-app-code-java-agent-plugin">Capturing custom metrics through OpenTelemetry API in code with Elastic</a></li>
  <li><a href="https://www.elastic.co/virtual-events/future-proof-your-observability-platform-with-opentelemetry-and-elastic">Future-proof your observability platform with OpenTelemetry and Elastic</a></li>
  <li><a href="https://www.elastic.co/blog/kubernetes-k8s-observability-elasticsearch-cncf">Elastic Observability: Built for open technologies like Kubernetes, OpenTelemetry, Prometheus, Istio, and more</a></li>
  </ul>
</blockquote>
<p>Don’t have an Elastic Cloud account yet? <a href="https://cloud.elastic.co/registration">Sign up for Elastic Cloud</a> and try out the instrumentation capabilities that I discussed above. I would be interested in getting your feedback about your experience in gaining visibility into your application stack with Elastic.</p>
<p><em>The release and timing of any features or functionality described in this post remain at Elastic's sole discretion. Any features or functionality not currently available may not be delivered on time or at all.</em></p>]]></content:encoded>
    <link>https://www.elastic.co/observability-labs/blog/manual-instrumentation-net-apps-opentelemetry</link>
    <guid isPermaLink="false">manual-instrumentation-net-apps-opentelemetry</guid>
    <category><![CDATA[OpenTelemetry]]></category>
    <category><![CDATA[APM]]></category>
    <category><![CDATA[Infrastructure Monitoring]]></category>
    <dc:creator><![CDATA[David Hope]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltd3bbe921bdcfc897/6a85ccebabdc29dbcb122538/observability-launch-series-4-net-manual.jpg" length="0" type="image/jpeg"/>
    <pubDate>Fri, 01 Sep 2023 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Auto-instrumentation of .NET applications with OpenTelemetry]]></title>
    <description><![CDATA[OpenTelemetry provides an observability framework for cloud-native software, allowing us to trace, monitor, and debug applications seamlessly. In this post, we'll explore how to automatically instrument a .NET application using OpenTelemetry.]]></description>
    <content:encoded><![CDATA[<p>In the fast-paced universe of software development, especially in the cloud-native realm, DevOps and SRE teams are increasingly emerging as essential partners in application stability and growth.</p>
<p>DevOps engineers continuously optimize software delivery, while SRE teams act as the stewards of application reliability, scalability, and top-tier performance. The challenge? These teams require a cutting-edge observability solution, one that encompasses full-stack insights, empowering them to rapidly manage, monitor, and rectify potential disruptions before they culminate into operational challenges.</p>
<p>Observability in our modern distributed software ecosystem goes beyond mere monitoring — it demands limitless data collection, precision in processing, and the correlation of this data into actionable insights. However, the road to achieving this holistic view is paved with obstacles, from navigating version incompatibilities to wrestling with restrictive proprietary code.</p>
<p>Enter <a href="https://opentelemetry.io/">OpenTelemetry (OTel)</a>, with the following benefits for those who adopt it:</p>
<ul>
<li>Escape vendor constraints with OTel, freeing yourself from vendor lock-in and ensuring top-notch observability.</li>
<li>See the harmony of unified logs, metrics, and traces come together to provide a complete system view.</li>
<li>Improve your application oversight through richer and enhanced instrumentations.</li>
<li>Embrace the benefits of backward compatibility to protect your prior instrumentation investments.</li>
<li>Embark on the OpenTelemetry journey with an easy learning curve, simplifying onboarding and scalability.</li>
<li>Rely on a proven, future-ready standard to boost your confidence in every investment.</li>
<li>Explore manual instrumentation, enabling customized data collection to fit your unique needs.</li>
<li>Ensure monitoring consistency across layers with a standardized observability data framework.</li>
<li>Decouple development from operations, driving peak efficiency for both.</li>
</ul>
<p>Given this context, OpenTelemetry emerges as an unmatched observability solution for cloud-native software, seamlessly enabling tracing, monitoring, and debugging. One of its strengths is the ability to auto-instrument applications, allowing developers the luxury of collecting invaluable telemetry without delving into code modifications.</p>
<p>In this post, we will dive into the methodology to instrument a .NET application using Docker, blending the best of both worlds: powerful observability without the code hassles.</p>
<h2 id="whatscovered">What's covered?</h2>
<ul>
<li>How APM works with .NET using CLR Profiler functionality</li>
<li>Creating a Docker image for a .NET application with the OpenTelemetry instrumentation baked in</li>
<li>Installing and running the OpenTelemetry .NET Profiler for automatic instrumentation</li>
</ul>
<h2 id="howapmworkswithnetusingclrprofilerfunctionality">How APM works with .NET using CLR Profiler functionality</h2>
<p>Before we delve into the details, let's clear up some confusion around .NET Profilers and CPU Profilers like Elastic<sup>®</sup>’s Universal Profiling tool — we don’t want to get these two things mixed up, as they have very different purposes.</p>
<p>When discussing profiling tools, especially in the context of .NET, it's not uncommon to encounter confusion between a ".NET profiler" and a "CPU profiler." Though both are used to diagnose and optimize applications, they serve different primary purposes and operate at different levels. Let's clarify the distinction:</p>
<h3 id="netprofiler">.NET Profiler</h3>
<ol>
<li><p><strong>Scope:</strong> Specifically targets .NET applications. It is designed to work with the .NET runtime (i.e., the Common Language Runtime (CLR)).</p></li>
<li><p><strong>Functionality:</strong></p></li>
<li><p><strong>Use cases:</strong></p></li>
</ol>
<h3 id="cpuprofiler">CPU Profiler</h3>
<ol>
<li><p><strong>Scope:</strong> More general than a .NET profiler. It can profile any application, irrespective of the language or runtime, as long as it runs on the CPU being profiled.</p></li>
<li><p><strong>Functionality:</strong></p></li>
<li><p><strong>Use cases:</strong></p></li>
</ol>
<p>While both .NET profilers and CPU profilers aid in optimizing and diagnosing application performance, their approach and depth differ. A .NET profiler offers deep insights specifically into the .NET ecosystem, allowing for fine-grained analysis and instrumentation. In contrast, a CPU profiler provides a broader view, focusing on CPU usage patterns across any application, regardless of its development platform.</p>
<p>It's worth noting that for comprehensive profiling of a .NET application, you might use both: the .NET profiler to understand code-level behaviors specific to .NET and the CPU profiler to get an overview of CPU resource utilization.</p>
<p>Now that we've cleared that up, let's focus on the .NET Profiler, which we are discussing in this blog for automatic instrumentation of .NET applications. First, let's familiarize ourselves with some foundational concepts and terminologies relevant to a .NET Profiler:</p>
<ul>
<li><strong>CLR (Common Language Runtime):</strong> CLR is a core component of the .NET framework, acting as the execution engine for .NET apps. It provides key services like memory management, exception handling, and type safety.</li>
<li><strong>Profiler API:</strong>.NET provides a set of APIs for profiling applications. These APIs let tools and developers monitor or manipulate .NET applications during runtime.</li>
<li><strong>IL (Intermediate Language):</strong> After compiling, .NET source code turns into IL, a low-level, platform-agnostic representation. This IL code is then compiled just-in-time (JIT) into machine code by the CLR during application execution.</li>
<li><strong>JIT compilation:</strong> JIT stands for just-in-time. In .NET, the CLR compiles IL to native code just before its execution.</li>
</ul>
<p>Now, let's explore how automatic instrumentation works using CLR Profiler.</p>
<p>Automatic instrumentation in .NET, much like Java's bytecode instrumentation, revolves around modifying the behavior of your application's methods during runtime, without changing the actual source code.</p>
<p>Here’s a step-by-step breakdown:</p>
<ol>
<li><p><strong>Attach the profiler:</strong> When launching your .NET application, you'll have to specify to load the profiler. The CLR checks for the presence of a profiler by reading environment variables. If it finds one, the CLR initializes the profiler before any user code is executed.</p></li>
<li><p><strong>Use Profiler API to monitor events:</strong> The Profiler API allows a profiler to monitor various events. For instance, method JIT compilation events can be tracked. When a method is about to be JIT compiled, the profiler gets notified.</p></li>
<li><p><strong>Manipulate IL code:</strong> Upon getting notified of a JIT compilation, the profiler can manipulate the IL code of the method. Using the Profiler API, the profiler can insert, delete, or replace IL instructions. This is analogous to how Java agents modify bytecode. For example, if you want to measure a method's execution time, you'd modify the IL to insert calls to start and stop a timer at the beginning and end of the method, respectively.</p></li>
<li><p><strong>Execution of transformed code:</strong> Once the IL has been modified, the JIT compiler will translate it into machine code. The application will then execute this machine code, which includes the additions made by the profiler.</p></li>
<li><p><strong>Gather and report data:</strong> The added instrumentation can collect various data, such as method execution times or call counts. This data can then be relayed to an application performance management (APM) tool, which can provide insights, visualizations, and alerts based on the data.</p></li>
</ol>
<p>In essence, automatic instrumentation with CLR Profiler is about modifying the behavior of your .NET methods at runtime. This is invaluable for monitoring, diagnosing, and fine-tuning the performance of .NET applications without intruding on the application's actual source code.</p>
<h2 id="prerequisites">Prerequisites</h2>
<ul>
<li>A basic understanding of Docker and .NET</li>
<li>Elastic Cloud</li>
<li>Docker installed on your machine (we recommend docker desktop)</li>
</ul>
<h2 id="viewtheexamplesourcecode">View the example source code</h2>
<p>The full source code, including the Dockerfile used in this blog, can be found on <a href="https://github.com/elastic/observability-examples/tree/main/Elastiflix/dotnet-login-otel-manual">GitHub</a>. The repository also contains the <a href="https://github.com/elastic/observability-examples/tree/main/Elastiflix/dotnet-login">same application without instrumentation</a>. This allows you to compare each file and see the differences.</p>
<p>The following steps will show you how to instrument this application and run it on the command line or in Docker. If you are interested in a more complete OTel example, take a look at the docker-compose file <a href="https://github.com/elastic/observability-examples/tree/main#start-the-app">here</a>, which will bring up the full project.</p>
<h2 id="stepbystepguide">Step-by-step guide</h2>
<p>This blog assumes you have an Elastic Cloud account — if not, follow the <a href="https://cloud.elastic.co/registration?elektra=en-cloud-page">instructions to get started on Elastic Cloud</a>.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltee94942c207c6253/6a85c7ec1aa1e1b6b1ff8ce7/elastic-blog-2-free-trial.png" alt="" /></p>
<h2 id="step1baseimagesetup">Step 1. Base image setup</h2>
<p>Start with the .NET runtime image for the base layer of our Dockerfile:</p>
<pre><code>FROM ${ARCH}mcr.microsoft.com/dotnet/aspnet:7.0. AS base
WORKDIR /app
EXPOSE 8000
</code></pre>
<p>Here, we're setting up the application's runtime environment.</p>
<h2 id="step2buildingthenetapplication">Step 2. Building the .NET application</h2>
<p>This feature of Docker is just the best. Here, we compile our .NET application using the SDK image. In the bad old days, we used to build on a different platform and then put the compiled code into the Docker container. This way, we are much more confident our build will replicate from a developer’s desktop and into production by using Docker all the way through.</p>
<pre><code>FROM --platform=$BUILDPLATFORM mcr.microsoft.com/dotnet/sdk:8.0-preview AS build
ARG TARGETPLATFORM

WORKDIR /src
COPY ["login.csproj", "./"]
RUN dotnet restore "./login.csproj"
COPY . .
WORKDIR "/src/."
RUN dotnet build "login.csproj" -c Release -o /app/build
</code></pre>
<p>This section ensures that our .NET code is properly restored and compiled.</p>
<h2 id="step3publishingtheapplication">Step 3. Publishing the application</h2>
<p>Once built, we'll publish the app:</p>
<pre><code>FROM build AS publish
RUN dotnet publish "login.csproj" -c Release -o /app/publish
</code></pre>
<h2 id="step4preparingthefinalimage">Step 4. Preparing the final image</h2>
<p>Now, let's set up the final runtime image:</p>
<pre><code>FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish
</code></pre>
<h2 id="step5installingopentelemetry">Step 5. Installing OpenTelemetry</h2>
<p>We'll install dependencies and download the OpenTelemetry auto-instrumentation script:</p>
<pre><code>RUN apt-get update &amp;&amp; apt-get install -y zip curl
RUN mkdir /otel
RUN curl -L -o /otel/otel-dotnet-install.sh https://github.com/open-telemetry/opentelemetry-dotnet-instrumentation/releases/download/v0.7.0/otel-dotnet-auto-install.sh
RUN chmod +x /otel/otel-dotnet-install.sh
</code></pre>
<h2 id="step6configureopentelemetry">Step 6. Configure OpenTelemetry</h2>
<p>Designate where OpenTelemetry should reside and execute the installation script. Note that the ENV OTEL_DOTNET_AUTO_HOME is required as the script looks for it:</p>
<pre><code>ENV OTEL_DOTNET_AUTO_HOME=/otel
RUN /bin/bash /otel/otel-dotnet-install.sh
</code></pre>
<h2 id="step7additionalconfiguration">Step 7. Additional configuration</h2>
<p>Make sure the auto-instrumentation and platform detection scripts are executable and run the platform detection script.</p>
<pre><code>COPY platform-detection.sh /otel/
RUN chmod +x /otel/instrument.sh
RUN chmod +x /otel/platform-detection.sh &amp;&amp; /otel/platform-detection.sh
</code></pre>
<p>This platform detection script will check if the Docker build is for ARM64 and implement a workaround to get the OpenTelemetry instrumentation to work on MacOS. If you happen to be running locally on MacOS M1 or M2 processors, you will be grateful for this script.</p>
<h2 id="step8entrypointsetup">Step 8. Entry point setup</h2>
<p>Lastly, set the Docker image's entry point to both source the OpenTelemetry instrumentation, which sets up the environment variables required to bootstrap the .NET Profiler, and then we start our .NET application:</p>
<pre><code>ENTRYPOINT ["/bin/bash", "-c", "source /otel/instrument.sh &amp;&amp; dotnet login.dll"]
</code></pre>
<h2 id="step9runningthedockerimagewithenvironmentvariables">Step 9. Running the Docker image with environment variables</h2>
<p>To build and run the Docker image, you'd typically follow these steps:</p>
<h3 id="buildthedockerimage">Build the Docker image</h3>
<p>First, you'd want to build the Docker image from your Dockerfile. Let's assume the Dockerfile is in the current directory, and you'd like to name/tag your image dotnet-login-otel-image.</p>
<pre><code>docker build -t dotnet-login-otel-image .
</code></pre>
<h3 id="runthedockerimage">Run the Docker image</h3>
<p>After building the image, you'd run it with the specified environment variables. For this, the docker <strong>run</strong> command is used with the -e flag for each environment variable.</p>
<pre><code>docker run \
       -e OTEL_EXPORTER_OTLP_HEADERS="Authorization=Bearer ${ELASTIC_APM_SECRET_TOKEN}" \
       -e OTEL_EXPORTER_OTLP_ENDPOINT="${ELASTIC_APM_SERVER_URL}" \
       -e OTEL_METRICS_EXPORTER="otlp" \
       -e OTEL_RESOURCE_ATTRIBUTES="service.version=1.0,deployment.environment=production" \
       -e OTEL_SERVICE_NAME="dotnet-login-otel-auto" \
       -e OTEL_TRACES_EXPORTER="otlp" \
       dotnet-login-otel-image
</code></pre>
<p>Make sure that <code>${ELASTIC_APM_SECRET_TOKEN}</code> and <code>${ELASTIC_APM_SERVER_URL}</code> are set in your shell environment, and replace them with their actual values from the cloud as shown below.<br />
Getting Elastic Cloud variables</p>
<p>You can copy the endpoints and token from Kibana<sup>®</sup> under the path <code>/app/home#/tutorial/apm</code>.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt6e92903598e3f7b2/6a85c7ef1aa1e13db4ff8ceb/elastic-blog-3-apm-agents.png" alt="apm agents" /></p>
<p>You can also use an environment file with docker run --env-file to make the command less verbose if you have multiple environment variables.</p>
<p>Once you have this up and running, you can ping the endpoint for your instrumented service (in our case, this is /login), and you should see the app appear in Elastic APM, as shown below:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt953edf94dcada272/6a85c7f2331d7a8430c316fd/services-3.png" alt="services" /></p>
<p>It will begin by tracking throughput and latency critical metrics for SREs to pay attention to.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt564046f1b5aca688/6a85c7f633f2441fd249f478/dotnet-login-otel-auto-1.png" alt="dotnet-login-otel-auto-1" /></p>
<p>Digging in, we can see an overview of all our Transactions.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt8288064e8ce34deb/6a85c7f9ba7accdfb99920e6/dotnet-login-otel-auto-2.png" alt="dotnet-login-otel-auto-2" /></p>
<p>And look at specific transactions:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltae4f2575265ca818/6a85c7fb078290ac2a321700/specific_transactions.png" alt="specific transactions" /></p>
<p>There is clearly an outlier here, where one transaction took over 200ms. This is likely to be due to the .NET CLR warming up. Click on <strong>Logs</strong> , and we see that logs are also brought over. The OTel Agent will automatically bring in logs and correlate them with traces for you:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltd46cc91508749702/6a85c7fe8c29444f1cb88fc6/otel_agent.png" alt="otel agent" /></p>
<h2 id="wrappingup">Wrapping up</h2>
<p>With this Dockerfile, you've transformed your simple .NET application into one that's automatically instrumented with OpenTelemetry. This will aid greatly in understanding application performance, tracing errors, and gaining insights into how users interact with your software.</p>
<p>Remember, observability is a crucial aspect of modern application development, especially in distributed systems. With tools like OpenTelemetry, understanding complex systems becomes a tad bit easier.</p>
<p>In this blog, we discussed the following:</p>
<ul>
<li>How to auto-instrument .NET with OpenTelemetry.</li>
<li>Using standard commands in a Docker file, auto-instrumentation was done efficiently and without adding code in multiple places enabling manageability.</li>
<li>Using OpenTelemetry and its support for multiple languages, DevOps and SRE teams can auto-instrument their applications with ease gaining immediate insights into the health of the entire application stack and reduce mean time to resolution (MTTR).</li>
</ul>
<p>Since Elastic can support a mix of methods for ingesting data, whether it be using auto-instrumentation of open-source OpenTelemetry or manual instrumentation with its native APM agents, you can plan your migration to OTel by focusing on a few applications first and then using OpenTelemety across your applications later on in a manner that best fits your business needs.</p>
<blockquote>
  <p>Developer resources:</p>
  <ul>
  <li><a href="https://www.elastic.co/blog/getting-started-opentelemetry-instrumentation-sample-app">Elastiflix application</a>, a guide to instrument different languages with OpenTelemetry</li>
  <li>Python: <a href="https://www.elastic.co/blog/auto-instrumentation-of-python-applications-opentelemetry">Auto-instrumentation</a>, <a href="https://www.elastic.co/blog/manual-instrumentation-of-python-applications-opentelemetry">Manual-instrumentation</a></li>
  <li>Java: <a href="https://www.elastic.co/blog/auto-instrumentation-of-java-applications-opentelemetry">Auto-instrumentation</a>, <a href="https://www.elastic.co/blog/manual-instrumentation-of-java-applications-opentelemetry">Manual-instrumentation</a></li>
  <li>Node.js: <a href="https://www.elastic.co/blog/auto-instrument-nodejs-applications-opentelemetry">Auto-instrumentation</a>, <a href="https://www.elastic.co/blog/manual-instrumentation-of-nodejs-applications-opentelemetry">Manual-instrumentation</a></li>
  <li>.NET: <a href="https://www.elastic.co/blog/auto-instrumentation-net-applications-opentelemetry">Auto-instrumentation</a>, <a href="https://www.elastic.co/blog/manual-instrumentation-of-net-applications-opentelemetry">Manual-instrumentation</a></li>
  <li>Go: <a href="https://elastic.co/blog/manual-instrumentation-of-go-applications-opentelemetry">Manual-instrumentation</a></li>
  <li><a href="https://www.elastic.co/blog/best-practices-instrumenting-opentelemetry">Best practices for instrumenting OpenTelemetry</a></li>
  </ul>
  <p>General configuration and use case resources:</p>
  <ul>
  <li><a href="https://www.elastic.co/blog/opentelemetry-observability">Independence with OpenTelemetry on Elastic</a></li>
  <li><a href="https://www.elastic.co/blog/implementing-kubernetes-observability-security-opentelemetry">Modern observability and security on Kubernetes with Elastic and OpenTelemetry</a></li>
  <li><a href="https://www.elastic.co/blog/3-models-logging-opentelemetry-elastic">3 models for logging with OpenTelemetry and Elastic</a></li>
  <li><a href="https://www.elastic.co/blog/adding-free-and-open-elastic-apm-as-part-of-your-elastic-observability-deployment">Adding free and open Elastic APM as part of your Elastic Observability deployment</a></li>
  <li><a href="https://www.elastic.co/blog/custom-metrics-app-code-java-agent-plugin">Capturing custom metrics through OpenTelemetry API in code with Elastic</a></li>
  <li><a href="https://www.elastic.co/virtual-events/future-proof-your-observability-platform-with-opentelemetry-and-elastic">Future-proof your observability platform with OpenTelemetry and Elastic</a></li>
  <li><a href="https://www.elastic.co/blog/kubernetes-k8s-observability-elasticsearch-cncf">Elastic Observability: Built for open technologies like Kubernetes, OpenTelemetry, Prometheus, Istio, and more</a></li>
  </ul>
</blockquote>
<p>Don’t have an Elastic Cloud account yet? <a href="https://cloud.elastic.co/registration">Sign up for Elastic Cloud</a> and try out the auto-instrumentation capabilities that I discussed above. I would be interested in getting your feedback about your experience in gaining visibility into your application stack with Elastic.</p>
<p><em>The release and timing of any features or functionality described in this post remain at Elastic's sole discretion. Any features or functionality not currently available may not be delivered on time or at all.</em></p>]]></content:encoded>
    <link>https://www.elastic.co/observability-labs/blog/auto-instrumentation-net-applications-opentelemetry</link>
    <guid isPermaLink="false">auto-instrumentation-net-applications-opentelemetry</guid>
    <category><![CDATA[OpenTelemetry]]></category>
    <category><![CDATA[APM]]></category>
    <category><![CDATA[Infrastructure Monitoring]]></category>
    <dc:creator><![CDATA[David Hope]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltb1add1b117d08e30/6a85c801eaf2451645a49eef/observability-launch-series-4-net-auto.jpg" length="0" type="image/jpeg"/>
    <pubDate>Fri, 01 Sep 2023 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Manual instrumentation with OpenTelemetry for Python applications]]></title>
    <description><![CDATA[In this blog post, we will show you how to manually instrument Python applications using OpenTelemetry. We will explore how to use the proper OpenTelemetry Python libraries and in particular work on instrumenting tracing in a Python application.]]></description>
    <content:encoded><![CDATA[<p>DevOps and SRE teams are transforming the process of software development. While DevOps engineers focus on efficient software applications and service delivery, SRE teams are key to ensuring reliability, scalability, and performance. These teams must rely on a full-stack observability solution that allows them to manage and monitor systems and ensure issues are resolved before they impact the business.</p>
<p>Observability across the entire stack of modern distributed applications requires data collection, processing, and correlation often in the form of dashboards. Ingesting all system data requires installing agents across stacks, frameworks, and providers — a process that can be challenging and time-consuming for teams who have to deal with version changes, compatibility issues, and proprietary code that doesn't scale as systems change.</p>
<p>Thanks to <a href="http://opentelemetry.io">OpenTelemetry</a> (OTel), DevOps and SRE teams now have a standard way to collect and send data that doesn't rely on proprietary code and have a large support community reducing vendor lock-in.</p>
<p>In a <a href="https://www.elastic.co/blog/opentelemetry-observability">previous blog</a>, we also reviewed how to use the <a href="https://github.com/elastic/opentelemetry-demo">OpenTelemetry demo</a> and connect it to Elastic<sup>®</sup>, as well as some of Elastic’s capabilities with OpenTelemetry and Kubernetes.</p>
<p>In this blog, we will show how to use <a href="https://opentelemetry.io/docs/instrumentation/python/manual/">manual instrumentation for OpenTelemetry</a> with the Python service of our <a href="https://github.com/elastic/observability-examples">application called Elastiflix</a>. This approach is slightly more complex than using <a href="https://www.elastic.co/blog/auto-instrumentation-of-python-applications-opentelemetry">automatic instrumentation</a>.</p>
<p>The beauty of this is that there is <strong>no need for the otel-collector</strong>! This setup enables you to slowly and easily migrate an application to OTel with Elastic according to a timeline that best fits your business.</p>
<h2 id="applicationprerequisitesandconfig">Application, prerequisites, and config</h2>
<p>The application that we use for this blog is called <a href="https://github.com/elastic/observability-examples">Elastiflix</a>, a movie streaming application. It consists of several micro-services written in .NET, NodeJS, Go, and Python.</p>
<p>Before we instrument our sample application, we will first need to understand how Elastic can receive the telemetry data.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt9872326d55e43cf5/6a85cd008c2944e11fb8907f/elastic-blog-1-config.png" alt="configuration" /></p>
<p>All of Elastic Observability’s APM capabilities are available with OTel data. Some of these include:</p>
<ul>
<li>Service maps</li>
<li>Service details (latency, throughput, failed transactions)</li>
<li>Dependencies between services, distributed tracing</li>
<li>Transactions (traces)</li>
<li>Machine learning (ML) correlations</li>
<li>Log correlation</li>
</ul>
<p>In addition to Elastic’s APM and a unified view of the telemetry data, you will also be able to use Elastic’s powerful machine learning capabilities to reduce the analysis, and alerting to help reduce MTTR.</p>
<h2 id="prerequisites">Prerequisites</h2>
<ul>
<li>An Elastic Cloud account — <a href="https://cloud.elastic.co/">sign up now</a></li>
<li>A clone of the <a href="https://github.com/elastic/observability-examples">Elastiflix demo application</a>, or your own Python application</li>
<li>Basic understanding of Docker — potentially install <a href="https://www.docker.com/products/docker-desktop/">Docker Desktop</a></li>
<li>Basic understanding of Python</li>
</ul>
<h2 id="viewtheexamplesourcecode">View the example source code</h2>
<p>The full source code, including the Dockerfile used in this blog, can be found on <a href="https://github.com/elastic/observability-examples/tree/main/Elastiflix/python-favorite-otel-auto">GitHub</a>. The repository also contains the <a href="https://github.com/elastic/observability-examples/tree/main/Elastiflix/python-favorite">same application without instrumentation</a>. This allows you to compare each file and see the differences.</p>
<p>The following steps will show you how to instrument this application and run it on the command line or in Docker. If you are interested in a more complete OTel example, take a look at the docker-compose file <a href="https://github.com/elastic/observability-examples/tree/main#start-the-app">here</a>, which will bring up the full project.</p>
<p>Before we begin, let’s look at the non-instrumented code first.</p>
<p>This is our simple Python Flask application that can receive a GET request. (This is a portion of the full <a href="https://github.com/elastic/observability-examples/blob/main/Elastiflix/python-favorite/main.py">main.py</a> file.)</p>
<pre><code>from flask import Flask, request
import sys

import logging
import redis
import os
import ecs_logging
import datetime
import random
import time

redis_host = os.environ.get('REDIS_HOST') or 'localhost'
redis_port = os.environ.get('REDIS_PORT') or 6379

application_port = os.environ.get('APPLICATION_PORT') or 5000

app = Flask(__name__)

# Get the Logger
logger = logging.getLogger("app")
logger.setLevel(logging.DEBUG)

# Add an ECS formatter to the Handler
handler = logging.StreamHandler()
handler.setFormatter(ecs_logging.StdlibFormatter())
logger.addHandler(handler)
logging.getLogger('werkzeug').setLevel(logging.ERROR)
logging.getLogger('werkzeug').addHandler(handler)

r = redis.Redis(host=redis_host, port=redis_port, decode_responses=True)

@app.route('/favorites', methods=['GET'])
def get_favorite_movies():
    user_id = str(request.args.get('user_id'))

    logger.info('Getting favorites for user ' + user_id, extra={
        "event.dataset": "favorite.log",
        "user.id": request.args.get('user_id')
    })

    favorites = r.smembers(user_id)

    # convert to list
    favorites = list(favorites)
    logger.info('User ' + user_id + ' has favorites: ' + str(favorites), extra={
        "event.dataset": "favorite.log",
        "user.id": user_id
    })
    return { "favorites": favorites}

logger.info('App startup')
app.run(host='0.0.0.0', port=application_port)
logger.info('App Stopped')
</code></pre>
<h2 id="stepbystepguide">Step-by-step guide</h2>
<h3 id="step0logintoyourelasticcloudaccount">Step 0. Log in to your Elastic Cloud account</h3>
<p>This blog assumes you have an Elastic Cloud account — if not, follow the <a href="https://cloud.elastic.co/registration?elektra=en-cloud-page">instructions to get started on Elastic Cloud</a>.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt48e32c5f69261876/6a85cd039d2b71c977f939e0/elastic-blog-2-trial.png" alt="trial" /></p>
<h3 id="step1installandinitializeopentelemetry">Step 1. Install and initialize OpenTelemetry</h3>
<p>As a first step, we’ll need to add some additional libraries to our application.</p>
<pre><code>from opentelemetry import trace
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor

from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter
from opentelemetry.instrumentation.flask import FlaskInstrumentor
from opentelemetry.instrumentation.requests import RequestsInstrumentor
from opentelemetry.instrumentation.redis import RedisInstrumentor
from opentelemetry.sdk.resources import Resource
</code></pre>
<p>This code imports necessary OpenTelemetry libraries, including those for tracing, exporting, and instrumenting specific libraries like Flask, Requests, and Redis.</p>
<p>Next we read the variables:</p>
<pre><code>OTEL_EXPORTER_OTLP_HEADERS
OTEL_EXPORTER_OTLP_ENDPOINT
</code></pre>
<p>And then initialize the exporter.</p>
<pre><code>otel_exporter_otlp_headers = os.environ.get('OTEL_EXPORTER_OTLP_HEADERS')

otel_exporter_otlp_endpoint = os.environ.get('OTEL_EXPORTER_OTLP_ENDPOINT')

exporter = OTLPSpanExporter(endpoint=otel_exporter_otlp_endpoint, headers=otel_exporter_otlp_headers)
</code></pre>
<p>In order to pass additional parameters to OpenTelemetry, we will read the OTEL_RESOURCE_ATTRIBUTES variable and convert it into an object.</p>
<pre><code>resource_attributes = os.environ.get('OTEL_RESOURCE_ATTRIBUTES') or 'service.version=1.0,deployment.environment=production'
key_value_pairs = resource_attributes.split(',')
result_dict = {}

for pair in key_value_pairs:
    key, value = pair.split('=')
    result_dict[key] = value
</code></pre>
<p>Next, we will then use these parameters to populate the resources configuration.</p>
<pre><code>resourceAttributes = {
     "service.name": otel_service_name,
     "service.version": result_dict['service.version'],
     "deployment.environment": result_dict['deployment.environment']
}

resource = Resource.create(resourceAttributes)
</code></pre>
<p>We then set up the trace provider using the previously created resource. The trace provider will allow us to create spans later after getting a tracer instance from it.</p>
<p>Additionally, we specify the use of BatchSPanProcessor. The Span processor is an interface that allows hooks for span start and end method invocations.</p>
<p>In OpenTelemetry, different Span processors are offered. The BatchSPanProcessor batches span and sends them in bulk. Multiple Span processors can be configured to be active at the same time using the MultiSpanProcessor. <a href="https://opentelemetry.io/docs/instrumentation/java/manual/#span-processor">See OpenTelemetry documentation</a>.</p>
<p>Additionally, we added the resource module. This allows us to specify attributes such as service.name, version, and more. See <a href="https://opentelemetry.io/docs/specs/otel/resource/semantic_conventions/#semantic-attributes-with-sdk-provided-default-value">OpenTelemetry semantic conventions documentation</a> for more details.</p>
<pre><code>provider = TracerProvider(resource=resource)
processor = BatchSpanProcessor(exporter)
provider.add_span_processor(processor)

# Sets the global default tracer provider
trace.set_tracer_provider(provider)

# Creates a tracer from the global tracer provider
tracer = trace.get_tracer(otel_service_name)
</code></pre>
<p>Finally, because we are using Flask and Redis, we also add the following, which allows us to automatically instrument both Flask and Redis.</p>
<p>Technically you could consider this “cheating.” We are using some parts of the Python auto-instrumentation. However, it’s generally a good approach to resort to using some of the auto-instrumentation modules. This saves you a lot of time, and in addition, it ensures that functionality like distributed tracing will work automatically for any requests you receive or send.</p>
<pre><code>FlaskInstrumentor().instrument_app(app)
RequestsInstrumentor().instrument()
RedisInstrumentor().instrument()
</code></pre>
<h3 id="step2addingcustomspans">Step 2. Adding Custom Spans</h3>
<p>Now that we have everything added and initialized, we can add custom spans.</p>
<p>If we want to have additional instrumentation for a part of our app, we simply wrap the /favoritesGET function code using Python with:</p>
<pre><code>with tracer.start_as_current_span("add_favorite_movies", set_status_on_exception=True) as span:
        ...
</code></pre>
<p>The wrapped code is as follows:</p>
<pre><code>@app.route('/favorites', methods=['GET'])
def get_favorite_movies():
    # add artificial delay if enabled
    if delay_time &gt; 0:
        time.sleep(max(0, random.gauss(delay_time/1000, delay_time/1000/10)))

    with tracer.start_as_current_span("get_favorite_movies") as span:
        user_id = str(request.args.get('user_id'))

        logger.info('Getting favorites for user ' + user_id, extra={
            "event.dataset": "favorite.log",
            "user.id": request.args.get('user_id')
        })

        favorites = r.smembers(user_id)

        # convert to list
        favorites = list(favorites)
        logger.info('User ' + user_id + ' has favorites: ' + str(favorites), extra={
            "event.dataset": "favorite.log",
            "user.id": user_id
        })
</code></pre>
<p><strong>Additional code</strong></p>
<p>In addition to modules and span instrumentation, the sample application also checks some environment variables at startup. When sending data to Elastic without an OTel collector, the OTEL_EXPORTER_OTLP_HEADERS variable is required as it contains the authentication. The same is true for OTEL_EXPORTER_OTLP_ENDPOINT, the host where we’ll send the telemetry data.</p>
<pre><code>otel_exporter_otlp_headers = os.environ.get('OTEL_EXPORTER_OTLP_HEADERS')
# fail if secret token not set
if otel_exporter_otlp_headers is None:
    raise Exception('OTEL_EXPORTER_OTLP_HEADERS environment variable not set')


otel_exporter_otlp_endpoint = os.environ.get('OTEL_EXPORTER_OTLP_ENDPOINT')
# fail if server url not set
if otel_exporter_otlp_endpoint is None:
    raise Exception('OTEL_EXPORTER_OTLP_ENDPOINT environment variable not set')
else:
    exporter = OTLPSpanExporter(endpoint=otel_exporter_otlp_endpoint, headers=otel_exporter_otlp_headers)
</code></pre>
<p><strong>Final code</strong><br />
For comparison, this is the instrumented code of our sample application. You can find the full source code in <a href="https://github.com/elastic/observability-examples/tree/main/Elastiflix/python-favorite-otel-manual">GitHub</a>.</p>
<pre><code>from flask import Flask, request
import sys

import logging
import redis
import os
import ecs_logging
import datetime
import random
import time

from opentelemetry import trace
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor

#Using grpc exporter since per the instructions in OTel docs this is needed for any endpoint receiving OTLP.

from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter
from opentelemetry.instrumentation.flask import FlaskInstrumentor
from opentelemetry.instrumentation.requests import RequestsInstrumentor
from opentelemetry.instrumentation.redis import RedisInstrumentor
#from opentelemetry.instrumentation.wsgi import OpenTelemetryMiddleware
from opentelemetry.sdk.resources import Resource

redis_host = os.environ.get('REDIS_HOST') or 'localhost'
redis_port = os.environ.get('REDIS_PORT') or 6379
otel_traces_exporter = os.environ.get('OTEL_TRACES_EXPORTER') or 'otlp'
otel_metrics_exporter = os.environ.get('OTEL_TRACES_EXPORTER') or 'otlp'
environment = os.environ.get('ENVIRONMENT') or 'dev'
otel_service_version = os.environ.get('OTEL_SERVICE_VERSION') or '1.0.0'
resource_attributes = os.environ.get('OTEL_RESOURCE_ATTRIBUTES') or 'service.version=1.0,deployment.environment=production'

otel_exporter_otlp_headers = os.environ.get('OTEL_EXPORTER_OTLP_HEADERS')
# fail if secret token not set
if otel_exporter_otlp_headers is None:
    raise Exception('OTEL_EXPORTER_OTLP_HEADERS environment variable not set')
#else:
#    otel_exporter_otlp_fheaders= f"Authorization=Bearer%20{secret_token}"

otel_exporter_otlp_endpoint = os.environ.get('OTEL_EXPORTER_OTLP_ENDPOINT')
# fail if server url not set
if otel_exporter_otlp_endpoint is None:
    raise Exception('OTEL_EXPORTER_OTLP_ENDPOINT environment variable not set')
else:
    exporter = OTLPSpanExporter(endpoint=otel_exporter_otlp_endpoint, headers=otel_exporter_otlp_headers)


key_value_pairs = resource_attributes.split(',')
result_dict = {}

for pair in key_value_pairs:
    key, value = pair.split('=')
    result_dict[key] = value

resourceAttributes = {
     "service.name": result_dict['service.name'],
     "service.version": result_dict['service.version'],
     "deployment.environment": result_dict['deployment.environment']
#     # Add more attributes as needed
}

resource = Resource.create(resourceAttributes)


provider = TracerProvider(resource=resource)
processor = BatchSpanProcessor(exporter)
provider.add_span_processor(processor)

# Sets the global default tracer provider
trace.set_tracer_provider(provider)

# Creates a tracer from the global tracer provider
tracer = trace.get_tracer("favorite")


application_port = os.environ.get('APPLICATION_PORT') or 5000

app = Flask(__name__)


FlaskInstrumentor().instrument_app(app)
#OpenTelemetryMiddleware().instrument()
RequestsInstrumentor().instrument()
RedisInstrumentor().instrument()

#app.wsgi_app = OpenTelemetryMiddleware(app.wsgi_app)

# Get the Logger
logger = logging.getLogger("app")
logger.setLevel(logging.DEBUG)

# Add an ECS formatter to the Handler
handler = logging.StreamHandler()
handler.setFormatter(ecs_logging.StdlibFormatter())
logger.addHandler(handler)
logging.getLogger('werkzeug').setLevel(logging.ERROR)
logging.getLogger('werkzeug').addHandler(handler)

r = redis.Redis(host=redis_host, port=redis_port, decode_responses=True)

@app.route('/favorites', methods=['GET'])
def get_favorite_movies():
    with tracer.start_as_current_span("get_favorite_movies") as span:
        user_id = str(request.args.get('user_id'))

        logger.info('Getting favorites for user ' + user_id, extra={
            "event.dataset": "favorite.log",
            "user.id": request.args.get('user_id')
        })

        favorites = r.smembers(user_id)

        # convert to list
        favorites = list(favorites)
        logger.info('User ' + user_id + ' has favorites: ' + str(favorites), extra={
            "event.dataset": "favorite.log",
            "user.id": user_id
        })
        return { "favorites": favorites}

logger.info('App startup')
app.run(host='0.0.0.0', port=application_port)
logger.info('App Stopped')
</code></pre>
<h3 id="step3runningthedockerimagewithenvironmentvariables">Step 3. Running the Docker image with environment variables</h3>
<p>As specified in the <a href="https://opentelemetry.io/docs/instrumentation/python/automatic/#configuring-the-agent">OTEL documentation</a>, we will use environment variables and pass in the configuration values to enable it to connect with <a href="https://www.elastic.co/guide/en/observability/current/apm-open-telemetry.html">Elastic Observability’s APM server</a>.</p>
<p>Because Elastic accepts OTLP natively, we just need to provide the Endpoint and authentication where the OTEL Exporter needs to send the data, as well as some other environment variables.</p>
<p><strong>Getting Elastic Cloud variables</strong><br />
You can copy the endpoints and token from Kibana<sup>®</sup> under the path <code>/app/home#/tutorial/apm</code>.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blte89b58dfd2d7d759/6a85cd05f9373d290b96f5ca/elastic-blog-3-apm.png" alt="apm agents" /></p>
<p>You will need to copy the following environment variables:</p>
<pre><code>OTEL_EXPORTER_OTLP_ENDPOINT
OTEL_EXPORTER_OTLP_HEADERS
</code></pre>
<p><strong>Build the image</strong></p>
<pre><code>docker build -t  python-otel-manual-image .
</code></pre>
<p><strong>Run the image</strong></p>
<pre><code>docker run \
       -e OTEL_EXPORTER_OTLP_ENDPOINT="&lt;REPLACE WITH OTEL_EXPORTER_OTLP_ENDPOINT&gt;" \
       -e OTEL_EXPORTER_OTLP_HEADERS="Authorization=Bearer &lt;REPLACE WITH TOKEN&gt;" \
       -e OTEL_RESOURCE_ATTRIBUTES="service.version=1.0,deployment.environment=production,service.name=python-favorite-otel-manual" \
       -p 3001:3001 \
       python-otel-manual-image
</code></pre>
<p>You can now issue a few requests in order to generate trace data. Note that these requests are expected to return an error, as this service relies on a connection to Redis that you don’t currently have running. As mentioned before, you can find a more complete example using docker-compose <a href="https://github.com/elastic/observability-examples/tree/main/Elastiflix">here</a>.</p>
<pre><code>curl localhost:500/favorites
# or alternatively issue a request every second

while true; do curl "localhost:5000/favorites"; sleep 1; done;
</code></pre>
<h3 id="step4exploretracesmetricsandlogsinelasticapm">Step 4. Explore traces, metrics, and logs in Elastic APM</h3>
<p>Now that the service is instrumented, you should see the following output in Elastic APM when looking at the transactions section of your Python service:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt2771387c853ec52c/6a85cd0827c5cd50915f7436/elastic-blog-4-graph1.png" alt="graph-1" /></p>
<p>Notice how this is slightly different from the auto-instrumented version, as we now also have our custom span in this view.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltfd6ed8ba3e4fecb9/6a85cd0bf5f1a01dae2ec94d/elastic-blog-5-graph2.png" alt="graph-2" /></p>
<h2 id="isitworthit">Is it worth it?</h2>
<p>This is the million-dollar question. Depending on what level of detail you need, it's potentially necessary to manually instrument. Manual instrumentation lets you add custom spans, custom labels, and metrics where you want or need them. It allows you to get a level of detail that otherwise would not be possible and is oftentimes important for tracking business-specific KPIs.</p>
<p>Your operations, and whether you need to troubleshoot or analyze the performance of specific parts of the code, will dictate when and what to instrument. But it’s helpful to know that you have the option to manually instrument.</p>
<p>If you noticed we didn’t yet instrument metrics, that is another blog. We discussed logs in a <a href="https://www.elastic.co/blog/3-models-logging-opentelemetry-elastic">previous blog</a>.</p>
<h2 id="conclusion">Conclusion</h2>
<p>In this blog, we discussed the following:</p>
<ul>
<li>How to manually instrument Python with OpenTelemetry</li>
<li>How to properly initialize OpenTelemetry and add a custom span</li>
<li>How to easily set the OTLP ENDPOINT and OTLP HEADERS with Elastic without the need for a collector</li>
</ul>
<p>Hopefully, this provides an easy-to-understand walk-through of instrumenting Python with OpenTelemetry and how easy it is to send traces into Elastic.</p>
<blockquote>
  <p>Developer resources:</p>
  <ul>
  <li><a href="https://www.elastic.co/blog/getting-started-opentelemetry-instrumentation-sample-app">Elastiflix application</a>, a guide to instrument different languages with OpenTelemetry</li>
  <li>Python: <a href="https://www.elastic.co/blog/auto-instrumentation-of-python-applications-opentelemetry">Auto-instrumentation</a>, <a href="https://www.elastic.co/observability-labs/blog/manual-instrumentation-python-apps-opentelemetry">Manual-instrumentation</a></li>
  <li>Java: <a href="https://www.elastic.co/blog/auto-instrumentation-of-java-applications-opentelemetry">Auto-instrumentation</a>, <a href="https://www.elastic.co/blog/manual-instrumentation-of-java-applications-opentelemetry">Manual-instrumentation</a></li>
  <li>Node.js: <a href="https://www.elastic.co/blog/auto-instrument-nodejs-applications-opentelemetry">Auto-instrumentation</a>, <a href="https://www.elastic.co/blog/manual-instrumentation-of-nodejs-applications-opentelemetry">Manual-instrumentation</a></li>
  <li>.NET: <a href="https://www.elastic.co/blog/auto-instrumentation-of-net-applications-opentelemetry">Auto-instrumentation</a>, <a href="https://www.elastic.co/blog/manual-instrumentation-of-net-applications-opentelemetry">Manual-instrumentation</a></li>
  <li>Go: <a href="https://elastic.co/blog/manual-instrumentation-of-go-applications-opentelemetry">Manual-instrumentation</a></li>
  <li><a href="https://www.elastic.co/blog/best-practices-instrumenting-opentelemetry">Best practices for instrumenting OpenTelemetry</a></li>
  </ul>
  <p>General configuration and use case resources:</p>
  <ul>
  <li><a href="https://www.elastic.co/blog/opentelemetry-observability">Independence with OpenTelemetry on Elastic</a></li>
  <li><a href="https://www.elastic.co/blog/implementing-kubernetes-observability-security-opentelemetry">Modern observability and security on Kubernetes with Elastic and OpenTelemetry</a></li>
  <li><a href="https://www.elastic.co/blog/3-models-logging-opentelemetry-elastic">3 models for logging with OpenTelemetry and Elastic</a></li>
  <li><a href="https://www.elastic.co/blog/adding-free-and-open-elastic-apm-as-part-of-your-elastic-observability-deployment">Adding free and open Elastic APM as part of your Elastic Observability deployment</a></li>
  <li><a href="https://www.elastic.co/blog/custom-metrics-app-code-java-agent-plugin">Capturing custom metrics through OpenTelemetry API in code with Elastic</a></li>
  <li><a href="https://www.elastic.co/virtual-events/future-proof-your-observability-platform-with-opentelemetry-and-elastic">Future-proof your observability platform with OpenTelemetry and Elastic</a></li>
  <li><a href="https://www.elastic.co/blog/kubernetes-k8s-observability-elasticsearch-cncf">Elastic Observability: Built for open technologies like Kubernetes, OpenTelemetry, Prometheus, Istio, and more</a></li>
  </ul>
</blockquote>
<p>Don’t have an Elastic Cloud account yet? <a href="https://cloud.elastic.co/registration">Sign up for Elastic Cloud</a> and try out the auto-instrumentation capabilities that I discussed above. I would be interested in getting your feedback about your experience in gaining visibility into your application stack with Elastic</p>
<p><em>The release and timing of any features or functionality described in this post remain at Elastic's sole discretion. Any features or functionality not currently available may not be delivered on time or at all.</em></p>]]></content:encoded>
    <link>https://www.elastic.co/observability-labs/blog/manual-instrumentation-python-apps-opentelemetry</link>
    <guid isPermaLink="false">manual-instrumentation-python-apps-opentelemetry</guid>
    <category><![CDATA[OpenTelemetry]]></category>
    <category><![CDATA[APM]]></category>
    <category><![CDATA[Infrastructure Monitoring]]></category>
    <dc:creator><![CDATA[Bahubali Shetti]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltc68eb6cebdc0eb2e/6a85cd0e342d69087121b12d/observability-launch-series-2-python-manual_(1).jpg" length="0" type="image/jpeg"/>
    <pubDate>Thu, 31 Aug 2023 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Manual instrumentation with OpenTelemetry for Node.js applications]]></title>
    <description><![CDATA[In this blog post, we will show you how to manually instrument Node.js applications using OpenTelemetry. We will explore how to use the proper OpenTelemetry Node.js libraries and in particular work on instrumenting tracing in a Node.js application.]]></description>
    <content:encoded><![CDATA[<p>DevOps and SRE teams are transforming the process of software development. While DevOps engineers focus on efficient software applications and service delivery, SRE teams are key to ensuring reliability, scalability, and performance. These teams must rely on a full-stack observability solution that allows them to manage and monitor systems and ensure issues are resolved before they impact the business.</p>
<p>Observability across the entire stack of modern distributed applications requires data collection, processing, and correlation often in the form of dashboards. Ingesting all system data requires installing agents across stacks, frameworks, and providers — a process that can be challenging and time-consuming for teams who have to deal with version changes, compatibility issues, and proprietary code that doesn't scale as systems change.</p>
<p>Thanks to <a href="http://opentelemetry.io">OpenTelemetry</a> (OTel), DevOps and SRE teams now have a standard way to collect and send data that doesn't rely on proprietary code and have a large support community reducing vendor lock-in.</p>
<p>In a <a href="https://www.elastic.co/blog/opentelemetry-observability">previous blog</a>, we also reviewed how to use the <a href="https://github.com/elastic/opentelemetry-demo">OpenTelemetry demo</a> and connect it to Elastic<sup>®</sup>, as well as some of Elastic’s capabilities with OpenTelemetry and Kubernetes.</p>
<p>In this blog, we will show how to use <a href="https://opentelemetry.io/docs/instrumentation/java/manual/">manual instrumentation for OpenTelemetry</a> with the Node.js service of our <a href="https://github.com/elastic/observability-examples">application called Elastiflix</a>. This approach is slightly more complex than using <a href="https://www.elastic.co/blog/auto-instrument-nodejs-applications-opentelemetry">auto-instrumentation</a>.</p>
<p>The beauty of this is that there is <strong>no need for the otel-collector</strong>! This setup enables you to slowly and easily migrate an application to OTel with Elastic according to a timeline that best fits your business.</p>
<h2 id="applicationprerequisitesandconfig">Application, prerequisites, and config</h2>
<p>The application that we use for this blog is called <a href="https://github.com/elastic/observability-examples">Elastiflix</a>, a movie streaming application. It consists of several micro-services written in .NET, NodeJS, Go, and Python.</p>
<p>Before we instrument our sample application, we will first need to understand how Elastic can receive the telemetry data.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt3fe9d71f697e142b/6a85ccef9829261daa58392e/elastic-blog-1-config.png" alt="Configuration" /></p>
<p>All of Elastic Observability’s APM capabilities are available with OTel data. Some of these include:</p>
<ul>
<li>Service maps</li>
<li>Service details (latency, throughput, failed transactions)</li>
<li>Dependencies between services, distributed tracing</li>
<li>Transactions (traces)</li>
<li>Machine learning (ML) correlations</li>
<li>Log correlation</li>
</ul>
<p>In addition to Elastic’s APM and a unified view of the telemetry data, you will also be able to use Elastic’s powerful machine learning capabilities to reduce the analysis, and alerting to help reduce MTTR.</p>
<h2 id="prerequisites">Prerequisites</h2>
<ul>
<li>An Elastic Cloud account — <a href="https://cloud.elastic.co/">sign up now</a></li>
<li>A clone of the <a href="https://github.com/elastic/observability-examples">Elastiflix demo application</a>, or your own Node.js application</li>
<li>Basic understanding of Docker — potentially install <a href="https://www.docker.com/products/docker-desktop/">Docker Desktop</a></li>
<li>Basic understanding of Node.js</li>
</ul>
<h2 id="viewtheexamplesourcecode">View the example source code</h2>
<p>The full source code, including the Dockerfile used in this blog, can be found on <a href="https://github.com/elastic/observability-examples/tree/main/Elastiflix/node-server-otel-manual">GitHub</a>. The repository also contains the <a href="https://github.com/elastic/observability-examples/tree/main/Elastiflix/node-server">same application without instrumentation</a>. This allows you to compare each file and see the differences.</p>
<p>Before we begin, let’s look at the non-instrumented code first.</p>
<p>This is our simple index.js file that can receive a POST request. See the full code <a href="https://github.com/elastic/observability-examples/blob/main/Elastiflix/node-server-otel-manual/index.js">here</a>.</p>
<pre><code>const pino = require("pino");
const ecsFormat = require("@elastic/ecs-pino-format"); //
const log = pino({ ...ecsFormat({ convertReqRes: true }) });
const expressPino = require("express-pino-logger")({ logger: log });

var API_ENDPOINT_FAVORITES =
  process.env.API_ENDPOINT_FAVORITES || "127.0.0.1:5000";
API_ENDPOINT_FAVORITES = API_ENDPOINT_FAVORITES.split(",");

const express = require("express");
const cors = require("cors")({ origin: true });
const cookieParser = require("cookie-parser");
const { json } = require("body-parser");

const PORT = process.env.PORT || 3001;

const app = express().use(cookieParser(), cors, json(), expressPino);

const axios = require("axios");

app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use((err, req, res, next) =&gt; {
  log.error(err.stack);
  res.status(500).json({ error: err.message, code: err.code });
});

var favorites = {};

app.post("/api/favorites", (req, res) =&gt; {
  var randomIndex = Math.floor(Math.random() * API_ENDPOINT_FAVORITES.length);
  if (process.env.THROW_NOT_A_FUNCTION_ERROR == "true" &amp;&amp; Math.random() &lt; 0.5) {
    // randomly choose one of the endpoints
    axios
      .post(
        "http://" +
          API_ENDPOINT_FAVORITES[randomIndex] +
          "/favorites?user_id=1",
        req.body
      )
      .then(function (response) {
        favorites = response.data;
        // quiz solution: "42"
        res.jsonn({ favorites: favorites });
      })
      .catch(function (error) {
        res.json({ error: error, favorites: [] });
      });
  } else {
    axios
      .post(
        "http://" +
          API_ENDPOINT_FAVORITES[randomIndex] +
          "/favorites?user_id=1",
        req.body
      )
      .then(function (response) {
        favorites = response.data;
        res.json({ favorites: favorites });
      })
      .catch(function (error) {
        res.json({ error: error, favorites: [] });
      });
  }
});

app.listen(PORT, () =&gt; {
  console.log(`Server listening on ${PORT}`);
});
</code></pre>
<h2 id="stepbystepguide">Step-by-step guide</h2>
<h3 id="step0logintoyourelasticcloudaccount">Step 0. Log in to your Elastic Cloud account</h3>
<p>This blog assumes you have an Elastic Cloud account — if not, follow the <a href="https://cloud.elastic.co/registration?elektra=en-cloud-page">instructions to get started on Elastic Cloud</a>.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt211a6288bba0d038/6a85ccf2d7b2e7ea7dfe8522/elastic-blog-2-trial.png" alt="trial" /></p>
<h3 id="step1installandinitializeopentelemetry">Step 1. Install and initialize OpenTelemetry</h3>
<p>As a first step, we’ll need to add some additional modules to our application.</p>
<pre><code>const opentelemetry = require("@opentelemetry/api");
const { NodeTracerProvider } = require("@opentelemetry/sdk-trace-node");
const { BatchSpanProcessor } = require("@opentelemetry/sdk-trace-base");
const { Resource } = require("@opentelemetry/resources");
const {
  SemanticResourceAttributes,
} = require("@opentelemetry/semantic-conventions");

const { registerInstrumentations } = require("@opentelemetry/instrumentation");
const { HttpInstrumentation } = require("@opentelemetry/instrumentation-http");
const {
  ExpressInstrumentation,
} = require("@opentelemetry/instrumentation-express");
</code></pre>
<p>We start by creating a collectorOptions object with parameters such as the url and headers for connecting to the Elastic APM Server or OpenTelemetry collector.</p>
<pre><code>const collectorOptions = {
  url: OTEL_EXPORTER_OTLP_ENDPOINT,
  headers: OTEL_EXPORTER_OTLP_HEADERS,
};
</code></pre>
<p>In order to pass additional parameters to OpenTelemetry, we will read the OTEL_RESOURCE_ATTRIBUTES variable and convert it into an object.</p>
<pre><code>const envAttributes = process.env.OTEL_RESOURCE_ATTRIBUTES || "";

// Parse the environment variable string into an object
const attributes = envAttributes.split(",").reduce((acc, curr) =&gt; {
  const [key, value] = curr.split("=");
  if (key &amp;&amp; value) {
    acc[key.trim()] = value.trim();
  }
  return acc;
}, {});
</code></pre>
<p>Next we will then use these parameters to populate the resources configuration.</p>
<pre><code>const resource = new Resource({
  [SemanticResourceAttributes.SERVICE_NAME]:
    attributes["service.name"] || "node-server-otel-manual",
  [SemanticResourceAttributes.SERVICE_VERSION]:
    attributes["service.version"] || "1.0.0",
  [SemanticResourceAttributes.DEPLOYMENT_ENVIRONMENT]:
    attributes["deployment.environment"] || "production",
});
</code></pre>
<p>We then set up the trace provider using the previously created resource, followed by the exporter which takes the collectorOptions from before. The trace provider will allow us to create spans later.</p>
<p>Additionally, we specify the use of BatchSPanProcessor. The Span processor is an interface that allows hooks for span start and end method invocations.</p>
<p>In OpenTelemetry, different Span processors are offered. The BatchSPanProcessor batches span and sends them in bulk. Multiple Span processors can be configured to be active at the same time using the MultiSpanProcessor. <a href="https://opentelemetry.io/docs/instrumentation/java/manual/#span-processor">See OpenTelemetry documentation</a>.</p>
<p>Additionally, we added the resource module. This allows us to specify attributes such as service.name, version, and more. See <a href="https://opentelemetry.io/docs/specs/otel/resource/semantic_conventions/#semantic-attributes-with-sdk-provided-default-value">OpenTelemetry semantic conventions documentation</a> for more details.</p>
<pre><code>const tracerProvider = new NodeTracerProvider({
  resource: resource,
});

const exporter = new OTLPTraceExporter(collectorOptions);
tracerProvider.addSpanProcessor(new BatchSpanProcessor(exporter));
tracerProvider.register();
</code></pre>
<p>Next, we are going to register some instrumentations. This will automatically instrument Express and HTTP for us. While it’s possible to do this step fully manually as well, it would be complex and a waste of time. This way we can ensure that any incoming and outgoing request is captured properly and that functionality such as distributed tracing works without any additional work.</p>
<pre><code>registerInstrumentations({
  instrumentations: [new HttpInstrumentation(), new ExpressInstrumentation()],
  tracerProvider: tracerProvider,
});
</code></pre>
<p>As a last step, we will now get an instance of the tracer that we can use to create custom spans.</p>
<pre><code>const tracer = opentelemetry.trace.getTracer();
</code></pre>
<h3 id="step2addingcustomspans">Step 2. Adding custom spans</h3>
<p>Now that we have the modules added and initialized, we can add custom spans.</p>
<p>Our sample application has a POST request which calls a downstream service. If we want to have additional instrumentation for this part of our app, we simply wrap the function code with:</p>
<pre><code>tracer.startActiveSpan('favorites',   tracer.startActiveSpan('favorites', (span) =&gt; {...
</code></pre>
<p>The wrapped code is as follows:</p>
<pre><code>app.post("/api/favorites", (req, res, next) =&gt; {
  tracer.startActiveSpan("favorites", (span) =&gt; {
    axios
      .post(
        "http://" + API_ENDPOINT_FAVORITES + "/favorites?user_id=1",
        req.body
      )
      .then(function (response) {
        favorites = response.data;
        span.end();
        res.jsonn({ favorites: favorites });
      })
      .catch(next);
  });
});
</code></pre>
<p><strong>Automatic error handling</strong><br />
For automatic error handling, we are adding a function that we use in Express which captures the exception for any error that happens during runtime.</p>
<pre><code>app.use((err, req, res, next) =&gt; {
  log.error(err.stack);
  span = opentelemetry.trace.getActiveSpan();
  span.recordException(error);
  span.end();
  res.status(500).json({ error: err.message, code: err.code });
});
</code></pre>
<p><strong>Additional code</strong><br />
n addition to modules and span instrumentation, the sample application also checks some environment variables at startup. When sending data to Elastic without an OTel collector, the OTEL_EXPORTER_OTLP_HEADERS variable is required as it contains the authentication. The same is true for OTEL_EXPORTER_OTLP_ENDPOINT, the host where we’ll send the telemetry data.</p>
<pre><code>const OTEL_EXPORTER_OTLP_HEADERS = process.env.OTEL_EXPORTER_OTLP_HEADERS;
// error if secret token is not set
if (!OTEL_EXPORTER_OTLP_HEADERS) {
  throw new Error("OTEL_EXPORTER_OTLP_HEADERS environment variable is not set");
}

const OTEL_EXPORTER_OTLP_ENDPOINT = process.env.OTEL_EXPORTER_OTLP_ENDPOINT;
// error if server url is not set
if (!OTEL_EXPORTER_OTLP_ENDPOINT) {
  throw new Error(
    "OTEL_EXPORTER_OTLP_ENDPOINT environment variable is not set"
  );
}
</code></pre>
<p><strong>Final code</strong><br />
For comparison, this is the instrumented code of our sample application. You can find the full source code in <a href="https://github.com/elastic/observability-examples/tree/main/Elastiflix/node-server-otel-manual">GitHub</a>.</p>
<pre><code>const pino = require("pino");
const ecsFormat = require("@elastic/ecs-pino-format"); //
const log = pino({ ...ecsFormat({ convertReqRes: true }) });
const expressPino = require("express-pino-logger")({ logger: log });

// Add OpenTelemetry packages
const opentelemetry = require("@opentelemetry/api");
const { NodeTracerProvider } = require("@opentelemetry/sdk-trace-node");
const { BatchSpanProcessor } = require("@opentelemetry/sdk-trace-base");
const {
  OTLPTraceExporter,
} = require("@opentelemetry/exporter-trace-otlp-grpc");
const { Resource } = require("@opentelemetry/resources");
const {
  SemanticResourceAttributes,
} = require("@opentelemetry/semantic-conventions");

const { registerInstrumentations } = require("@opentelemetry/instrumentation");

// Import OpenTelemetry instrumentations
const { HttpInstrumentation } = require("@opentelemetry/instrumentation-http");
const {
  ExpressInstrumentation,
} = require("@opentelemetry/instrumentation-express");

var API_ENDPOINT_FAVORITES =
  process.env.API_ENDPOINT_FAVORITES || "127.0.0.1:5000";
API_ENDPOINT_FAVORITES = API_ENDPOINT_FAVORITES.split(",");

const OTEL_EXPORTER_OTLP_HEADERS = process.env.OTEL_EXPORTER_OTLP_HEADERS;
// error if secret token is not set
if (!OTEL_EXPORTER_OTLP_HEADERS) {
  throw new Error("OTEL_EXPORTER_OTLP_HEADERS environment variable is not set");
}

const OTEL_EXPORTER_OTLP_ENDPOINT = process.env.OTEL_EXPORTER_OTLP_ENDPOINT;
// error if server url is not set
if (!OTEL_EXPORTER_OTLP_ENDPOINT) {
  throw new Error(
    "OTEL_EXPORTER_OTLP_ENDPOINT environment variable is not set"
  );
}

const collectorOptions = {
  // url is optional and can be omitted - default is http://localhost:4317
  // Unix domain sockets are also supported: 'unix:///path/to/socket.sock'
  url: OTEL_EXPORTER_OTLP_ENDPOINT,
  headers: OTEL_EXPORTER_OTLP_HEADERS,
};

const envAttributes = process.env.OTEL_RESOURCE_ATTRIBUTES || "";

// Parse the environment variable string into an object
const attributes = envAttributes.split(",").reduce((acc, curr) =&gt; {
  const [key, value] = curr.split("=");
  if (key &amp;&amp; value) {
    acc[key.trim()] = value.trim();
  }
  return acc;
}, {});

// Create and configure the resource object
const resource = new Resource({
  [SemanticResourceAttributes.SERVICE_NAME]:
    attributes["service.name"] || "node-server-otel-manual",
  [SemanticResourceAttributes.SERVICE_VERSION]:
    attributes["service.version"] || "1.0.0",
  [SemanticResourceAttributes.DEPLOYMENT_ENVIRONMENT]:
    attributes["deployment.environment"] || "production",
});

// Create and configure the tracer provider
const tracerProvider = new NodeTracerProvider({
  resource: resource,
});
const exporter = new OTLPTraceExporter(collectorOptions);
tracerProvider.addSpanProcessor(new BatchSpanProcessor(exporter));
tracerProvider.register();

//Register instrumentations
registerInstrumentations({
  instrumentations: [new HttpInstrumentation(), new ExpressInstrumentation()],
  tracerProvider: tracerProvider,
});

const express = require("express");
const cors = require("cors")({ origin: true });
const cookieParser = require("cookie-parser");
const { json } = require("body-parser");

const PORT = process.env.PORT || 3001;

const app = express().use(cookieParser(), cors, json(), expressPino);

const axios = require("axios");

app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use((err, req, res, next) =&gt; {
  log.error(err.stack);
  span = opentelemetry.trace.getActiveSpan();
  span.recordException(error);
  span.end();
  res.status(500).json({ error: err.message, code: err.code });
});

const tracer = opentelemetry.trace.getTracer();

var favorites = {};

app.post("/api/favorites", (req, res, next) =&gt; {
  tracer.startActiveSpan("favorites", (span) =&gt; {
    var randomIndex = Math.floor(Math.random() * API_ENDPOINT_FAVORITES.length);

    if (
      process.env.THROW_NOT_A_FUNCTION_ERROR == "true" &amp;&amp;
      Math.random() &lt; 0.5
    ) {
      // randomly choose one of the endpoints
      axios
        .post(
          "http://" +
            API_ENDPOINT_FAVORITES[randomIndex] +
            "/favorites?user_id=1",
          req.body
        )
        .then(function (response) {
          favorites = response.data;
          // quiz solution: "42"
          span.end();
          res.jsonn({ favorites: favorites });
        })
        .catch(next);
    } else {
      axios
        .post(
          "http://" +
            API_ENDPOINT_FAVORITES[randomIndex] +
            "/favorites?user_id=1",
          req.body
        )
        .then(function (response) {
          favorites = response.data;
          span.end();
          res.json({ favorites: favorites });
        })
        .catch(next);
    }
  });
});

app.listen(PORT, () =&gt; {
  log.info(`Server listening on ${PORT}`);
});
</code></pre>
<h3 id="step3runningthedockerimagewithenvironmentvariables">Step 3. Running the Docker image with environment variables</h3>
<p>We will use environment variables and pass in the configuration values to enable it to connect with <a href="https://www.elastic.co/guide/en/observability/current/apm-open-telemetry.html">Elastic Observability’s APM server</a>.</p>
<p>Because Elastic accepts OTLP natively, we just need to provide the Endpoint and authentication where the OTEL Exporter needs to send the data, as well as some other environment variables.</p>
<p><strong>Getting Elastic Cloud variables</strong><br />
You can copy the endpoints and token from Kibana<sup>®</sup> under the path <code>/app/home#/tutorial/apm</code>.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt118d2bf7fe9c04d2/6a85ccf580984cd3c2669010/elastic-blog-3-apm.png" alt="apm" /></p>
<p>You will need to copy the following environment variables:</p>
<pre><code>OTEL_EXPORTER_OTLP_ENDPOINT
OTEL_EXPORTER_OTLP_HEADERS
</code></pre>
<p><strong>Build the image</strong></p>
<pre><code>docker build -t  node-otel-manual-image .
</code></pre>
<p><strong>Run the image</strong></p>
<pre><code>docker run \
       -e OTEL_EXPORTER_OTLP_ENDPOINT="&lt;REPLACE WITH OTEL_EXPORTER_OTLP_ENDPOINT&gt;" \
       -e OTEL_EXPORTER_OTLP_HEADERS="Authorization=Bearer &lt;REPLACE WITH TOKEN&gt;" \
       -e OTEL_RESOURCE_ATTRIBUTES="service.version=1.0,deployment.environment=production,service.name=node-server-otel-manual" \
       -p 3001:3001 \
       node-otel-manual-image
</code></pre>
<p>You can now issue a few requests in order to generate trace data. Note that these requests are expected to return an error, as this service relies on some downstream services that you may not have running on your machine.</p>
<pre><code>curl localhost:3001/api/login
curl localhost:3001/api/favorites

# or alternatively issue a request every second

while true; do curl "localhost:3001/api/favorites"; sleep 1; done;
</code></pre>
<h3 id="step4exploreinelasticapm">Step 4. Explore in Elastic APM</h3>
<p>Now that the service is instrumented, you should see the following output in Elastic APM when looking at the transactions section of your Node.js service:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt9df327136c5c23fe/6a85ccf89a32f10f50a7e022/elastic-blog-4-graphs.png" alt="graphs" /></p>
<p>Notice how this mirrors the auto-instrumented version.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt9df327136c5c23fe/6a85ccf89a32f10f50a7e022/elastic-blog-4-graphs.png" alt="graphs-2" /></p>
<h2 id="isitworthit">Is it worth it?</h2>
<p>This is the million-dollar question. Depending on what level of detail you need, it's potentially necessary to manually instrument. Manual instrumentation lets you add custom spans, custom labels, and metrics where you want or need them. It allows you to get a level of detail that otherwise would not be possible and is oftentimes important for tracking business-specific KPIs.</p>
<p>Your operations, and whether you need to troubleshoot or analyze the performance of specific parts of the code, will dictate when and what to instrument. But it’s helpful to know that you have the option to manually instrument.</p>
<p>If you noticed we didn’t yet instrument metrics, that is another blog. We discussed logs in a <a href="https://www.elastic.co/blog/3-models-logging-opentelemetry-elastic">previous blog</a>.</p>
<h2 id="conclusion">Conclusion</h2>
<p>In this blog, we discussed the following:</p>
<ul>
<li>How to manually instrument Node.js with OpenTelemetry</li>
<li>The different modules needed when using Express</li>
<li>How to properly initialize and instrument span</li>
<li>How to easily set the OTLP ENDPOINT and OTLP HEADERS from Elastic without the need for a collector</li>
</ul>
<p>Hopefully, this provides an easy-to-understand walk-through of instrumenting Node.js with OpenTelemetry and how easy it is to send traces into Elastic.</p>
<blockquote>
  <p>Developer resources:</p>
  <ul>
  <li><a href="https://www.elastic.co/blog/getting-started-opentelemetry-instrumentation-sample-app">Elastiflix application</a>, a guide to instrument different languages with OpenTelemetry</li>
  <li>Python: <a href="https://www.elastic.co/blog/auto-instrumentation-of-python-applications-opentelemetry">Auto-instrumentation</a>, <a href="https://www.elastic.co/blog/manual-instrumentation-of-python-applications-opentelemetry">Manual-instrumentation</a></li>
  <li>Java: <a href="https://www.elastic.co/blog/auto-instrumentation-of-java-applications-opentelemetry">Auto-instrumentation</a>, <a href="https://www.elastic.co/blog/manual-instrumentation-of-java-applications-opentelemetry">Manual-instrumentation</a></li>
  <li>Node.js: <a href="https://www.elastic.co/blog/auto-instrument-nodejs-applications-opentelemetry">Auto-instrumentation</a>, <a href="https://www.elastic.co/observability-labs/blog/manual-instrumentation-nodejs-apps-opentelemetry">Manual-instrumentation</a></li>
  <li>.NET: <a href="https://www.elastic.co/blog/auto-instrumentation-of-net-applications-opentelemetry">Auto-instrumentation</a>, <a href="https://www.elastic.co/blog/manual-instrumentation-of-net-applications-opentelemetry">Manual-instrumentation</a></li>
  <li>Go: <a href="https://elastic.co/blog/manual-instrumentation-of-go-applications-opentelemetry">Manual-instrumentation</a></li>
  <li><a href="https://www.elastic.co/blog/best-practices-instrumenting-opentelemetry">Best practices for instrumenting OpenTelemetry</a></li>
  </ul>
  <p>General configuration and use case resources:</p>
  <ul>
  <li><a href="https://www.elastic.co/blog/opentelemetry-observability">Independence with OpenTelemetry on Elastic</a></li>
  <li><a href="https://www.elastic.co/blog/implementing-kubernetes-observability-security-opentelemetry">Modern observability and security on Kubernetes with Elastic and OpenTelemetry</a></li>
  <li><a href="https://www.elastic.co/blog/3-models-logging-opentelemetry-elastic">3 models for logging with OpenTelemetry and Elastic</a></li>
  <li><a href="https://www.elastic.co/blog/adding-free-and-open-elastic-apm-as-part-of-your-elastic-observability-deployment">Adding free and open Elastic APM as part of your Elastic Observability deployment</a></li>
  <li><a href="https://www.elastic.co/blog/custom-metrics-app-code-java-agent-plugin">Capturing custom metrics through OpenTelemetry API in code with Elastic</a></li>
  <li><a href="https://www.elastic.co/virtual-events/future-proof-your-observability-platform-with-opentelemetry-and-elastic">Future-proof your observability platform with OpenTelemetry and Elastic</a></li>
  <li><a href="https://www.elastic.co/blog/kubernetes-k8s-observability-elasticsearch-cncf">Elastic Observability: Built for open technologies like Kubernetes, OpenTelemetry, Prometheus, Istio, and more</a></li>
  </ul>
</blockquote>
<p>Don’t have an Elastic Cloud account yet? <a href="https://cloud.elastic.co/registration">Sign up for Elastic Cloud</a> and try out the auto-instrumentation capabilities that I discussed above. I would be interested in getting your feedback about your experience in gaining visibility into your application stack with Elastic.</p>
<p><em>The release and timing of any features or functionality described in this post remain at Elastic's sole discretion. Any features or functionality not currently available may not be delivered on time or at all.</em></p>]]></content:encoded>
    <link>https://www.elastic.co/observability-labs/blog/manual-instrumentation-nodejs-apps-opentelemetry</link>
    <guid isPermaLink="false">manual-instrumentation-nodejs-apps-opentelemetry</guid>
    <category><![CDATA[OpenTelemetry]]></category>
    <category><![CDATA[APM]]></category>
    <category><![CDATA[Infrastructure Monitoring]]></category>
    <dc:creator><![CDATA[Bahubali Shetti]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt20f27145e9c4a798/6a85ccfc9bf994191f0a05a9/observability-launch-series-1-node-js-manual_(1).jpg" length="0" type="image/jpeg"/>
    <pubDate>Thu, 31 Aug 2023 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Manual instrumentation of Java applications with OpenTelemetry]]></title>
    <description><![CDATA[OpenTelemetry provides an observability framework for cloud-native software, allowing us to trace, monitor, and debug applications seamlessly. In this post, we'll explore how to manually instrument a Java application using OpenTelemetry.]]></description>
    <content:encoded><![CDATA[<p>In the fast-paced universe of software development, especially in the cloud-native realm, DevOps and SRE teams are increasingly emerging as essential partners in application stability and growth.</p>
<p>DevOps engineers continuously optimize software delivery, while SRE teams act as the stewards of application reliability, scalability, and top-tier performance. The challenge? These teams require a cutting-edge observability solution, one that encompasses full-stack insights, empowering them to rapidly manage, monitor, and rectify potential disruptions before they culminate into operational challenges.</p>
<p>Observability in our modern distributed software ecosystem goes beyond mere monitoring—it demands limitless data collection, precision in processing, and the correlation of this data into actionable insights. However, the road to achieving this holistic view is paved with obstacles: from navigating version incompatibilities to wrestling with restrictive proprietary code.</p>
<p>Enter <a href="https://opentelemetry.io/">OpenTelemetry (OTel)</a>, with the following benefits for those who adopt it:</p>
<ul>
<li>Escape vendor constraints with OTel, freeing yourself from vendor lock-in and ensuring top-notch observability.</li>
<li>See the harmony of unified logs, metrics, and traces come together to provide a complete system view.</li>
<li>Improve your application oversight through richer and enhanced instrumentations.</li>
<li>Embrace the benefits of backward compatibility to protect your prior instrumentation investments.</li>
<li>Embark on the OpenTelemetry journey with an easy learning curve, simplifying onboarding and scalability.</li>
<li>Rely on a proven, future-ready standard to boost your confidence in every investment.</li>
</ul>
<p>In this blog, we will explore how you can use <a href="https://opentelemetry.io/docs/instrumentation/java/manual/">manual instrumentation in your Java</a> application using Docker, without the need to refactor any part of your application code. We will use an <a href="https://github.com/elastic/observability-examples">application called Elastiflix</a>. This approach is slightly more complex than using <a href="https://www.elastic.co/blog/auto-instrumentation-of-java-applications-opentelemetry">automatic instrumentation</a>.</p>
<p>The beauty of this is that there is <strong>no need for the otel-collector</strong>! This setup enables you to slowly and easily migrate an application to OTel with Elastic according to a timeline that best fits your business.</p>
<h2 id="applicationprerequisitesandconfig">Application, prerequisites, and config</h2>
<p>The application that we use for this blog is called <a href="https://github.com/elastic/observability-examples">Elastiflix</a>, a movie streaming application. It consists of several micro-services written in .NET, NodeJS, Go, and Python.</p>
<p>Before we instrument our sample application, we will first need to understand how Elastic can receive the telemetry data.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt1a6d077c474076c7/6a85ccc2501a859004fbb36b/elastic-blog-1-config.png" alt="Elastic configuration options for OpenTelemetry" /></p>
<p>All of Elastic Observability’s APM capabilities are available with OTel data. Some of these include:</p>
<ul>
<li>Service maps</li>
<li>Service details (latency, throughput, failed transactions)</li>
<li>Dependencies between services, distributed tracing</li>
<li>Transactions (traces)</li>
<li>Machine learning (ML) correlations</li>
<li>Log correlation</li>
</ul>
<p>In addition to Elastic’s APM and a unified view of the telemetry data, you will also be able to use Elastic’s powerful machine learning capabilities to reduce the analysis, and alerting to help reduce MTTR.</p>
<h2 id="prerequisites">Prerequisites</h2>
<ul>
<li>An Elastic Cloud account — <a href="https://cloud.elastic.co/">sign up now</a></li>
<li>A clone of the <a href="https://github.com/elastic/observability-examples">Elastiflix demo application</a>, or your own Java application</li>
<li>Basic understanding of Docker — potentially install <a href="https://www.docker.com/products/docker-desktop/">Docker Desktop</a></li>
<li>Basic understanding of Java</li>
</ul>
<h2 id="viewtheexamplesourcecode">View the example source code</h2>
<p>The full source code, including the Dockerfile used in this blog, can be found on <a href="https://github.com/elastic/observability-examples/tree/main/Elastiflix/python-favorite-otel-auto">GitHub</a>. The repository also contains the <a href="https://github.com/elastic/observability-examples/tree/main/Elastiflix/python-favorite">same application without instrumentation</a>. This allows you to compare each file and see the differences.</p>
<p>In particular, we will be working through the following file:</p>
<pre><code>Elastiflix/java-favorite/src/main/java/com/movieapi/ApiServlet.java
</code></pre>
<p>The following steps will show you how to instrument this application and run it on the command line or in Docker. If you are interested in a more complete OTel example, take a look at the docker-compose file <a href="https://github.com/elastic/observability-examples/tree/main#start-the-app">here</a>, which will bring up the full project.</p>
<p>Before we begin, let’s look at the non-instrumented code first.</p>
<h2 id="stepbystepguide">Step-by-step guide</h2>
<h3 id="step0logintoyourelasticcloudaccount">Step 0. Log in to your Elastic Cloud account</h3>
<p>This blog assumes you have an Elastic Cloud account — if not, follow the <a href="https://cloud.elastic.co/registration?elektra=en-cloud-page">instructions to get started on Elastic Cloud</a>.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt593b4c58f88c6eda/6a85ccc5982926f0f6583926/elastic-blog-2-trial.png" alt="trial" /></p>
<h3 id="step1setupopentelemetry">Step 1. Set up OpenTelemetry</h3>
<p>The first step is to set up the OpenTelemetry SDK in your Java application. You can start by adding the OpenTelemetry Java SDK and its dependencies to your project's build file, such as Maven or Gradle. In our example application, we are using Maven. Add the dependencies below to your pom.xml:</p>
<pre><code>&lt;dependency&gt;
      &lt;groupId&gt;io.opentelemetry.instrumentation&lt;/groupId&gt;
      &lt;artifactId&gt;opentelemetry-logback-mdc-1.0&lt;/artifactId&gt;
      &lt;version&gt;1.25.1-alpha&lt;/version&gt;
    &lt;/dependency&gt;

    &lt;dependency&gt;
      &lt;groupId&gt;io.opentelemetry&lt;/groupId&gt;
      &lt;artifactId&gt;opentelemetry-api&lt;/artifactId&gt;
    &lt;/dependency&gt;
    &lt;dependency&gt;
      &lt;groupId&gt;io.opentelemetry&lt;/groupId&gt;
      &lt;artifactId&gt;opentelemetry-sdk&lt;/artifactId&gt;
    &lt;/dependency&gt;
    &lt;dependency&gt;
      &lt;groupId&gt;io.opentelemetry&lt;/groupId&gt;
      &lt;artifactId&gt;opentelemetry-exporter-otlp&lt;/artifactId&gt;
    &lt;/dependency&gt;
    &lt;dependency&gt;
      &lt;groupId&gt;io.opentelemetry&lt;/groupId&gt;
      &lt;artifactId&gt;opentelemetry-semconv&lt;/artifactId&gt;
    &lt;/dependency&gt;
    &lt;dependency&gt;
      &lt;groupId&gt;io.opentelemetry&lt;/groupId&gt;
      &lt;artifactId&gt;opentelemetry-exporter-otlp-logs&lt;/artifactId&gt;
    &lt;/dependency&gt;
    &lt;dependency&gt;
      &lt;groupId&gt;io.opentelemetry.instrumentation&lt;/groupId&gt;
      &lt;artifactId&gt;opentelemetry-logback-appender-1.0&lt;/artifactId&gt;
      &lt;version&gt;1.25.1-alpha&lt;/version&gt;
    &lt;/dependency&gt;
</code></pre>
<p>And add the following bill of materials from OpenTelemetry too:</p>
<pre><code>&lt;dependencyManagement&gt;
    &lt;dependencies&gt;
      &lt;dependency&gt;
        &lt;groupId&gt;io.opentelemetry&lt;/groupId&gt;
        &lt;artifactId&gt;opentelemetry-bom&lt;/artifactId&gt;
        &lt;version&gt;1.25.0&lt;/version&gt;
        &lt;type&gt;pom&lt;/type&gt;
        &lt;scope&gt;import&lt;/scope&gt;
      &lt;/dependency&gt;
      &lt;dependency&gt;
        &lt;groupId&gt;io.opentelemetry&lt;/groupId&gt;
        &lt;artifactId&gt;opentelemetry-bom-alpha&lt;/artifactId&gt;
        &lt;version&gt;1.25.0-alpha&lt;/version&gt;
        &lt;type&gt;pom&lt;/type&gt;
        &lt;scope&gt;import&lt;/scope&gt;
      &lt;/dependency&gt;
    &lt;/dependencies&gt;
  &lt;/dependencyManagement&gt;
</code></pre>
<h3 id="step2addtheapplicationconfiguration">Step 2. Add the application configuration</h3>
<p>We recommend that you add the following configuration to the application’s main method, to start before any application code. Doing it like this gives you a bit more control and flexibility and ensures that OpenTelemetry will be available at any stage of the application lifecycle. In the examples, we put this code before the Spring Boot Application startup. Elastic supports OTLP over HTTP and OTLP over GRPC. In this example, we are using GRPC.</p>
<pre><code>String SERVICE_NAME = System.getenv("OTEL_SERVICE_NAME");

// set service name on all OTel signals
Resource resource = Resource.getDefault().merge(Resource.create(Attributes.of(ResourceAttributes.SERVICE_NAME,SERVICE_NAME,ResourceAttributes.SERVICE_VERSION,"1.0",ResourceAttributes.DEPLOYMENT_ENVIRONMENT,"production")));

// init OTel logger provider with export to OTLP
SdkLoggerProvider sdkLoggerProvider = SdkLoggerProvider.builder().setResource(resource).addLogRecordProcessor(BatchLogRecordProcessor.builder(OtlpGrpcLogRecordExporter.builder().setEndpoint(System.getenv("OTEL_EXPORTER_OTLP_ENDPOINT")).addHeader("Authorization", "Bearer " + System.getenv("ELASTIC_APM_SECRET_TOKEN")).build()).build()).build();

// init OTel trace provider with export to OTLP
SdkTracerProvider sdkTracerProvider = SdkTracerProvider.builder().setResource(resource).setSampler(Sampler.alwaysOn()).addSpanProcessor(BatchSpanProcessor.builder(OtlpGrpcSpanExporter.builder().setEndpoint(System.getenv("OTEL_EXPORTER_OTLP_ENDPOINT")).addHeader("Authorization", "Bearer " + System.getenv("ELASTIC_APM_SECRET_TOKEN")).build()).build()).build();

// init OTel meter provider with export to OTLP
SdkMeterProvider sdkMeterProvider = SdkMeterProvider.builder().setResource(resource).registerMetricReader(PeriodicMetricReader.builder(OtlpGrpcMetricExporter.builder().setEndpoint(System.getenv("OTEL_EXPORTER_OTLP_ENDPOINT")).addHeader("Authorization", "Bearer " + System.getenv("ELASTIC_APM_SECRET_TOKEN")).build()).build()).build();

// create sdk object and set it as global
OpenTelemetrySdk sdk = OpenTelemetrySdk.builder().setTracerProvider(sdkTracerProvider).setLoggerProvider(sdkLoggerProvider).setMeterProvider(sdkMeterProvider).setPropagators(ContextPropagators.create(W3CTraceContextPropagator.getInstance())).build();

GlobalOpenTelemetry.set(sdk);
// connect logger
GlobalLoggerProvider.set(sdk.getSdkLoggerProvider());
// Add hook to close SDK, which flushes logs
Runtime.getRuntime().addShutdownHook(new Thread(sdk::close));
</code></pre>
<h3 id="step3createthetracerandstarttheopentelemetryspaninsidethetracingfilter">Step 3. Create the Tracer and start the OpenTelemetry Span inside the TracingFilter</h3>
<p>In the Spring Boot, example you will notice that we have a TracingFilter class which extends the OncePerRequestFilter class. This Filter is a component placed at the front of the request processing chain. Its primary roles are to intercept incoming requests and outgoing responses, performing tasks such as logging, authentication, transformation of request/response entities, and more. So what we do here is intercept the request as it comes into the Favorite service, so that we can pull out the headers which may contain tracing information from upstream systems.</p>
<p>We start by using the OpenTelemetry Tracer, which is a core component of OpenTelemetry that allows you to create spans, start and stop them, and add attributes and events. In your Java code, import the necessary OpenTelemetry classes and create an instance of the Tracer within your application.</p>
<p>We use this to create a new downstream span, which will continue as a child from the span created in the upstream system using the information we got from the upstream request. In our Elastiflix example, this will be the nodejs application.</p>
<pre><code>@Override
protected void doFilterInternal(jakarta.servlet.http.HttpServletRequest request, jakarta.servlet.http.HttpServletResponse response, jakarta.servlet.FilterChain filterChain) throws jakarta.servlet.ServletException, IOException {
        Tracer tracer = GlobalOpenTelemetry.getTracer(SERVICE_NAME);

        Context extractedContext = GlobalOpenTelemetry.getPropagators()
                .getTextMapPropagator()
                .extract(Context.current(), request, getter);

        Span span = tracer.spanBuilder(request.getRequestURI())
                .setSpanKind(SpanKind.SERVER)
                .setParent(extractedContext)
                .startSpan();

        try (Scope scope = span.makeCurrent()) {
            filterChain.doFilter(request, response);
        } catch (Exception e) {
            span.setStatus(StatusCode.ERROR);
            throw e;
        } finally {
            span.end();
        }
    }
</code></pre>
<h3 id="step4instrumentotherinterestingcodewithspans">Step 4. Instrument other interesting code with spans</h3>
<p>To instrument with spans and track specific regions of your code, you can use the Tracer's SpanBuilder to create spans. To accurately measure the duration of a specific operation, make sure to start and stop the spans at the appropriate locations in your code. Use the startSpan and endSpan methods provided by the Tracer to mark the beginning and end of the span. For example, you can create a span around a specific method or operation in your code, as shown here in the handleCanary method:</p>
<pre><code>private void handleCanary() throws Exception {
        Span span = GlobalOpenTelemetry.getTracer(SERVICE_NAME).spanBuilder("handleCanary").startSpan();
        Scope scope = span.makeCurrent();

///.....


 span.setStatus(StatusCode.OK);

        span.end();

        scope.close();
    }
</code></pre>
<h3 id="step5addattributesandeventstospans">Step 5. Add attributes and events to spans</h3>
<p>You can enhance the spans with additional attributes and events to provide more context and details about the operation being tracked. Attributes can be key-value pairs that describe the span, while events can be used to mark significant points in the span's lifecycle. This is also shown in the handleCanary method:</p>
<pre><code>private void handleCanary() throws Exception {

            Span.current().setAttribute("canary", "test-new-feature");
            Span.current().setAttribute("quiz_solution", "correlations");

            span.addEvent("a span event", Attributes
                    .of(AttributeKey.longKey("someKey"), Long.valueOf(93)));
    }
</code></pre>
<h3 id="step6instrumentbackends">Step 6. Instrument backends</h3>
<p>Let's consider an example where we are instrumenting a Redis database call. We're using the Java OpenTelemetry SDK, and our goal is to create a trace that captures each "Post User Favorites" operation to the database.</p>
<p>Below is the Java method that performs the operation and collects telemetry data:</p>
<pre><code>public void postUserFavorites(String user_id, String movieID) {
  ...
}
</code></pre>
<p>Let's go through it line by line:</p>
<p><strong>Initializing a span</strong><br />
The first important line of our method is where we initialize a span. A span represents a single operation within a trace, which could be a database call, a remote procedure call (RPC), or any segment of code that you want to measure.</p>
<pre><code>Span span = GlobalOpenTelemetry.getTracer(SERVICE_NAME).spanBuilder("Redis.Post").setSpanKind(SpanKind.CLIENT).startSpan();
</code></pre>
<p><strong>Setting span attributes</strong><br />
Next, we add attributes to our span. Attributes are key-value pairs that provide additional information about the span. In order to get the backend call to appear correctly in the service map, it is critical that the attributes are set correctly for the backend call type. In this example, we set the db.system attribute to redis.</p>
<pre><code>span.setAttribute("db.system", "redis");
span.setAttribute("db.connection_string", redisHost);
span.setAttribute(
  "db.statement",
  "POST user_id " + user_id + " AND movie_id " + movieID
);
</code></pre>
<p>This will ensure calls to the backend redis backend are tracked as shown below:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt959d425fb0f0a065/6a85ccc8f5f1a08e522ec941/elastic-blog-3-flowchart.png" alt="flowchart" /></p>
<p><strong>Capturing the result of the operation</strong><br />
We then execute the operation we're interested in, within a try-catch block. If an exception occurs during the execution of the operation, we record it in the span.</p>
<pre><code>try (Scope scope = span.makeCurrent()) {
    ...
} catch (Exception e) {
    span.setStatus(StatusCode.ERROR, "Error while getting data from Redis");
    span.recordException(e);
}
</code></pre>
<p><strong>Closing resources</strong><br />
Finally, we close the Redis connection and end the span.</p>
<pre><code>finally {
    jedis.close();
    span.end();
}
</code></pre>
<h3 id="step7configurelogging">Step 7. Configure logging</h3>
<p>Logging is an essential part of application monitoring and troubleshooting. OpenTelemetry allows you to integrate with existing logging frameworks, such as Logback or Log4j, to capture logs along with the telemetry data. Configure the logging framework of your choice to capture logs related to the instrumented spans. In our example application, check out the logback configuration, which shows how to export logs directly to Elastic.</p>
<pre><code>&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;configuration debug="true"&gt;

    &lt;appender name="otel-otlp"
        class="io.opentelemetry.instrumentation.logback.appender.v1_0.OpenTelemetryAppender"&gt;
        &lt;captureExperimentalAttributes&gt;false&lt;/captureExperimentalAttributes&gt;
        &lt;captureCodeAttributes&gt;true&lt;/captureCodeAttributes&gt;
        &lt;captureKeyValuePairAttributes&gt;true&lt;/captureKeyValuePairAttributes&gt;
    &lt;/appender&gt;

    &lt;appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"&gt;
        &lt;encoder&gt;
            &lt;pattern&gt;%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n&lt;/pattern&gt;
        &lt;/encoder&gt;
    &lt;/appender&gt;

    &lt;root level="DEBUG"&gt;
     &lt;appender-ref ref="otel-otlp" /&gt;
        &lt;appender-ref ref="STDOUT" /&gt;

    &lt;/root&gt;
&lt;/configuration&gt;
</code></pre>
<h3 id="step8runningthedockerimagewithenvironmentvariables">Step 8. Running the Docker image with environment variables</h3>
<p>As specified in the <a href="https://opentelemetry.io/docs/instrumentation/java/automatic/">OTEL Java documentation</a>, we will use environment variables and pass in the configuration values to enable it to connect with <a href="https://www.elastic.co/guide/en/apm/guide/current/open-telemetry.html">Elastic Observability’s APM server</a>.</p>
<p>Because Elastic accepts OTLP natively, we just need to provide the Endpoint and authentication where the OTEL Exporter needs to send the data, as well as some other environment variables.</p>
<p><strong>Getting Elastic Cloud variables</strong><br />
You can copy the endpoints and token from Kibana under the path <code>/app/home#/tutorial/apm</code>.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blta28447a53d11f965/6a85ccca33f2447adb49f54b/elastic-blog-3-apm.png" alt="apm agents" /></p>
<p>You will need to copy the following environment variable:</p>
<pre><code>OTEL_EXPORTER_OTLP_ENDPOINT
</code></pre>
<p>As well as the token from:</p>
<pre><code>OTEL_EXPORTER_OTLP_HEADERS
</code></pre>
<p><strong>Build the Docker image</strong></p>
<pre><code>docker build -t java-otel-manual-image .
</code></pre>
<p><strong>Run the Docker image</strong></p>
<pre><code>docker run \
       -e OTEL_EXPORTER_OTLP_ENDPOINT="REPLACE WITH OTEL_EXPORTER_OTLP_ENDPOINT" \
       -e ELASTIC_APM_SECRET_TOKEN="REPLACE WITH TOKEN" \
       -e OTEL_RESOURCE_ATTRIBUTES="service.version=1.0,deployment.environment=production" \
       -e OTEL_SERVICE_NAME="java-favorite-otel-manual" \
       -p 5000:5000 \
       java-otel-manual-image
</code></pre>
<p>You can now issue a few requests in order to generate trace data. Note that these requests are expected to return an error, as this service relies on a connection to Redis that you don’t currently have running. As mentioned before, you can find a more complete example using docker-compose <a href="https://github.com/elastic/observability-examples/tree/main/Elastiflix">here</a>.</p>
<pre><code>curl localhost:5000/favorites

# or alternatively issue a request every second

while true; do curl "localhost:5000/favorites"; sleep 1; done;
</code></pre>
<h3 id="step9exploretracesandlogsinelasticapm">Step 9. Explore traces and logs in Elastic APM</h3>
<p>Once you have this up and running, you can ping the endpoint for your instrumented service (in our case, this is /favorites), and you should see the app appear in Elastic APM, as shown below:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt7109b37a59669a3c/6a85ccce331d7ae811c317db/elastic-blog-5-services.png" alt="services" /></p>
<p>It will begin by tracking throughput and latency critical metrics for SREs to pay attention to.</p>
<p>Digging in, we can see an overview of all our Transactions.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blte8073add1961dc20/6a85ccd111893c48e9a7abba/elastic-blog-6-java-fave-otel.png" alt="java favorite otel graph" /></p>
<p>And look at specific transactions:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltc65873c14d47ac1c/6a85ccd4342d6992fb21b127/elastic-blog-7-graph1.png" alt="graph2" /></p>
<p>Click on <strong>Logs</strong> , and we see that logs are also brought over. The OTel Agent will automatically bring in logs and correlate them with traces for you:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltbf3006d2bd96ea91/6a85ccd7682666dca91eac47/elastic-blog-8-graph2.png" alt="graph3" /></p>
<p>This gives you complete visibility across logs, metrics, and traces!</p>
<h2 id="wrappingup">Wrapping up</h2>
<p>Manually instrumenting your Java applications with OpenTelemetry gives you greater control over what to track and monitor. By following the steps outlined in this blog post, you can effectively monitor the performance of your Java applications, identify issues, and gain insights into the overall health of your application.</p>
<p>Remember, OpenTelemetry is a powerful tool, and proper instrumentation requires careful consideration of what metrics, traces, and logs are essential for your specific use case. Experiment with different configurations, leverage the OpenTelemetry SDK for Java documentation, and continuously iterate to achieve the observability goals of your application.</p>
<p>In this blog, we discussed the following:</p>
<ul>
<li>How to manually instrument Java with OpenTelemetry</li>
<li>How to properly initialize and instrument span</li>
<li>How to easily set the OTLP ENDPOINT and OTLP HEADERS from Elastic without the need for a collector</li>
</ul>
<p>Hopefully, this provided an easy-to-understand walk-through of instrumenting Java with OpenTelemetry and how easy it is to send traces into Elastic.</p>
<blockquote>
  <p>Developer resources:</p>
  <ul>
  <li><a href="https://www.elastic.co/blog/getting-started-opentelemetry-instrumentation-sample-app">Elastiflix application</a>, a guide to instrument different languages with OpenTelemetry</li>
  <li>Python: <a href="https://www.elastic.co/blog/auto-instrumentation-of-python-applications-opentelemetry">Auto-instrumentation</a>, <a href="https://www.elastic.co/blog/manual-instrumentation-of-python-applications-opentelemetry">Manual-instrumentation</a></li>
  <li>Java: <a href="https://www.elastic.co/blog/auto-instrumentation-of-java-applications-opentelemetry">Auto-instrumentation</a>, <a href="https://www.elastic.co/blog/manual-instrumentation-java-apps-opentelemetry">Manual-instrumentation</a></li>
  <li>Node.js: <a href="https://www.elastic.co/blog/auto-instrument-nodejs-applications-opentelemetry">Auto-instrumentation</a>, <a href="https://www.elastic.co/blog/manual-instrumentation-of-nodejs-applications-opentelemetry">Manual-instrumentation</a></li>
  <li>.NET: <a href="https://www.elastic.co/blog/auto-instrumentation-of-net-applications-opentelemetry">Auto-instrumentation</a>, <a href="https://www.elastic.co/blog/manual-instrumentation-of-net-applications-opentelemetry">Manual-instrumentation</a></li>
  <li>Go: <a href="https://elastic.co/blog/manual-instrumentation-of-go-applications-opentelemetry">Manual-instrumentation</a></li>
  <li><a href="https://www.elastic.co/blog/best-practices-instrumenting-opentelemetry">Best practices for instrumenting OpenTelemetry</a></li>
  </ul>
  <p>General configuration and use case resources:</p>
  <ul>
  <li><a href="https://www.elastic.co/blog/opentelemetry-observability">Independence with OpenTelemetry on Elastic</a></li>
  <li><a href="https://www.elastic.co/blog/implementing-kubernetes-observability-security-opentelemetry">Modern observability and security on Kubernetes with Elastic and OpenTelemetry</a></li>
  <li><a href="https://www.elastic.co/blog/3-models-logging-opentelemetry-elastic">3 models for logging with OpenTelemetry and Elastic</a></li>
  <li><a href="https://www.elastic.co/blog/adding-free-and-open-elastic-apm-as-part-of-your-elastic-observability-deployment">Adding free and open Elastic APM as part of your Elastic Observability deployment</a></li>
  <li><a href="https://www.elastic.co/blog/custom-metrics-app-code-java-agent-plugin">Capturing custom metrics through OpenTelemetry API in code with Elastic</a></li>
  <li><a href="https://www.elastic.co/virtual-events/future-proof-your-observability-platform-with-opentelemetry-and-elastic">Future-proof your observability platform with OpenTelemetry and Elastic</a></li>
  <li><a href="https://www.elastic.co/blog/kubernetes-k8s-observability-elasticsearch-cncf">Elastic Observability: Built for open technologies like Kubernetes, OpenTelemetry, Prometheus, Istio, and more</a></li>
  </ul>
</blockquote>
<p>Don’t have an Elastic Cloud account yet? <a href="https://cloud.elastic.co/registration">Sign up for Elastic Cloud</a> and try out the auto-instrumentation capabilities that I discussed above. I would be interested in getting your feedback about your experience in gaining visibility into your application stack with Elastic.</p>
<p><em>The release and timing of any features or functionality described in this post remain at Elastic's sole discretion. Any features or functionality not currently available may not be delivered on time or at all.</em></p>]]></content:encoded>
    <link>https://www.elastic.co/observability-labs/blog/manual-instrumentation-java-apps-opentelemetry</link>
    <guid isPermaLink="false">manual-instrumentation-java-apps-opentelemetry</guid>
    <category><![CDATA[OpenTelemetry]]></category>
    <category><![CDATA[APM]]></category>
    <category><![CDATA[Infrastructure Monitoring]]></category>
    <dc:creator><![CDATA[David Hope]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt6bb53438aa2f6928/6a85ccdaf61d6e405e9c2b53/observability-launch-series-3-java-manual.jpg" length="0" type="image/jpeg"/>
    <pubDate>Thu, 31 Aug 2023 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Automatic instrumentation with OpenTelemetry for Python applications]]></title>
    <description><![CDATA[Learn how to auto-instrument Python applications using OpenTelemetry. With standard commands in a Docker file, applications can be instrumented quickly without writing code in multiple places, enabling rapid change, scale, and easier management.]]></description>
    <content:encoded><![CDATA[<p>DevOps and SRE teams are transforming the process of software development. While DevOps engineers focus on efficient software applications and service delivery, SRE teams are key to ensuring reliability, scalability, and performance. These teams must rely on a full-stack observability solution that allows them to manage and monitor systems and ensure issues are resolved before they impact the business.</p>
<p>Observability across the entire stack of modern distributed applications requires data collection, processing, and correlation often in the form of dashboards. Ingesting all system data requires installing agents across stacks, frameworks, and providers — a process that can be challenging and time-consuming for teams who have to deal with version changes, compatibility issues, and proprietary code that doesn't scale as systems change.</p>
<p>Thanks to <a href="http://opentelemetry.io">OpenTelemetry</a> (OTel), DevOps and SRE teams now have a standard way to collect and send data that doesn't rely on proprietary code and has a large support community reducing vendor lock-in.</p>
<p>In a <a href="https://www.elastic.co/blog/opentelemetry-observability">previous blog</a>, we also reviewed how to use the <a href="https://github.com/elastic/opentelemetry-demo">OpenTelemetry demo</a> and connect it to Elastic<sup>®</sup>, as well as some of Elastic’s capabilities with <a href="https://www.elastic.co/observability/opentelemetry">OpenTelemetry visualizations</a> and Kubernetes.</p>
<p>In this blog, we will show how to use <a href="https://opentelemetry.io/docs/instrumentation/python/">automatic instrumentation for OpenTelemetry</a> with the Python service of our <a href="https://github.com/elastic/observability-examples">application called Elastiflix</a>, which helps highlight auto-instrumentation in a simple way.</p>
<p>The beauty of this is that there is <strong>no need for the otel-collector</strong>! This setup enables you to slowly and easily migrate an application to OTel with Elastic according to a timeline that best fits your business.</p>
<h2 id="applicationprerequisitesandconfig">Application, prerequisites, and config</h2>
<p>The application that we use for this blog is called <a href="https://github.com/elastic/observability-examples">Elastiflix</a>, a movie-streaming application. It consists of several micro-services written in .NET, NodeJS, Go, and Python.</p>
<p>Before we instrument our sample application, we will first need to understand how Elastic can receive the telemetry data.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltac807a8013a63051/6a85c80627c5cdaecc5f7384/elastic-blog-1-otel-config-options.png" alt="Elastic configuration options for OpenTelemetry" /></p>
<p>All of Elastic Observability’s APM capabilities are available with OTel data. Some of these include:</p>
<ul>
<li>Service maps</li>
<li>Service details (latency, throughput, failed transactions)</li>
<li>Dependencies between services, distributed tracing</li>
<li>Transactions (traces)</li>
<li>Machine learning (ML) correlations</li>
<li>Log correlation</li>
</ul>
<p>In addition to Elastic’s APM and a unified view of the telemetry data, you will also be able to use Elastic’s powerful machine learning capabilities to reduce the analysis, and alerting to help reduce MTTR.</p>
<h3 id="prerequisites">Prerequisites</h3>
<ul>
<li>An Elastic Cloud account — <a href="https://cloud.elastic.co/">sign up now</a></li>
<li>A clone of the <a href="https://github.com/elastic/observability-examples">Elastiflix demo application</a>, or your own Python application</li>
<li>Basic understanding of Docker — potentially install <a href="https://www.docker.com/products/docker-desktop/">Docker Desktop</a></li>
<li>Basic understanding of Python</li>
</ul>
<h3 id="viewtheexamplesourcecode">View the example source code</h3>
<p>The full source code, including the Dockerfile used in this blog, can be found on <a href="https://github.com/elastic/observability-examples/tree/main/Elastiflix/python-favorite-otel-auto">GitHub</a>. The repository also contains the <a href="https://github.com/elastic/observability-examples/tree/main/Elastiflix/python-favorite">same application without instrumentation</a>. This allows you to compare each file and see the differences.</p>
<p>The following steps will show you how to instrument this application and run it on the command line or in Docker. If you are interested in a more complete OTel example, take a look at the docker-compose file <a href="https://github.com/elastic/observability-examples/tree/main#start-the-app">here</a>, which will bring up the full project.</p>
<h2 id="stepbystepguide">Step-by-step guide</h2>
<h3 id="step0logintoyourelasticcloudaccount">Step 0. Log in to your Elastic Cloud account</h3>
<p>This blog assumes you have an Elastic Cloud account — if not, follow the <a href="https://cloud.elastic.co/registration?elektra=en-cloud-page">instructions to get started on Elastic Cloud</a>.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltbdc1532cbf8a9e1e/6a85c809e2447a53d08b1396/elastic-blog-2-free-trial.png" alt="free trial" /></p>
<h3 id="step1configureautoinstrumentationforthepythonservice">Step 1. Configure auto-instrumentation for the Python Service</h3>
<p>We are going to use automatic instrumentation with Python service from the <a href="https://github.com/elastic/observability-examples">Elastiflix demo application</a>.</p>
<p>We will be using the following service from Elastiflix:</p>
<pre><code>Elastiflix/python-favorite-otel-auto
</code></pre>
<p>Per the <a href="https://opentelemetry.io/docs/instrumentation/js/automatic/">OpenTelemetry Automatic Instrumentation for Python documentation</a>, you will simply install the appropriate Python packages using pip install.</p>
<pre><code>&gt;pip install opentelemetry-distro \
    opentelemetry-exporter-otlp

&gt;opentelemetry-bootstrap -a install
</code></pre>
<p>If you are running the Python service on the command line, then you can use the following command:</p>
<pre><code>opentelemetry-instrument python main.py
</code></pre>
<p>For our application, we do this as part of the Dockerfile.</p>
<p><strong>Dockerfile</strong></p>
<pre><code>FROM python:3.9-slim as base

# get packages
COPY requirements.txt .
RUN pip install -r requirements.txt
WORKDIR /favoriteservice

#install opentelemetry packages
RUN pip install opentelemetry-distro \
    opentelemetry-exporter-otlp

RUN opentelemetry-bootstrap -a install

# Add the application
COPY . .

EXPOSE 5000
ENTRYPOINT [ "opentelemetry-instrument", "python", "main.py"]
</code></pre>
<h3 id="step2runningthedockerimagewithenvironmentvariables">Step 2. Running the Docker image with environment variables</h3>
<p>As specified in the <a href="https://opentelemetry.io/docs/instrumentation/python/automatic/#configuring-the-agent">OTEL Python documentation</a>, we will use environment variables and pass in the configuration values to enable it to connect with <a href="https://www.elastic.co/guide/en/apm/guide/current/open-telemetry.html">Elastic Observability’s APM server</a>.</p>
<p>Because Elastic accepts OTLP natively, we just need to provide the Endpoint and authentication where the OTEL Exporter needs to send the data, as well as some other environment variables.</p>
<p><strong>Getting Elastic Cloud variables</strong><br />
You can copy the endpoints and token from Kibana<sup>®</sup> under the path /app/home#/tutorial/apm.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltfbe8bebe5c4c3d96/6a85c80c4710c60b8dd3cae9/elastic-blog-3-apm-agents.png" alt="apm agents" /></p>
<p>You will need to copy the following environment variables:</p>
<pre><code>OTEL_EXPORTER_OTLP_ENDPOINT
OTEL_EXPORTER_OTLP_HEADERS
</code></pre>
<p><strong>Build the image</strong></p>
<pre><code>docker build -t  python-otel-auto-image .
</code></pre>
<p><strong>Run the image</strong></p>
<pre><code>docker run \
       -e OTEL_EXPORTER_OTLP_ENDPOINT="&lt;REPLACE WITH OTEL_EXPORTER_OTLP_ENDPOINT&gt;" \
       -e OTEL_EXPORTER_OTLP_HEADERS="Authorization=Bearer%20&lt;REPLACE WITH TOKEN&gt;" \
       -e OTEL_RESOURCE_ATTRIBUTES="service.version=1.0,deployment.environment=production" \
       -e OTEL_SERVICE_NAME="python-favorite-otel-auto" \
       -p 5001:5001 \
       python-otel-auto-image
</code></pre>
<p><strong>Important:</strong> Note that the “OTEL_EXPORTER_OTLP_HEADERS” variable has the whitespace after Bearer escaped as “%20” — this is a requirement for Python.</p>
<p>You can now issue a few requests in order to generate trace data. Note that these requests are expected to return an error, as this service relies on a connection to Redis that you don’t currently have running. As mentioned before, you can find a more complete example using docker-compose <a href="https://github.com/elastic/observability-examples/tree/main/Elastiflix">here</a>.</p>
<pre><code>curl localhost:5000/favorites

# or alternatively issue a request every second

while true; do curl "localhost:5000/favorites"; sleep 1; done;
</code></pre>
<h3 id="step3exploretracesmetricsandlogsinelasticapm">Step 3: Explore traces, metrics, and logs in Elastic APM</h3>
<p>Exploring the Services section in Elastic APM, you’ll see the Python service displayed.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt6f53920fc85feae7/6a85c80f682666a5e91eab8f/elastic-blog-4-services.png" alt="services" /></p>
<p>Clicking on the python-favorite-otel-auto service , you can see that it is ingesting telemetry data using OpenTelemetry.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt9cd8a6f6fb6d9a22/6a85c8129bf99430930a04fb/elastic-blog-5-graph-view.png" alt="graph view" /></p>
<p>In this blog, we discussed the following:</p>
<ul>
<li>How to auto-instrument Python with OpenTelemetry</li>
<li>Using standard commands in a Dockerfile, auto-instrumentation was done efficiently and without adding code in multiple places</li>
</ul>
<p>Since Elastic can support a mix of methods for ingesting data, whether it be using auto-instrumentation of open-source OpenTelemetry or manual instrumentation with its native APM agents, you can plan your migration to OTel by focusing on a few applications first and then using OpenTelemety across your applications later on in a manner that best fits your business needs.</p>
<blockquote>
  <p>Developer resources:</p>
  <ul>
  <li><a href="https://www.elastic.co/blog/getting-started-opentelemetry-instrumentation-sample-app">Elastiflix application</a>, a guide to instrument different languages with OpenTelemetry</li>
  <li>Python: <a href="https://www.elastic.co/blog/auto-instrumentation-python-applications-opentelemetry">Auto-instrumentation</a>, <a href="https://www.elastic.co/blog/manual-instrumentation-of-python-applications-opentelemetry">Manual-instrumentation</a></li>
  <li>Java: <a href="https://www.elastic.co/blog/auto-instrumentation-of-java-applications-opentelemetry">Auto-instrumentation</a>, <a href="https://www.elastic.co/blog/manual-instrumentation-of-java-applications-opentelemetry">Manual-instrumentation</a></li>
  <li>Node.js: <a href="https://www.elastic.co/blog/auto-instrument-nodejs-applications-opentelemetry">Auto-instrumentation</a>, <a href="https://www.elastic.co/blog/manual-instrumentation-of-nodejs-applications-opentelemetry">Manual-instrumentation</a></li>
  <li>.NET: <a href="https://www.elastic.co/blog/auto-instrumentation-of-net-applications-opentelemetry">Auto-instrumentation</a>, <a href="https://www.elastic.co/blog/manual-instrumentation-of-net-applications-opentelemetry">Manual-instrumentation</a></li>
  <li>Go: <a href="https://elastic.co/blog/manual-instrumentation-of-go-applications-opentelemetry">Manual-instrumentation</a></li>
  <li><a href="https://www.elastic.co/blog/best-practices-instrumenting-opentelemetry">Best practices for instrumenting OpenTelemetry</a></li>
  </ul>
  <p>General configuration and use case resources:</p>
  <ul>
  <li><a href="https://www.elastic.co/blog/opentelemetry-observability">Independence with OpenTelemetry on Elastic</a></li>
  <li><a href="https://www.elastic.co/blog/implementing-kubernetes-observability-security-opentelemetry">Modern observability and security on Kubernetes with Elastic and OpenTelemetry</a></li>
  <li><a href="https://www.elastic.co/blog/3-models-logging-opentelemetry-elastic">3 models for logging with OpenTelemetry and Elastic</a></li>
  <li><a href="https://www.elastic.co/blog/adding-free-and-open-elastic-apm-as-part-of-your-elastic-observability-deployment">Adding free and open Elastic APM as part of your Elastic Observability deployment</a></li>
  <li><a href="https://www.elastic.co/blog/custom-metrics-app-code-java-agent-plugin">Capturing custom metrics through OpenTelemetry API in code with Elastic</a></li>
  <li><a href="https://www.elastic.co/virtual-events/future-proof-your-observability-platform-with-opentelemetry-and-elastic">Future-proof your observability platform with OpenTelemetry and Elastic</a></li>
  <li><a href="https://www.elastic.co/blog/kubernetes-k8s-observability-elasticsearch-cncf">Elastic Observability: Built for open technologies like Kubernetes, OpenTelemetry, Prometheus, Istio, and more</a></li>
  </ul>
</blockquote>
<p>Don’t have an Elastic Cloud account yet? <a href="https://cloud.elastic.co/registration">Sign up for Elastic Cloud</a> and try out the auto-instrumentation capabilities that I discussed above. I would be interested in getting your feedback about your experience in gaining visibility into your application stack with Elastic.</p>
<p><em>The release and timing of any features or functionality described in this post remain at Elastic's sole discretion. Any features or functionality not currently available may not be delivered on time or at all.</em></p>]]></content:encoded>
    <link>https://www.elastic.co/observability-labs/blog/auto-instrumentation-python-applications-opentelemetry</link>
    <guid isPermaLink="false">auto-instrumentation-python-applications-opentelemetry</guid>
    <category><![CDATA[OpenTelemetry]]></category>
    <category><![CDATA[APM]]></category>
    <category><![CDATA[Infrastructure Monitoring]]></category>
    <dc:creator><![CDATA[Bahubali Shetti]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt8fad8320a5eb60f5/6a85c8159a32f1a545a7df96/observability-launch-series-2-python-auto_(1).jpg" length="0" type="image/jpeg"/>
    <pubDate>Thu, 31 Aug 2023 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Auto-instrumentation of Java applications with OpenTelemetry]]></title>
    <description><![CDATA[Instrumenting Java applications with OpenTelemetry provides insights into application performance, dependencies, and errors. We'll show you how to automatically instrument a Java application using Docker, with no changes to your application code.]]></description>
    <content:encoded><![CDATA[<p>In the fast-paced universe of software development, especially in the cloud-native realm, DevOps and SRE teams are increasingly emerging as essential partners in application stability and growth.</p>
<p>DevOps engineers continuously optimize software delivery, while SRE teams act as the stewards of application reliability, scalability, and top-tier performance. The challenge? These teams require a cutting-edge observability solution, one that encompasses full-stack insights, empowering them to rapidly manage, monitor, and rectify potential disruptions before they culminate into operational challenges.</p>
<p>Observability in our modern distributed software ecosystem goes beyond mere monitoring — it demands limitless data collection, precision in processing, and the correlation of this data into actionable insights. However, the road to achieving this holistic view is paved with obstacles, from navigating version incompatibilities to wrestling with restrictive proprietary code.</p>
<p>Enter <a href="https://opentelemetry.io/">OpenTelemetry (OTel)</a>, with the following benefits for those who adopt it:</p>
<ul>
<li>Escape vendor constraints with OTel, freeing yourself from vendor lock-in and ensuring top-notch observability.</li>
<li>See the harmony of unified logs, metrics, and traces come together to provide a complete system view.</li>
<li>Improve your application oversight through richer and enhanced instrumentations.</li>
<li>Embrace the benefits of backward compatibility to protect your prior instrumentation investments.</li>
<li>Embark on the OpenTelemetry journey with an easy learning curve, simplifying onboarding and scalability.</li>
<li>Rely on a proven, future-ready standard to boost your confidence in every investment.</li>
</ul>
<p>In this blog, we will explore how you can use <a href="https://opentelemetry.io/docs/instrumentation/java/automatic/">automatic instrumentation in your Java</a> application using Docker, without the need to refactor any part of your application code. We will use an <a href="https://github.com/elastic/observability-examples">application called Elastiflix</a>, which helps highlight auto-instrumentation in a simple way.</p>
<h2 id="applicationprerequisitesandconfig">Application, prerequisites, and config</h2>
<p>The application that we use for this blog is called <a href="https://github.com/elastic/observability-examples">Elastiflix</a>, a movie-streaming application. It consists of several micro-services written in .NET, NodeJS, Go, and Python.</p>
<p>Before we instrument our sample application, we will first need to understand how Elastic can receive the telemetry data.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt7d21eeef97ab704d/6a85c7d1bc5bb34702f81a5d/elastic-blog-1-config.png" alt="Elastic configuration options for OpenTelemetry" /></p>
<p>All of Elastic Observability’s APM capabilities are available with OTel data. Some of these include:</p>
<ul>
<li>Service maps</li>
<li>Service details (latency, throughput, failed transactions)</li>
<li>Dependencies between services, distributed tracing</li>
<li>Transactions (traces)</li>
<li>Machine learning (ML) correlations</li>
<li>Log correlation</li>
</ul>
<p>In addition to Elastic’s APM and a unified view of the telemetry data, you will also be able to use Elastic’s powerful machine learning capabilities to reduce the analysis, and alerting to help reduce MTTR.</p>
<h3 id="prerequisites">Prerequisites</h3>
<ul>
<li>An Elastic Cloud account — <a href="https://cloud.elastic.co/">sign up now</a>.</li>
<li>A clone of the <a href="https://github.com/elastic/observability-examples">Elastiflix demo application</a>, or your own Java application</li>
<li>Basic understanding of Docker — potentially install <a href="https://www.docker.com/products/docker-desktop/">Docker Desktop</a></li>
<li>Basic understanding of Java</li>
</ul>
<h3 id="viewtheexamplesourcecode">View the example source code</h3>
<p>The full source code, including the Dockerfile used in this blog, can be found on <a href="https://github.com/elastic/observability-examples/tree/main/Elastiflix/python-favorite-otel-auto">GitHub</a>. The repository also contains the <a href="https://github.com/elastic/observability-examples/tree/main/Elastiflix/python-favorite">same application without instrumentation</a>. This allows you to compare each file and see the differences.</p>
<p>The following steps will show you how to instrument this application and run it on the command line or in Docker. If you are interested in a more complete OTel example, take a look at the docker-compose file <a href="https://github.com/elastic/observability-examples/tree/main#start-the-app">here</a>, which will bring up the full project.</p>
<h2 id="stepbystepguide">Step-by-step guide</h2>
<h3 id="step0logintoyourelasticcloudaccount">Step 0. Log in to your Elastic Cloud account</h3>
<p>This blog assumes you have an Elastic Cloud account — if not, follow the <a href="https://cloud.elastic.co/registration?elektra=en-cloud-page">instructions to get started on Elastic Cloud</a>.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt2d5313e4a98398f4/6a85c7d4f5f1a02cef2ec861/elastic-blog-2-trial.png" alt="free trial" /></p>
<h3 id="step1configureautoinstrumentationforthejavaservice">Step 1. Configure auto-instrumentation for the Java service</h3>
<p>We are going to use automatic instrumentation with Java service from the <a href="https://github.com/elastic/observability-examples/tree/main/Elastiflix/java-favorite-otel-auto">Elastiflix demo application</a>.</p>
<p>We will be using the following service from Elastiflix:</p>
<pre><code>Elastiflix/java-favorite-otel-auto
</code></pre>
<p>Per the <a href="https://opentelemetry.io/docs/instrumentation/java/automatic/">OpenTelemetry Automatic Instrumentation for Java documentation</a> and documentation, you will simply install the appropriate Java packages.</p>
<p>Create a local OTel directory to download the OpenTelemetry Java agent. Download opentelemetry-javaagent.jar.</p>
<pre><code>&gt;mkdir /otel

&gt;curl -L https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/latest/download/opentelemetry-javaagent.jar –output /otel/opentelemetry-javaagent.jar
</code></pre>
<p>If you are going to run the service on the command line, then you can use the following command:</p>
<pre><code>java -javaagent:/otel/opentelemetry-javaagent.jar \
-jar /usr/src/app/target/favorite-0.0.1-SNAPSHOT.jar --server.port=5000
</code></pre>
<p>For our application, we will do this as part of the Dockerfile.</p>
<p><strong>Dockerfile</strong></p>
<pre><code>Start with a base image containing Java runtime
FROM maven:3.8.2-openjdk-17-slim as build

# Make port 8080 available to the world outside this container
EXPOSE 5000

# Change to the app directory
WORKDIR /usr/src/app

# Copy the local code to the container
COPY . .

# Build the application
RUN mvn clean install

USER root
RUN apt-get update &amp;&amp; apt-get install -y zip curl
RUN mkdir /otel
RUN curl -L -o /otel/opentelemetry-javaagent.jar https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/download/v1.28.0/opentelemetry-javaagent.jar

COPY start.sh /start.sh
RUN chmod +x /start.sh

ENTRYPOINT ["/start.sh"]
</code></pre>
<h3 id="step2runningthedockerimagewithenvironmentvariables">Step 2. Running the Docker Image with environment variables</h3>
<p>As specified in the <a href="https://opentelemetry.io/docs/instrumentation/java/automatic/">OTEL Java documentation</a>, we will use environment variables and pass in the configuration values to enable it to connect with <a href="https://www.elastic.co/guide/en/observability/current/apm-open-telemetry.html">Elastic Observability’s APM server</a>.</p>
<p>Because Elastic accepts OTLP natively, we just need to provide the Endpoint and authentication where the OTEL Exporter needs to send the data, as well as some other environment variables.</p>
<p><strong>Getting Elastic Cloud variables</strong><br />
You can copy the endpoints and token from Kibana under the path <code>/app/home#/tutorial/apm</code>.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt5e33ab3f17634420/6a85c7d7f61d6e81459c2aa7/elastic-blog-3-apm-agents.png" alt="apm agents" /></p>
<p>You will need to copy the following environment variables:</p>
<pre><code>OTEL_EXPORTER_OTLP_ENDPOINT
OTEL_EXPORTER_OTLP_HEADERS
</code></pre>
<p><strong>Build the Docker image</strong></p>
<pre><code>docker build -t java-otel-auto-image .
</code></pre>
<p><strong>Run the Docker image</strong></p>
<pre><code>docker run \
       -e OTEL_EXPORTER_OTLP_ENDPOINT="REPLACE WITH OTEL_EXPORTER_OTLP_ENDPOINT" \
       -e ELASTIC_APM_SECRET_TOKEN="REPLACE WITH THE BIT AFTER Authorization=Bearer " \
       -e OTEL_RESOURCE_ATTRIBUTES="service.version=1.0,deployment.environment=production" \
       -e OTEL_SERVICE_NAME="java-favorite-otel-auto" \
       -p 5000:5000 \
       java-otel-auto-image
</code></pre>
<p>You can now issue a few requests in order to generate trace data. Note that these requests are expected to return an error, as this service relies on a connection to Redis that you don’t currently have running. As mentioned before, you can find a more complete example using docker-compose <a href="https://github.com/elastic/observability-examples/tree/main/Elastiflix">here</a>.</p>
<pre><code>curl localhost:5000/favorites

# or alternatively issue a request every second

while true; do curl "localhost:5000/favorites"; sleep 1; done;
</code></pre>
<h3 id="step3exploretracesandlogsinelasticapm">Step 3: Explore traces and logs in Elastic APM</h3>
<p>Once you have this up and running, you can ping the endpoint for your instrumented service (in our case, this is /favorites), and you should see the app appear in Elastic APM, as shown below:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt1bf45157d67d5eb0/6a85c7da8c29446e70b88fba/elastic-blog-4-services.png" alt="services" /></p>
<p>It will begin by tracking throughput and latency critical metrics for SREs to pay attention to.</p>
<p>Digging in, we can see an overview of all our Transactions.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt6d5956fe18a5cacc/6a85c7dc43c0b7cd712f05a2/elastic-blog-5-services2.png" alt="services-2" /></p>
<p>And look at specific transactions:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltf78af69dc184afe3/6a85c7e0eaf2452ab3a49ee3/elastic-blog-6-graph-colored.png" alt="graph colored lines" /></p>
<p>Click on <strong>Logs,</strong> and we see that logs are also brought over. The OTel Agent will automatically bring in logs and correlate them with traces for you:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt63cfce87c6b0ae44/6a85c7e38c29445a4fb88fc2/elastic-blog-7-graph-no-colors.png" alt="graph-no-colors" /></p>
<p>This gives you complete visibility across logs, metrics, and traces!</p>
<h2 id="basicconceptshowapmworkswithjava">Basic concepts: How APM works with Java</h2>
<p>Before we continue, let's first understand a few basic concepts and terms.</p>
<ul>
<li><strong>Java Agent:</strong> This is a tool that can be used to instrument (or modify) the bytecode of class files in the Java Virtual Machine (JVM). Java agents are used for many purposes like performance monitoring, logging, security, and more.</li>
<li><strong>Bytecode:</strong> This is the intermediary code generated by the Java compiler from your Java source code. This code is interpreted or compiled on the fly by the JVM to produce machine code that can be executed.</li>
<li><strong>Byte Buddy:</strong> Byte Buddy is a code generation and manipulation library for Java. It is used to create, modify, or adapt Java classes at runtime. In the context of a Java Agent, Byte Buddy provides a powerful and flexible way to modify bytecode. <strong>Both the Elastic APM Agent and the OpenTelemetry Agent use Byte Buddy under the covers.</strong></li>
</ul>
<p><strong>Now, let's talk about how automatic instrumentation works with Byte Buddy:</strong></p>
<p>Automatic instrumentation is the process by which an agent modifies the bytecode of your application's classes, often to insert monitoring code. The agent doesn't modify the source code directly, but rather the bytecode that is loaded into the JVM. This is done while the JVM is loading the classes, so the modifications are in effect during runtime.</p>
<p>Here's a simplified explanation of the process:</p>
<ol>
<li><p><strong>Start the JVM with the agent:</strong> When starting your Java application, you specify the Java agent with the -javaagent command line option. This instructs the JVM to load your agent before the main method of your application is invoked. At this point, the agent has the opportunity to set up class transformers.</p></li>
<li><p><strong>Register a class file transformer with Byte Buddy:</strong> Your agent will register a class file transformer with Byte Buddy. A transformer is a piece of code that is invoked every time a class is loaded into the JVM. This transformer receives the bytecode of the class, and it can modify this bytecode before the class is actually used.</p></li>
<li><p><strong>Transform the bytecode:</strong> When your transformer is invoked, it will use Byte Buddy's API to modify the bytecode. Byte Buddy allows you to specify your transformations in a high-level, expressive way rather than manually writing complex bytecode. For example, you could specify a certain class and method within that class that you want to instrument and provide an "interceptor" that will add new behavior to that method.</p></li>
<li><p><strong>Use the transformed classes:</strong> Once the agent has set up its transformers, the JVM continues to load classes as usual. Each time a class is loaded, your transformers are invoked, allowing them to modify the bytecode. Your application then uses these transformed classes as if they were the original ones, but they now have the extra behavior that you've injected through your interceptor.</p></li>
</ol>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt3b2426bbad6b36b4/6a85c7e543c0b72e2a2f05a6/elastic-blog-8-flowchart.png" alt="flowchart" /></p>
<p>In essence, automatic instrumentation with Byte Buddy is about modifying the behavior of your Java classes at runtime, without needing to alter the source code directly. This is especially useful for cross-cutting concerns like logging, monitoring, or security, as it allows you to centralize this code in your Java Agent, rather than scattering it throughout your application.</p>
<h2 id="summary">Summary</h2>
<p>With this Dockerfile, you've transformed your simple Java application into one that's automatically instrumented with OpenTelemetry. This will aid greatly in understanding application performance, tracing errors, and gaining insights into how users interact with your software.</p>
<p>Remember, observability is a crucial aspect of modern application development, especially in distributed systems. With tools like OpenTelemetry, understanding complex systems becomes a tad bit easier.</p>
<p>In this blog, we discussed the following:</p>
<ul>
<li>How to auto-instrument Java with OpenTelemetry.</li>
<li>Using standard commands in a Docker file, auto-instrumentation was done efficiently and without adding code in multiple places enabling manageability.</li>
<li>Using OpenTelemetry and its support for multiple languages, DevOps and SRE teams can auto-instrument their applications with ease gaining immediate insights into the health of the entire application stack and reduce mean time to resolution (MTTR).</li>
</ul>
<p>Since Elastic can support a mix of methods for ingesting data, whether it be using auto-instrumentation of open-source OpenTelemetry or manual instrumentation with its native APM agents, you can plan your migration to OTel by focusing on a few applications first and then using OpenTelemety across your applications later on in a manner that best fits your business needs.</p>
<blockquote>
  <p>Developer resources:</p>
  <ul>
  <li><a href="https://www.elastic.co/blog/getting-started-opentelemetry-instrumentation-sample-app">Elastiflix application</a>, a guide to instrument different languages with OpenTelemetry</li>
  <li>Python: <a href="https://www.elastic.co/blog/auto-instrumentation-of-python-applications-opentelemetry">Auto-instrumentation</a>, <a href="https://www.elastic.co/blog/manual-instrumentation-of-python-applications-opentelemetry">Manual-instrumentation</a></li>
  <li>Java: <a href="https://www.elastic.co/blog/auto-instrumentation-java-applications-opentelemetry">Auto-instrumentation</a>, <a href="https://www.elastic.co/blog/manual-instrumentation-of-java-applications-opentelemetry">Manual-instrumentation</a></li>
  <li>Node.js: <a href="https://www.elastic.co/blog/auto-instrument-nodejs-applications-opentelemetry">Auto-instrumentation</a>, <a href="https://www.elastic.co/blog/manual-instrumentation-of-nodejs-applications-opentelemetry">Manual-instrumentation</a></li>
  <li>.NET: <a href="https://www.elastic.co/blog/auto-instrumentation-of-net-applications-opentelemetry">Auto-instrumentation</a>, <a href="https://www.elastic.co/blog/manual-instrumentation-of-net-applications-opentelemetry">Manual-instrumentation</a></li>
  <li>Go: <a href="https://elastic.co/blog/manual-instrumentation-of-go-applications-opentelemetry">Manual-instrumentation</a></li>
  <li><a href="https://www.elastic.co/blog/best-practices-instrumenting-opentelemetry">Best practices for instrumenting OpenTelemetry</a></li>
  </ul>
  <p>General configuration and use case resources:</p>
  <ul>
  <li><a href="https://www.elastic.co/blog/opentelemetry-observability">Independence with OpenTelemetry on Elastic</a></li>
  <li><a href="https://www.elastic.co/blog/implementing-kubernetes-observability-security-opentelemetry">Modern observability and security on Kubernetes with Elastic and OpenTelemetry</a></li>
  <li><a href="https://www.elastic.co/blog/3-models-logging-opentelemetry-elastic">3 models for logging with OpenTelemetry and Elastic</a></li>
  <li><a href="https://www.elastic.co/blog/adding-free-and-open-elastic-apm-as-part-of-your-elastic-observability-deployment">Adding free and open Elastic APM as part of your Elastic Observability deployment</a></li>
  <li><a href="https://www.elastic.co/blog/custom-metrics-app-code-java-agent-plugin">Capturing custom metrics through OpenTelemetry API in code with Elastic</a></li>
  <li><a href="https://www.elastic.co/virtual-events/future-proof-your-observability-platform-with-opentelemetry-and-elastic">Future-proof your observability platform with OpenTelemetry and Elastic</a></li>
  <li><a href="https://www.elastic.co/blog/kubernetes-k8s-observability-elasticsearch-cncf">Elastic Observability: Built for open technologies like Kubernetes, OpenTelemetry, Prometheus, Istio, and more</a></li>
  </ul>
</blockquote>
<p>Don’t have an Elastic Cloud account yet? <a href="https://cloud.elastic.co/registration">Sign up for Elastic Cloud</a> and try out the auto-instrumentation capabilities that I discussed above. I would be interested in getting your feedback about your experience in gaining visibility into your application stack with Elastic.</p>
<p><em>The release and timing of any features or functionality described in this post remain at Elastic's sole discretion. Any features or functionality not currently available may not be delivered on time or at all.</em></p>]]></content:encoded>
    <link>https://www.elastic.co/observability-labs/blog/auto-instrumentation-java-applications-opentelemetry</link>
    <guid isPermaLink="false">auto-instrumentation-java-applications-opentelemetry</guid>
    <category><![CDATA[OpenTelemetry]]></category>
    <category><![CDATA[APM]]></category>
    <category><![CDATA[Infrastructure Monitoring]]></category>
    <dc:creator><![CDATA[David Hope]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt90b40c245a46b729/6a85c7e880984c7b39668f6c/observability-launch-series-3-java-auto.jpg" length="0" type="image/jpeg"/>
    <pubDate>Thu, 31 Aug 2023 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Automatic instrumentation with OpenTelemetry for Node.js applications]]></title>
    <description><![CDATA[Learn how to auto-instrument Node.js applications using OpenTelemetry. With standard commands in a Docker file, applications can be instrumented quickly without writing code in multiple places, enabling rapid change, scale, and easier management.]]></description>
    <content:encoded><![CDATA[<p>DevOps and SRE teams are transforming the process of software development. While DevOps engineers focus on efficient software applications and service delivery, SRE teams are key to ensuring reliability, scalability, and performance. These teams must rely on a full-stack observability solution that allows them to manage and monitor systems and ensure issues are resolved before they impact the business.</p>
<p>Observability across the entire stack of modern distributed applications requires data collection, processing, and correlation often in the form of dashboards. Ingesting all system data requires installing agents across stacks, frameworks, and providers — a process that can be challenging and time-consuming for teams who have to deal with version changes, compatibility issues, and proprietary code that doesn't scale as systems change.</p>
<p>Thanks to <a href="http://opentelemetry.io">OpenTelemetry</a> (OTel), DevOps and SRE teams now have a standard way to collect and send data that doesn't rely on proprietary code and have a large support community reducing vendor lock-in.</p>
<p>In a <a href="https://www.elastic.co/blog/opentelemetry-observability">previous blog</a>, we also reviewed how to use the <a href="https://github.com/elastic/opentelemetry-demo">OpenTelemetry demo</a> and connect it to Elastic<sup>®</sup>, as well as some of Elastic’s capabilities with OpenTelemetry and Kubernetes.</p>
<p>In this blog, we will show how to use <a href="https://opentelemetry.io/docs/instrumentation/js/automatic/">automatic instrumentation for OpenTelemetry</a> with the Node.js service of our <a href="https://github.com/elastic/observability-examples">application called Elastiflix</a>, which helps highlight auto-instrumentation in a simple way.</p>
<p>The beauty of this is that there is <strong>no need for the otel-collector</strong>! This setup enables you to slowly and easily migrate an application to OTel with Elastic according to a timeline that best fits your business.</p>
<h2 id="applicationprerequisitesandconfig">Application, prerequisites, and config</h2>
<p>The application that we use for this blog is called <a href="https://github.com/elastic/observability-examples">Elastiflix</a>, a movie streaming application. It consists of several micro-services written in .NET, NodeJS, Go, and Python.</p>
<p>Before we instrument our sample application, we will first need to understand how Elastic can receive the telemetry data.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt043556fcb3946f75/6a85c7c10782902dad3216f6/elastic-blog-1-otel-config-options.png" alt="options" /></p>
<p>All of Elastic Observability’s APM capabilities are available with OTel data. Some of these include:</p>
<ul>
<li>Service maps</li>
<li>Service details (latency, throughput, failed transactions)</li>
<li>Dependencies between services, distributed tracing</li>
<li>Transactions (traces)</li>
<li>Machine learning (ML) correlations</li>
<li>Log correlation</li>
</ul>
<p>In addition to Elastic’s APM and a unified view of the telemetry data, you will also be able to use Elastic’s powerful machine learning capabilities to reduce the analysis, and alerting to help reduce MTTR.</p>
<h3 id="prerequisites">Prerequisites</h3>
<ul>
<li>An Elastic Cloud account — <a href="https://cloud.elastic.co/">sign up now</a></li>
<li>A clone of the <a href="https://github.com/elastic/observability-examples">Elastiflix demo application</a>, or your own <strong>Node.js</strong> application</li>
<li>Basic understanding of Docker — potentially install <a href="https://www.docker.com/products/docker-desktop/">Docker Desktop</a></li>
<li>Basic understanding of Node.js</li>
</ul>
<h3 id="viewtheexamplesourcecode">View the example source code</h3>
<p>The full source code, including the Dockerfile used in this blog, can be found on <a href="https://github.com/elastic/observability-examples/tree/main/Elastiflix/python-favorite-otel-auto">GitHub</a>. The repository also contains the <a href="https://github.com/elastic/observability-examples/tree/main/Elastiflix/python-favorite">same application without instrumentation</a>. This allows you to compare each file and see the differences.</p>
<p>The following steps will show you how to instrument this application and run it on the command line or in Docker. If you are interested in a more complete OTel example, take a look at the docker-compose file <a href="https://github.com/elastic/observability-examples/tree/main#start-the-app">here</a>, which will bring up the full project.</p>
<h2 id="stepbystepguide">Step-by-step guide</h2>
<h3 id="step0logintoyourelasticcloudaccount">Step 0. Log in to your Elastic Cloud account</h3>
<p>This blog assumes you have an Elastic Cloud account — if not, follow the <a href="https://cloud.elastic.co/registration?elektra=en-cloud-page">instructions to get started on Elastic Cloud</a>.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltd59feb054794c506/6a85c7c4d7b2e7ef9afe844e/elastic-blog-2-free-trial.png" alt="free trial" /></p>
<h3 id="step1configureautoinstrumentationforthenodejsservice">Step 1. Configure auto-instrumentation for the Node.js Service</h3>
<p>We are going to use automatic instrumentation with Node.js service from the <a href="https://github.com/elastic/observability-examples">Elastiflix demo application</a>.</p>
<p>We will be using the following service from Elastiflix:</p>
<pre><code>Elastiflix/node-server-otel-manual
</code></pre>
<p>Per the <a href="https://opentelemetry.io/docs/instrumentation/js/automatic/">OpenTelemetry JavaScript documentation</a> and <a href="https://www.npmjs.com/package/@opentelemetry/auto-instrumentations-node">@open-telemetry/auto-instrumentions-node</a> documentation, you will simply install the appropriate node packages using npm.</p>
<pre><code>npm install --save @opentelemetry/api
npm install --save @opentelemetry/auto-instrumentations-node
</code></pre>
<p>If you are running the Node.js service on the command line, then here is how you can run auto-instrument with Node.js.</p>
<pre><code>node --require '@opentelemetry/auto-instrumentations-node/register' app.js
</code></pre>
<p>For our application, we do this as part of the Dockerfile.</p>
<p><strong>Dockerfile</strong></p>
<pre><code>FROM node:14

WORKDIR /app

COPY ["package.json", "./"]
RUN ls
RUN npm install --production
COPY . .

RUN npm install --save @opentelemetry/api
RUN npm install --save @opentelemetry/auto-instrumentations-node


EXPOSE 3001

CMD ["node", "--require", "@opentelemetry/auto-instrumentations-node/register", "index.js"]
</code></pre>
<h3 id="step2runningthedockerimagewithenvironmentvariables">Step 2. Running the Docker image with environment variables</h3>
<p>As specified in the <a href="https://opentelemetry.io/docs/instrumentation/python/automatic/#configuring-the-agent">OTEL documentation</a>, we will use environment variables and pass in the configuration values to enable it to connect with <a href="https://www.elastic.co/guide/en/apm/guide/current/open-telemetry.html">Elastic Observability’s APM server</a>.</p>
<p>Because Elastic accepts OTLP natively, we just need to provide the Endpoint and authentication where the OTEL Exporter needs to send the data, as well as some other environment variables.</p>
<p><strong>Getting Elastic Cloud variables</strong><br />
You can copy the endpoints and token from Kibana<sup>®</sup> under the path /app/home#/tutorial/apm.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt7ee6995ab7d0d882/6a85c7c7f61d6e9f1c9c2a9f/elastic-blog-3-apm-agents.png" alt="apm agents" /></p>
<p>You will need to copy the following environment variables:</p>
<pre><code>OTEL_EXPORTER_OTLP_ENDPOINT
OTEL_EXPORTER_OTLP_HEADERS
</code></pre>
<p><strong>Build the image</strong></p>
<pre><code>docker build -t  node-otel-auto-image .
</code></pre>
<p><strong>Run the image</strong></p>
<pre><code>docker run \
       -e OTEL_EXPORTER_OTLP_ENDPOINT="&lt;REPLACE WITH OTEL_EXPORTER_OTLP_ENDPOINT&gt;" \
       -e OTEL_EXPORTER_OTLP_HEADERS="Authorization=Bearer &lt;REPLACE WITH TOKEN&gt;" \
       -e OTEL_RESOURCE_ATTRIBUTES="service.version=1.0,deployment.environment=production" \
       -e OTEL_SERVICE_NAME="node-server-otel-auto" \
       -p 3001:3001 \
       node-server-otel-auto
</code></pre>
<p>You can now issue a few requests in order to generate trace data. Note that these requests are expected to return an error, as this service relies on some downstream services that you may not have running on your machine.</p>
<pre><code>curl localhost:3001/api/login
curl localhost:3001/api/favorites

# or alternatively issue a request every second

while true; do curl "localhost:3001/api/favorites"; sleep 1; done;
</code></pre>
<h3 id="step3exploretracesmetricsandlogsinelasticapm">Step 3: Explore traces, metrics, and logs in Elastic APM</h3>
<p>Exploring the Services section in Elastic APM, you’ll see the Node service displayed.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt87470325d2447bbd/6a85c7ca80984cd08f668f5c/elastic-blog-4-services.png" alt="services" /></p>
<p>Clicking on the node-server-otel-auto service, you can see that it is ingesting telemetry data using OpenTelemetry.</p>
<h2 id="summary">Summary</h2>
<p>In this blog, we discussed the following:</p>
<ul>
<li>How to auto-instrument Node.js with OpenTelemetry</li>
<li>Using standard commands in a Dockerfile, auto-instrumentation was done efficiently and without adding code in multiple places enabling manageability</li>
</ul>
<p>Since Elastic can support a mix of methods for ingesting data, whether it be using auto-instrumentation of open-source OpenTelemetry or manual instrumentation with its native APM agents, you can plan your migration to OTel by focusing on a few applications first and then using OpenTelemety across your applications later on in a manner that best fits your business needs.</p>
<blockquote>
  <p>Developer resources:</p>
  <ul>
  <li><a href="https://www.elastic.co/blog/getting-started-opentelemetry-instrumentation-sample-app">Elastiflix application</a>, a guide to instrument different languages with OpenTelemetry</li>
  <li>Python: <a href="https://www.elastic.co/blog/auto-instrumentation-of-python-applications-opentelemetry">Auto-instrumentation</a>, <a href="https://www.elastic.co/blog/manual-instrumentation-of-python-applications-opentelemetry">Manual-instrumentation</a></li>
  <li>Java: <a href="https://www.elastic.co/blog/auto-instrumentation-of-java-applications-opentelemetry">Auto-instrumentation</a>, <a href="https://www.elastic.co/blog/manual-instrumentation-of-java-applications-opentelemetry">Manual-instrumentation</a></li>
  <li>Node.js: <a href="https://www.elastic.co/blog/auto-instrument-nodejs-apps-opentelemetry">Auto-instrumentation</a>, <a href="https://www.elastic.co/blog/manual-instrumentation-of-nodejs-applications-opentelemetry">Manual-instrumentation</a></li>
  <li>.NET: <a href="https://www.elastic.co/blog/auto-instrumentation-of-net-applications-opentelemetry">Auto-instrumentation</a>, <a href="https://www.elastic.co/blog/manual-instrumentation-of-net-applications-opentelemetry">Manual-instrumentation</a></li>
  <li>Go: <a href="https://elastic.co/blog/manual-instrumentation-of-go-applications-opentelemetry">Manual-instrumentation</a></li>
  <li><a href="https://www.elastic.co/blog/best-practices-instrumenting-opentelemetry">Best practices for instrumenting OpenTelemetry</a></li>
  </ul>
  <p>General configuration and use case resources:</p>
  <ul>
  <li><a href="https://www.elastic.co/blog/opentelemetry-observability">Independence with OpenTelemetry on Elastic</a></li>
  <li><a href="https://www.elastic.co/blog/implementing-kubernetes-observability-security-opentelemetry">Modern observability and security on Kubernetes with Elastic and OpenTelemetry</a></li>
  <li><a href="https://www.elastic.co/blog/3-models-logging-opentelemetry-elastic">3 models for logging with OpenTelemetry and Elastic</a></li>
  <li><a href="https://www.elastic.co/blog/adding-free-and-open-elastic-apm-as-part-of-your-elastic-observability-deployment">Adding free and open Elastic APM as part of your Elastic Observability deployment</a></li>
  <li><a href="https://www.elastic.co/blog/custom-metrics-app-code-java-agent-plugin">Capturing custom metrics through OpenTelemetry API in code with Elastic</a></li>
  <li><a href="https://www.elastic.co/virtual-events/future-proof-your-observability-platform-with-opentelemetry-and-elastic">Future-proof your observability platform with OpenTelemetry and Elastic</a></li>
  <li><a href="https://www.elastic.co/blog/kubernetes-k8s-observability-elasticsearch-cncf">Elastic Observability: Built for open technologies like Kubernetes, OpenTelemetry, Prometheus, Istio, and more</a></li>
  </ul>
</blockquote>
<p>Don’t have an Elastic Cloud account yet? <a href="https://cloud.elastic.co/registration">Sign up for Elastic Cloud</a> and try out the auto-instrumentation capabilities that I discussed above. I would be interested in getting your feedback about your experience in gaining visibility into your application stack with Elastic.</p>
<p><em>The release and timing of any features or functionality described in this post remain at Elastic's sole discretion. Any features or functionality not currently available may not be delivered on time or at all.</em></p>]]></content:encoded>
    <link>https://www.elastic.co/observability-labs/blog/auto-instrument-nodejs-apps-opentelemetry</link>
    <guid isPermaLink="false">auto-instrument-nodejs-apps-opentelemetry</guid>
    <category><![CDATA[OpenTelemetry]]></category>
    <category><![CDATA[APM]]></category>
    <category><![CDATA[Infrastructure Monitoring]]></category>
    <dc:creator><![CDATA[Bahubali Shetti]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt0c24822f35077032/6a85c7cd9829268c70583868/observability-launch-series-1-node-js-auto_(1).jpg" length="0" type="image/jpeg"/>
    <pubDate>Wed, 30 Aug 2023 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[How to deploy Hello World Elastic Observability on Google Cloud Run]]></title>
    <description><![CDATA[Follow the step-by-step process of instrumenting Elastic Observability for a Hello World web app running on Google Cloud Run.]]></description>
    <content:encoded><![CDATA[<p>Elastic Cloud Observability is the premiere tool to provide visibility into your running web apps. Google Cloud Run is the serverless platform of choice to run your web apps that need to scale up massively and scale down to zero. Elastic Observability combined with Google Cloud Run is the perfect solution for developers to deploy <a href="https://www.elastic.co/blog/observability-powerful-flexible-efficient">web apps that are auto-scaled with fully observable operations</a>, in a way that’s straightforward to implement and manage.</p>
<p>This blog post will show you how to deploy a simple Hello World web app to Cloud Run and then walk you through the steps to instrument the Hello World web app to enable observation of the application’s operations with Elastic Cloud.</p>
<h2 id="elasticobservabilitysetup">Elastic Observability setup</h2>
<p>We’ll start with setting up an Elastic Cloud deployment, which is where observability will take place for the web app we’ll be deploying.</p>
<p>From the <a href="https://cloud.elastic.co">Elastic Cloud console</a>, select <strong>Create deployment</strong>.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt794a66370574c9d9/6a85cb45331d7a5ad6c3179b/elastic-blog-1-create-deployment.png" alt="create deployment" /></p>
<p>Enter a deployment name and click <strong>Create deployment</strong>. It takes a few minutes for your deployment to be created. While waiting, you are prompted to save the admin credentials for your deployment, which provides you with superuser access to your Elastic<sup>®</sup> deployment. Keep these credentials safe as they are shown only once.</p>
<p>Elastic Observability requires an APM Server URL and an APM Secret token for an app to send observability data to Elastic Cloud. Once the deployment is created, we’ll copy the Elastic Observability server URL and secret token and store them somewhere safely for adding to our web app code in a later step.</p>
<p>To copy the APM Server URL and the APM Secret Token, go to <a href="https://cloud.elastic.co/home">Elastic Cloud</a>. Then go to the <a href="https://cloud.elastic.co/deployments">Deployments</a> page which lists all of the deployments you have created. Select the deployment you want to use, which will open the deployment details page. In the <strong>Kibana</strong> row of links, click on <strong>Open</strong> to open <strong>Kibana</strong> for your deployment.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt1b98ae3828170fd6/6a85cb479bf99464a60a0579/elastic-blog-2-my-deployment.png" alt="my deployment" /></p>
<p>Select <strong>Integrations</strong> from the top-level menu. Then click the <strong>APM</strong> tile.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt970877511664c0bb/6a85cb4a682666c4891eac0f/elastic-blog-3-apm.png" alt="apm" /></p>
<p>On the APM Agents page, copy the secretToken and the serverUrl values and save them for use in a later step.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt306a2bfcec5a0c94/6a85cb4dbc5bb3e6a7f81b07/elastic-blog-4-apm-agents.png" alt="apm agents" /></p>
<p>Now that we’ve completed the Elastic Cloud setup, the next step is to set up our Google Cloud project for deploying apps to Cloud Run.</p>
<h2 id="googlecloudrunsetup">Google Cloud Run setup</h2>
<p>First we’ll need a Google Cloud project, so let’s create one by going to the <a href="https://console.cloud.google.com">Google Cloud console</a> and creating a new project. Select the project menu and then click the <strong>New Project</strong> button.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt1361049eefbcb893/6a85cb5093ffb94956b91439/elastic-blog-5-google-cloud-gray-dropdown.png" alt="google cloud with gray dropdown" /></p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt50aaf60c2197b967/6a85cb53501a850afbfbb331/elastic-blog-5-select-a-project.png" alt="select a project" /></p>
<p>Once the new project is created, we’ll need to enable the necessary APIs that our Hello World app will be using. This can be done by clicking this <a href="https://console.cloud.google.com/flows/enableapi?apiid=compute.googleapis.com,,run.googleapis.com,containerregistry.googleapis.com,cloudbuild.googleapis.com">enable APIs</a> link, which opens a page in the Google Cloud console that lists the APIs that will be enabled and allows us to confirm their activation.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltaffea5bf8711fd60/6a85cb560782907802321774/elastic-blog-6-enable-apis.png" alt="enable apis" /></p>
<p>After we’ve enabled the necessary APIs, we’ll need to set up the required permissions for our Hello World app, which can be done in the <a href="https://console.cloud.google.com/iam-admin">IAM section</a> of the Google Cloud Console. Within the IAM section, select the <strong>Compute Engine</strong> default service account and add the following roles:</p>
<ul>
<li>Logs Viewer</li>
<li>Monitoring Viewer</li>
<li>Pub/Sub Subscriber</li>
</ul>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt4acf3604f25e2b44/6a85cb59501a855036fbb337/elastic-blog-7-principals.png" alt="principals" /></p>
<h2 id="deployahelloworldwebapptocloudrun">Deploy a Hello World web app to Cloud Run</h2>
<p>We’ll perform the process of deploying a Node.js Hello World web app to Cloud Run using the handy Google Cloud tool called <a href="https://console.cloud.google.com/cloudshelleditor">Cloud Shell Editor</a>. To deploy the Hello World app, we’ll perform the following five steps:</p>
<ol>
<li>In Cloud Shell Editor, in the terminal window that appears at the bottom of the screen, clone a <a href="https://github.com/elastic/observability-examples/tree/main/gcp/run/helloworld">Node.js Hello World sample app</a> repo from GitHub by entering the following command.</li>
</ol>
<pre><code>git clone https://github.com/elastic/observability-examples
</code></pre>
<ol>
<li>Change directory to the location of the Hello World web app code.</li>
</ol>
<pre><code>cd gcp/run/helloworld
</code></pre>
<ol>
<li>Build the Hello World app image and push the image to Google Container Registry by running the command below in the terminal. Be sure to replace your-project-id in the command below with your actual Google Cloud project ID.</li>
</ol>
<pre><code>gcloud builds submit --tag gcr.io/your-project-id/elastic-helloworld
</code></pre>
<ol>
<li>Deploy the Hello World app to Google Cloud Run by running the command below. Be sure to replace your-project-id in the command below with your actual Google Cloud project ID.</li>
</ol>
<pre><code>gcloud run deploy elastic-helloworld --image gcr.io/your-project-id/elastic-helloworld
</code></pre>
<ol>
<li>When the deployment process is complete, a Service URL will be displayed within the terminal. Copy and paste the Service URL in a browser to view the Hello World app running in Cloud Run.</li>
</ol>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt878d233fbf177588/6a85cb5cf61d6ec4629c2b27/elastic-blog-8-hello-world.png" alt="hello world" /></p>
<h2 id="instrumentthehelloworldwebappwithelasticobservability">Instrument the Hello World web app with Elastic Observability</h2>
<p>With a web app successfully running in Cloud Run, we’re now ready to add the minimal code necessary to start monitoring the app. To enable observability for the Hello World app in Elastic Cloud, we’ll perform the following six steps:</p>
<ol>
<li>In the Google Cloud Shell Editor, edit the Dockerfile file to add the following Elastic Open Telemetry environment variables along with the commands to install and run the Elastic APM agent. Replace the ELASTIC_APM_SERVER_URL text and the ELASTIC_APM_SECRET_TOKEN text with the APM Server URL and the APM Secret Token values that you copied and saved in an earlier step.</li>
</ol>
<pre><code>ENV OTEL_EXPORTER_OTLP_ENDPOINT='ELASTIC_APM_SERVER_URL'
ENV OTEL_EXPORTER_OTLP_HEADERS='Authorization=Bearer ELASTIC_APM_SECRET_TOKEN'
ENV OTEL_LOG_LEVEL=info
ENV OTEL_METRICS_EXPORTER=otlp
ENV OTEL_RESOURCE_ATTRIBUTES=service.version=1.0,deployment.environment=production
ENV OTEL_SERVICE_NAME=helloworld
ENV OTEL_TRACES_EXPORTER=otlp
RUN npm install --save @opentelemetry/api
RUN npm install --save @opentelemetry/auto-instrumentations-node
CMD ["node", "--require", "@opentelemetry/auto-instrumentations-node/register", "index.js"]
</code></pre>
<p>The updated Dockerfile should look something like this:</p>
<pre><code>FROM node:18-slim
WORKDIR /usr/src/app
COPY package*.json ./
RUN npm install --only=production
COPY . ./
OTEL_EXPORTER_OTLP_ENDPOINT='https://******.apm.us-central1.gcp.cloud.es.io:443'
ENV OTEL_EXPORTER_OTLP_HEADERS='Authorization=Bearer ******************'
ENV OTEL_LOG_LEVEL=info
ENV OTEL_METRICS_EXPORTER=otlp
ENV OTEL_RESOURCE_ATTRIBUTES=service.version=1.0,deployment.environment=production
ENV OTEL_SERVICE_NAME=helloworld
ENV OTEL_TRACES_EXPORTER=otlp
RUN npm install --save @opentelemetry/api
RUN npm install --save @opentelemetry/auto-instrumentations-node
CMD ["node", "--require", "@opentelemetry/auto-instrumentations-node/register", "index.js"]
</code></pre>
<ol>
<li>In the Google Cloud Shell Editor, edit the package.json file to add the Elastic APM dependency. The dependencies section in package.json should look something like this:</li>
</ol>
<pre><code>"dependencies": {
      "express": "^4.18.2",
      "elastic-apm-node": "^3.49.1"
  },
</code></pre>
<ol>
<li>In the Google Cloud Shell Editor, edit the index.js file:</li>
</ol>
<ul>
<li>Add the code required to initialize the Elastic Open Telemetry APM agent:</li>
</ul>
<pre><code>const otel = require("@opentelemetry/api");
const tracer = otel.trace.getTracer("hello-world");
</code></pre>
<ul>
<li>Replace the “Hello World!” output code . . .</li>
</ul>
<pre><code>res.send(`&lt;h1&gt;Hello World!&lt;/h1&gt;`);
</code></pre>
<p>…with the “Hello Elastic Observability” code block.</p>
<pre><code>res.send(
  `&lt;div style="text-align: center;"&gt;
   &lt;h1 style="color: #005A9E; font-family:'Verdana'"&gt;
   Hello Elastic Observability - Google Cloud Run - Node.js
   &lt;/h1&gt;
   &lt;img src="https://storage.googleapis.com/elastic-helloworld/elastic-logo.png"&gt;
   &lt;/div&gt;`
);
</code></pre>
<ul>
<li>Add a trace “hi” before the “Hello Elastic Observability” code block and add a trace “bye” after the “Hello Elastic Observability” code block.</li>
</ul>
<pre><code>tracer.startActiveSpan("hi", (span) =&gt; {
  console.log("hello");
  span.end();
});
res.send(
  `&lt;div style="text-align: center;"&gt;
   &lt;h1 style="color: #005A9E; font-family:'Verdana'"&gt;
   Hello Elastic Observability - Google Cloud Run - Node.js
   &lt;/h1&gt;
   &lt;img src="https://storage.googleapis.com/elastic-helloworld/elastic-logo.png"&gt;
   &lt;/div&gt;`
);
tracer.startActiveSpan("bye", (span) =&gt; {
  console.log("goodbye");
  span.end();
});
</code></pre>
<ul>
<li>The completed index.js file should look something like this:</li>
</ul>
<pre><code>const otel = require("@opentelemetry/api");
const tracer = otel.trace.getTracer("hello-world");

const express = require("express");
const app = express();

app.get("/", (req, res) =&gt; {
  tracer.startActiveSpan("hi", (span) =&gt; {
    console.log("hello");
    span.end();
  });
  res.send(
    `&lt;div style="text-align: center;"&gt;
    &lt;h1 style="color: #005A9E; font-family:'Verdana'"&gt;
    Hello Elastic Observability - Google Cloud Run - Node.js
    &lt;/h1&gt;
   &lt;img src="https://storage.googleapis.com/elastic-helloworld/elastic-logo.png"&gt;
    &lt;/div&gt;`
  );
  tracer.startActiveSpan("bye", (span) =&gt; {
    console.log("goodbye");
    span.end();
  });
});

const port = parseInt(process.env.PORT) || 8080;
app.listen(port, () =&gt; {
  console.log(`helloworld: listening on port ${port}`);
});
</code></pre>
<ol>
<li>Rebuild the Hello World app image and push the image to the Google Container Registry by running the command below in the terminal. Be sure to replace your-project-id in the command below with your actual Google Cloud project ID.</li>
</ol>
<pre><code>gcloud builds submit --tag gcr.io/your-project-id/elastic-helloworld
</code></pre>
<ol>
<li>Redeploy the Hello World app to Google Cloud Run by running the command below. Be sure to replace your-project-id in the command below with your actual Google Cloud project ID.</li>
</ol>
<pre><code>gcloud run deploy elastic-helloworld --image gcr.io/your-project-id/elastic-helloworld
</code></pre>
<ol>
<li>When the deployment process is complete, a Service URL will be displayed within the terminal. Copy and paste the Service URL in a browser to view the updated Hello World app running in Cloud Run.</li>
</ol>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt3a655bc1371c8bc8/6a85cb5fd7b2e7b297fe84da/elastic-blog-9-elastic-logo.png" alt="elastic logo" /></p>
<h2 id="observethehelloworldwebapp">Observe the Hello World web app</h2>
<p>Now that we’ve instrumented the web app to send observability data to Elastic Observability, we can now use Elastic Cloud to monitor the web app’s operations.</p>
<ol>
<li><p>In Elastic Cloud, select the Observability <strong>Services</strong> menu item.</p></li>
<li><p>Click the <strong>helloworld</strong> service.</p></li>
<li><p>Click the <strong>Transactions</strong> tab.</p></li>
<li><p>Scroll down and click the <strong>GET /</strong> transaction.</p></li>
<li><p>Scroll down to the <strong>Trace Sample</strong> section to see the <strong>GET /</strong> , <strong>hi</strong> and <strong>bye</strong> trace samples.</p></li>
</ol>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltee3cd0244463c5a9/6a85cb622d64d5f7f3081d48/elastic-blog-10-trace-sample.png" alt="trace sample" /></p>
<h2 id="observabilitymadetoscale">Observability made to scale</h2>
<p>You’ve seen the entire process of deploying a web app to Google Cloud Run that is instrumented with Elastic Observability. The end result is a web app that will scale up and down with demand combined with the observability tools to monitor the web app as it serves a single user or millions of users.</p>
<p>Now that you’ve seen how to deploy a serverless web app instrumented with observability, visit <a href="https://www.elastic.co/observability">Elastic Observability</a> to learn more about how to implement a complete observability solution for your apps. Or visit <a href="https://www.elastic.co/getting-started/google-cloud">Getting started with Elastic on Google Cloud</a> for more examples of how you can drive the data insights you need by combining <a href="https://www.elastic.co/observability/google-cloud-monitoring">Google Cloud monitoring</a> and cloud computing services with Elastic’s search-powered platform.</p>
<p><em>The release and timing of any features or functionality described in this post remain at Elastic's sole discretion. Any features or functionality not currently available may not be delivered on time or at all.</em></p>]]></content:encoded>
    <link>https://www.elastic.co/observability-labs/blog/deploy-observability-google-cloud-run</link>
    <guid isPermaLink="false">deploy-observability-google-cloud-run</guid>
    <category><![CDATA[Infrastructure Monitoring]]></category>
    <category><![CDATA[APM]]></category>
    <dc:creator><![CDATA[Jonathan Simon]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltab62612301e9c429/6a85cb65f5f1a02e662ec909/illustration-dev-sec-ops-cloud-automations-1680x980.png" length="0" type="image/png"/>
    <pubDate>Mon, 28 Aug 2023 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Optimizing cloud resources and cost with APM metadata in Elastic Observability]]></title>
    <description><![CDATA[Optimize cloud costs with Elastic APM. Learn how to leverage cloud metadata, calculate pricing, and make smarter decisions for better performance.]]></description>
    <content:encoded><![CDATA[<p>Application performance monitoring (APM) is much more than capturing and tracking errors and stack traces. Today’s cloud-based businesses deploy applications across various regions and even cloud providers. So, harnessing the power of metadata provided by the Elastic APM agents becomes more critical. Leveraging the metadata, including crucial information like cloud region, provider, and machine type, allows us to track costs across the application stack. In this blog post, we look at how we can use cloud metadata to empower businesses to make smarter and cost-effective decisions, all while improving resource utilization and the user experience.</p>
<p>First, we need an example application that allows us to monitor infrastructure changes effectively. We use a Python Flask application with the Elastic Python APM agent. The application is a simple calculator taking the numbers as a REST request. We utilize Locust — a simple load-testing tool to evaluate performance under varying workloads.</p>
<p>The next step includes obtaining the pricing information associated with the cloud services. Every cloud provider is different. Most of them offer an option to retrieve pricing through an API. But today, we will focus on Google Cloud and will leverage their pricing calculator to retrieve relevant cost information.</p>
<h2 id="thecalculatorandgooglecloudpricing">The calculator and Google Cloud pricing</h2>
<p>To perform a cost analysis, we need to know the cost of the machines in use. Google provides a billing <a href="https://cloud.google.com/billing/v1/how-tos/catalog-api">API</a> and <a href="https://cloud.google.com/billing/docs/reference/libraries#client-libraries-install-python">Client Library</a> to fetch the necessary data programmatically. In this blog, we are not covering the API approach. Instead, the <a href="https://cloud.google.com/products/calculator">Google Cloud Pricing Calculator</a> is enough. Select the machine type and region in the calculator and set the count 1 instance. It will then report the total estimated cost for this machine. Doing this for an e2-standard-4 machine type results in 107.7071784 US$ for a runtime of 730 hours.</p>
<p>Now, let’s go to our Kibana® where we will create a new index inside Dev Tools. Since we don’t want to analyze text, we will tell Elasticsearch® to treat every text as a keyword. The index name is cloud-billing. I might want to do the same for Azure and AWS, then I can append it to the same index.</p>
<pre><code>PUT cloud-billing
{
  "mappings": {
    "dynamic_templates": [
      {
        "stringsaskeywords": {
          "match": "*",
          "match_mapping_type": "string",
          "mapping": {
            "type": "keyword"
          }
        }
      }
    ]
  }
}
</code></pre>
<p>Next up is crafting our billing document:</p>
<pre><code>POST cloud-billing/_doc/e2-standard-4_europe-west4
{
  "machine": {
    "enrichment": "e2-standard-4_europe-west4"
  },
  "cloud": {
    "machine": {
       "type": "e2-standard-4"
    },
    "region": "europe-west4",
    "provider": "google"
  },
  "stats": {
    "cpu": 4,
    "memory": 8
  },
  "price": {
    "minute": 0.002459068,
    "hour": 0.14754408,
    "month": 107.7071784
  }
}
</code></pre>
<p>We create a document and set a custom ID. This ID matches the instance name and the region since the machines' costs may differ in each region. Automatic IDs could be problematic because I might want to update what a machine costs regularly. I could use a timestamped index for that and only ever use the latest document matching. But this way, I can update and don’t have to worry about it. I calculated the price down to minute and hour prices as well. The most important thing is the machine.enrichment field, which is the same as the ID. The same instance type can exist in multiple regions, but our enrichment processor is limited to match or range. We create a matching name that can explicitly match as in e2-standard-4_europe-west4. It’s up to you to decide whether you want the cloud provider in there and make it google_e2-standard-4_europ-west-4.</p>
<h2 id="calculatingthecost">Calculating the cost</h2>
<p>There are multiple ways of achieving this in the Elastic Stack. In this case, we will use an enrich policy, ingest pipeline, and transform.</p>
<p>The enrich policy is rather easy to setup:</p>
<pre><code>PUT _enrich/policy/cloud-billing
{
  "match": {
    "indices": "cloud-billing",
    "match_field": "machine.enrichment",
    "enrich_fields": ["price.minute", "price.hour", "price.month"]
  }
}

POST _enrich/policy/cloud-billing/_execute
</code></pre>
<p>Don’t forget to run the _execute at the end of it. This is necessary to make the internal indices used by the enrichment in the ingest pipeline. The ingest pipeline is rather minimalistic — it calls the enrichment and renames a field. This is where our machine.enrichment field comes in. One caveat around enrichment is that when you add new documents to the cloud-billing index, you need to rerun the _execute statement. The last bit calculates the total cost with the count of unique machines seen.</p>
<pre><code>PUT _ingest/pipeline/cloud-billing
{
  "processors": [
    {
      "set": {
        "field": "_temp.machine_type",
        "value": "{{cloud.machine.type}}_{{cloud.region}}"
      }
    },
    {
      "enrich": {
        "policy_name": "cloud-billing",
        "field": "_temp.machine_type",
        "target_field": "enrichment"
      }
    },
    {
      "rename": {
        "field": "enrichment.price",
        "target_field": "price"
      }
    },
    {
      "remove": {
        "field": [
          "_temp",
          "enrichment"
        ]
      }
    },
    {
      "script": {
        "source": "ctx.total_price=ctx.count_machines*ctx.price.hour"
      }
    }
  ]
}
</code></pre>
<p>Since this is all configured now, we are ready for our Transform. For this, we need a data view that matches the APM data_streams. This is traces-apm*, metrics-apm.*, logs-apm.*. For the Transform, go to the Transform UI in Kibana and configure it in the following way:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blta18f78e9e834482f/6a85cdeef61d6e83339c2b75/elastic-blog-1-transform-configuration.png" alt="transform configuration" /></p>
<p>We are doing an hourly breakdown, therefore, I get a document per service, per hour, per machine type. The interesting bit is the aggregations. I want to see the average CPU usage and the 75,95,99 percentile, to view the CPU usage on an hourly basis. Allowing me to identify the CPU usage across an hour. At the bottom, give the transform a name and select an index cloud-costs and select the cloud-billing ingest pipeline.</p>
<p>Here is the entire transform as a JSON document:</p>
<pre><code>PUT _transform/cloud-billing
{
  "source": {
    "index": [
      "traces-apm*",
      "metrics-apm.*",
      "logs-apm.*"
    ],
    "query": {
      "bool": {
        "filter": [
          {
            "bool": {
              "should": [
                {
                  "exists": {
                    "field": "cloud.provider"
                  }
                }
              ],
              "minimum_should_match": 1
            }
          }
        ]
      }
    }
  },
  "pivot": {
    "group_by": {
      "@timestamp": {
        "date_histogram": {
          "field": "@timestamp",
          "calendar_interval": "1h"
        }
      },
      "cloud.provider": {
        "terms": {
          "field": "cloud.provider"
        }
      },
      "cloud.region": {
        "terms": {
          "field": "cloud.region"
        }
      },
      "cloud.machine.type": {
        "terms": {
          "field": "cloud.machine.type"
        }
      },
      "service.name": {
        "terms": {
          "field": "service.name"
        }
      }
    },
    "aggregations": {
      "avg_cpu": {
        "avg": {
          "field": "system.cpu.total.norm.pct"
        }
      },
      "percentiles_cpu": {
        "percentiles": {
          "field": "system.cpu.total.norm.pct",
          "percents": [
            75,
            95,
            99
          ]
        }
      },
      "avg_transaction_duration": {
        "avg": {
          "field": "transaction.duration.us"
        }
      },
      "percentiles_transaction_duration": {
        "percentiles": {
          "field": "transaction.duration.us",
          "percents": [
            75,
            95,
            99
          ]
        }
      },
      "count_machines": {
        "cardinality": {
          "field": "cloud.instance.id"
        }
      }
    }
  },
  "dest": {
    "index": "cloud-costs",
    "pipeline": "cloud-costs"
  },
  "sync": {
    "time": {
      "delay": "120s",
      "field": "@timestamp"
    }
  },
  "settings": {
    "max_page_search_size": 1000
  }
}
</code></pre>
<p>Once the transform is created and running, we need a Kibana Data View for the index: cloud-costs. For the transaction, use the custom formatter inside Kibana and set its format to “Duration” in “microseconds.”</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt294ff077415936ae/6a85cdf18c2944544ab890b7/elastic-blog-2-cloud-costs.png" alt="cloud costs" /></p>
<p>With that, everything is arranged and ready to go.</p>
<h2 id="observinginfrastructurechanges">Observing infrastructure changes</h2>
<p>Below I created a dashboard that allows us to identify:</p>
<ul>
<li>How much costs a certain service creates</li>
<li>CPU usage</li>
<li>Memory usage</li>
<li>Transaction duration</li>
<li>Identify cost-saving potential</li>
</ul>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt0d759db22735c588/6a85cdf41aa1e12e81ff8dbb/elastic-blog-3-graphs.png" alt="graphs" /></p>
<p>From left to right, we want to focus on the very first chart. We have the bars representing the CPU as average in green and 95th percentile in blue on top. It goes from 0 to 100% and is normalized, meaning that even with 8 CPU cores, it will still read 100% usage and not 800%. The line graph represents the transaction duration, the average being in red, and the 95th percentile in purple. Last, we have the orange area at the bottom, which is the average memory usage on that host.</p>
<p>We immediately realize that our calculator does not need a lot of memory. Hovering over the graph reveals 2.89% memory usage. The e2-standard-8 machine that we are using has 32 GB of memory. We occasionally spike to 100% CPU in the 95th percentile. When this happens, we see that the average transaction duration spikes to 2.5 milliseconds. However, every hour this machine costs us a rounded 30 cents. Using this information, we can now downsize to a better fit. The average CPU usage is around 11-13%, and the 95th percentile is not that far away.</p>
<p>Because we are using 8 CPUs, one could now say that 12.5% represents a full core, but that is just an assumption on a piece of paper. Nonetheless, we know there is a lot of headroom, and we can downscale quite a bit. In this case, I decided to go to 2 CPUs and 2 GB of RAM, known as e2-highcpu2. This should fit my calculator application better. We barely touched the RAM, 2.89% out of 32GB are roughly 1GB of use. After the change and reboot of the calculator machine, I started the same Locust test to identify my CPU usage and, more importantly, if my transactions get slower, and if so, by how much. Ultimately, I want to decide whether 1 millisecond more latency is worth 10 more cents per hour. I added the change as an annotation in Lens.</p>
<p>After letting it run for a bit, we can now identify the smaller hosts' impact. In this case, we can see that the average did not change. However, the 95th percentile — as in 95% of all transactions are below this value — did spike up. Again, it looks bad at first, but checking in, it went from ~1.5 milliseconds to ~2.10 milliseconds, a ~0.6 millisecond increase. Now, you can decide whether that 0.6 millisecond increase is worth paying ~180$ more per month or if the current latency is good enough.</p>
<h2 id="conclusion">Conclusion</h2>
<p>Observability is more than just collecting logs, metrics, and traces. Linking user experience to cloud costs allows your business to identify areas where you can save money. Having the right tools at your disposal will help you generate those insights quickly. Making informed decisions about how to optimize your cloud cost and ultimately improve the user experience is the bottom-line goal.</p>
<p>The dashboard and data view can be found in my <a href="https://github.com/philippkahr/blogs/tree/main/apm-cost-optimisation">GitHub repository</a>. You can download the .ndjson file and import it using the Saved Objects inside Stack Management in Kibana.</p>
<h2 id="caveats">Caveats</h2>
<p>Pricing is only for base machines without any disk information, static public IP addresses, and any other additional cost, such as licenses for operating systems. Furthermore, it excludes spot pricing, discounts, or free credits. Additionally, data transfer costs between services are also not included. We only calculate it based on the minute rate of the service running — we are not checking billing intervals from Google Cloud. In our case, we would bill per minute, regardless of what Google Cloud has. Using the count for unique instance.ids work as intended. However, if a machine is only running for one minute, we calculate it based on the hourly rate. So, a machine running for one minute, will cost the same as running for 50 minutes — at least how we calculate it. The transform uses calendar hour intervals; therefore, it's 8 am-9 am, 9 am-10 am, and so on.</p>
<p><em>The release and timing of any features or functionality described in this post remain at Elastic's sole discretion. Any features or functionality not currently available may not be delivered on time or at all.</em></p>]]></content:encoded>
    <link>https://www.elastic.co/observability-labs/blog/optimize-cloud-resources-apm-observability</link>
    <guid isPermaLink="false">optimize-cloud-resources-apm-observability</guid>
    <category><![CDATA[Infrastructure Monitoring]]></category>
    <category><![CDATA[Metrics]]></category>
    <dc:creator><![CDATA[Philipp Kahr,Nathan Smith]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt0f205658f212e5d5/6a85cdf7bc5bb38ab5f81b49/illustration-out-of-box-data-vis-1680x980.png" length="0" type="image/png"/>
    <pubDate>Wed, 16 Aug 2023 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Managing your applications on Amazon ECS EC2-based clusters with Elastic Observability]]></title>
    <description><![CDATA[Learn how to manage applications on Amazon ECS clusters based on EC2 instances and how simple it is to use Elastic agents with the AWS and docker integrations to provide a complete picture of your apps, ECS service, and corresponding EC2 instances.]]></description>
    <content:encoded><![CDATA[<p>In previous blogs, we explored how Elastic Observability can help you monitor various AWS services and analyze them effectively:</p>
<ul>
<li><a href="https://www.elastic.co/blog/aws-service-metrics-monitor-observability-easy">Managing fundamental AWS services such as Amazon EC2, Amazon RDS, Amazon VPC, and NAT gateway</a></li>
<li><a href="https://www.elastic.co/blog/aws-kinesis-data-firehose-elastic-observability-analytics">Data can be ingested into Elastic observability using a serverless forwarder or Amazon Kinesis Data Firehose</a></li>
<li><a href="https://www.elastic.co/blog/vpc-flow-logs-monitoring-analytics-observability">Ingesting and analyzing AWS VPC Flow logs</a></li>
</ul>
<p>One of the more heavily used AWS container services is Amazon ECS (Elastic Container Service). While there is a trend toward using Fargate to simplify the setup and management of ECS clusters, many users still prefer using Amazon ECS with EC2 instances. It may not be as straightforward or efficient as AWS Fargate, but it offers more control over the underlying infrastructure.</p>
<p>In the most recent blog, we explored how <a href="https://www.elastic.co/blog/elastic-agent-monitor-ecs-aws-fargate-elastic-observability">Elastic Observability helps manage Amazon ECS with Fargate</a>. However, this blog will review how to manage an Amazon ECS cluster with EC2 instances using Elastic Observability instead.</p>
<p>In general, when setting up Amazon ECS-based clusters with EC2, you may or may not have access to the EC2 instances. This determines what you can use with Elastic Observability in monitoring your EC2-based ECS cluster. Hence, there are two components you can use in monitoring the EC2-based ECS cluster with Elastic Observability:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt147b9faa326e450c/6a85cc71bc5bb384fcf81b2b/elastic-blog-1-amazon-ecs.png" alt="amazon ecs" /></p>
<p>As you can see in the diagram above, the two components are:</p>
<ol>
<li><p> <strong>Baseline setup</strong> __ <strong>:</strong>  The elastic agent running the AWS integration is configured to obtain ECS metrics and logs from cloud watch. This agent runs on an instance that is not part of the ECS cluster because it allows you to see ALL ECS clusters and other AWS Services, such as EKS, RDS, and EC2.</p></li>
<li><p> <strong>Additional setup:</strong>  If you have access to the EC2 instances in the ECS cluster, then you can run Elastic’s docker integration in each EC2 instance. This gives you significantly more details on the containers than AWS container insights. And it does not require AWS Cloudwatch, which can be fairly costly.</p></li>
</ol>
<p>Using either just the baseline or the additional setup, you will have to set up AWS CloudWatch Container Insights for the ECS cluster. However, the docker integration with the additional setup can provide additional information to the AWS CloudWatch Container Insights.</p>
<p>Hence, we will review how you can monitor the various components of an EC2-based ECS cluster:</p>
<ul>
<li>EC2 instances in the ASG group</li>
<li>ECS services running in the ECS cluster</li>
<li>ECS tasks (containers)</li>
</ul>
<p>Also, we will review how you can obtain metrics and logs from the ECS cluster with and without AWS Cloudwatch. We’ll show you how to use:</p>
<ul>
<li>AWS CloudWatch Container Insights (from Cloudwatch)</li>
<li>Docker metrics (non-Cloudwatch)</li>
<li>Amazon ECS logs via Cloudwatch</li>
</ul>
<h2 id="prerequisitesandconfiguration">Prerequisites and configuration</h2>
<p>If you plan on following this blog, here are some of the components and details we used to set up the configuration:</p>
<ul>
<li>An account on <a href="http://cloud.elastic.co">Elastic Cloud</a> and a deployed stack (<a href="https://www.elastic.co/guide/en/elastic-stack/current/installing-elastic-stack.html">see instructions here</a>) — ensure that you have both.</li>
<li>A <a href="https://hub.docker.com/_/nginx">nginx</a> container and a <a href="https://github.com/containerstack/alpine-stress">stress container</a> — we will use these two basic containers to help highlight the load on the Elastic ECS Cluster.</li>
<li>An ECS EC2 Cluster in an Auto Scaling Group — ensure you have access in order to load up the Elastic agent on the EC2 instances, or you can create an AMI and use that as the baseline image for your ECS cluster.</li>
<li>An EC2 instance anywhere in your account that is not part of the ECS cluster and has public access (to send metrics and logs)</li>
</ul>
<h2 id="whatwillyouseeinelasticobservabilityonceitsallsetup">What will you see in Elastic Observability once it's all set up?</h2>
<p>If you utilize the baseline configuration with ECS EC2 cluster configured with AWS CloudWatch Container Insights configured, the Elastic Agent configured with the following Elastic agent integrations:</p>
<ul>
<li>ECS integration</li>
<li>EC2 integration</li>
<li>AWS Cloudwatch Integration with metrics and logging</li>
</ul>
<p>Then you will be able to get the following information in Elastic dashboards:</p>
<ul>
<li>Containers in the cluster (AWS CloudWatch Container Insights via Elastic Agent and AWS Cloudwatch integration)</li>
<li>Services in the cluster (AWS CloudWatch Container Insights via Elastic Agent and AWS Cloudwatch integration)</li>
<li>CPU and memory utilization of the ECS Cluster (Elastic Agent with ECS integration)</li>
<li>EC2 CPU and memory utilization of the instance in the cluster (Elastic Agent with EC2 integration)</li>
<li>CPU and memory utilization per container (via AWS CloudWatch Container Insights via Elastic Agent and AWS Cloudwatch integration)</li>
</ul>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt6f1993af959a0a6e/6a85cc741aa1e1343eff8d83/elastic-blog-2-containers-in-cluster.png" alt="containers in cluster" /></p>
<p>If the additional configuration using Elastic agents with docker integration per ECS EC2 instance is used, you will be able to get a direct feed of metrics via docker. The following metrics can be viewed:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltbe62d55a71be2bf2/6a85cc77f9373d3a8c96f5b8/elastic-blog-3-metrics-graphs.png" alt="metrics graphs" /></p>
<p>Let’s see how to set this all up.</p>
<h2 id="settingitallup">Setting it all up</h2>
<p>Over the next few steps, I’ll walk through:</p>
<ul>
<li>Getting an account on Elastic Cloud</li>
<li>Bringing up an ECS EC2 cluster and potentially setting up your own AMI</li>
<li>Setting up the containers <a href="https://hub.docker.com/_/nginx">nginx</a> and a <a href="https://github.com/containerstack/alpine-stress">stress container</a></li>
<li>Setting up the Elastic agent with docker container integration on the ECS EC2 instances</li>
<li>Setting up the Elastic agent with AWS, Cloudwatch, and ECS integrations on an independent EC2 instance</li>
</ul>
<h3 id="step1createanaccountonelasticcloud">Step 1: Create an account on Elastic Cloud</h3>
<p>Follow the instructions to <a href="https://cloud.elastic.co/registration?fromURI=/home">get started on Elastic Cloud</a>.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt729e8dd0d43f9e98/6a85cc7a99083f38e540fa01/elastic-blog-4-free-trial.png" alt="free trial" /></p>
<h3 id="step2setupanecsclusterwithec2instances">Step 2: Set up an ECS Cluster with EC2 instances</h3>
<p>When creating a cluster, you have two options when setting it up using the console:</p>
<ul>
<li>Create a new ASG group where you will only be allowed to use the preloaded set of Amazon Linux (2 or 2023) based AMIs</li>
<li>Set up your own ASG Cluster prior to setting up the ECS Cluster and select this from the options. This option will give you more control over what Linux version and the ability to add things like Elastic agents in the AMI used for the instances in the ASG.</li>
</ul>
<p>Regardless of either option, you will need to turn on <strong>Container Insights</strong> (see the bottom part of the image below).</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt23b7fd2c3de3b660/6a85cc7df5f1a031362ec933/elastic-blog-5-infrastructure.png" alt="infrastructure" /></p>
<p>Once the cluster is setup, you can go to AWS Cloudwatch where you should see Container Insights for your cluster:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt5b91cb55114bb5e7/6a85cc809a32f17b2ba7e010/elastic-blog-6-container-insights.png" alt="container insights" /></p>
<h3 id="step3setupelasticagentwithdockerintegration">Step 3: Set up Elastic agent with docker integration</h3>
<p>Next, you will need to add an Elastic agent to each one of the instances. In the Elastic cloud, set up an Elastic policy with the docker and system integrations as such:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt3ae989d6345a02e9/6a85cc824710c68330d3cb8f/elastic-blog-7-ecs-ec2-cluster-policy.png" alt="cluster policy" /></p>
<p>Next, add an agent for the policy, then copy the appropriate install script (in our case it was Linux since we were running Amazon Linux 2), and run it on every EC2 instance in the cluster:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt16988c0d57d08572/6a85cc866826662b0e1eac3b/elastic-blog-8-add-agent.png" alt="add agent" /></p>
<p>Once this is added you should see agents in the fleet. Each agent will be on each EC2 instance:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltf6a1f58551ef7adc/6a85cc89abdc29807812252c/elastic-blog-9-fleet.png" alt="fleet" /></p>
<p>If you decide to set up an ECS EC2 cluster with your own ASG and don’t use Amazon Lunix AMIs (2 or 2023 version), you will have to:</p>
<ul>
<li>Pick your base image to base an AMI on</li>
<li><a href="https://docs.aws.amazon.com/AmazonECS/latest/developerguide/ecs-agent-install.html">Add an ECS agent and register each instance to the AMI base image manually</a></li>
<li><a href="https://www.elastic.co/guide/en/fleet/current/install-standalone-elastic-agent.html">Add the Elastic agent — standalone version</a> — this step will require you to configure your Elastic endpoint and API key (or simply add the script in the “add agent” part of the configuration above when using the UI)</li>
<li>Create the AMI once all the above components are added</li>
<li>Use the newly created AMI in creating the ASG for ECS cluster</li>
</ul>
<h3 id="step4setupanelasticagentwiththeawsintegration">Step 4: Set up an Elastic agent with the AWS integration</h3>
<p>From the integrations tab in Elastic Cloud, select AWS integration and select add agent. You will then have to walk through the configuration of the AWS integration.</p>
<p>At a minimum, ensure that you have the following configuration options turned on:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt8a64f60f0c19f3e2/6a85cc8b342d6983cc21b113/elastic-blog-10-toggles.png" alt="toggles" /></p>
<p>This will ensure that not only EC2 metrics and logs are ingested but that all CloudWatch metrics and logs are also ingested. ECS metrics and logs are stored in CloudWatch.</p>
<p>If you want to ensure only logs from the specific ECS cluster are ingested, you can also restrict what to ingest by several parameters. In our setup, we are collecting only logs from Log Group with a prefix of /aws/ecs/containerinsights/EC2BasedCluster/.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltd5a5c6cf8e1ad163/6a85cc8e1aa1e126dfff8d8d/elastic-blog-11-cloudwatch.png" alt="cloudwatch" /></p>
<p>Once this policy is set up, add an agent like in Step 1.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt85dd93aa8f483a84/6a85cc919bf994835f0a059b/elastic-blog-12-add-agent-testing-aws.png" alt="add agent testing aws" /></p>
<p>However, this agent needs to be added to an EC2 instance which is independent of the ECS cluster.</p>
<p>Once installed, this agent will help pull in:</p>
<ul>
<li>All EC2 instance metrics across your account (which can be adjusted in the integration policy)</li>
<li>Ingest AWS CloudWatch Container Insights from ECS</li>
<li>ECS metrics such as:</li>
<li>aws.ecs.metrics.CPUReservation.avg</li>
<li>aws.ecs.metrics.CPUUtilization.avg</li>
<li>aws.ecs.metrics.GPUReservation.avg</li>
<li>aws.ecs.metrics.MemoryReservation.avg</li>
<li>aws.ecs.metrics.MemoryUtilization.avg</li>
<li><a href="https://docs.elastic.co/integrations/aws/ecs">More - see the full list here</a></li>
</ul>
<h3 id="step5settingupservicesandcontainers">Step 5: Setting up services and containers</h3>
<p>In running this configuration, we used <a href="https://hub.docker.com/_/nginx">nginx</a> and a <a href="https://github.com/containerstack/alpine-stress">stress container</a> before we go into the task.</p>
<p>In order to initiate service and containers on ECS, you will need to set up a task for each of these containers. But more importantly, you will need to ensure that the roles for both of the following:</p>
<p>"taskRoleArn": "arn:aws:iam::xxxxx:role/ecsTaskExecutionRol"executionRoleArn":,</p>
<p>"arn:aws:iam::xxxxx:role/ecsTaskExecutionRole",</p>
<p>have the following permissions:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt83a1840913b40744/6a85cc94bc5bb39d39f81b31/elastic-blog-13-permissions.png" alt="permissions" /></p>
<p>Most importantly, you should ensure that this permission is added:</p>
<p>AmazonEC2ContainerServiceforEC2Role</p>
<p>It will ensure containers can be brought up on the EC2 instances in the cluster.</p>
<p>Once you have the right permissions, then set up the following tasks.</p>
<p>Here is the task JSON for NGINX:</p>
<pre><code>{
  "family": "NGINX",
  "containerDefinitions": [
    {
      "name": "nginx",
      "image": "nginx: latest",
      "cpu": 0,
      "portMappings": [
        {
          "name": "nginx-80-tcp",
          "containerPort": 80,
          "hostPort": 80,
          "protocol": "tcp",
          "appProtocol": "http"
        }
      ],
      "essential": true,
      "environment": [],
      "environmentFiles": [],
      "mountPoints": [],
      "volumesFrom": [],
      "ulimits": [],
      "logConfiguration": {
        "logDriver": "awslogs",
        "options": {
          "awslogs-create-group": "true",
          "awslogs-group": "/ecs/",
          "awslogs-region": "us-west-2",
          "awslogs-stream-prefix": "ecs"
        },
        "secretOptions": []
      }
    }
  ],
  "taskRoleArn": "arn:aws:iam::xxxxxx:role/ecsTaskExecutionRole",
  "executionRoleArn": "arn:aws:iam::xxxxx:role/ecsTaskExecutionRole",
  "networkMode": "awsvpc",
  "requiresCompatibilities": ["EC2"],
  "cpu": "256",
  "memory": "512",
  "runtimePlatform": {
    "cpuArchitecture": "X86_64",
    "operatingSystemFamily": "LINUX"
  }
}
</code></pre>
<p>Here is the task JSON for stress container:</p>
<pre><code>{
  "family": "stressLoad",
  "containerDefinitions": [
    {
      "name": "stressLoad",
      "image": "containerstack/alpine-stress",
      "cpu": 0,
      "memory": 512,
      "memoryReservation": 512,
      "portMappings": [],
      "essential": true,
      "entryPoint": ["sh", "-c"],
      "command": [
        "/usr/local/bin/stress --cpu 2 --io 2 --vm 1 --vm-bytes 128M --timeout 6000s"
      ],
      "environment": [],
      "mountPoints": [],
      "volumesFrom": [],
      "logConfiguration": {
        "logDriver": "awslogs",
        "options": {
          "awslogs-create-group": "true",
          "awslogs-group": "/ecs/",
          "awslogs-region": "us-west-2",
          "awslogs-stream-prefix": "ecs"
        }
      }
    }
  ],
  "taskRoleArn": "arn:aws:iam::xxxxx:role/ecsTaskExecutionRole",
  "executionRoleArn": "arn:aws:iam::xxxxx:role/ecsTaskExecutionRole",
  "networkMode": "awsvpc",
  "requiresCompatibilities": ["EC2"],
  "cpu": "256",
  "memory": "512",
  "runtimePlatform": {
    "cpuArchitecture": "X86_64",
    "operatingSystemFamily": "LINUX"
  }
}
</code></pre>
<p>Once you have defined the tasks, ensure you bring up each service (one for each task) with the launch type of EC2:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltd5e714ecefbb58cf/6a85cc979829261768583922/elastic-blog-14-environment.png" alt="environment" /></p>
<p>You should have two services running now.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt6e6c3364b0c587f3/6a85cc9b501a85c7a6fbb363/elastic-blog-15-ec2basedcluster.png" alt="ec2basedcluster" /></p>
<h3 id="step6checkonmetricsandlogsinelasticcloud">Step 6: Check on metrics and logs in Elastic Cloud</h3>
<p>Go to Elastic Cloud and ensure that you are getting metrics and logs from the ECS Cluster. First, check to see if you are receiving metrics by viewing the built-in dashboard called [Metrics Docker] Overview.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt19fc8feb232fe845/6a85cc9ef9373d4bfb96f5c0/elastic-blog-16-docker.png" alt="Docker image" /></p>
<p> <strong>With some work on this dashboard by adding in container insight metrics and docker metrics, you should be able to see:</strong> </p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltbe62d55a71be2bf2/6a85cc77f9373d3a8c96f5b8/elastic-blog-3-metrics-graphs.png" alt="graphs" /></p>
<p>If you only have the ECS integration and the Elastic agent in Step 2, then you will need to create a new dashboard:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt6f1993af959a0a6e/6a85cc741aa1e1343eff8d83/elastic-blog-2-containers-in-cluster.png" alt="cluster" /></p>
<p>This dashboard can be set up with the following metrics:</p>
<ul>
<li>Containers in the cluster (containerInsights via Elastic Agent and AWS Cloudwatch integration). Set up a TSVB panel using the following metric: aws.dimensions.ClusterName : "EC2BasedCluster" with aws.containerinsights.metrics.TaskCount.max</li>
<li>Services in the cluster (containerInsights via Elastic Agent and AWS Cloudwatch integration). Use the following configuration to setup the chart:</li>
</ul>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt068290e85c9a03c6/6a85cca280984c6fef66900a/elastic-blog-17-table.png" alt="table" /></p>
<ul>
<li>CPU and memory utilization of the ECS Cluster (Elastic Agent with ECS integration). Use the following configuration to set up both CPU and memory utilization charts:</li>
</ul>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltd33eca51e079f3e0/6a85cca4342d690caf21b117/elastic-blog-18-line.png" alt="line" /></p>
<ul>
<li>EC2 CPU and storage utilization of the instance in the cluster (Elastic Agent with EC2 integration). Use the following configuration to set up both CPU and memory utilization charts:</li>
</ul>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blteb520047e596d3b5/6a85cca75c2790ca4af59b43/elastic-blog-19-bar-vertical-stacked.png" alt="bar vertical stacked" /></p>
<ul>
<li>(Not shown): CPU and memory utilization per container (via containerInsights via Elastic Agent and AWS Cloudwatch integration)</li>
</ul>
<h3 id="step7lookatlogsfromyourecscluster">Step 7: Look at logs from your ECS cluster</h3>
<p>Since we set up AWS CloudWatch logs collection in Step 2, we can view these logs in Discover by filtering on the logs group arn /aws/ecs/containerinsights/EC2BasedCluster/.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt0a81cad78b73a182/6a85ccab078290ccad321794/elastic-blog-20-logs.png" alt="logs" /></p>
<h2 id="summary">Summary</h2>
<p>I hope you’ve gotten an appreciation for how Elastic Observability can help your <a href="https://www.elastic.co/observability/aws-monitoring">AWS monitoring</a> ECS service metrics. Here’s a quick recap of lessons and what you learned:</p>
<ul>
<li>Elastic Observability supports ingesting and analysis of AWS ECS service metrics and the corresponding EC2 metrics through the AWS integration on the Elastic Agent. It’s easy to set up ingest from AWS Services via the Elastic Agent.</li>
<li>Elastic Observability can also get container metrics via the Docker integration running on Elastic agents on each of the EC2 instances in the ECS EC2 auto scaling group.</li>
<li>Elastic has multiple out-of-the-box (OOTB) AWS service dashboards that can be used as baselines to get your own customized view.</li>
</ul>
<p>Ready to get started? Start your own <a href="https://aws.amazon.com/marketplace/pp/prodview-voru33wi6xs7k?trk=5fbc596b-6d2a-433a-8333-0bd1f28e84da%E2%89%BBchannel=el">7-day free trial</a> by signing up via <a href="https://aws.amazon.com/marketplace/pp/prodview-voru33wi6xs7k?trk=d54b31eb-671c-49ba-88bb-7a1106421dfa%E2%89%BBchannel=el">AWS Marketplace</a> and quickly spin up a deployment in minutes on any of the <a href="https://www.elastic.co/guide/en/cloud/current/ec-reference-regions.html#ec_amazon_web_services_aws_regions">Elastic Cloud regions on AWS</a> around the world. Your AWS Marketplace purchase of Elastic will be included in your monthly consolidated billing statement and will draw against your committed spend with AWS.</p>
<p><em>The release and timing of any features or functionality described in this post remain at Elastic's sole discretion. Any features or functionality not currently available may not be delivered on time or at all.</em></p>]]></content:encoded>
    <link>https://www.elastic.co/observability-labs/blog/manage-applications-amazon-ecs-ec2-clusters-observability</link>
    <guid isPermaLink="false">manage-applications-amazon-ecs-ec2-clusters-observability</guid>
    <category><![CDATA[Infrastructure Monitoring]]></category>
    <category><![CDATA[AI]]></category>
    <dc:creator><![CDATA[Bahubali Shetti]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt2a5f17f66772a6d4/6a85ccad18249c460318f7f9/library-branding-elastic-observability-midnight-1680x980.png" length="0" type="image/png"/>
    <pubDate>Tue, 15 Aug 2023 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Exploring Nginx metrics with Elastic time series data streams]]></title>
    <description><![CDATA[Elasticsearch recently released time series metrics as GA. In this blog, we dive into details of what a time series metric document is and the mapping used for enabling time series by using an existing OOTB Nginx integration.]]></description>
    <content:encoded><![CDATA[<p>Elasticsearch<sup>®</sup> recently released time series data streams for metrics. This not only provides better metrics support in Elastic Observability, but it also helps reduce <a href="https://www.elastic.co/blog/whats-new-elasticsearch-8-7-0">storage costs</a>. We discussed this in a <a href="https://www.elastic.co/blog/elasticsearch-time-series-data-streams-observability-metrics">previous blog</a>.</p>
<p>In this blog, we dive into how to enable and use time series data streams by reviewing what a time series metrics <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/documents-indices.html">document</a> is and the <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/mapping.html">mapping</a> used for enabling time series. In particular, we will showcase this by using Elastic Observability’s Nginx integration. As Elastic<sup>®</sup> <a href="https://www.elastic.co/guide/en/elasticsearch/reference/8.8/tsds.html">time series data stream (TSDS)</a> metrics capabilities evolve, some of the scenarios below will change.</p>
<p>Elastic TSDS stores metrics in indices optimized for a time series database (<a href="https://en.wikipedia.org/wiki/Time_series_database">TSDB</a>), which is used to store time series metrics. <a href="https://www.elastic.co/blog/whats-new-elasticsearch-8-7-0">Elastic’s TSDB also got a significant optimization in 8.7</a> by reducing storage costs by upward of 70%.</p>
<h2 id="whatisanelastictimeseriesdatastream">What is an Elastic time series data stream?</h2>
<p>A time series data stream (TSDS) models timestamped metrics data as one or more time series. In a TSDS, each Elasticsearch document represents an observation or data point in a specific time series. Although a TSDS can contain multiple time series, a document can only belong to one time series. A time series can’t span multiple data streams.</p>
<p>A regular <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/data-streams.html">data stream</a> can have different usages including logs. For metrics usage, however, a time series data stream is recommended. A time series data stream is different from a regular data stream in <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/tsds.html#differences-from-regular-data-stream">multiple ways</a>. A TSDS contains more than one predefined dimension and multiple metrics.</p>
<h2 id="nginxmetricsasanexample">Nginx metrics as an example</h2>
<p><a href="https://www.elastic.co/integrations/data-integrations?solution=observability">Integrations</a> provide an easy way to ingest observability metrics for a large number of services and systems. We use the <a href="https://docs.elastic.co/en/integrations/nginx">Nginx</a> integration <a href="https://docs.elastic.co/en/integrations/nginx#metrics-reference">metrics</a> data set as an example here. This is one of the integrations, on which time series has been recently enabled.</p>
<h2 id="processofenablingtsdsonapackage">Process of enabling TSDS on a package</h2>
<p>Time series is <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/tsds.html#time-series-mode">enabled</a> on a metrics data stream of an <a href="https://www.elastic.co/integrations/">integration</a> package, after adding the relevant time series <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/tsds.html#time-series-metric">metrics</a> and <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/tsds.html#time-series-dimension">dimension</a> mappings. Existing integrations with metrics data streams will come with time series metrics enabled, so that users can use them as-is without any additional configuration.</p>
<p>The image below captures a high-level summary of a time series data stream, the corresponding index template, the time series indices and a single document. We will shortly dive into the details of each of the fields in the document.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt1941b44b80b21a70/6a7f0e97c2cc0991d524963a/elastic-blog-1-time-series-data-stream-2.png" alt="time series data stream" /></p>
<h2 id="tsdsmetricdocument">TSDS metric document</h2>
<p>Below we provide a snippet of an ingested Elastic document with time series metrics and dimension together.</p>
<pre><code>{
  "@timestamp": "2023-06-29T03:58:12.772Z",

  "nginx": {
    "stubstatus": {
      "accepts": 202,
      "active": 2,
      "current": 3,
      "dropped": 0,
      "handled": 202,
      "hostname": "host.docker.internal:80",
      "reading": 0,
      "requests": 10217,
      "waiting": 1,
      "writing": 1
    }
  }
}
</code></pre>
<p><strong>Multiple metrics per document:</strong><br />
An ingested <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/documents-indices.html">document</a> has a collection of fields, including metrics fields. Multiple related metrics fields can be part of a single document. A document is part of a single <a href="https://www.elastic.co/guide/en/fleet/current/data-streams.html">data stream</a>, and typically all the metrics it contains are related. All the metrics in a document are part of the same time series.</p>
<p><strong>Metric type and dimensions as mapping:</strong><br />
While the document contains the metrics details, the metric types and dimension details are defined as part of the field <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/mapping.html">mapping</a>. All the time series relevant field mappings are defined collectively for a given datastream, as part of the package development. All the integrations released with time series data stream, contain all the relevant time series field mappings, as part of the package release. There are two additional mappings needed in particular: <strong>time_series_metric</strong> mapping and <strong>time_series_dimension</strong> mapping.</p>
<h2 id="metricstypesfields">Metrics types fields</h2>
<p>A document contains the metric type fields (as shown above). The mappings for the metric type fields is done using <strong>time_series_metric</strong> mapping in the <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/index-templates.html">index templates</a> as given below:</p>
<pre><code>"nginx": {
    "properties": {
       "stubstatus": {
           "properties": {
                "accepts": {
                  "type": "long",
                  "time_series_metric": "counter"
                },
                "active": {
                  "type": "long",
                  "time_series_metric": "gauge"
                },
                "current": {
                  "type": "long",
                  "time_series_metric": "gauge"
                },
                "dropped": {
                  "type": "long",
                  "time_series_metric": "counter"
                },
                "handled": {
                  "type": "long",
                  "time_series_metric": "counter"
                },
                "reading": {
                  "type": "long",
                  "time_series_metric": "gauge"
                },
                "requests": {
                  "type": "long",
                  "time_series_metric": "counter"
                },
                "waiting": {
                  "type": "long",
                  "time_series_metric": "gauge"
                },
                "writing": {
                  "type": "long",
                  "time_series_metric": "gauge"
                }
           }
       }
    }
}
</code></pre>
<h2 id="dimensionfields">Dimension fields</h2>
<p><a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/tsds.html#time-series-dimension">Dimensions</a> are field names and values that, in combination, identify a document’s time series.</p>
<p>In Elastic time series, there are some additional considerations for dimensions:</p>
<ul>
<li>Dimension fields need to be defined for each time series. There will be no time series with zero dimension fields.</li>
<li>Keyword (or similar) type fields can be defined as dimensions.</li>
<li>There is a current limit on the number of dimensions that can be defined in a data stream. The limit restrictions will likely be lifted going forward.</li>
</ul>
<p>Dimension is common for all the metrics in a single document, as part of a data stream. Each time series data stream of a package (example: Nginx) already comes with a predefined set of dimension fields as below.</p>
<p>The document would contain more than one dimension field. In the case of Nginx, <em>agend.id</em> and <em>nginx.stubstatus.hostname</em> are some of the dimension fields. The mappings for the dimension fields is done using <strong>time_series_dimension</strong> mapping as below:</p>
<pre><code>"agent": {
   "properties": {
      "id": {
         "type": "keyword",
         "time_series_dimension": true
       }
    }
 },

"nginx": {
   "properties": {
       "stubstatus": {
            "properties": {
                "hostname": {
                  "type": "keyword",
                  "time_series_dimension": true
                },
            }
       }
    }
}
</code></pre>
<h2 id="metafields">Meta fields</h2>
<p>Documents ingested also have additional meta fields apart from the <em>metric</em> and <em>dimension</em> fields explained above. These additional fields provide richer query capabilities for the metrics.</p>
<p><strong>Example Elastic meta fields</strong></p>
<pre><code>"data_stream": {
      "dataset": "nginx.stubstatus",
      "namespace": "default",
      "type": "metrics"
 }
</code></pre>
<h2 id="discoverandvisualizationinkibana">Discover and visualization in Kibana</h2>
<p>Elastic provides comprehensive search and visualization for the time series metrics. Time series metrics can be searched as-is in <a href="https://www.elastic.co/guide/en/kibana/current/discover.html">Discover</a>. In the search below, the counter and gauges metrics are captured as <em>different icons</em>. Below we also provide examples of visualization for the time series metrics using <a href="https://www.elastic.co/kibana/kibana-lens">Lens</a> and OOTB dashboard included as part of the Nginx integration package.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltc0e8b439bf3f9f92/6a7f0e9abdcff037b5c42ef1/elastic-blog-2-discover-search-tsds.png" alt="Discover search for TSDS metrics" /></p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt5242a1da45d3f167/6a7f0e9db4377009764d6d43/elastic-blog-3-lens.png" alt="Maximum of counter field nginx.stubstatus.accepts visualized using Lens" /></p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt71c4c54d6de8bf51/6a7f0ea02f00b2ff3fefec08/elastic-blog-4-median-gauge.png" alt="Median of gauge field nginx.stubstatus.active visualized using Lens" /></p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt7172bc5ca118f3e3/6a7f0ea33cab1c43a20e490e/elastic-blog-5-multiple-line-graphs.png" alt="OOTB Nginx dashboard with the TSDS metrics visualizations " /></p>
<h2 id="tryitout">Try it out!</h2>
<p>We have provided a detailed example of a time series document ingested by the Elastic Nginx integration. We have walked through how time series metrics are modeled in Elastic and the additional time series mappings with examples. We provided details of dimension requirements for Elastic time series, as well as brief examples of search/visualization/dashboard of TSDS metrics in Kibana<sup>®</sup>.</p>
<p>Don’t have an Elastic Cloud account yet? <a href="https://cloud.elastic.co/registration">Sign up for Elastic Cloud</a> and try out the auto-instrumentation capabilities that I discussed above. I would be interested in getting your feedback about your experience in gaining visibility into your application stack with Elastic.</p>
<blockquote>
  <ul>
  <li><a href="https://www.elastic.co/blog/elasticsearch-time-series-data-streams-observability-metrics">How to use Elasticsearch and Time Series Data Streams for observability metrics</a></li>
  <li><a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/tsds.html">Time Series Data Stream in Elastic documentation</a> </li>
  <li><a href="https://www.elastic.co/blog/whats-new-elasticsearch-8-7-0">Efficient storage with Elastic Time Series Database</a><a href="https://www.elastic.co/integrations/">Elastic integrations catalog</a></li>
  <li><a href="https://www.elastic.co/integrations/">Elastic integrations catalog</a></li>
  </ul>
</blockquote>]]></content:encoded>
    <link>https://www.elastic.co/observability-labs/blog/nginx-metrics-elastic-time-series-data-streams</link>
    <guid isPermaLink="false">nginx-metrics-elastic-time-series-data-streams</guid>
    <category><![CDATA[Metrics]]></category>
    <category><![CDATA[Infrastructure Monitoring]]></category>
    <dc:creator><![CDATA[Lalit Satapathy]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt320155e641e1dfc9/6a7f0ea6eab5be716e20a793/time-series-data-streams-blog-720x420-1.jpg" length="0" type="image/jpeg"/>
    <pubDate>Mon, 10 Jul 2023 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Using the Elastic Agent to monitor Amazon ECS and AWS Fargate with Elastic Observability]]></title>
    <description><![CDATA[In this article, we’ll guide you through how to install the Elastic Agent with the AWS Fargate integration as a sidecar container to send host metrics and logs to Elastic Observability.]]></description>
    <content:encoded><![CDATA[<h2 id="serverlessandawsecsfargate">Serverless and AWS ECS Fargate</h2>
<p>AWS Fargate is a serverless pay-as-you-go engine used for Amazon Elastic Container Service (ECS) to run Docker containers without having to manage servers or clusters. The goal of Fargate is to containerize your application and specify the OS, CPU and memory, networking, and IAM policies needed for launch. Additionally, AWS Fargate can be used with Elastic Kubernetes Service (EKS) in a <a href="https://docs.aws.amazon.com/eks/latest/userguide/fargate.html">similar manner</a>.</p>
<p>Although the provisioning of servers would be handled by a third party, the need to understand the health and performance of containers within your serverless environment becomes even more vital in identifying root causes and system interruptions. Serverless still requires observability. Elastic Observability can provide observability for not only AWS ECS with Fargate, as we will discuss in this blog, but also for a number of AWS services (EC2, RDS, ELB, etc). See our <a href="https://www.elastic.co/blog/aws-service-metrics-monitor-observability-easy">previous blog</a> on managing an EC2-based application with Elastic Observability.</p>
<h2 id="gainingfullvisibilitywithelasticobservability">Gaining full visibility with Elastic Observability</h2>
<p>Elastic Observability is governed by the three pillars involved in creating full visibility within a system: logs, metrics, and traces. Logs list all the events that have taken place in the system. Metrics keep track of data that will tell you if the system is down, like response time, CPU usage, memory usage, and latency. Traces give a good indication of the performance of your system based on the execution of requests.</p>
<p>These pillars by themselves offer some insight, but combining them allows for you to see the full scope of your system and how it handles increases in load or traffic over time. Connecting Elastic Observability to your serverless environment will help you deal with outages quicker and perform root cause analysis to prevent any future problems.</p>
<p>In this article, we’ll guide you through how to install the Elastic Agent with the <a href="https://docs.elastic.co/integrations/awsfargate">AWS Fargate</a> integration as a sidecar container to send host metrics and logs to Elastic Observability.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt803b7dadd7538890/6a85c8b42d64d57d43081cea/Screenshot_2023-06-16_at_12.58.05_PM.png" alt="" /></p>
<h2 id="prerequisites">Prerequisites:</h2>
<ul>
<li>AWS account with AWS CLI configured</li>
<li>GitHub account</li>
<li>Elastic Cloud account</li>
<li>An app running on a container in AWS</li>
</ul>
<p>This tutorial is divided into two parts:</p>
<ol>
<li>Set up the Fleet server to be used by the sidecar container in AWS.</li>
<li>Create the sidecar container in AWS Fargate to send data back to Elastic Observability.</li>
</ol>
<h2 id="partisetupthefleetserver">Part I: Set up the Fleet server</h2>
<p>First, let’s log in to Elastic Cloud.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt8c64699a3a4d241e/6a85c8b74710c65ef1d3cb05/image4.png" alt="" /></p>
<p>You can either create a new deployment or use an existing one.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt6c2c2758222a2477/6a85c8bad7b2e7717efe849c/image35.png" alt="" /></p>
<p>From the <strong>Home</strong> page, use the side panel to scroll to Management &gt; Fleet &gt; Agent policies. Click <strong>Add policy</strong>.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt0fcb9b2f065f6980/6a85c8bd5c27905f1ef59acf/image30.png" alt="" /></p>
<p>Click <strong>Create agent policy</strong>. Here we’ll create a policy to attach to the Fleet agent.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltf7486ca256e624a4/6a85c8c093ffb9c405b913eb/image38.png" alt="" /></p>
<p>Give the policy a name and save changes.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt8affe45d856644e2/6a85c8c30782905f6c32172e/image44.png" alt="" /></p>
<p>Click <strong>Create agent policy</strong>. You should see the agent policy AWS Fargate in the list of policies.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltb90b92edc7f54ea0/6a85c8c69d2b71099cf93945/image42.png" alt="" /></p>
<p>Now that we have an agent policy, let’s add the integration to collect logs and metrics from the host. Click on <strong>AWS Fargate -&gt; Add integration</strong>.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt66bcb99f5f15a63f/6a85c8c8abdc29673b1224ac/image19.png" alt="" /></p>
<p>We’ll be adding to the policy AWS to collect overall AWS metrics and AWS Fargate to collect metrics from this integration. You can find each one by typing them in the search bar.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltd0b1c57451639be3/6a85c8cb11893c866da7ab3a/image1.png" alt="" /></p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blta30ddfb73a520417/6a85c8ce2d64d5e12a081cf2/image34.png" alt="" /></p>
<p>Once you click on the integration, it will take you to its landing page, where you can add it to the policy.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt29db850a592cc5da/6a85c8d1d6cf290f0ebb08bc/image48.png" alt="" /></p>
<p>For the AWS integration, the only collection settings that we will configure are Collect billing metrics, Collect logs from CloudWatch, Collect metrics from CloudWatch, Collect ECS metrics, and Collect Usage metrics. Everything else can be left disabled.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blta9356012ac813662/6a85c8d45c27907614f59ad7/Screenshot_2023-06-15_at_11.35.28_AM.png" alt="" /></p>
<p>Another thing to keep in mind when using this integration is the set of permissions required to collect data from AWS. This can be found on the AWS integration page under AWS permissions. Take note of these permissions, as we will use them to create an IAM policy.</p>
<p>Next, we will add the AWS Fargate integration, which doesn’t require further configuration settings.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt143ff336054d94af/6a85c8d79bf99466ec0a052d/image37.png" alt="" /></p>
<p>Now that we have created the agent policy and attached the proper integrations, let’s create the agent that will implement the policy. Navigate back to the main Fleet page and click <strong>Add agent</strong>.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt70004b5c001ab658/6a85c8dabc5bb3ac12f81aa7/image41.png" alt="" /></p>
<p>Since we’ll be connecting to AWS Fargate through ECS, the host type should be set to this value. All the other default values can stay the same.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt25c143567249b28d/6a85c8dc5c279077d4f59adb/image15.png" alt="" /></p>
<p>Lastly, let’s create the enrollment token and attach the agent policy. This will enable AWS ECS Fargate to access Elastic and send data.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt6f72869a6ec7b0ee/6a85c8df43c0b77c5c2f05d8/image6.png" alt="" /></p>
<p>Once created, you should be able to see policy name, secret, and agent policy listed.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt5f34035a6752e29a/6a85c8e2bc5bb317c8f81aad/image43.png" alt="" /></p>
<p>We’ll be using our Fleet credentials in the next step to send data to Elastic from AWS Fargate.</p>
<h2 id="partiisenddatatoelasticobservability">Part II: Send data to Elastic Observability</h2>
<p>It’s time to create our ECS Cluster, Service, and task definition in order to start running the container.</p>
<p>Log in to your AWS account and navigate to ECS.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt7b7234b6f3e2fa20/6a85c8e418249c2d6c18f755/image46.png" alt="" /></p>
<p>We’ll start by creating the cluster.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltbc0ac0f14d810fda/6a85c8e7eaf24536dda49f19/image9.png" alt="" /></p>
<p>Add a name to the Cluster. And for subnets, only select the first two for us-east-1a and us-eastlb.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt9997e6a16775a270/6a85c8ea501a85704bfbb2e2/image10.png" alt="" /></p>
<p>For the sake of the demo, we’ll keep the rest of the options set to default. Click <strong>Create</strong>.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltadfa4acf490f8759/6a85c8ed331d7a9211c31743/image11.png" alt="" /></p>
<p>We should see the cluster we created listed below.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt4737257dd5cf14ff/6a85c8ef18249c82f618f759/Screenshot_2023-06-15_at_11.15.51_AM.png" alt="" /></p>
<p>Now that we’ve created our cluster to host our container, we want to create a task definition that will be used to set up our container. But before we do this, we will need to create a task role with an associated policy. This task role will allow for AWS metrics to be sent from AWS to the Elastic Agent.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltfe7aa2800cf35d7b/6a85c8f28c2944847eb88ff9/image47.png" alt="" /></p>
<p>Navigate to IAM in AWS.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blta32f8d2eeb1538e1/6a85c8f568266660a61eabbf/image32.png" alt="" /></p>
<p>Go to <strong>Policies -&gt; Create policy</strong>.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt60aaab8d80cfd8da/6a85c8f893ffb9fa6fb913f9/image31.png" alt="" /></p>
<p>Now we will reference the AWS permissions from the Fleet AWS integration page and use them to configure the policy. In addition to these permissions, we will also add the GetAtuhenticationToken action for ECR.</p>
<p>You can configure each one using the visual editor.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltb1ffec16aa32f905/6a85c8fad7b2e7d13cfe84a8/image22.png" alt="" /></p>
<p>Or, use the JSON option. Don’t forget to replace the \&lt;account_id&gt; with your own.</p>
<pre><code>{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "VisualEditor0",
      "Effect": "Allow",
      "Action": [
        "sqs:DeleteMessage",
        "sqs:ChangeMessageVisibility",
        "sqs:ReceiveMessage",
        "ecr:GetDownloadUrlForLayer",
        "ecr:UploadLayerPart",
        "ecr:PutImage",
        "sts:AssumeRole",
        "rds:ListTagsForResource",
        "ecr:BatchGetImage",
        "ecr:CompleteLayerUpload",
        "rds:DescribeDBInstances",
        "logs:FilterLogEvents",
        "ecr:InitiateLayerUpload",
        "ecr:BatchCheckLayerAvailability"
      ],
      "Resource": [
        "arn:aws:iam::&lt;account_id&gt;:role/*",
        "arn:aws:logs:*:&lt;account_id&gt;:log-group:*",
        "arn:aws:sqs:*:&lt;account_id&gt;:*",
        "arn:aws:ecr:*:&lt;account_id&gt;:repository/*",
        "arn:aws:rds:*:&lt;account_id&gt;:target-group:*",
        "arn:aws:rds:*:&lt;account_id&gt;:subgrp:*",
        "arn:aws:rds:*:&lt;account_id&gt;:pg:*",
        "arn:aws:rds:*:&lt;account_id&gt;:ri:*",
        "arn:aws:rds:*:&lt;account_id&gt;:cluster-snapshot:*",
        "arn:aws:rds:*:&lt;account_id&gt;:cev:*/*/*",
        "arn:aws:rds:*:&lt;account_id&gt;:og:*",
        "arn:aws:rds:*:&lt;account_id&gt;:db:*",
        "arn:aws:rds:*:&lt;account_id&gt;:es:*",
        "arn:aws:rds:*:&lt;account_id&gt;:db-proxy-endpoint:*",
        "arn:aws:rds:*:&lt;account_id&gt;:secgrp:*",
        "arn:aws:rds:*:&lt;account_id&gt;:cluster:*",
        "arn:aws:rds:*:&lt;account_id&gt;:cluster-pg:*",
        "arn:aws:rds:*:&lt;account_id&gt;:cluster-endpoint:*",
        "arn:aws:rds:*:&lt;account_id&gt;:db-proxy:*",
        "arn:aws:rds:*:&lt;account_id&gt;:snapshot:*"
      ]
    },
    {
      "Sid": "VisualEditor1",
      "Effect": "Allow",
      "Action": [
        "sqs:ListQueues",
        "organizations:ListAccounts",
        "ec2:DescribeInstances",
        "tag:GetResources",
        "cloudwatch:GetMetricData",
        "ec2:DescribeRegions",
        "iam:ListAccountAliases",
        "sns:ListTopics",
        "sts:GetCallerIdentity",
        "cloudwatch:ListMetrics"
      ],
      "Resource": "*"
    },
    {
      "Sid": "VisualEditor2",
      "Effect": "Allow",
      "Action": "ecr:GetAuthorizationToken",
      "Resource": "arn:aws:ecr:*:&lt;account_id&gt;:repository/*"
    }
  ]
}
</code></pre>
<p>Review your changes.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltfa30238f739c3cbf/6a85c8fed7b2e74b05fe84ac/image3.png" alt="" /></p>
<p>Now let’s attach this policy to a role. Navigate to <strong>IAM -&gt; Roles</strong>. Click <strong>Create role</strong>.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt7927c953da8dd23c/6a85c9014710c60f32d3cb0b/image45.png" alt="" /></p>
<p>Select AWS service as Trusted entity type and select EC2 as Use case. Click <strong>Next</strong>.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt09f9f2b78eccb086/6a85c90480984cadea668f9e/image24.png" alt="" /></p>
<p>Under permissions policies, select the policy we just created, as well as CloudWatchLogsFullAccess and AmazonEC2ContainerRegistryFullAccess. Click <strong>Next</strong>.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt76a43671e00db1ea/6a85c90768266655661eabc7/image27.png" alt="" /></p>
<p>Give the task role a name and description.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt3f49c33f73d635e1/6a85c90a93ffb98d45b913fd/image39.png" alt="" /></p>
<p>Click <strong>Create role</strong>.</p>
<p>Now it’s time to create the task definition. Navigate to <strong>ECS -&gt; Task definitions</strong>. Click <strong>Create new task definition</strong>.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt710c84e6d06460ef/6a85c90c501a8573a7fbb2e8/image21.png" alt="" /></p>
<p>Let’s give this task definition a name.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt60dc8e5f34f7de35/6a85c90f9d2b7111f2f9394d/image14.png" alt="" /></p>
<p>After giving the task definition a name, you’ll add the Fleet credentials to the container section, which you can obtain from the Enrollment Tokens section of the Fleet section in Elastic Cloud. This allows us to host the Elastic Agent on the ECS container as a sidecar and send data to Elastic using Fleet credentials.</p>
<ul>
<li><p>Container name: <strong>elastic-agent-container</strong></p></li>
<li><p>Image: <strong>docker.elastic.co/beats/elastic-agent:8.19.13</strong></p></li>
</ul>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt801abae39b4a9621/6a85c91ebc5bb3be21f81abf/image40.png" alt="" /></p>
<p>Now let’s add the environment variables:</p>
<ul>
<li><p>FLEET_ENROLL: <strong>yes</strong></p></li>
<li><p>FLEET_ENROLLMENT_TOKEN: <strong>\</strong></p></li>
<li><p>FLEET_URL: <strong>\</strong></p></li>
</ul>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt7d33d06272460b61/6a85c92133f244901449f4da/image26.png" alt="" /></p>
<p>For the sake of the demo, leave Environment, Monitoring, Storage, and Tags as default values. Now we will need to create a second container to run the image for the golang app stored in ECR. Click <strong>Add more containers</strong>.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltcfc6c6fd29e5e54b/6a85c924abdc29751c1224b8/image5.png" alt="" /></p>
<p>For Environment, we will reserve 1 vCPU and 3 GB of memory. Under Task role, search for the role we created that uses the IAM policy.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltaa0e4e527e1f6176/6a85c92718249c3d0418f789/image7.png" alt="" /></p>
<p>Review the changes, then click <strong>Create</strong>.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt6145ac851750d6bb/6a85c929e2447ae70a8b13c4/image25.png" alt="" /></p>
<p>You should see your new task definition included in the list.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt0c4109a155aa44bf/6a85c92cabdc293fae1224bc/image20.png" alt="" /></p>
<p>The final step is to create the service that will connect directly to the fleet server.<br />
Navigate to the cluster you created and click <strong>Create</strong> under the Service tab.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltbe52f20828680633/6a85c92f18249cfd8018f78d/image18.png" alt="" /></p>
<p>Let’s get our service environment configured.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt06147dd1ec890489/6a85c932abdc296cfa1224c0/image28.png" alt="" /></p>
<p>Set up the deployment configuration. Here you should provide the name of the task definition you created in the previous step. Also, provide the service with a unique name. Set the number of <strong>desired tasks</strong> to 2 instead of 1.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt83da0a1232f4abe8/6a85c93493ffb97168b91405/image16.png" alt="" /></p>
<p>Click <strong>Create</strong>. Now your service is running two tasks in your cluster using the task definition you provided.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt4f77cfa47a90aeae/6a85c937f9373d15c996f568/image33.png" alt="" /></p>
<p>To recap, we set up a Fleet server in Elastic Cloud to receive AWS Fargate data. We then created our AWS Fargate cluster task definition with the Fleet credentials implemented within the container. Lastly, we created the service to send data about our host to Elastic.</p>
<p>Now let’s verify our Elastic Agent is healthy and properly receiving data from AWS Fargate.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt3d79a96ce36d7900/6a85c93a68266621071eabd3/image36.png" alt="" /></p>
<p>We can also view a better breakdown of our agent on the Observability Overview page.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt04e50bfd77416b38/6a85c93d342d69d55721b0bb/image2.png" alt="" /></p>
<p>If we drill down to hosts, by clicking on host name we should be able to see more granular data. For instance, we can see the CPU Usage of the Elastic Agent itself that is deployed in our AWS Fargate environment.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt5655c577c37482f4/6a85c93f11893c1a84a7ab5c/image8.png" alt="" /></p>
<p>Lastly, we can view the AWS Fargate dashboard generated using the data collected by our Elastic Agent. This is an out-of-the-box dashboard that can also be customized based on the data you would like to visualize.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt640bb8ca9841e713/6a85c9439bf9947fa00a0543/image23.png" alt="" /></p>
<p>As you can see in the dashboard we’re able to filter based on running tasks, as well as see a list of containers running in our environment. Something else that could be useful to show is the CPU usage per cluster as shown under CPU Utilization per Cluster.</p>
<p>The dashboard can pull data from different sources and in this case shows data for both AWS Fargate and the greater ECS cluster. The two containers at the bottom display the CPU and memory usage directly from ECS.</p>
<h2 id="conclusion">Conclusion</h2>
<p>In this article, we showed how to send data from AWS Fargate to Elastic Observability using the Elastic Agent and Fleet. Serverless architectures are quickly becoming industry standard in offloading the management of servers to third parties. However, this does not alleviate the responsibility of operations engineers to manage the data generated within these environments. Elastic Observability provides a way to not only ingest the data from serverless architectures, but also establish a roadmap to address future problems.</p>
<p>Start your own <a href="https://aws.amazon.com/marketplace/pp/prodview-voru33wi6xs7k?trk=5fbc596b-6d2a-433a-8333-0bd1f28e84da%E2%89%BBchannel=el">7-day free trial</a> by signing up via <a href="https://aws.amazon.com/marketplace/pp/prodview-voru33wi6xs7k?trk=d54b31eb-671c-49ba-88bb-7a1106421dfa%E2%89%BBchannel=el">AWS Marketplace</a> and quickly spin up a deployment in minutes on any of the <a href="https://www.elastic.co/guide/en/cloud/current/ec-reference-regions.html#ec_amazon_web_services_aws_regions">Elastic Cloud regions on AWS</a> around the world. Your AWS Marketplace purchase of Elastic will be included in your monthly consolidated billing statement and will draw against your committed spend with AWS.</p>
<p><strong>More resources on serverless and observability and AWS:</strong></p>
<ul>
<li><a href="https://www.elastic.co/blog/aws-service-metrics-monitor-observability-easy">Analyze your AWS application’s service metrics on Elastic Observability (EC2, ELB, RDS, and NAT)</a></li>
<li><a href="https://www.elastic.co/blog/observability-apm-aws-lambda-serverless-functions">Get visibility into AWS Lambda serverless functions with Elastic Observability</a></li>
<li><a href="https://www.elastic.co/blog/trace-based-testing-elastic-apm-tracetest">Trace-based testing with Elastic APM and Tracetest</a></li>
<li><a href="https://www.elastic.co/blog/aws-kinesis-data-firehose-elastic-observability-analytics">Sending AWS logs into Elastic via AWS Firehose</a></li>
</ul>
<p><em>The release and timing of any features or functionality described in this post remain at Elastic's sole discretion. Any features or functionality not currently available may not be delivered on time or at all.</em></p>]]></content:encoded>
    <link>https://www.elastic.co/observability-labs/blog/elastic-agent-monitor-ecs-aws-fargate-observability</link>
    <guid isPermaLink="false">elastic-agent-monitor-ecs-aws-fargate-observability</guid>
    <category><![CDATA[Infrastructure Monitoring]]></category>
    <category><![CDATA[Metrics]]></category>
    <category><![CDATA[Kubernetes]]></category>
    <dc:creator><![CDATA[Alexis Roberson]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt916eb77a3c2b2a74/6a85c945682666aa6a1eabd7/blog-thumb-observability-pattern-color.png" length="0" type="image/png"/>
    <pubDate>Thu, 15 Jun 2023 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Gain insights into Kubernetes errors with Elastic Observability logs and OpenAI]]></title>
    <description><![CDATA[This blog post provides an example of how one can analyze error messages in Elasticsearch with ChatGPT using the OpenAI API via Elasticsearch.]]></description>
    <content:encoded><![CDATA[<p>As we’ve shown in previous blogs, Elastic<sup>®</sup> provides a way to ingest and manage telemetry from the <a href="https://www.elastic.co/blog/kubernetes-cluster-metrics-logs-monitoring">Kubernetes cluster</a> and the <a href="https://www.elastic.co/blog/opentelemetry-observability">application</a> running on it. Elastic provides out-of-the-box dashboards to help with tracking metrics, <a href="https://www.elastic.co/blog/log-management-observability-operations">log management and analytics</a>, <a href="https://www.elastic.co/blog/adding-free-and-open-elastic-apm-as-part-of-your-elastic-observability-deployment">APM functionality</a> (which also supports <a href="https://www.elastic.co/blog/opentelemetry-observability">native OpenTelemetry</a>), and the ability to analyze everything with <a href="https://www.elastic.co/blog/observability-logs-machine-learning-aiops">AIOps features</a> and <a href="https://www.elastic.co/what-is/elasticsearch-machine-learning?elektra=home">machine learning</a> (ML). While you can use pre-existing <a href="https://www.elastic.co/blog/improving-information-retrieval-elastic-stack-search-relevance">ML models in Elastic</a>, <a href="https://www.elastic.co/blog/aiops-automation-analytics-elastic-observability-use-cases">out-of-the-box AIOps features</a>, or your own ML models, there is a need to dig deeper into the root cause of an issue.</p>
<p>Elastic helps reduce the operational work to support more efficient operations, but users still need a way to investigate and understand everything from the cause of an issue to the meaning of specific error messages. As an operations user, if you haven’t run into a particular error before or it's part of some runbook, you will likely go to Google and start searching for information.</p>
<p>OpenAI’s ChatGPT is becoming an interesting generative AI tool that helps provide more information using the models behind it. What if you could use OpenAI to obtain deeper insights (even simple semantics) for an error in your production or development environment? You can easily tie Elastic to OpenAI’s API to achieve this.</p>
<p>Kubernetes, a mainstay in most deployments (on-prem or in a cloud service provider) requires a significant amount of expertise — even if that expertise is to manage a service like GKE, EKS, or AKS.</p>
<p>In this blog, I will cover how you can use <a href="https://www.elastic.co/guide/en/kibana/current/watcher-ui.html">Elastic’s watcher</a> capability to connect Elastic to OpenAI and ask it for more information about the error logs Elastic is ingesting from a Kubernetes cluster(s). More specifically, we will use <a href="https://azure.microsoft.com/en-us/products/cognitive-services/openai-service">Azure’s OpenAI Service</a>. Azure OpenAI is a partnership between Microsoft and OpenAI, so the same models from OpenAI are available in the Microsoft version.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blteb7528d40990ace0/6a85cc4de2447afd238b1428/blog-elastic-azure-openai.png" alt="elastic azure openai" /></p>
<p>While this blog goes over a specific example, it can be modified for other types of errors Elastic receives in logs. Whether it's from AWS, the application, databases, etc., the configuration and script described in this blog can be modified easily.</p>
<h2 id="prerequisitesandconfig">Prerequisites and config</h2>
<p>If you plan on following this blog, here are some of the components and details we used to set up the configuration:</p>
<ul>
<li>Ensure you have an account on <a href="http://cloud.elastic.co">Elastic Cloud</a> and a deployed stack (<a href="https://www.elastic.co/guide/en/elastic-stack/current/installing-elastic-stack.html">see instructions here</a>).</li>
<li>We used a GCP GKE Kubernetes cluster, but you can use any Kubernetes cluster service (on-prem or cloud based) of your choice.</li>
<li>We’re also running with a version of the OpenTelemetry Demo. Directions for using Elastic with OpenTelemetry Demo are <a href="https://github.com/elastic/opentelemetry-demo">here</a>.</li>
<li>We also have an Azure account and <a href="https://azure.microsoft.com/en-us/products/cognitive-services/openai-service">Azure OpenAI service configured</a>. You will need to get the appropriate tokens from Azure and the proper URL endpoint from Azure’s OpenAI service.</li>
<li>We will use <a href="https://www.elastic.co/guide/en/kibana/current/devtools-kibana.html">Elastic’s dev tools</a>, the console to be specific, to load up and run the script, which is an <a href="https://www.elastic.co/guide/en/kibana/current/watcher-ui.html">Elastic watcher</a>.</li>
<li>We will also add a new index to store the results from the OpenAI query.</li>
</ul>
<p>Here is the configuration we will set up in this blog:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltf5a82668ffd902a5/6a85cc5033f2444aab49f528/blog-elastic-configuration.png" alt="Configuration to analyze Kubernetes cluster errors" /></p>
<p>As we walk through the setup, we’ll also provide the alternative setup with OpenAI versus Azure OpenAI Service.</p>
<h2 id="settingitallup">Setting it all up</h2>
<p>Over the next few steps, I’ll walk through:</p>
<ul>
<li>Getting an account on Elastic Cloud and setting up your K8S cluster and application</li>
<li>Gaining Azure OpenAI authorization (alternative option with OpenAI)</li>
<li>Identifying Kubernetes error logs</li>
<li>Configuring the watcher with the right script</li>
<li>Comparing the output from Azure OpenAI/OpenAI versus ChatGPT UI</li>
</ul>
<h3 id="step0createanaccountonelasticcloud">Step 0: Create an account on Elastic Cloud</h3>
<p>Follow the instructions to <a href="https://cloud.elastic.co/registration?fromURI=/home">get started on Elastic Cloud</a>.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt3c2915ffc1f2fa55/6a85cc538c2944b302b89067/blog-elastic-start-cloud-trial.png" alt="elastic start cloud trial" /></p>
<p>Once you have the Elastic Cloud login, set up your Kubernetes cluster and application. A complete step-by-step instructions blog is available <a href="https://www.elastic.co/blog/kubernetes-cluster-metrics-logs-monitoring">here</a>. This also provides an overview of how to see Kubernetes cluster metrics in Elastic and how to monitor them with dashboards.</p>
<h3 id="step1azureopenaiserviceandauthorization">Step 1: Azure OpenAI Service and authorization</h3>
<p>When you log in to your Azure subscription and set up an instance of Azure OpenAI Service, you will be able to get your keys under Manage Keys.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltc803bb9220bc5e6a/6a85cc56abdc296504122528/blog-elastic-microsoft-azure-manage-keys.png" alt="microsoft azure manage keys" /></p>
<p>There are two keys for your OpenAI instance, but you only need KEY 1 .</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltd656cf4c96ad1ded/6a85cc5993ffb96abab9145d/blog-elastic-pme-openai-keys-and-endpoint.png" alt="Used with permission from Microsoft." /></p>
<p>Additionally, you will need to get the service URL. See the image above with our service URL blanked out to understand where to get the KEY 1 and URL.</p>
<p>If you are not using Azure OpenAI Service and the standard OpenAI service, then you can get your keys at:</p>
<pre><code>**https** ://platform.openai.com/account/api-keys
</code></pre>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltda718eafc61eb455/6a85cc5c5c27903359f59b35/blog-elastic-api-keys.png" alt="api keys" /></p>
<p>You will need to create a key and save it. Once you have the key, you can go to Step 2.</p>
<h3 id="step2identifyingkuberneteserrorsinelasticlogs">Step 2: Identifying Kubernetes errors in Elastic logs</h3>
<p>As your Kubernetes cluster is running, <a href="https://docs.elastic.co/en/integrations/kubernetes">Elastic’s Kubernetes integration</a> running on the Elastic agent daemon set on your cluster is sending logs and metrics to Elastic. <a href="https://www.elastic.co/blog/log-monitoring-management-enterprise">The telemetry is ingested, processed, and indexed</a>. Kubernetes logs are stored in an index called .ds-logs-kubernetes.container_logs-default-* (* is for the date), and an automatic data stream logs-kubernetes.container_logs is also pre-loaded. So while you can use some of the out-of-the-box dashboards to investigate the metrics, you can also look at all the logs in Elastic Discover.</p>
<p>While any error from Kubernetes can be daunting, the more nuanced issues occur with errors from the pods running in the kube-system namespace. Take the pod konnectivity agent, which is essentially a network proxy agent running on the node to help establish tunnels and is a vital component in Kubernetes. Any error will cause the cluster to have connectivity issues and lead to a cascade of issues, so it’s important to understand and troubleshoot these errors.</p>
<p>When we filter out for error logs from the konnectivity agent, we see a good number of errors.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt61aec76988119b34/6a85cc604710c6e78bd3cb7d/blog-elastic-expanded-document.png" alt="expanded document" /></p>
<p>But unfortunately, we still can’t understand what these errors mean.</p>
<p>Enter OpenAI to help us understand the issue better. Generally, you would take the error message from Discover and paste it with a question in ChatGPT (or run a Google search on the message).</p>
<p>One error in particular that we’ve run into but do not understand is:</p>
<pre><code>E0510 02:51:47.138292       1 client.go:388] could not read stream err=rpc error: code = Unavailable desc = error reading from server: read tcp 10.120.0.8:46156-&gt;35.230.74.219:8132: read: connection timed out serverID=632d489f-9306-4851-b96b-9204b48f5587 agentID=e305f823-5b03-47d3-a898-70031d9f4768
</code></pre>
<p>The OpenAI output is as follows:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt1cdff890473ba8c8/6a85cc63bc5bb35efdf81b21/blog-elastic-openai-output.png" alt="openai output" /></p>
<p>ChatGPT has given us a fairly nice set of ideas on why this rpc error is occurring against our konnectivity-agent.</p>
<p>So how can we get this output automatically for any error when those errors occur?</p>
<h3 id="step3configuringthewatcherwiththerightscript">Step 3: Configuring the watcher with the right script</h3>
<p><a href="https://www.elastic.co/guide/en/kibana/current/watcher-ui.html">What is an Elastic watcher?</a> Watcher is an Elasticsearch feature that you can use to create actions based on conditions, which are periodically evaluated using queries on your data. Watchers are helpful for analyzing mission-critical and business-critical streaming data. For example, you might watch application logs for errors causing larger operational issues.</p>
<p>Once a watcher is configured, it can be:</p>
<ol>
<li>Manually triggered</li>
<li>Run periodically</li>
<li>Created using a UI or a script</li>
</ol>
<p>In this scenario, we will use a script, as we can modify it easily and run it as needed.</p>
<p>We’re using the DevTools Console to enter the script and test it out:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt009cda608e30cff4/6a85cc66f61d6e61f59c2b4f/blog-elastic-test-script.png" alt="test script" /></p>
<p>The script is listed at the end of the blog in the <strong>appendix</strong>. It can also be downloaded <a href="https://github.com/elastic/chatgpt-error-analysis"><strong>here</strong></a> <strong>.</strong></p>
<p>The script does the following:</p>
<ol>
<li>It runs continuously every five minutes.</li>
<li>It will search the logs for errors from the container konnectivity-agent.</li>
<li>It will take the first error’s message, transform it (re-format and clean up), and place it into a variable first_hit.</li>
</ol>
<pre><code>"script": "return ['first_hit': ctx.payload.first.hits.hits.0._source.message.replace('\"', \"\")]"
</code></pre>
<ol>
<li>The error message is sent into OpenAI with a query:</li>
</ol>
<pre><code>What are the potential reasons for the following kubernetes error:
  { { ctx.payload.second.first_hit } }
</code></pre>
<ol>
<li>If the search yielded an error, it will proceed to then create an index and place the error message, pod.name (which is konnectivity-agent-6676d5695b-ccsmx in our setup), and OpenAI output into a new index called chatgpt_k8_analyzed.</li>
</ol>
<p>To see the results, we created a new data view called chatgpt_k8_analyzed against the newly created index:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blte281840a289fb3f9/6a85cc6899083f864140f9f9/blog-elastic-edit-data-view.png" alt="edit data view" /></p>
<p>In Discover, the output on the data view provides us with the analysis of the errors.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltf80847d904c25e91/6a85cc6c2d64d54455081d6a/blog-elastic-analysis-of-errors.png" alt="analysis of errors" /></p>
<p>For every error the script sees in the five minute interval, it will get an analysis of the error. We could alternatively also use a range as needed to analyze during a specific time frame. The script would just need to be modified accordingly.</p>
<h3 id="step4outputfromazureopenaiopenaivschatgptui">Step 4. Output from Azure OpenAI/OpenAI vs. ChatGPT UI</h3>
<p>As you noticed above, we got relatively the same result from the Azure OpenAI API call as we did by testing out our query in the ChatGPT UI. This is because we configured the API call to run the same/similar model as what was selected in the UI.</p>
<p>For the API call, we used the following parameters:</p>
<pre><code>"request": {
             "method" : "POST",
             "Url": "https://XXX.openai.azure.com/openai/deployments/pme-gpt-35-turbo/chat/completions?api-version=2023-03-15-preview",
             "headers": {"api-key" : "XXXXXXX",
                         "content-type" : "application/json"
                        },
             "body" : "{ \"messages\": [ { \"role\": \"system\", \"content\": \"You are a helpful assistant.\"}, { \"role\": \"user\", \"content\": \"What are the potential reasons for the following kubernetes error: {{ctx.payload.second.first_hit}}\"}], \"temperature\": 0.5, \"max_tokens\": 2048}" ,
              "connection_timeout": "60s",
               "read_timeout": "60s"
                            }
</code></pre>
<p>By setting the role: system with You are a helpful assistant and using the gpt-35-turbo url portion, we are essentially setting the API to use the davinci model, which is the same as the ChatGPT UI model set by default.</p>
<p>Additionally, for Azure OpenAI Service, you will need to set the URL to something similar the following:</p>
<pre><code>https://YOURSERVICENAME.openai.azure.com/openai/deployments/pme-gpt-35-turbo/chat/completions?api-version=2023-03-15-preview
</code></pre>
<p>If you use OpenAI (versus Azure OpenAI Service), the request call (against <a href="https://api.openai.com/v1/completions">https://api.openai.com/v1/completions</a>) would be as such:</p>
<pre><code>"request": {
            "scheme": "https",
            "host": "api.openai.com",
            "port": 443,
            "method": "post",
            "path": "\/v1\/completions",
            "params": {},
            "headers": {
               "content-type": "application\/json",
               "authorization": "Bearer YOUR_ACCESS_TOKEN"
                        },
            "body": "{ \"model\": \"text-davinci-003\",  \"prompt\": \"What are the potential reasons for the following kubernetes error: {{ctx.payload.second.first_hit}}\",  \"temperature\": 1,  \"max_tokens\": 512,     \"top_p\": 1.0,      \"frequency_penalty\": 0.0,   \"presence_penalty\": 0.0 }",
            "connection_timeout_in_millis": 60000,
            "read_timeout_millis": 60000
          }
</code></pre>
<p>If you are interested in creating a more OpenAI-based version, you can <a href="https://elastic-content-share.eu/downloads/watcher-job-to-integrate-chatgpt-in-elasticsearch/">download an alternative script</a> and look at <a href="https://mar1.hashnode.dev/unlocking-the-power-of-aiops-with-chatgpt-and-elasticsearch">another blog from an Elastic community member</a>.</p>
<h2 id="gainingotherinsightsbeyondkuberneteslogs">Gaining other insights beyond Kubernetes logs</h2>
<p>Now that the script is up and running, you can modify it using different:</p>
<ul>
<li>Inputs</li>
<li>Conditions</li>
<li>Actions</li>
<li>Transforms</li>
</ul>
<p>Learn more on how to modify it <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/xpack-alerting.html">here</a>. Some examples of modifications could include:</p>
<ol>
<li>Look for error logs from application components (e.g., cartService, frontEnd, from the OTel demo), cloud service providers (e.g., AWS/Azure/GCP logs), and even logs from components such as Kafka, databases, etc.</li>
<li>Vary the time frame from running continuously to running over a specific <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-range-query.html">range</a>.</li>
<li>Look for specific errors in the logs.</li>
<li>Query for analysis on a set of errors at once versus just one, which we demonstrated.</li>
</ol>
<p>The modifications are endless, and of course you can run this with OpenAI rather than Azure OpenAI Service.</p>
<h2 id="conclusion">Conclusion</h2>
<p>I hope you’ve gotten an appreciation for how Elastic Observability can help you connect to OpenAI services (Azure OpenAI, as we showed, or even OpenAI) to better analyze an error log message instead of having to run several Google searches and hunt for possible insights.</p>
<p>Here’s a quick recap of what we covered:</p>
<ul>
<li>Developing an Elastic watcher script that can be used to find and send Kubernetes errors into OpenAI and insert them into a new index</li>
<li>Configuring Azure OpenAI Service or OpenAI with the right authorization and request parameters</li>
</ul>
<p>Ready to get started? Sign up <a href="https://cloud.elastic.co/registration">for Elastic Cloud</a> and try out the features and capabilities I’ve outlined above to get the most value and visibility out of your OpenTelemetry data.</p>
<h2 id="appendix">Appendix</h2>
<p>Watcher script</p>
<pre><code>PUT _watcher/watch/chatgpt_analysis
{
    "trigger": {
      "schedule": {
        "interval": "5m"
      }
    },
    "input": {
      "chain": {
          "inputs": [
              {
                  "first": {
                      "search": {
                          "request": {
                              "search_type": "query_then_fetch",
                              "indices": [
                                "logs-kubernetes*"
                              ],
                              "rest_total_hits_as_int": true,
                              "body": {
                                "query": {
                                  "bool": {
                                    "must": [
                                      {
                                        "match": {
                                          "kubernetes.container.name": "konnectivity-agent"
                                        }
                                      },
                                      {
                                        "match" : {
                                          "message":"error"
                                        }
                                      }
                                    ]
                                  }
                                },
                                "size": "1"
                              }
                            }
                        }
                    }
                },
                {
                    "second": {
                        "transform": {
                            "script": "return ['first_hit': ctx.payload.first.hits.hits.0._source.message.replace('\"', \"\")]"
                        }
                    }
                },
                {
                    "third": {
                        "http": {
                            "request": {
                                "method" : "POST",
                                "url": "https://XXX.openai.azure.com/openai/deployments/pme-gpt-35-turbo/chat/completions?api-version=2023-03-15-preview",
                                "headers": {
                                    "api-key" : "XXX",
                                    "content-type" : "application/json"
                                },
                                "body" : "{ \"messages\": [ { \"role\": \"system\", \"content\": \"You are a helpful assistant.\"}, { \"role\": \"user\", \"content\": \"What are the potential reasons for the following kubernetes error: {{ctx.payload.second.first_hit}}\"}], \"temperature\": 0.5, \"max_tokens\": 2048}" ,
                                "connection_timeout": "60s",
                                "read_timeout": "60s"
                            }
                        }
                    }
                }
            ]
        }
    },
    "condition": {
      "compare": {
        "ctx.payload.first.hits.total": {
          "gt": 0
        }
      }
    },
    "actions": {
        "index_payload" : {
            "transform": {
                "script": {
                    "source": """
                        def payload = [:];
                        payload.timestamp = new Date();
                        payload.pod_name = ctx.payload.first.hits.hits[0]._source.kubernetes.pod.name;
                        payload.error_message = ctx.payload.second.first_hit;
                        payload.chatgpt_analysis = ctx.payload.third.choices[0].message.content;
                        return payload;
                    """
                }
            },
            "index" : {
                "index" : "chatgpt_k8s_analyzed"
            }
        }
    }
}
</code></pre>
<h3 id="additionalloggingresources">Additional logging resources:</h3>
<ul>
<li><a href="https://www.elastic.co/getting-started/observability/collect-and-analyze-logs">Getting started with logging on Elastic (quickstart)</a></li>
<li><a href="https://www.elastic.co/guide/en/observability/current/logs-metrics-get-started.html">Ingesting common known logs via integrations (compute node example)</a></li>
<li><a href="https://docs.elastic.co/integrations">List of integrations</a></li>
<li><a href="https://www.elastic.co/blog/log-monitoring-management-enterprise">Ingesting custom application logs into Elastic</a></li>
<li><a href="https://www.elastic.co/blog/observability-logs-parsing-schema-read-write">Enriching logs in Elastic</a></li>
<li>Analyzing Logs with <a href="https://www.elastic.co/blog/reduce-mttd-ml-machine-learning-observability">Anomaly Detection (ML)</a> and <a href="https://www.elastic.co/blog/observability-logs-machine-learning-aiops">AIOps</a></li>
</ul>
<h3 id="commonusecaseexampleswithlogs">Common use case examples with logs:</h3>
<ul>
<li><a href="https://youtu.be/ax04ZFWqVCg">Nginx log management</a></li>
<li><a href="https://www.elastic.co/blog/vpc-flow-logs-monitoring-analytics-observability">AWS VPC Flow log management</a></li>
<li><a href="https://youtu.be/Li5TJAWbz8Q">PostgreSQL issue analysis with AIOps</a></li>
</ul>
<p><em>In this blog post, we may have used third party generative AI tools, which are owned and operated by their respective owners. Elastic does not have any control over the third party tools and we have no responsibility or liability for their content, operation or use, nor for any loss or damage that may arise from your use of such tools. Please exercise caution when using AI tools with personal, sensitive or confidential information. Any data you submit may be used for AI training or other purposes. There is no guarantee that information you provide will be kept secure or confidential. You should familiarize yourself with the privacy practices and terms of use of any generative AI tools prior to use.</em></p>
<p><em>Elastic, Elasticsearch and associated marks are trademarks, logos or registered trademarks of Elasticsearch N.V. in the United States and other countries. All other company and product names are trademarks, logos or registered trademarks of their respective owners.</em></p>
<p><em>Screenshots of Microsoft products used with permission from Microsoft.</em></p>]]></content:encoded>
    <link>https://www.elastic.co/observability-labs/blog/kubernetes-errors-observability-logs-openai</link>
    <guid isPermaLink="false">kubernetes-errors-observability-logs-openai</guid>
    <category><![CDATA[Kubernetes]]></category>
    <category><![CDATA[Infrastructure Monitoring]]></category>
    <category><![CDATA[LLM Observability]]></category>
    <dc:creator><![CDATA[Bahubali Shetti]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltf5a82668ffd902a5/6a85cc5033f2444aab49f528/blog-elastic-configuration.png" length="0" type="image/png"/>
    <pubDate>Thu, 18 May 2023 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Unleash the power of Elastic and Amazon Kinesis Data Firehose to enhance observability and data analytics]]></title>
    <description><![CDATA[AWS users can now leverage the new Amazon Kinesis Firehose Delivery Stream to directly ingest logs into Elastic Cloud in real time for centralized alerting, troubleshooting, and analytics across your cloud and on-premises infrastructure.]]></description>
    <content:encoded><![CDATA[<p>As more organizations leverage the Amazon Web Services (AWS) cloud platform and services to drive operational efficiency and bring products to market, managing logs becomes a critical component of maintaining visibility and safeguarding multi-account AWS environments. Traditionally, logs are stored in Amazon Simple Storage Service (Amazon S3) and then shipped to an external monitoring and analysis solution for further processing.</p>
<p>To simplify this process and reduce management overhead, AWS users can now leverage the new Amazon Kinesis Firehose Delivery Stream to ingest logs into Elastic Cloud in AWS in real time and view them in the Elastic Stack alongside other logs for centralized analytics. This eliminates the necessity for time-consuming and expensive procedures such as VM provisioning or data shipper operations.</p>
<p>Elastic Observability unifies logs, metrics, and application performance monitoring (APM) traces for a full contextual view across your hybrid <a href="https://www.elastic.co/blog/aws-service-metrics-monitor-observability-easy">AWS environments alongside their on-premises data sets</a>. Elastic Observability enables you to track and monitor performance <a href="https://www.elastic.co/observability/aws-monitoring">across a broad range of AWS services</a>, including AWS Lambda, Amazon Elastic Compute Cloud (EC2), Amazon Elastic Container Service (ECS), Amazon Elastic Kubernetes Service (EKS), Amazon Simple Storage Service (S3), Amazon Cloudtrail, Amazon Network Firewall, and more.</p>
<p>In this blog, we will walk you through how to use the Amazon Kinesis Data Firehose integration — <a href="https://aws.amazon.com/blogs/big-data/accelerate-data-insights-with-elastic-and-amazon-kinesis-data-firehose/">Elastic is listed in the Amazon Kinesis Firehose</a> drop-down list — to simplify your architecture and send logs to Elastic, so you can monitor and safeguard your multi-account AWS environments.</p>
<h2 id="announcingthekinesisfirehosemethod">Announcing the Kinesis Firehose method</h2>
<p>Elastic currently provides both agent-based and serverless mechanisms, and we are pleased to announce the addition of the Kinesis Firehose method. This new method enables customers to directly ingest logs from AWS into Elastic, supplementing our existing options.</p>
<ul>
<li><a href="https://www.youtube.com/watch?v=pnGXjljuEnY"><strong>Elastic Agent</strong></a> pulls metrics and logs from CloudWatch and S3 where logs are generally pushed from a service (for example, EC2, ELB, WAF, Route53) and ingests them into Elastic Cloud.</li>
<li><a href="https://www.elastic.co/blog/elastic-and-aws-serverless-application-repository-speed-time-to-actionable-insights-with-frictionless-log-ingestion-from-amazon-s3"><strong>Elastic’s Serverless Forwarder</strong></a> (runs Lambda and available in AWS SAR) sends logs from Kinesis Data Stream, Amazon S3, and AWS Cloudwatch log groups into Elastic. To learn more about this topic, please see this <a href="https://www.elastic.co/blog/elastic-and-aws-serverless-application-repository-speed-time-to-actionable-insights-with-frictionless-log-ingestion-from-amazon-s3">blog post</a>.</li>
<li><a href="https://docs.aws.amazon.com/firehose/latest/dev/what-is-this-service.html"><strong>Amazon Kinesis Firehose</strong></a> directly ingests logs from AWS into Elastic (specifically, if you are running the Elastic Cloud on AWS).</li>
</ul>
<p>In this blog, we will cover the last option since we have recently released the Amazon Kinesis Data Firehose integration. Specifically, we'll review:</p>
<ul>
<li>A general overview of the Amazon Kinesis Data Firehose integration and how it works with AWS</li>
<li>Step-by-step instructions to set up the Amazon Kinesis Data Firehose integration on AWS and on <a href="http://cloud.elastic.co">Elastic Cloud</a></li>
</ul>
<p>By the end of this blog, you'll be equipped with the knowledge and tools to simplify your AWS log management with Elastic Observability and Amazon Kinesis Data Firehose.</p>
<h2 id="prerequisitesandconfigurations">Prerequisites and configurations</h2>
<p>If you intend to follow the steps outlined in this blog post, there are a few prerequisites and configurations that you should have in place beforehand.</p>
<ol>
<li>You will need an account on <a href="http://cloud.elastic.co">Elastic Cloud</a> and a deployed stack on AWS. Instructions for deploying a stack on AWS can be found <a href="https://www.elastic.co/guide/en/elastic-stack/current/installing-elastic-stack.html">here</a>. This is necessary for AWS Firehose Log ingestion.</li>
<li>You will also need an AWS account with the necessary permissions to pull data from AWS. Details on the required permissions can be found in our <a href="https://docs.elastic.co/en/integrations/aws#aws-permissions">documentation</a>.</li>
<li>Finally, be sure to turn on VPC Flow Logs for the VPC where your application is deployed and send them to AWS Firehose.</li>
</ol>
<h2 id="elasticsamazonkinesisdatafirehoseintegration">Elastic’s Amazon Kinesis Data Firehose integration</h2>
<p>Elastic has collaborated with AWS to offer a seamless integration of Amazon Kinesis Data Firehose with Elastic, enabling direct ingestion of data from Amazon Kinesis Data Firehose into Elastic without the need for Agents or Beats. All you need to do is configure the Amazon Kinesis Data Firehose delivery stream to send its data to Elastic's endpoint. In this configuration, we will demonstrate how to ingest VPC Flow logs and Firewall logs into Elastic. You can follow a similar process to ingest other logs from your AWS environment into Elastic.</p>
<p>There are three distinct configurations available for ingesting VPC Flow and Network firewall logs into Elastic. One configuration involves sending logs through CloudWatch, and another uses S3 and Kinesis Firehose; each has its own unique setup. With Cloudwatch and S3 you can store and forward but with Kinesis Firehose you will have to ingest immediately. However, in this blog post, we will focus on this new configuration that involves sending VPC Flow logs and Network Firewall logs directly to Elastic.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt49b9db35922e8fd7/6a85c8192d64d540e0081cd0/image2.png" alt="AWS elastic configuration" /></p>
<p>We will guide you through the configuration of the easiest setup, which involves directly sending VPC Flow logs and Firewalls logs to Amazon Kinesis Data Firehose and then into Elastic Cloud.</p>
<p><strong>Note:</strong> It's important to note that this setup is only compatible with Elastic Cloud on AWS and cannot be used with self-managed or on-premise or other cloud provider Elastic deployments.</p>
<h2 id="settingitallup">Setting it all up</h2>
<p>To begin setting up the integration between Amazon Kinesis Data Firehose and Elastic, let's go through the necessary steps.</p>
<h3 id="step0getanaccountonelasticcloud">Step 0: Get an account on Elastic Cloud</h3>
<p>Create an account on Elastic Cloud by following the instructions provided to <a href="https://cloud.elastic.co/registration?fromURI=/home">get started on Elastic Cloud</a>.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt3844d213efe58534/6a85c81c4710c65156d3caed/Screenshot_2023-05-18_at_6.00.28_PM.png" alt="elastic free trial" /></p>
<h3 id="step1deployelasticonaws">Step 1: Deploy Elastic on AWS</h3>
<p>You can deploy Elastic on AWS via two different approaches: through the UI or through Terraform. We’ll start first with the UI option.</p>
<p>After logging into Elastic Cloud, create a deployment on Elastic. It's crucial to make sure that the deployment is on Elastic Cloud on AWS since the Amazon Kinesis Data Firehose connects to a specific endpoint that must be on AWS.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltdf563b506826bc9f/6a85c81eabdc29061c122486/blog-elastic-create-a-deployment.png" alt="create a deployment" /></p>
<p>After your deployment is created, it's essential to copy the Elasticsearch endpoint to ensure a seamless configuration process.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt2920135cb8ad6097/6a85c82118249c36fe18f735/blog-elastic-O11y-log.png" alt="O11y log" /></p>
<p>The Elasticsearch HTTP endpoint should be copied and used for Amazon Firehose destination configuration purposes, as it will be required. Here's an example of what the endpoint should look like:</p>
<pre><code>https://elastic-O11y-log.es.us-east-1.aws.found.io
</code></pre>
<h3 id="_alternativeapproachusingterraform_"><em>Alternative approach using Terraform</em></h3>
<p>An alternative approach to deploying Elastic Cloud on AWS is by using Terraform. It's also an effective way to automate and streamline the deployment process.</p>
<p>To begin, simply create a Terraform configuration file that outlines the necessary infrastructure. This file should include resources for your Elastic Cloud deployment and any required IAM roles and policies. By using this approach, you can simplify the deployment process and ensure consistency across environments.</p>
<p>One easy way to create your Elastic Cloud deployment with Terraform is to use this Github <a href="https://github.com/aws-ia/terraform-elastic-cloud">repo</a>. This resource lets you specify the region, version, and deployment template for your Elastic Cloud deployment, as well as any additional settings you require.</p>
<h3 id="step2toturnonelasticsawsintegrationsnavigatetotheelasticintegrationsectioninyourdeployment">Step 2: To turn on Elastic's AWS integrations, navigate to the Elastic Integration section in your deployment</h3>
<p>To install AWS assets in your deployment's Elastic Integration section, follow these steps:</p>
<ol>
<li>Log in to your Elastic Cloud deployment and open <strong>Kibana</strong>.</li>
<li>To get started, go to the <strong>management</strong> section of Kibana and click on " <strong>Integrations.</strong>"</li>
<li>Navigate to the <strong>AWS</strong> integration and click on the "Install AWS Assets" button in the <strong>settings</strong>.This step is important as it installs the necessary assets such as <strong>dashboards</strong> and <strong>ingest pipelines</strong> to enable data ingestion from AWS services into Elastic.</li>
</ol>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt67355ec9c39e6455/6a85c8249a32f14240a7df9a/blog-elastic-aws-settings.png" alt="aws settings" /></p>
<h3 id="step3setuptheamazonkinesisdatafirehosedeliverystreamontheawsconsole">Step 3: Set up the Amazon Kinesis Data Firehose delivery stream on the AWS Console</h3>
<p>You can set up the Kinesis Data Firehose delivery stream via two different approaches: through the AWS Management Console or through Terraform. We’ll start first with the console option.</p>
<p>To set up the Kinesis Data Firehose delivery stream on AWS, follow these <a href="https://docs.aws.amazon.com/firehose/latest/dev/create-destination.html#create-destination-elastic">steps</a>:</p>
<ol>
<li><p>Go to the AWS Management Console and select Amazon Kinesis Data Firehose.</p></li>
<li><p>Click on Create delivery stream.</p></li>
<li><p>Choose a delivery stream name and select Direct PUT or other sources as the source.</p></li>
</ol>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt47da0e781330cf8a/6a85c8272d64d5808b081cd4/blog-elastic-create-delivery-stream.png" alt="create delivery stream" /></p>
<ol>
<li><p>Choose Elastic as the destination.</p></li>
<li><p>In the Elastic destination section, enter the Elastic endpoint URL that you copied from your Elastic Cloud deployment.</p></li>
</ol>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt2997b8a64bfe401d/6a85c82a342d692d7121b08b/blog-elastic-destination-settings.png" alt="destination settings" /></p>
<ol>
<li><p>Choose the content encoding and retry duration as shown above.</p></li>
<li><p>Enter the appropriate parameter values for your AWS log type. For example, for VPC Flow logs, you would need to specify the  <strong>es_datastream_name</strong>  and  <strong>logs-aws.vpc flow-default</strong> _.</p></li>
<li><p>Configure the Amazon S3 bucket as the source backup for the Amazon Kinesis Data Firehose delivery stream failed data or all data, and configure any required tags for the delivery stream.</p></li>
<li><p>Review the settings and click on Create delivery stream.</p></li>
</ol>
<p>In the example above, we are using the <strong>es_datastream_name</strong> parameter to pull in VPC Flow logs through the <strong>logs-aws.vpcflow-default</strong> datastream. Depending on your use case, this parameter can be configured with one of the following types of logs:</p>
<ul>
<li>logs-aws.cloudfront_logs-default (AWS CloudFront logs)</li>
<li>logs-aws.ec2_logs-default (EC2 logs in AWS CloudWatch)</li>
<li>logs-aws.elb_logs-default (Amazon Elastic Load Balancing logs)</li>
<li>logs-aws.firewall_logs-default (AWS Network Firewall logs)</li>
<li>logs-aws.route53_public_logs-default (Amazon Route 53 public DNS queries logs)</li>
<li>logs-aws.route53_resolver_logs-default (Amazon Route 53 DNS queries &amp; responses logs)</li>
<li>logs-aws.s3access-default (Amazon S3 server access log)</li>
<li>logs-aws.vpcflow-default (AWS VPC flow logs)</li>
<li>logs-aws.waf-default (AWS WAF Logs)</li>
</ul>
<h3 id="_alternativeapproachusingterraform_-1"><em>Alternative approach using Terraform</em></h3>
<p>Using the " <strong>aws_kinesis_firehose_delivery_stream</strong>" resource in <strong>Terraform</strong> is another way to create a Kinesis Firehose delivery stream, allowing you to specify the delivery stream name, data source, and destination - in this case, an Elasticsearch HTTP endpoint. To authenticate, you'll need to provide the endpoint URL and an API key. Leveraging this Terraform resource is a fantastic way to automate and streamline your deployment process, resulting in greater consistency and efficiency.</p>
<p>Here's an example code that shows you how to create a Kinesis Firehose delivery stream with Terraform that sends data to an Elasticsearch HTTP endpoint:</p>
<pre><code>resource "aws_kinesis_firehose_delivery_stream" “Elasticcloud_stream" {
  name        = "terraform-kinesis-firehose-ElasticCloud-stream"
  destination = "http_endpoint”
  s3_configuration {
    role_arn           = aws_iam_role.firehose.arn
    bucket_arn         = aws_s3_bucket.bucket.arn
    buffer_size        = 5
    buffer_interval    = 300
    compression_format = "GZIP"
  }
  http_endpoint_configuration {
    url        = "https://cloud.elastic.co/"
    name       = “ElasticCloudEndpoint"
    access_key = “ElasticApi-key"
    buffering_hints {
      size_in_mb = 5
      interval_in_seconds = 300
    }

   role_arn       = "arn:Elastic_role"
   s3_backup_mode = "FailedDataOnly"
  }
}
</code></pre>
<h3 id="step4configurevpcflowlogstosendtoamazonkinesisdatafirehose">Step 4: Configure VPC Flow Logs to send to Amazon Kinesis Data Firehose</h3>
<p>To complete the setup, you'll need to configure VPC Flow logs in the VPC where your application is deployed and send them to the Amazon Kinesis Data Firehose delivery stream you set up in Step 3.</p>
<p>Enabling VPC flow logs in AWS is a straightforward process that involves several steps. Here's a step-by-step details to enable VPC flow logs in your AWS account:</p>
<ol>
<li><p>Select the VPC for which you want to enable flow logs.</p></li>
<li><p>In the VPC dashboard, click on "Flow Logs" under the "Logs" section.</p></li>
<li><p>Click on the "Create Flow Log" button to create a new flow log.</p></li>
<li><p>In the "Create Flow Log" wizard, provide the following information:</p></li>
</ol>
<p>Choose the target for your flow logs: In this case, Amazon Kinesis Data Firehose in the same AWS account.</p>
<ul>
<li>Provide a name for your flow log.</li>
<li>Choose the VPC and the network interface(s) for which you want to enable flow logs.</li>
<li>Choose the flow log format: either AWS default or Custom format.</li>
</ul>
<ol>
<li><p>Configure the IAM role for the flow logs. If you have an existing IAM role, select it. Otherwise, create a new IAM role that grants the necessary permissions for the flow logs.</p></li>
<li><p>Review the flow log configuration and click "Create."</p></li>
</ol>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltb6f869a25b23466b/6a85c82df9373d075096f518/blog-elastic-flow-log-settings.png" alt="flow log settings" /></p>
<p>Create the VPC Flow log.</p>
<h3 id="step5afterafewminutescheckifflowsarecomingintoelastic">Step 5: After a few minutes, check if flows are coming into Elastic</h3>
<p>To confirm that the VPC Flow logs are ingesting into Elastic, you can check the logs in Kibana. You can do this by searching for the index in the Kibana Discover tab and filtering the results by the appropriate index and time range. If VPC Flow logs are flowing in, you should see a list of documents representing the VPC Flow logs.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltd978cd1933be3889/6a85c82f99083fa20340f96b/blog-elastic-expanded-document.png" alt="expanded document" /></p>
<h3 id="step6navigatetokibanatoseeyourlogsparsedandvisualizedinthelogsawsvpcflowlogoverviewdashboard">Step 6: Navigate to Kibana to see your logs parsed and visualized in the [Logs AWS] VPC Flow Log Overview dashboard</h3>
<p>Finally, there is an Elastic out-of-the-box (OOTB) VPC Flow logs dashboard that displays the top IP addresses that are hitting your VPC, their geographic location, time series of the flows, and a summary of VPC flow log rejects within the selected time frame. This dashboard can provide valuable insights into your network traffic and potential security threats.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltc671e25ad44ecc87/6a85c833ba7acced7b9920ec/blog-elastic-VPC-flow-log-map.png" alt="vpc flow log map" /></p>
<p><em>Note: For additional VPC flow log analysis capabilities, please refer to</em> <a href="https://www.elastic.co/blog/vpc-flow-logs-monitoring-analytics-observability"><em>this blog</em></a><em>.</em></p>
<h3 id="step7configureawsnetworkfirewalllogstosendtokinesisfirehose">Step 7: Configure AWS Network Firewall Logs to send to Kinesis Firehose</h3>
<p>To create a Kinesis Data Firehose delivery stream for AWS Network firewall logs, first log in to the AWS Management Console, navigate to the Kinesis service, select "Data Firehose", and follow the step-by-step instructions as shown in Step 3. Specify the Elasticsearch endpoint, API key, add a parameter (_ <strong>es_datastream_name=logs-aws.firewall_logs-default</strong> _), and create the delivery stream.</p>
<p>Second, to set up a Network Firewall rule group to send logs to the Kinesis Firehose, go to the Network Firewall section of the console, create a rule group, add a rule to allow traffic to the Kinesis endpoint, and attach the rule group to your Network Firewall configuration. Finally, test the configuration by sending traffic through the Network Firewall to the Kinesis Firehose endpoint and verify that logs are being delivered to your S3 bucket.</p>
<p>Kindly follow the instructions below to set up a firewall rule and logging.</p>
<ol>
<li>Set up a Network Firewall rule group to send logs to Amazon Kinesis Data Firehose:</li>
</ol>
<ul>
<li>Go to the AWS Management Console and select Network Firewall.</li>
<li>Click on "Rule groups" in the left menu and then click "Create rule group."</li>
<li>Choose "Stateless" or "Stateful" depending on your requirements, and give your rule group a name. Click "Create rule group."</li>
<li>Add a rule to the rule group to allow traffic to the Kinesis Firehose endpoint. For example, if you are using the us-east-1 region, you would add a rule like this:json</li>
</ul>
<pre><code>{
  "RuleDefinition": {
    "Actions": [
      {
        "Type": "AWS::KinesisFirehose::DeliveryStream",
        "Options": {
          "DeliveryStreamArn": "arn:aws:firehose:us-east-1:12387389012:deliverystream/my-delivery-stream"
        }
      }
    ],
    "MatchAttributes": {
      "Destination": {
        "Addresses": ["api.firehose.us-east-1.amazonaws.com"]
      },
      "Protocol": {
        "Numeric": 6,
        "Type": "TCP"
      },
      "PortRanges": [
        {
          "From": 443,
          "To": 443
        }
      ]
    }
  },
  "RuleOptions": {
    "CustomTCPStarter": {
      "Enabled": true,
      "PortNumber": 443
    }
  }
}
</code></pre>
<ul>
<li>Save the rule group.</li>
</ul>
<ol>
<li>Attach the rule group to your Network Firewall configuration:</li>
</ol>
<ul>
<li>Go to the AWS Management Console and select Network Firewall.</li>
<li>Click on "Firewall configurations" in the left menu and select the configuration you want to attach the rule group to.</li>
<li>Scroll down to "Associations" and click "Edit."</li>
<li>Select the rule group you created in Step 2 and click "Save."</li>
</ul>
<ol>
<li>Test the configuration:</li>
</ol>
<ul>
<li>Send traffic through the Network Firewall to the Kinesis Firehose endpoint and verify that logs are being delivered to your S3 bucket.</li>
</ul>
<h3 id="step8navigatetokibanatoseeyourlogsparsedandvisualizedinthelogsawsfirewalllogdashboard">Step 8: Navigate to Kibana to see your logs parsed and visualized in the [Logs AWS] Firewall Log dashboard</h3>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt1f31976122afa373/6a85c83680984c4281668f72/blog-elastic-firewall-log-dashboard.png" alt="firewall log dashboard" /></p>
<h2 id="wrappingup">Wrapping up</h2>
<p>We’re excited to bring you this latest integration for AWS Cloud and Kinesis Data Firehose into production. The ability to consolidate logs and metrics to gain visibility across your cloud and on-premises environment is crucial for today’s distributed environments and applications.</p>
<p>From EC2, Cloudwatch, Lambda, ECS and SAR, <a href="https://www.elastic.co/integrations/data-integrations?solution=all-solutions&amp;category=aws">Elastic Integrations</a> allow you to quickly and easily get started with ingesting your telemetry data for monitoring, analytics, and observability. Elastic is constantly delivering frictionless customer experiences, allowing anytime, anywhere access to all of your telemetry data — this streamlined, native integration with AWS is the latest example of our commitment.</p>
<h2 id="startafreetrialtoday">Start a free trial today</h2>
<p>You can begin with a <a href="https://aws.amazon.com/marketplace/pp/prodview-voru33wi6xs7k">7-day free trial</a> of Elastic Cloud within the AWS Marketplace to start monitoring and improving your users' experience today!</p>
<p><em>The release and timing of any features or functionality described in this post remain at Elastic's sole discretion. Any features or functionality not currently available may not be delivered on time or at all.</em></p>]]></content:encoded>
    <link>https://www.elastic.co/observability-labs/blog/aws-kinesis-data-firehose-observability-analytics</link>
    <guid isPermaLink="false">aws-kinesis-data-firehose-observability-analytics</guid>
    <category><![CDATA[Infrastructure Monitoring]]></category>
    <category><![CDATA[Logs Analytics]]></category>
    <dc:creator><![CDATA[Udayasimha Theepireddy,Bahubali Shetti]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt49b9db35922e8fd7/6a85c8192d64d540e0081cd0/image2.png" length="0" type="image/png"/>
    <pubDate>Thu, 18 May 2023 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Trace your Azure Function application with Elastic Observability]]></title>
    <description><![CDATA[Serverless applications deployed on Azure Functions are growing in usage. This blog shows how to deploy a serverless application on Azure functions with Elastic Agent and use Elastic's APM capability to manage and troubleshoot issues.]]></description>
    <content:encoded><![CDATA[<p>Adoption of Azure Functions in cloud-native applications on Microsoft Azure has been increasing exponentially over the last few years. Serverless functions, such as the Azure Functions, provide a high level of abstraction from the underlying infrastructure and orchestration, given these tasks are managed by the cloud provider. Software development teams can then focus on the implementation of business and application logic. Some additional benefits include billing for serverless functions based on the actual compute and memory resources consumed, along with automatic on-demand scaling.</p>
<p>While the benefits of using serverless functions are manifold, it is also necessary to make them observable in the wider end-to-end microservices architecture context.</p>
<h2 id="elasticobservabilityapmforazurefunctionsthearchitecture">Elastic Observability (APM) for Azure Functions: The architecture</h2>
<p><a href="https://www.elastic.co/blog/whats-new-elastic-observability-8-7-0">Elastic Observability 8.7</a> introduced distributed tracing for Microsoft Azure Functions — available for the Elastic APM Agents for .NET, Node.js, and Python. Auto-instrumentation of HTTP requests is supported out-of-the-box, enabling the detection of performance bottlenecks and sources of errors.</p>
<p>The key components of the solution for observing Azure Functions are:</p>
<ol>
<li>The Elastic APM Agent for the relevant language</li>
<li>Elastic Observability</li>
</ol>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltb31ffdc829e957a8/6a85ce28eaf2456480a49fb7/blog-elastic-azure-function.png" alt="azure function" /></p>
<p>The APM server validates and processes incoming events from individual APM Agents and transforms them into Elasticsearch documents. The APM Agent provides auto-instrumentation capabilities for the application being observed. The Node.js APM Agent can trace function invocations in an Azure Functions app.</p>
<h2 id="settingupelasticapmforazurefunctions">Setting up Elastic APM for Azure Functions</h2>
<p>To demonstrate the setup and usage of Elastic APM, we will use a <a href="https://github.com/elastic/azure-functions-apm-nodejs-sample-app">sample Node.js application</a>.</p>
<h3 id="applicationoverview">Application overview</h3>
<p>The Node.js application has two <a href="https://learn.microsoft.com/en-us/azure/azure-functions/functions-bindings-http-webhook">HTTP-triggered</a> functions named "<a href="https://github.com/elastic/azure-functions-apm-nodejs-sample-app/blob/main/Hello/index.js">Hello</a>" and "<a href="https://github.com/elastic/azure-functions-apm-nodejs-sample-app/blob/main/Goodbye/index.js">Goodbye</a>." Once deployed, they can be called as follows, and tracing data will be sent to the configured Elastic Observability deployment.</p>
<pre><code>curl -i https://&lt;APP_NAME&gt;.azurewebsites.net/api/hello
curl -i https://&lt;APP_NAME&gt;.azurewebsites.net/api/goodbye
</code></pre>
<h3 id="setup">Setup</h3>
<p><strong>Step 0. Prerequisites</strong></p>
<p>To run the sample application, you will need:</p>
<ul>
<li>An installation of <a href="https://nodejs.org/">Node.js</a> (v14 or later)</li>
<li>Access to an Azure subscription with an appropriate role to create resources</li>
<li>The <a href="https://learn.microsoft.com/en-us/cli/azure/install-azure-cli">Azure CLI (az)</a> logged into an Azure subscription</li>
</ul>
<ol>
<li>Use az login to login</li>
<li>See the output of az account show</li>
</ol>
<ul>
<li>The <a href="https://learn.microsoft.com/en-us/azure/azure-functions/functions-run-local?tabs=v4%2Cwindows%2Ccsharp%2Cportal%2Cbash#install-the-azure-functions-core-tools">Azure Functions Core Tools (func)</a> (func --version should show a 4.x version)</li>
<li>An Elastic Observability deployment to which monitoring data will be sent</li>
</ul>
<ol>
<li>The simplest way to get started with Elastic APM Microsoft Azure is through Elastic Cloud. <a href="https://www.elastic.co/guide/en/elastic-stack-deploy/current/azure-marketplace-getting-started.html">Get started with Elastic Cloud on Azure Marketplace</a> or <a href="https://www.elastic.co/cloud/elasticsearch-service/signup">sign up for a trial on Elastic Cloud</a>.</li>
</ol>
<ul>
<li>The APM server URL (serverUrl) and secret token (secretToken) from your Elastic stack deployment for configuration below</li>
</ul>
<ol>
<li><a href="https://www.elastic.co/guide/en/apm/guide/8.7/install-and-run.html">How to get the serverUrl and secretToken documentation</a></li>
</ol>
<p><strong>Step 1. Clone the sample application repo and install dependencies</strong></p>
<pre><code>git clone https://github.com/elastic/azure-functions-apm-nodejs-sample-app.git
cd azure-functions-apm-nodejs-sample-app
npm install
</code></pre>
<p><strong>Step 2. Deploy the Azure Function App</strong><br />
Caution icon! Deploying a function app to Azure can incur <a href="https://azure.microsoft.com/en-us/pricing/details/functions/">costs</a>. The following setup uses the free tier of Azure Functions. Step 5 covers the clean-up of resources.</p>
<p><strong>Step 2.1</strong><br />
To avoid name collisions with others that have independently run this demo, we need a short unique identifier for some resource names that need to be globally unique. We'll call it the DEMO_ID. You can run the following to generate one and save it to DEMO_ID and the "demo-id" file.</p>
<pre><code>if [[ ! -f demo-id ]]; then node -e 'console.log(crypto.randomBytes(3).toString("hex"))' &gt;demo-id; fi
export DEMO_ID=$(cat demo-id)
echo $DEMO_ID
</code></pre>
<p><strong>Step 2.2</strong><br />
Before you can deploy to Azure, you will need to create some Azure resources: a Resource Group, Storage Account, and the Function App. For this demo, you can use the following commands. (See <a href="https://learn.microsoft.com/en-us/azure/azure-functions/create-first-function-cli-node#create-supporting-azure-resources-for-your-function">this Azure docs section</a> for more details.)</p>
<pre><code>REGION=westus2   # Or use another region listed in 'az account list-locations'.
az group create --name "AzureFnElasticApmNodeSample-rg" --location "$REGION"
az storage account create --name "eapmdemostor${DEMO_ID}" --location "$REGION" \
    --resource-group "AzureFnElasticApmNodeSample-rg" --sku Standard_LRS
az functionapp create --name "azure-functions-apm-nodejs-sample-app-${DEMO_ID}" \
    --resource-group "AzureFnElasticApmNodeSample-rg" \
    --consumption-plan-location "$REGION" --runtime node --runtime-version 18 \
    --functions-version 4 --storage-account "eapmdemostor${DEMO_ID}"
</code></pre>
<p><strong>Step 2.3</strong><br />
Next, configure your Function App with the APM server URL and secret token for your Elastic deployment. This can be done in the <a href="https://portal.azure.com/">Azure Portal</a> or with the az CLI.</p>
<p>In the Azure portal, browse to your Function App, then its Application Settings (<a href="https://learn.microsoft.com/en-us/azure/azure-functions/functions-how-to-use-azure-function-app-settings?tabs=portal#settings">Azure user guide</a>). You'll need to add two settings:</p>
<p>First set your APM URL and token.</p>
<pre><code>export ELASTIC_APM_SERVER_URL="&lt;your serverUrl&gt;"
export ELASTIC_APM_SECRET_TOKEN="&lt;your secretToken&gt;"
</code></pre>
<p>Or you can use the az functionapp config appsettings set … CLI command as follows:</p>
<pre><code>az functionapp config appsettings set \
  -g "AzureFnElasticApmNodeSample-rg" -n "azure-functions-apm-nodejs-sample-app-${DEMO_ID}" \
  --settings "ELASTIC_APM_SERVER_URL=${ELASTIC_APM_SERVER_URL}"
az functionapp config appsettings set \
  -g "AzureFnElasticApmNodeSample-rg" -n "azure-functions-apm-nodejs-sample-app-${DEMO_ID}" \
  --settings "ELASTIC_APM_SECRET_TOKEN=${ELASTIC_APM_SECRET_TOKEN}"
</code></pre>
<p>The ELASTIC_APM_SERVER_URL and ELASTIC_APM_SECRET_TOKEN are set in Azure function’s settings for the app and used by the Elastic APM Agent. This is initiated by the initapm.js file, which starts the Elastic APM agent with:</p>
<pre><code>require("elastic-apm-node").start();
</code></pre>
<p>When you log in to Azure and look at the function’s configuration, you will see them set:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt17f40523d90b8ed0/6a85ce2a1aa1e1b268ff8dc3/blog-elastic-azure-functions-application-settings.png" alt="azure functions application settings" /></p>
<p><strong>Step 2.4</strong><br />
Now you can publish your app. (Re-run this command every time you make a code change.)</p>
<pre><code>func azure functionapp publish "azure-functions-apm-nodejs-sample-app-${DEMO_ID}"
</code></pre>
<p>You should log in to Azure to see the function running.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt461d0d8fdb235dc9/6a85ce2d2d64d52d7c081d9a/blog-elastic-azure-function-app.png" alt="azure function app" /></p>
<p><strong>Step 3. Try it out</strong></p>
<pre><code>% curl https://azure-functions-apm-nodejs-sample-app-${DEMO_ID}.azurewebsites.net/api/Hello
{"message":"Hello."}
% curl https://azure-functions-apm-nodejs-sample-app-${DEMO_ID}.azurewebsites.net/api/Goodbye
{"message":"Goodbye."}
</code></pre>
<p>In a few moments, the APM app in your Elastic deployment will show tracing data for your Azure Function app.</p>
<p><strong>Step 4. Apply some load to your app</strong><br />
To get some more interesting data, you can run the following to generate some load on your deployed function app:</p>
<pre><code>npm run loadgen
</code></pre>
<p>This uses the <a href="https://github.com/mcollina/autocannon">autocannon</a> node package to generate some light load (2 concurrent users, each calling at 5 requests/s for 60s) on the "Goodbye" function.</p>
<p><strong>Step 5. Clean up resources</strong><br />
If you deployed to Azure, you should make sure to delete any resources so you don't incur any costs.</p>
<pre><code>az group delete --name "AzureFnElasticApmNodeSample-rg"
</code></pre>
<h2 id="analyzingazurefunctionapmdatainelastic">Analyzing Azure Function APM data in Elastic</h2>
<p>Once you have successfully set up the sample application and started generating load, you should see APM data appearing in the Elastic Observability APM Services capability.</p>
<h2 id="servicemap">Service map</h2>
<p>With the default setup, you will see two services in the APM Service map.</p>
<p>The main function: azure-functions-apm-nodejs-sample-app</p>
<p>And the end point where your function is accessible: azure-functions-apm-nodejs-sample-app-ec7d4c.azurewebsites.net</p>
<p>You will see that there is a connection between the two as your application is taking requests and answering through the endpoint.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt54f1085b82117c5a/6a85ce3018249c0a4218f833/blog-elastic-observability-services.png" alt="observability services" /></p>
<p>From the <a href="https://www.elastic.co/observability/application-performance-monitoring">APM Service</a> map you can further investigate the function, analyze traces, look at logs, and more.</p>
<h3 id="servicedetails">Service details</h3>
<p>When we dive into the details, we can see several items.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt7320943d10a3b565/6a85ce33f9373d5edb96f5ec/blog-elastic-observability-azure-functions-apm.png" alt="observability azure functions apm" /></p>
<ul>
<li>Latency for the recent load we ran against the application</li>
<li>Transactions (Goodbye and Hello)</li>
<li>Average throughput</li>
<li>And more</li>
</ul>
<h3 id="transactiondetails">Transaction details</h3>
<p>We can see transaction details.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt57205ffed9ed8840/6a85ce366826666d441eac65/blog-elastic-observability-get-api-goodbye.png" alt="observability get api goodbye" /></p>
<p>An individual trace shows us that the "Goodbye" function <a href="https://github.com/elastic/azure-functions-apm-nodejs-sample-app/blob/main/Goodbye/index.js#L6-L10">calls the "Hello" function</a> in the same function app before returning:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blta3f4a958241d2755/6a85ce39342d69cc5021b15d/blog-elastic-latency-distribution-trace-sample.png" alt="latency distribution trace sample" /></p>
<h3 id="machinelearningbasedlatencycorrelation">Machine learning based latency correlation</h3>
<p>As we’ve mentioned in other blogs, we can also correlate issues such as higher than normal latency. Since we see a spike at 1s, we run the embedded latency correlation, which uses machine learning to help analyze the potential impacting component by analyzing logs, metrics, and traces.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt4cc5483b0e4f032c/6a85ce3bbc5bb384a7f81b53/blog-elastic-latency-distribution-correlations.png" alt="latency distribution correlations" /></p>
<p>The correlation indicated there is a potential cause (25%) due to the host sending the load (my machine).</p>
<h3 id="coldstartdetection">Cold start detection</h3>
<p>Also, we can see the impact a <a href="https://azure.microsoft.com/en-ca/blog/understanding-serverless-cold-start/">cold start</a> can have on the latency of a request:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt2bc48a2793c2d457/6a85ce3e27c5cd6b9f5f7454/blog-elastic-trace-sample.png" alt="trace sample" /></p>
<h2 id="summary">Summary</h2>
<p>Elastic Observability provides real-time monitoring of Azure Functions in your production environment for a broad range of use cases. Curated dashboards assist DevOps teams in performing root cause analysis for performance bottlenecks and errors. SRE teams can quickly view upstream and downstream dependencies, as well as perform analyses in the context of distributed microservices architecture.</p>
<h2 id="learnmore">Learn more</h2>
<p>To learn how to add the Elastic APM Agent to an existing Node.js Azure Function app, read <a href="https://www.elastic.co/guide/en/apm/agent/nodejs/master/azure-functions.html">Monitoring Node.js Azure Functions</a>. Additional resources include:</p>
<ul>
<li><a href="https://www.elastic.co/blog/getting-started-with-the-azure-integration-enhancement">How to deploy and manage Elastic Observability on Microsoft Azure</a></li>
<li><a href="https://www.elastic.co/guide/en/apm/guide/current/apm-quick-start.html">Elastic APM Quickstart</a></li>
</ul>]]></content:encoded>
    <link>https://www.elastic.co/observability-labs/blog/trace-azure-function-application-observability</link>
    <guid isPermaLink="false">trace-azure-function-application-observability</guid>
    <category><![CDATA[APM]]></category>
    <category><![CDATA[Infrastructure Monitoring]]></category>
    <dc:creator><![CDATA[Trent Mick,Bahubali Shetti,Hemant Malik]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltb7529874ffa275f9/6a85ce419d2b719823f93a0e/09-road.jpeg" length="0" type="image/jpeg"/>
    <pubDate>Tue, 16 May 2023 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[How to monitor Kafka and Confluent Cloud with Elastic Observability]]></title>
    <description><![CDATA[This blog post will take you through best practices to observe Kafka-based solutions implemented on Confluent Cloud with Elastic Observability.]]></description>
    <content:encoded><![CDATA[<p>The blog will take you through best practices to observe Kafka-based solutions implemented on Confluent Cloud with Elastic Observability. (To monitor Kafka brokers that are not in Confluent Cloud, I recommend checking out <a href="https://www.elastic.co/blog/how-to-monitor-containerized-kafka-with-elastic-observability">this blog</a>.) We will instrument Kafka applications with <a href="https://www.elastic.co/observability/application-performance-monitoring">Elastic APM</a>, use the Confluent Cloud metrics endpoint to get data about brokers, and pull it all together with a unified Kafka and Confluent Cloud monitoring dashboard in <a href="https://www.elastic.co/observability">Elastic Observability</a>.</p>
<h2 id="usingfullstackelasticobservabilitytounderstandkafkaandconfluentperformance">Using full-stack Elastic Observability to understand Kafka and Confluent performance</h2>
<p>In the <a href="https://dice.viewer.foleon.com/ebooks/dice-tech-salary-report-explore/">2023 Dice Tech Salary Report</a>, Elasticsearch and Kakfa are ranked #3 and #5 out of the top 12 <a href="https://dice.viewer.foleon.com/ebooks/dice-tech-salary-report-explore/salary-trends#Skills">most in demand skills</a> at the moment, so it’s no surprise that we are seeing a large number of customers who are implementing data in motion with Kafka.</p>
<p><a href="https://www.elastic.co/integrations/data-integrations?search=kafka">Kafka</a> comes with some additional complexities that go beyond traditional architectures and which make observability an even more important topic. Understanding where the bottlenecks are in messaging and stream-based architectures can be tough. This is why you need a comprehensive observability solution with <a href="https://www.elastic.co/blog/aiops-use-cases-observability-operations">machine learning</a> to help you.</p>
<p>In this blog, we will explore how to get Kafka applications instrumented with <a href="https://www.elastic.co/blog/apm-correlations-elastic-observability-root-cause-transactions">Elastic APM</a>, how to collect performance data with JMX, and how you can use the Elasticsearch Platform to pull in data from Confluent Cloud — which is by far the easiest and most cost-effective way to implement Kafka architectures.</p>
<p>For this blog post, we will be following the code at this <a href="https://github.com/davidgeorgehope/multi-cloud">git repository</a>. There are three services here that are designed to run on two clouds and push data from one cloud to the other and finally into Google BigQuery. We want to monitor all of this using Elastic Observability to give you a complete picture of Confluent and Kafka Services performance as a teaser — this is the goal below:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt2b83acb7398304ef/6a85cb8e80984cb656668fec/blog-elastic-observability-producer_metrics.png" alt="kafka producer metrics" /></p>
<h2 id="alookatthearchitecture">A look at the architecture</h2>
<p>As mentioned, we have three <a href="https://www.elastic.co/observability/cloud-monitoring">multi-cloud services</a> implemented in our example application.</p>
<p>The first service is a Spring WebFlux service that runs inside AWS EKS. This service will take a message from a REST Endpoint and simply put it straight on to a Kafka topic.</p>
<p>The second service, which is also a Spring WebFlux service hosted inside Google Cloud Platform (GCP) with its <a href="https://www.elastic.co/observability/google-cloud-monitoring">Google Cloud monitoring</a>, will then pick this up and forward it to another service that will put the message into BigQuery.</p>
<p>These services are all instrumented using Elastic APM. For this blog, we have decided to use Spring config to inject and configure the APM agent. You could of course use the “-javaagent” argument to inject the agent instead if preferred.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt26abffd850b918eb/6a85cb90078290b03c32177c/blog-elastic-obsevability-aws-kafka-google-cloud.png" alt="aws kafka google cloud" /></p>
<h2 id="gettingstartedwithelasticobservabilityandconfluentcloud">Getting started with Elastic Observability and Confluent Cloud</h2>
<p>Before we dive into the application and its configuration, you will want to get an Elastic Cloud and Confluent Cloud account. You can sign up here for <a href="https://www.elastic.co/cloud/">Elastic</a> and here for <a href="https://www.confluent.io/confluent-cloud/">Confluent Cloud</a>. There are some initial configuration steps we need to do inside Confluent Cloud, as you will need to create three topics: gcpTopic, myTopic, and topic_2.</p>
<p>When you sign up for Confluent Cloud, you will be given an option of what type of cluster to create. For this walk-through, a Basic cluster is fine (as shown) — if you are careful about usage, it will not cost you a penny.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltc4c5c643934dbd2f/6a85cb9411893c4c32a7aba0/blog-elastic-observability-confluent-create-cluster.png" alt="confluent create cluster" /></p>
<p>Once you have a cluster, go ahead and create the three topics.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltb7f03631f81bcf62/6a85cb96331d7aaed8c317a9/blog-elastic-observability-confluent-topics.png" alt="confluent topics" /></p>
<p>For this walk-through, you will only need to create single partition topics as shown below:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt480e1df0acaafce5/6a85cb999bf99456280a0581/blog-elastic-observability-new-topic.png" alt="new topic" /></p>
<p>Now we are ready to set up the Elastic Cloud cluster.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt3c52f84578c22908/6a85cb9c18249c40f018f7cb/blog-elastic-observability-create-a-deployment.png" alt="create a deployment" /></p>
<p>One thing to note here is that when setting up an Elastic cluster, the defaults are mostly OK. With one minor tweak to add in the Machine Learning under “Advanced Settings,” add capacity for machine learning here.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt6d43d7fb6ea1311d/6a85cb9f99083f43c340f9e5/blog-elastic-observability-machine-learning-instances.png" alt="machine learning instances" /></p>
<h2 id="gettingapmupandrunning">Getting APM up and running</h2>
<p>The first thing we want to do here is get our Spring Boot Webflux-based services up and running. For this blog, I have decided to implement this using the Spring Configuration, as you can see below. For brevity, I have not listed all the JMX configuration information, but you can see those details in <a href="https://github.com/davidgeorgehope/multi-cloud/blob/main/aws-multi-cloud/src/main/java/com/elastic/multicloud/ElasticApmConfig.java">GitHub</a>.</p>
<pre><code>package com.elastic.multicloud;
import co.elastic.apm.attach.ElasticApmAttacher;
import jakarta.annotation.PostConstruct;
import lombok.Setter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;

import java.util.HashMap;
import java.util.Map;

@Setter
@Configuration
@ConfigurationProperties(prefix = "elastic.apm")
@ConditionalOnProperty(value = "elastic.apm.enabled", havingValue = "true")
public class ElasticApmConfig {

    private static final String SERVER_URL_KEY = "server_url";
    private String serverUrl;

    private static final String SERVICE_NAME_KEY = "service_name";
    private String serviceName;

    private static final String SECRET_TOKEN_KEY = "secret_token";
    private String secretToken;

    private static final String ENVIRONMENT_KEY = "environment";
    private String environment;

    private static final String APPLICATION_PACKAGES_KEY = "application_packages";
    private String applicationPackages;

    private static final String LOG_LEVEL_KEY = "log_level";
    private String logLevel;
    private static final Logger LOGGER = LoggerFactory.getLogger(ElasticApmConfig.class);

    @PostConstruct
    public void init() {
        LOGGER.info(environment);

        Map&lt;String, String&gt; apmProps = new HashMap&lt;&gt;(6);
        apmProps.put(SERVER_URL_KEY, serverUrl);
        apmProps.put(SERVICE_NAME_KEY, serviceName);
        apmProps.put(SECRET_TOKEN_KEY, secretToken);
        apmProps.put(ENVIRONMENT_KEY, environment);
        apmProps.put(APPLICATION_PACKAGES_KEY, applicationPackages);
        apmProps.put(LOG_LEVEL_KEY, logLevel);
        apmProps.put("enable_experimental_instrumentations","true");
          apmProps.put("capture_jmx_metrics","object_name[kafka.producer:type=producer-metrics,client-id=*] attribute[batch-size-avg:metric_name=kafka.producer.batch-size-avg]");


        ElasticApmAttacher.attach(apmProps);
    }
}
</code></pre>
<p>Now obviously this requires some dependencies, which you can see here in the Maven pom.xml.</p>
<pre><code>&lt;dependency&gt;
            &lt;groupId&gt;co.elastic.apm&lt;/groupId&gt;
            &lt;artifactId&gt;apm-agent-attach&lt;/artifactId&gt;
            &lt;version&gt;1.35.1-SNAPSHOT&lt;/version&gt;
        &lt;/dependency&gt;
        &lt;dependency&gt;
            &lt;groupId&gt;co.elastic.apm&lt;/groupId&gt;
            &lt;artifactId&gt;apm-agent-api&lt;/artifactId&gt;
            &lt;version&gt;1.35.1-SNAPSHOT&lt;/version&gt;
        &lt;/dependency&gt;
</code></pre>
<p>Strictly speaking, the agent-api is not required, but it could be useful if you have a desire to add your own monitoring code (as per the example below). The agent will happily auto-instrument without needing to do that though.</p>
<pre><code>Transaction transaction = ElasticApm.currentTransaction();
        Span span = ElasticApm.currentSpan()
                .startSpan("external", "kafka", null)
                .setName("DAVID").setServiceTarget("kafka","gcp-elastic-apm-spring-boot-integration");
        try (final Scope scope = transaction.activate()) {
            span.injectTraceHeaders((name, value) -&gt; producerRecord.headers().add(name,value.getBytes()));
            return Mono.fromRunnable(() -&gt; {
                kafkaTemplate.send(producerRecord);
            });
        } catch (Exception e) {
            span.captureException(e);
            throw e;
        } finally {
            span.end();
        }
</code></pre>
<p>Now we have enough code to get our agent bootstrapped.</p>
<p>To get the code from the GitHub repository up and running, you will need the following installed on your system and to ensure that you have the credentials for your GCP and AWS cloud.</p>
<pre><code>Java
Maven
Docker
Kubernetes CLI (kubectl)
</code></pre>
<h3 id="clonetheproject">Clone the project</h3>
<p>Clone the multi-cloud Spring project to your local machine.</p>
<pre><code>git clone https://github.com/davidgeorgehope/multi-cloud
</code></pre>
<h3 id="buildtheproject">Build the project</h3>
<p>From each service in the project (aws-multi-cloud, gcp-multi-cloud, gcp-bigdata-consumer-multi-cloud), run the following commands to build the project.</p>
<pre><code>mvn clean install
</code></pre>
<p>Now you can run the Java project locally.</p>
<pre><code>java -jar gcp-bigdata-consumer-multi-cloud-0.0.1-SNAPSHOT.jar --spring.config.location=/Users/davidhope/applicaiton-gcp.properties
</code></pre>
<p>That will just get the Java application running locally, but you can also deploy this to Kubernetes using EKS and GKE as shown below.</p>
<h3 id="createadockerimage">Create a Docker image</h3>
<p>Create a Docker image from the built project using the dockerBuild.sh provided in the project. You may want to customize this shell script to upload the built docker image to your own docker repository.</p>
<pre><code>./dockerBuild.sh
</code></pre>
<h3 id="createanamespaceforeachservice">Create a namespace for each service</h3>
<pre><code>kubectl create namespace aws
</code></pre>
<pre><code>kubectl create namespace gcp-1
</code></pre>
<pre><code>kubectl create namespace gcp-2
</code></pre>
<p>Once you have the namespaces created, you can switch context using the following command:</p>
<pre><code>kubectl config set-context --current --namespace=my-namespace
</code></pre>
<h3 id="configurationforeachservice">Configuration for each service</h3>
<p>Each service needs an application.properties file. I have put an example <a href="https://github.com/davidgeorgehope/multi-cloud/blob/main/gcp-bigdata-consumer-multi-cloud/application.properties">here</a>.</p>
<p>You will need to replace the following properties with those you find in Elastic.</p>
<pre><code>elastic.apm.server-url=
elastic.apm.secret-token=
</code></pre>
<p>These can be found by going into Elastic Cloud and clicking on <strong>Services</strong> inside APM and then <strong>Add Data</strong> , which should be visible in the top right corner.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt74c077968f0c2141/6a85cba168266603f01eac21/blog-elastic-observability-add-data.png" alt="add data" /></p>
<p>From there you will see the following, which gives you the config information you need.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltaacc6d805866b018/6a85cba4501a852f79fbb341/blog-elastic-observability-apm-agents.png" alt="apm agents" /></p>
<p>You will need to replace the following properties with those you find in Confluent Cloud.</p>
<pre><code>elastic.kafka.producer.sasl-jaas-config=
</code></pre>
<p>This configuration comes from the Clients page in Confluent Cloud.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt79dc20646b2e6404/6a85cba7d7b2e75ea0fe84e8/blog-elastic-observability-confluent-new-client.png" alt="confluent new client" /></p>
<h3 id="addingtheconfigforeachserviceinkubernetes">Adding the config for each service in Kubernetes</h3>
<p>Once you have a fully configured application properties, you need to add it to your <a href="https://www.elastic.co/blog/kubernetes-cluster-metrics-logs-monitoring">Kubernetes environment</a> as below.</p>
<p>From the aws namespace.</p>
<pre><code>kubectl create secret generic my-app-config --from-file=application.properties
</code></pre>
<p>From the gcp-1 namespace.</p>
<pre><code>kubectl create secret generic my-app-config --from-file=application.properties
</code></pre>
<p>From the gcp-2 namespace.</p>
<pre><code>kubectl create secret generic bigdata-creds --from-file=elastic-product-marketing-e145e13fbc7c.json

kubectl create secret generic my-app-config-gcp-bigdata --from-file=application.properties
</code></pre>
<h3 id="createakubernetesdeployment">Create a Kubernetes deployment</h3>
<p>Create a Kubernetes deployment YAML file and add your Docker image to it. You can use the deployment.yaml file provided in the project as a template. Make sure to update the image name in the file to match the name of the Docker image you just created.</p>
<pre><code>kubectl apply -f deployment.yaml
</code></pre>
<h3 id="createakubernetesservice">Create a Kubernetes service</h3>
<p>Create a Kubernetes service YAML file and add your deployment to it. You can use the service.yaml file provided in the project as a template.</p>
<pre><code>kubectl apply -f service.yaml
</code></pre>
<h3 id="accessyourapplication">Access your application</h3>
<p>Your application is now running in a Kubernetes cluster. To access it, you can use the service's cluster IP and port. You can get the service's IP and port using the following command.</p>
<pre><code>kubectl get services
</code></pre>
<p>Now once you know where the service is, you need to execute it!</p>
<p>You can regularly poke the service endpoint using the following command.</p>
<pre><code>curl -X POST -H "Content-Type: application/json" -d '{"name": "linuxize", "email": "linuxize@example.com"}' http://localhost:8080/api/my-objects/publish
</code></pre>
<p>With this up and running, you should see the following service map build out in the Elastic APM product.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt6bcd176aacf5c2bf/6a85cbaa68266613df1eac25/blog-elastic-observability-aws-elastic-apm-spring-boot.png" alt="aws elastic apm spring boot" /></p>
<p>And traces will contain a waterfall graph showing all the spans that have executed across this distributed application, allowing you to pinpoint where any issues are within each transaction.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt94b9bc91dc092bb9/6a85cbad9bf994e9330a0585/blog-elastic-observability-services.png" alt="observability services" /></p>
<h2 id="jmxforkafkaproducerconsumermetrics">JMX for Kafka Producer/Consumer metrics</h2>
<p>In the previous part of this blog, we briefly touched on the JMX metric configuration you can see below.</p>
<pre><code>"capture_jmx_metrics","object_name[kafka.producer:type=producer-metrics,client-id=*] attribute[batch-size-avg:metric_name=kafka.producer.batch-size-avg]"
</code></pre>
<p>We can use this “capture_jmx_metrics” configuration to configure JMX for any Kafka Producer/Consumer metrics we want to monitor.</p>
<p>Check out the documentation <a href="https://www.elastic.co/guide/en/apm/agent/java/current/config-jmx.html">here</a> to understand how to configure this and <a href="https://docs.confluent.io/platform/current/kafka/monitoring.html">here</a> to see the available JMX metrics you can monitor. In the <a href="https://github.com/davidgeorgehope/multi-cloud/blob/main/gcp-bigdata-consumer-multi-cloud/src/main/java/com/elastic/multicloud/ElasticApmConfig.java">example code in GitHub</a>, we actually pull all the available metrics in, so you can check in there how to configure this.</p>
<p>One thing that’s worth pointing out here is that it’s important to use the “metric_name” property shown above or it gets quite difficult to find the metrics in Elastic Discover without being specific here.</p>
<h2 id="monitoringconfluentcloudwithelasticobservability">Monitoring Confluent Cloud with Elastic Observability</h2>
<p>So we now have some good monitoring set up for Kafka Producers and Consumers and we can trace transactions between services down to the lines of code that are executing. The core part of our Kafka infrastructure is hosted in Confluent Cloud. How, then, do we get data from there into our <a href="https://www.elastic.co/observability">full stack observability solution</a>?</p>
<p>Luckily, Confluent has done a fantastic job of making this easy. It provides important Confluent Cloud metrics via an open Prometheus-based metrics URL. So let's get down to business and configure this to bring data into our <a href="https://www.elastic.co/observability">observability tool</a>.</p>
<p>The first step is to configure Confluent Cloud with the MetricsViewer. The MetricsViewer role provides service account access to the Metrics API for all clusters in an organization. This role also enables service accounts to import metrics into third-party metrics platforms.</p>
<p>To assign the MetricsViewer role to a new service account:</p>
<ol>
<li>In the top-right administration menu (☰) in the upper-right corner of the Confluent Cloud user interface, click <strong>ADMINISTRATION &gt; Cloud API keys</strong>.</li>
<li>Click <strong>Add key</strong>.</li>
<li>Click the <strong>Granular access tile</strong> to set the scope for the API key. Click <strong>Next</strong>.</li>
<li>Click <strong>Create a new one</strong> and specify the service account name. Optionally, add a description. Click <strong>Next</strong>.</li>
<li>The API key and secret are generated for the service account. You will need this API key and secret to connect to the cluster, so be sure to safely store this information. Click <strong>Save</strong>. The new service account with the API key and associated ACLs is created. When you return to the API access tab, you can view the newly-created API key to confirm.</li>
<li>Return to Accounts &amp; access in the administration menu, and in the Accounts tab, click <strong>Service accounts</strong> to view your service accounts.</li>
<li>Select the service account that you want to assign the MetricsViewer role to.</li>
<li>In the service account’s details page, click <strong>Access</strong>.</li>
<li>In the tree view, open the resource where you want the service account to have the MetricsViewer role.</li>
<li>Click <strong>Add role assignment</strong> and select the MetricsViewer tile. Click <strong>Save</strong>.</li>
</ol>
<p>Next we can head to <a href="https://www.elastic.co/observability">Elastic Observability</a> and configure the Prometheus integration to pull in the metrics data.</p>
<p>Go to the integrations page in Kibana.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt18bf177b652a48e6/6a85cbb04710c62eb0d3cb55/blog-elastic-observability-integrations.png" alt="observability integrations" /></p>
<p>Find the Prometheus integration. We are using the Prometheus integration because the Confluent Cloud metrics server can provide data in prometheus format. Trust us, this works really well — good work Confluent!</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt4d48901dde740fee/6a85cbb243c0b72c932f0622/blog-elastic-observability-integrations-prometheus.png" alt="integrations prometheus" /></p>
<p>Add Prometheus in the next page.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt84626c46ada410b0/6a85cbb507829026aa321782/blog-elastic-observability-add-prometheus.png" alt="add prometheus" /></p>
<p>Configure the Prometheus plugin in the following way: In the hosts box, add the following URL, replacing the resource kafka id with the cluster id you want to monitor.</p>
<pre><code>https://api.telemetry.confluent.cloud:443/v2/metrics/cloud/export?resource.kafka.id=lkc-3rw3gw
</code></pre>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltb9a0004dfff705f8/6a85cbb793ffb91265b91441/blog-elastic-observability-collect-prometheus-metrics.png" alt="collect prometheus metrics" /></p>
<p>Add the username and password under the advanced options you got from the API keys step you executed against Confluent Cloud above.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt3cf8ea6231c6d4a4/6a85cbba9d2b716e39f9399c/blog-elastic-observability-http-config-options.png" alt="http config options" /></p>
<p>Once the Integration is created, <a href="https://www.elastic.co/guide/en/fleet/current/agent-policy.html#apply-a-policy">the policy needs to be applied</a> to an instance of a running Elastic Agent.</p>
<p>That’s it! It’s that easy to get all the data you need for a full stack observability monitoring solution.</p>
<p>Finally, let’s pull all this together in a dashboard.</p>
<h2 id="pullingitalltogether">Pulling it all together</h2>
<p>Using Kibana to generate dashboards is super easy. If you configured everything the way we recommended above, you should find the metrics (producer/consumer/brokers) you need to create your own dashboard as per the following screenshot.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt3ad8b73135f3f525/6a85cbbd27c5cdc4635f7400/blog-elastic-observability-dashboard-metrics.png" alt="dashboard metrics" /></p>
<p>Luckily, I made a dashboard for you and stored it in <a href="https://github.com/davidgeorgehope/multi-cloud/blob/main/export.ndjson">GitHub</a>. Take a look below and use this to import it into your own environments.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt2b83acb7398304ef/6a85cb8e80984cb656668fec/blog-elastic-observability-producer_metrics.png" alt="producer metrics" /></p>
<h2 id="addingtheicingonthecakemachinelearninganomalydetection">Adding the icing on the cake: machine learning anomaly detection</h2>
<p>Now that we have all the critical bits in place, we are going to add the icing on the cake: machine learning (ML)!</p>
<p>Within Kibana, let's head over to the Machine Learning tab in “Analytics.”</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt216b48d488ae22bf/6a85cbc0d7b2e7b72bfe84f0/blog-elastic-observability-kibana-analytics.png" alt="kibana analytics" /></p>
<p>Go to the jobs page, where we’ll get started creating our first anomaly detection job.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltda3a4d09361a209d/6a85cbc3eaf245fde1a49f6b/blog-elastic-observability-create-your-first-anomaly-detection-job.png" alt="create your first anomaly detection job" /></p>
<p>The metrics data view contains what we need to create this new anomaly detection job.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt790afdb1a6ce3000/6a85cbc580984c60f4668ff0/blog-elastic-observability-metrics.png" alt="observability metrics" /></p>
<p>Use the wizard and select a “Single Metric.”</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt035ea305957b7ced/6a85cbc84710c67cdcd3cb59/blog-elastic-observability-use-a-wizard.png" alt="use a wizard" /></p>
<p>Use the full data.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt18b90b2f2b100e42/6a85cbca93ffb9f68ab91445/blog-elastic-observability-use-full-data.png" alt="use full data" /></p>
<p>In this example, we are going to look for anomalies in the connection count. We really do not want a major deviation here, as this could indicate something very bad occurring if we suddenly have too many or too few things connecting to our Kafka cluster.</p>
<p>Once you have selected the connection count metric, you can proceed through the wizard and eventually your ML job will be created and you should be able to view the data as per the example below.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltbb7c65944c99c89b/6a85cbcdf61d6ebd009c2b35/blog-elastic-observability-single-metric-viewer.png" alt="single metric viewer" /></p>
<p>Congratulations, you have now created a machine learning job to alert you if there are any problems with your Kafka cluster, adding <a href="https://www.elastic.co/observability/aiops">a full AIOps solution</a> to your Kafka and Confluent observability!</p>
<h2 id="summary">Summary</h2>
<p>We looked at monitoring Kafka-based solutions implemented on Confluent Cloud using Elastic Observability.</p>
<p>We covered the architecture of a multi-cloud solution involving AWS EKS, Confluent Cloud, and GCP GKE. We looked at how to instrument Kafka applications with Elastic APM, use JMX for Kafka Producer/Consumer metrics, integrate Prometheus, and set up machine learning anomaly detection.</p>
<p>We went through a detailed walk-through with code snippets, configuration steps, and deployment instructions included to help you get started.</p>
<p>Interested in learning more about Elastic Observability? Check out the following resources:</p>
<ul>
<li><a href="https://www.elastic.co/virtual-events/intro-to-elastic-observability">An Introduction to Elastic Observability</a></li>
<li><a href="https://www.elastic.co/training/observability-fundamentals">Observability Fundamentals Training</a></li>
<li><a href="https://www.elastic.co/observability/demo">Watch an Elastic Observability demo</a></li>
<li><a href="https://www.elastic.co/blog/observability-predictions-trends-2023">Observability Predictions and Trends for 2023</a></li>
</ul>
<p>And sign up for our <a href="https://www.elastic.co/virtual-events/emerging-trends-in-observability">Elastic Observability Trends Webinar</a> featuring AWS and Forrester, not to be missed!</p>]]></content:encoded>
    <link>https://www.elastic.co/observability-labs/blog/monitor-kafka-confluent-cloud-elastic-observability</link>
    <guid isPermaLink="false">monitor-kafka-confluent-cloud-elastic-observability</guid>
    <category><![CDATA[Infrastructure Monitoring]]></category>
    <category><![CDATA[APM]]></category>
    <category><![CDATA[Kubernetes]]></category>
    <dc:creator><![CDATA[David Hope]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltdff999229029e1b1/6a85cbd0bc5bb32326f81b11/patterns-white-background-no-logo-observability_(1).png" length="0" type="image/png"/>
    <pubDate>Mon, 03 Apr 2023 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Using Elastic to observe GKE Autopilot clusters]]></title>
    <description><![CDATA[See how deploying the Elastic Agent onto a GKE Autopilot cluster makes observing the cluster’s behavior easy. Kibana integrations make visualizing the behavior a simple addition to your observability dashboards.]]></description>
    <content:encoded><![CDATA[<p>Elastic has formally supported Google Kubernetes Engine (GKE) since January 2020, when Elastic Cloud on Kubernetes was announced. Since then, Google has expanded GKE, with new service offerings and delivery mechanisms. One of those new offerings is GKE Autopilot. Where GKE is a managed Kubernetes environment, GKE Autopilot is a mode of Kubernetes operation where Google manages your cluster configuration, scaling, security, and more. It is production ready and removes many of the challenges associated with tasks like workload management, deployment automation, and scalability rules. Autopilot lets you focus on building and deploying your application while Google manages everything else.</p>
<p>Elastic is committed to supporting Google Kubernetes Engine (GKE) in all of its delivery modes. In October, during the Google Cloud Next ‘22 event, we announced our intention to integrate and certify Elastic Agent on Anthos, Autopilot, Google Distributed Cloud, and more.</p>
<p>Since that event, we have worked together with Google to get the Elastic Agent certified for use on Anthos, but we didn’t stop there.</p>
<p>Today we are happy to <a href="https://github.com/elastic/elastic-agent/blob/autopilotdocumentaton/docs/elastic-agent-gke-autopilot.md">announce</a> that we have been certified for operation on GKE Autopilot.</p>
<h2 id="handsonwithelasticandgkeautopilot">Hands on with Elastic and GKE Autopilot</h2>
<h3 id="kubernetesobservabilityhttpswwwelasticcoobservabilitykubernetesmonitoringhasneverbeeneasier"><a href="https://www.elastic.co/observability/kubernetes-monitoring">Kubernetes observability</a> has never been easier</h3>
<p>To show how easy it is to get started with Autopilot and Elastic, let's walk through deploying the Elastic Agent on an Autopilot cluster. I’ll show how easy it is to set up and monitor an Autopilot cluster with the Elastic Agent and observe the cluster’s behavior with Kibana integrations.</p>
<p>One of the main differences between GKE and GKE Autopilot is that Autopilot protects the system namespace “kube-system.” To increase the stability and security of a cluster, Autopilot prevents user space workloads from adding or modifying system pods. The default configuration for Elastic Agent is to install itself into the system namespace. The majority of the changes we will make here are to convince the Elastic Agent to run in a different namespace.</p>
<h2 id="letsgetstartedwithelasticstack">Let’s get started with Elastic Stack!</h2>
<p>While writing this article, I used the latest version of Elastic. The best way for you to get started with Elastic Observability is to:</p>
<ol>
<li>Get an account on <a href="https://cloud.elastic.co/registration?fromURI=/home">Elastic Cloud</a> and look at this <a href="https://www.elastic.co/videos/training-how-to-series-cloud">tutoria</a>l to help launch your first stack, or</li>
<li><a href="https://www.elastic.co/partners/google-cloud">Launch Elastic Cloud on your Google Account</a></li>
</ol>
<h2 id="provisioninganautopilotclusterandanelasticstack">Provisioning an Autopilot cluster and an Elastic stack</h2>
<p>To test the agent, I first deployed the recommended, default GKE Autopilot cluster. Elastic’s GKE integration supports kube-state-metrics (KSM), which will increase the number of reported metrics available for reporting and dashboards. Like the Elastic Agent, KSM defaults to running in the system namespace, so I modified its manifest to work with Autopilot. For my testing, I also deployed a basic Elastic stack on Elastic Cloud in the same Google region as my Autopilot cluster. I used a fresh cluster deployed on Elastic’s managed service (ESS), but the process is the same if you are using an Elastic Cloud subscription purchased through the Google marketplace.</p>
<h2 id="addingelasticobservabilitytogkeautopilot">Adding Elastic Observability to GKE Autopilot</h2>
<p>Because this is a brand new deployment, Elastic suggests adding integrations to it. Let’s add the Kubernetes integration into the new deployment:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt26e6f085f595e0c3/6a85ca80501a855e3dfbb312/blog-welcome-to-elastic.png" alt="elastic agent GKE autopilot welcome" /></p>
<p>Elastic offers hundreds of integrations; filter the list by typing “kub” into the search bar (1) and then click the Kubernetes integration (2).</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt0c060386e86965f9/6a85ca859829265d865838dc/blog-elastic-kubernetes-integration.png" alt="elastic agent GKE autopilot kubernetes integration" /></p>
<p>The Kubernetes integration page gives you an overview of the integration and lets you manage the Kubernetes clusters you want to observe. We haven’t added a cluster yet, so I clicked “Add Kubernetes” to add the first integration.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltc66c7d320ecbdd60/6a85ca88f61d6e6c539c2b05/blog-elastic-add-kubernetes.png" alt="elastic agent GKE autopilot add kubernetes" /></p>
<p>I changed the integration name to reflect the Kubernetes offering type and then clicked “Save and continue” to accept the integration defaults.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt91271c09731fd95b/6a85ca8c8c2944d9b0b8903f/blog-elastic-add-kubernetes-integration.png" alt="elastic agent GKE autopilot add kubernetes integration" /></p>
<p>At this point, an Agent policy has been created. Now it’s time to install the agent. I clicked on the “Kubernetes” integration.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blta51b4de42b224a86/6a85ca9080984c2503668fd2/blog-elastic-agent-policy-1.png" alt="elastic agent GKE autopilot agent policy" /></p>
<p>Then I selected the “integration policies” tab (1) and clicked “Add agent” (2).</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltc217bb87df0a00dd/6a85ca95982926f3e15838e0/blog-elastic-add-agent.png" alt="elastic agent GKE autopilot add agent" /></p>
<p>Finally, I downloaded the full manifest for a standard GKE environment.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltd95c9d2d38f1d2c0/6a85ca9bba7accf30999213e/blog-elastic-download-manifest.png" alt="elastic agent GKE autopilot download manifest" /></p>
<p>We won’t be using this manifest directly, but it contains many of the values that we will need to deploy the agent on Autopilot in the next section.</p>
<p>The Elastic stack is ready and waiting for the Autopilot logs, metrics, and events. It’s time to connect Autopilot to this deployment using the Elastic Agent for GKE.</p>
<h2 id="connectautopilottoelastic">Connect Autopilot to Elastic</h2>
<p>From the Google cloud terminal, I downloaded and edited the Elastic Agent manifest for GKE Autopilot.</p>
<pre><code>$ curl -o elastic-agent-managed-gke-autopilot.yaml \
https://github.com/elastic/elastic-agent/blob/autopilotdocumentaton/docs/manifests/elastic-agent-managed-gke-autopilot.yaml
</code></pre>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltf84444c5b0c8efe4/6a85ca9f93ffb917c9b91431/blog-elastic-cloud-shell-editor.png" alt="elastic agent GKE autopilot cloud shell editor" /></p>
<p>I used the cloud shell editor to configure the manifest for my Autopilot and Elastic clusters. For example, I updated the following:</p>
<pre><code>containers:
  - name: elastic-agent
    image: docker.elastic.co/beats/elastic-agent:8.19.13
</code></pre>
<p>I also changed the agent to the version of Elastic that I installed (8.6.0).</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt2d877effdd568e7e/6a85caa49d2b719c7bf93977/blog-elastic-google-cloud.png" alt="elastic agent GKE autopilot google cloud" /></p>
<p>From the Integration manifest I downloaded earlier, I copied the values for FLEET_URL and FLEET_ENROLLMENT_TOKEN into this YAML file.</p>
<p>Now it’s time to apply the updated manifest to the Autopilot instance.</p>
<p>Before I commit, I always like to see what’s going to be created (and check for syntax errors) with a dry run.</p>
<pre><code>$ clear
$ kubectl apply --dry-run="client" -f elastic-agent-managed-gke-autopilot.yaml
</code></pre>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt9f58f4750aa9227a/6a85caa8f5f1a0024a2ec8ef/blog-elastic-dry-run.png" alt="elastic agent GKE autopilot dry run" /></p>
<p>Everything looks good, so I’ll do it for real this time.</p>
<pre><code>$ clear
$ kubectl apply -f elastic-agent-managed-gke-autopilot.yaml
</code></pre>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt9b3944ad1a6bbec3/6a85caab6826669bd71eabf1/blog-elastic-autopilot-cluster.png" alt="elastic agent GKE autopilot cluster" /></p>
<p>After several minutes, metrics will start flowing from the Autopilot cluster directly into the Elastic deployment.</p>
<h2 id="addingaworkloadtotheautopilotcluster">Adding a workload to the Autopilot cluster</h2>
<p>Observing an Autopilot cluster without a workload is boring, so I deployed a modified version of Google’s <a href="https://github.com/bshetti/opentelemetry-microservices-demo">Hipster Shop</a> (which includes OpenTelemetry reporting):</p>
<pre><code>$ git clone https://github.com/bshetti/opentelemetry-microservices-demo
$ cd opentelemetry-microservices-demo
$ nano ./deploy-with-collector-k8s/otelcollector.yaml
</code></pre>
<p>To get the application’s telemetry talking to our Elastic stack, I replaced all instances of the exporter type from HTTP (otlphttp/elastic) to gRPC (otlp/elastic). I then replaced OTEL_EXPORTER_OTLP_ENDPOINT with my APM endpoint and I replaced OTEL_EXPORTER_OTLP_HEADERS with my APM OTEL Bearer and Token.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt50d29e192859e232/6a85caaf43c0b73bf42f060a/blog-elastic-terminal-telemetry.png" alt="elastic agent GKE autopilot terminal telemetry" /></p>
<p>Then I deployed the Hipster Shop.</p>
<pre><code>$ kubectl create -f ./deploy-with-collector-k8s/adservice.yaml
$ kubectl create -f ./deploy-with-collector-k8s/redis.yaml
$ kubectl create -f ./deploy-with-collector-k8s/cartservice.yaml
$ kubectl create -f ./deploy-with-collector-k8s/checkoutservice.yaml
$ kubectl create -f ./deploy-with-collector-k8s/currencyservice.yaml
$ kubectl create -f ./deploy-with-collector-k8s/emailservice.yaml
$ kubectl create -f ./deploy-with-collector-k8s/frontend.yaml
$ kubectl create -f ./deploy-with-collector-k8s/paymentservice.yaml
$ kubectl create -f ./deploy-with-collector-k8s/productcatalogservice.yaml
$ kubectl create -f ./deploy-with-collector-k8s/recommendationservice.yaml
$ kubectl create -f ./deploy-with-collector-k8s/shippingservice.yaml
$ kubectl create -f ./deploy-with-collector-k8s/loadgenerator.yaml
</code></pre>
<p>Once all of the shop’s pods were running, I deployed the OpenTelemetry collector.</p>
<pre><code>$ kubectl create -f ./deploy-with-collector-k8s/otelcollector.yaml
</code></pre>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt9e1ed7b460b68149/6a85cab2f5f1a033e02ec8f7/blog-elastic-deployed-opentelemetry-collector.png" alt="elastic agent GKE autopilot deployed opentelemetry collector" /></p>
<h2 id="observeandvisualizeautopilotsmetrics">Observe and visualize Autopilot’s metrics</h2>
<p>Now that we have added the Elastic Agent to our Autopilot cluster and added a workload, let's take a look at some of the Kubernetes visualizations the integration provides out of the box.</p>
<p>The “[Metrics Kubernetes] Overview” is a great place to start. It provides a high-level view of the resources used by the cluster and allows me to drill into more specific dashboards that I find interesting:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt109d07366e7595d6/6a85cab8342d69fd9421b0e3/blog-elastic-create-visualization.png" alt="elastic agent GKE autopilot create visualization" /></p>
<p>For example, the “[Metrics Kubernetes] Pods” gives me a high-level view of the pods deployed in the cluster:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltf2dbb5d3043b2758/6a85cabd501a85304bfbb31c/blog-elastic-pod.png" alt="elastic agent GKE autopilot pod" /></p>
<p>The “[Metrics Kubernetes] Volumes” gives me an in-depth view to how storage is allocated and used in the Autopilot cluster:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltb841c39379c9aee2/6a85cac043c0b745062f060e/blog-elastic-filesystem-information.png" alt="elastic agent GKE autopilot filesystem information" /></p>
<h2 id="creatinganalert">Creating an alert</h2>
<p>From here, I can easily discover patterns in my cluster’s behavior and even create Alerts. Here is an example of an alert to notify me if the the main storage volume (called “volume”) exceeds 80% of its allocated space:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt31d75eb5816f6a0c/6a85cac4501a85096ffbb320/blog-elastic-create-rule-elasticsearch-query.png" alt="elastic agent GKE autopilot create rule" /></p>
<p>With a little work, I created this view from the standard dashboard:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt48e17cb04ac74819/6a85cac79a32f1162da7dfde/blog-elastic-kubernetes-dashboard.png" alt="elastic agent GKE autopilot kubernetes dashboard" /></p>
<h2 id="conclusion">Conclusion</h2>
<p>Today I have shown how easy it is to monitor, observe, and generate alerts on a GKE Autopilot cluster. To get more information on what is possible, see the official Elastic documentation for <a href="https://github.com/elastic/elastic-agent/blob/autopilotdocumentaton/docs/elastic-agent-gke-autopilot.md">Autopilot observability with Elastic Agent</a>.</p>
<h2 id="nextsteps">Next steps</h2>
<p>If you don’t have Elastic yet, you can get started for free with an <a href="https://www.elastic.co/cloud/elasticsearch-service/signup">Elastic Trial</a> today. Get more from Elastic and Google together with a <a href="https://console.cloud.google.com/marketplace/browse?q=Elastic&amp;utm_source=Elastic&amp;utm_medium=qwiklabs&amp;utm_campaign=Qwiklabs+to+Marketplace">Marketplace subscription</a>. Elastic does more than just integrate with GKE — check out the almost <a href="https://www.elastic.co/integrations">300 integrations</a> that Elastic provides.</p>]]></content:encoded>
    <link>https://www.elastic.co/observability-labs/blog/observe-gke-autopilot-clusters</link>
    <guid isPermaLink="false">observe-gke-autopilot-clusters</guid>
    <category><![CDATA[Kubernetes]]></category>
    <category><![CDATA[Metrics]]></category>
    <category><![CDATA[Infrastructure Monitoring]]></category>
    <dc:creator><![CDATA[Eric Lowry]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt48e17cb04ac74819/6a85cac79a32f1162da7dfde/blog-elastic-kubernetes-dashboard.png" length="0" type="image/png"/>
    <pubDate>Wed, 15 Mar 2023 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Easily analyze AWS VPC Flow Logs with Elastic Observability]]></title>
    <description><![CDATA[Elastic Observability can ingest and help analyze AWS VPC Flow Logs from your application’s VPC. Learn how to ingest AWS VPC Flow Logs through a step-by-step method into Elastic, then analyze it and apply OOTB machine learning for insights.]]></description>
    <content:encoded><![CDATA[<p>Elastic Observability provides a full-stack observability solution, by supporting metrics, traces, and logs for applications and infrastructure. In <a href="https://www.elastic.co/blog/aws-service-metrics-monitor-observability-easy">a previous blog</a>, I showed you an <a href="https://www.elastic.co/observability/aws-monitoring">AWS monitoring</a> infrastructure running a three-tier application. Specifically we reviewed metrics ingest and analysis on Elastic Observability for EC2, VPC, ELB, and RDS. In this blog, we will cover how to ingest logs from AWS, and more specifically, we will review how to get VPC Flow Logs into Elastic and what you can do with this data.</p>
<p>Logging is an important part of observability, for which we generally think of metrics and/or tracing. However, the amount of logs an application or the underlying infrastructure output can be significantly daunting.</p>
<p>With Elastic Observability, there are three main mechanisms to ingest logs:</p>
<ul>
<li>The new Elastic Agent pulls metrics and logs from CloudWatch and S3 where logs are generally pushed from a service (for example, EC2, ELB, WAF, Route53, etc ). We reviewed Elastic agent metrics configuration for EC2, RDS (Aurora), ELB, and NAT metrics in this <a href="https://www.elastic.co/blog/aws-service-metrics-monitor-observability-easy">blog</a>.</li>
<li>Using <a href="https://www.elastic.co/blog/elastic-and-aws-serverless-application-repository-speed-time-to-actionable-insights-with-frictionless-log-ingestion-from-amazon-s3">Elastic’s Serverless Forwarder (runs on Lambda and available in AWS SAR)</a> to send logs from Firehose, S3, CloudWatch, and other AWS services into Elastic.</li>
<li>Beta feature (contact your Elastic account team): Using AWS Firehose to directly insert logs from AWS into Elastic — specifically if you are running the Elastic stack on AWS infrastructure.</li>
</ul>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt864e9aa3b4bf3d2a/6a7f1c302f00b28cbfefef61/Elastic-Observability-VPC-Flow-Logs.jpg" alt="" /></p>
<p>In this blog we will provide an overview of the second option, Elastic’s serverless forwarder collecting VPC Flow Logs from an application deployed on EC2 instances. Here’s what we'll cover:</p>
<ul>
<li>A walk-through on how to analyze VPC Flow Log info with Elastic’s Discover, dashboard, and ML analysis.</li>
<li>A detailed step-by-step overview and setup of the Elastic serverless forwarder on AWS as a pipeline for VPC Flow Logs into <a href="http://cloud.elastic.co">Elastic Cloud</a>.</li>
</ul>
<h2 id="elasticsserverlessforwarderonawslambda">Elastic’s serverless forwarder on AWS Lambda</h2>
<p>AWS users can quickly ingest logs stored in Amazon S3, CloudWatch, or Kinesis with the Elastic serverless forwarder, an AWS Lambda application, and view them in the Elastic Stack alongside other logs and metrics for centralized analytics. Once the AWS serverless forwarder is configured and deployed from AWS, Serverless Application Registry (SAR) logs will be ingested and available in Elastic for analysis. See the following links for further configuration guidance:</p>
<ul>
<li><a href="https://www.elastic.co/blog/elastic-and-aws-serverless-application-repository-speed-time-to-actionable-insights-with-frictionless-log-ingestion-from-amazon-s3">Elastic’s serverless forwarder (runs Lambda and available in AWS SAR)</a></li>
<li><a href="https://github.com/elastic/elastic-serverless-forwarder/blob/main/docs/README-AWS.md#s3_config_file">Serverless forwarder GitHub repo</a></li>
</ul>
<p>In our configuration we will ingest VPC Flow Logs into Elastic for the three-tier app deployed in the previous <a href="https://www.elastic.co/blog/aws-service-metrics-monitor-observability-easy">blog</a>.</p>
<p>There are three different configurations with the Elastic serverless forwarder:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt719dbab9506f0a1e/6a7f1c326c6eacb19af145d1/blog-elastic-vpc-flow-logs-3-configurations.png" alt="" /></p>
<p>Logs can be directly ingested from:</p>
<ul>
<li><strong>Amazon CloudWatch:</strong> Elastic serverless forwarder can pull VPC Flow Logs directly from an Amazon CloudWatch log group, which is a commonly used endpoint to store VPC Flow Logs in AWS.</li>
<li><strong>Amazon Kinesis:</strong> Elastic serverless forwarder can pull VPC Flow Logs directly from Kinesis, which is another location to <a href="https://docs.aws.amazon.com/vpc/latest/userguide/flow-logs-firehose.html">publish VPC Flow Logs</a>.</li>
<li><strong>Amazon S3:</strong> Elastic serverless forwarder can pull VPC Flow Logs from Amazon S3 via SQS event notifications, which is a common endpoint to publish VPC Flow Logs in AWS.</li>
</ul>
<p>We will review how to utilize a common configuration, which is to send VPC Flow Logs to Amazon S3 and into Elastic Cloud in the second half of this blog.</p>
<p>But first let's review how to analyze VPC Flow Logs on Elastic.</p>
<h2 id="analyzingvpcflowlogsinelastic">Analyzing VPC Flow Logs in Elastic</h2>
<p>Now that you have VPC Flow Logs in Elastic Cloud, how can you analyze them?</p>
<p>There are several analyses you can perform on the VPC Flow Log data:</p>
<ol>
<li>Use Elastic’s Analytics Discover capabilities to manually analyze the data.</li>
<li>Use Elastic Observability’s anomaly feature to identify anomalies in the logs.</li>
<li>Use an out-of-the-box (OOTB) dashboard to further analyze data.</li>
</ol>
<h3 id="usingelasticdiscover">Using Elastic Discover</h3>
<p>In Elastic analytics, you can search and filter your data, get information about the structure of the fields, and display your findings in a visualization. You can also customize and save your searches and place them on a dashboard. With Discover, you can:</p>
<ul>
<li>View logs in bulk, within specific time frames</li>
<li>Look at individual details of each entry (document)</li>
<li>Filter for specific values</li>
<li>Analyze fields</li>
<li>Create and save searches</li>
<li>Build visualizations</li>
</ul>
<p>For a complete understanding of Discover and all of Elastic’s analytics capabilities, look at <a href="https://www.elastic.co/guide/en/kibana/current/discover.html#">Elastic documentation</a>.</p>
<p>For VPC Flow Logs, an important stat is to understand:</p>
<ul>
<li>How many logs were accepted/rejected</li>
<li>Where potential security violations are occur (for example, source IPs from outside the VPC)</li>
<li>What port is generally being queried</li>
</ul>
<p>I’ve filtered the logs on the following:</p>
<ul>
<li>Amazon S3: bshettisartest</li>
<li>VPC Flow Log action: REJECT</li>
<li>VPC Network Interface: Webserver 1</li>
</ul>
<p>We want to see what IP addresses are trying to hit our web servers.</p>
<p>From that, we want to understand which IP addresses we are getting the most REJECTS from, and we simply find the <strong>source</strong>.ip field. Then, we can quickly get a breakdown that shows 185.242.53.156 is the most rejected for the last 3+ hours we’ve turned on VPC Flow Logs.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltd3966498d22108ca/6a7f1c36bd21989acc7584e1/blog-elastic-vpc-flow-logs-100-hits.png" alt="" /></p>
<p>Additionally, I can see a visualization by selecting the “Visualize” button. We get the following, which we can add to a dashboard:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt805432c53beff1e7/6a7f1c39eab5be37b020ab38/blog-elastic-vpc-flow-logs-add-to-a-dashboard.png" alt="" /></p>
<p>In addition to IP addresses, we want to also see what port is being hit on our web servers.<br />
We select the destination port field, and the quick pop-up shows us a list of ports being targeted. We can see that port 23 is being targeted (this port is generally used for telnet), port 445 is being targeted (used for Microsoft Active Directory), and port 433 (used for https ssl). We also see these are all REJECT.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt4a860d66b3ad9a19/6a7f1c3cbd219808a07584e7/blog-elastic-vpc-flow-logs-reject.png" alt="" /></p>
<h3 id="anomalydetectioninelasticobservabilitylogs">Anomaly detection in Elastic Observability logs</h3>
<p>Addition to Discover, Elastic Observability provides the ability to detect anomalies on logs. In Elastic Observability -&gt; logs -&gt; anomalies you can turn on machine learning for:</p>
<ul>
<li>Log rate: automatically detects anomalous log entry rates</li>
<li>Categorization: automatically categorizes log messages</li>
</ul>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt3ba725798202a6a9/6a7f1c3f5967e5f51a5dd6f3/blog-elastic-vpc-flow-logs-anomaly-detection-with-machine-learning.png" alt="" /></p>
<p>For our VPC Flow Log, we turned both on. And when we look at what has been detected for anomalous log entry rates, we see:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltf2131164c1682ec1/6a7f1c424c4bfb5f14ccd924/blog-elastic-vpc-flow-logs-anomalies.png" alt="" /></p>
<p>Elastic immediately detected a spike in logs when we turned on VPC Flow Logs for our application. The rate change is being detected because we’re also ingesting VPC Flow Logs from another application for a couple of days prior to adding the application in this blog.</p>
<p>We can further drill down into this anomaly with machine learning and analyze further.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltab04d37c363e03e3/6a7f1c455967e599895dd6f9/blog-elastic-vpc-flow-logs-anomaly-explorer.png" alt="" /></p>
<p>There is more machine learning analysis you can utilize with your logs — check out <a href="https://www.elastic.co/guide/en/kibana/8.5/xpack-ml.html">Elastic machine learning documentation</a>.</p>
<p>Since we know that a spike exists, we can also use Elastic AIOps Labs Explain Log Rate Spikes capability in Machine Learning. Additionally, we’ve grouped them to see what is causing some of the spikes.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltac4cc921055c5d85/6a7f1c4877b03481d23ff93d/blog-elastic-vpc-flow-logs-explain-log-rate-spikes.png" alt="" /></p>
<p>As we can see, a specific network interface is sending more VPC log flows than others. We can further drill down into this further in Discover.</p>
<h3 id="vpcflowlogdashboardonelasticobservability">VPC Flow Log dashboard on Elastic Observability</h3>
<p>Finally, Elastic also provides an OOTB dashboard to showing the top IP addresses hitting your VPC, geographically where they are coming from, the time series of the flows, and a summary of VPC Flow Log rejects within the time frame.</p>
<p>This is a baseline dashboard that can be enhanced with visualizations you find in Discover, as we reviewed in option 1 (Using Elastic’s Analytics Discover capabilities) above.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt400e83d9099b4364/6a7f1c4bead8ec4d64baac80/blog-elastic-vpc-flow-logs-action-geolocation.png" alt="" /></p>
<h2 id="settingitallup">Setting it all up</h2>
<p>Let’s walk through the details of configuring Amazon Kinesis Data Firehose and Elastic Observability to ingest data.</p>
<h3 id="prerequisitesandconfig">Prerequisites and config</h3>
<p>If you plan on following steps, here are some of the components and details we used to set up this demonstration:</p>
<ul>
<li>Ensure you have an account on <a href="http://cloud.elastic.co">Elastic Cloud</a> and a deployed stack (<a href="https://www.elastic.co/guide/en/elastic-stack/current/installing-elastic-stack.html">see instructions here</a>) on AWS. Deploying this on AWS is required for Elastic Serverless Forwarder.</li>
<li>Ensure you have an AWS account with permissions to pull the necessary data from AWS. Specifically, ensure you can configure the agent to pull data from AWS as needed. <a href="https://docs.elastic.co/integrations/aws#requirements">Please look at the documentation for details</a>.</li>
<li>We used <a href="https://github.com/aws-samples/aws-three-tier-web-architecture-workshop">AWS’s three-tier app</a> and installed it as instructed in GitHub. (<a href="https://www.elastic.co/blog/aws-service-metrics-monitor-observability-easy">See blog on ingesting metrics from the AWS services supporting this app</a>.)</li>
<li>Configure and install Elastic’s Serverless Forwarder.</li>
<li>Ensure you turn on VPC Flow Logs for the VPC where the application is deployed and send logs to AWS Firehose.</li>
</ul>
<h3 id="step0getanaccountonelasticcloud">Step 0: Get an account on Elastic Cloud</h3>
<p>Follow the instructions to <a href="https://cloud.elastic.co/registration?fromURI=/home">get started on Elastic Cloud</a>.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt7dce08a8b90bfacf/6a7f1c4e9090b01f8b84ee5f/blog-elastic-vpc-flow-logs-start-cloud-trial.png" alt="" /></p>
<h3 id="step1deployelasticonaws">Step 1: Deploy Elastic on AWS</h3>
<p>Once logged in to Elastic Cloud, create a deployment on AWS. It’s important to ensure that the deployment is on AWS. The Amazon Kinesis Data Firehose connects specifically to an endpoint that needs to be on AWS.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltd7a8f13d7eadf6fb/6a7f1c51ead8ec01fbbaac88/blog-elastic-vpc-flow-logs-create-a-deployment.png" alt="" /></p>
<p>Once your deployment is created, make sure you copy the Elasticsearch endpoint.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltaef67444a94269ce/6a7f1c55ea068d779af0a314/blog-elastic-vpc-flow-logs-aws-logs.png" alt="" /></p>
<p>The endpoint should be an AWS endpoint, such as:</p>
<pre><code>https://aws-logs.es.us-east-1.aws.found.io
</code></pre>
<h3 id="step2turnonelasticsawsintegrationsonaws">Step 2: Turn on Elastic’s AWS Integrations on AWS</h3>
<p>In your deployment’s Elastic Integration section, go to the AWS integration and select Install AWS assets.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt718f23ad192c845a/6a7f1c58448e4ea69d5c0b9b/blog-elastic-vpc-flow-logs-aws-settings.png" alt="" /></p>
<h3 id="step3deployyourapplication">Step 3: Deploy your application</h3>
<p>Follow the instructions listed out in <a href="https://github.com/aws-samples/aws-three-tier-web-architecture-workshop">AWS’s Three-Tier app</a> and instructions in the workshop link on GitHub. The workshop is listed <a href="https://catalog.us-east-1.prod.workshops.aws/workshops/85cd2bb2-7f79-4e96-bdee-8078e469752a/en-US">here</a>.</p>
<p>Once you’ve installed the app, get credentials from AWS. This will be needed for Elastic’s AWS integration.</p>
<p>There are several options for credentials:</p>
<ul>
<li>Use access keys directly</li>
<li>Use temporary security credentials</li>
<li>Use a shared credentials file</li>
<li>Use an IAM role Amazon Resource Name (ARN)</li>
</ul>
<p>View more details on specifics around necessary <a href="https://docs.elastic.co/en/integrations/aws#aws-credentials">credentials</a> and <a href="https://docs.elastic.co/en/integrations/aws#aws-permissions">permissions</a>.</p>
<h3 id="step4sendvpcflowlogstoamazons3andsetupamazonsqs">Step 4: Send VPC Flow Logs to Amazon S3 and set up Amazon SQS</h3>
<p>In the VPC for the application deployed in Step 3, you will need to configure VPC Flow Logs and point them to an Amazon S3 bucket. Specifically, you will want to keep it as AWS default format.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltf9a94d515651e20c/6a7f1c5b9090b0415f84ee6b/blog-elastic-vpc-flow-logs-create-flow-log.png" alt="" /></p>
<p>Create the VPC Flow log.</p>
<p>Next:</p>
<ul>
<li><a href="https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/sqs-getting-started.html">Set up an Amazon SQS queue</a></li>
<li><a href="https://docs.aws.amazon.com/AmazonS3/latest/userguide/ways-to-add-notification-config-to-bucket.html">Configure Amazon S3 event notifications</a></li>
</ul>
<h3 id="step5setupelasticserverlessforwarderonaws">Step 5: Set up Elastic Serverless Forwarder on AWS</h3>
<p>Follow instructions listed in <a href="https://www.elastic.co/guide/en/observability/8.5/aws-deploy-elastic-serverless-forwarder.html">Elastic’s documentation</a> and refer to the <a href="https://www.elastic.co/blog/elastic-and-aws-serverless-application-repository-speed-time-to-actionable-insights-with-frictionless-log-ingestion-from-amazon-s3">previous blog</a> providing an overview. The important bits during the configuration in Lambda’s application repository are to ensure you:</p>
<ul>
<li>Specify the S3 Bucket in ElasticServerlessForwarderS3Buckets where the VPC Flow Logs are being sent. The value is the ARN of the S3 Bucket you created in Step 4.</li>
<li>Specify the configuration file path in ElasticServerlessForwarderS3ConfigFile. The value is the S3 url in the format "s3://bucket-name/config-file-name" pointing to the configuration file (sarconfig.yaml).</li>
<li>Specify the S3 SQS Notifications queue used as the trigger of the Lambda function in ElasticServerlessForwarderS3SQSEvents. The value is the ARN of the SQS Queue you set up in Step 4.</li>
</ul>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt683de353f5f3d00e/6a7f1c5eeab5bea4c420ab44/blog-elastic-vpc-flow-logs-application-settings.png" alt="" /></p>
<p>Once Amazon CloudFormation finishes setting up Elastic serverless forwarder, you should see two Amazon Lambda functions:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt0a8541ccc1858f9d/6a7f1c61e02fac26945d69f3/blog-elastic-vpc-flow-logs-functions.png" alt="" /></p>
<p>In order to check if logs are coming in, go to the function with “ <strong>ApplicationElasticServer</strong> ” in the name, and go to monitor and look at <strong>logs</strong>. You should see the logs being pulled from S3.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt57ff998a2a6fea3a/6a7f1c64ead8ec5455baac94/blog-elastic-vpc-flow-logs-function-overview.png" alt="" /></p>
<h3 id="step6checkandensureyouhavelogsinelastic">Step 6: Check and ensure you have logs in Elastic</h3>
<p>Now that steps 1–4 are complete, you can go to Elastic’s Discover capability and you should see VPC Flow Logs coming in. In the image below, we’ve filtered by Amazon S3 bucket <strong>bshettisartest</strong>.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt6e7507f7a157ee2a/6a7f1c675967e5b74c5dd705/blog-elastic-vpc-flow-log-dashboard-filter.png" alt="" /></p>
<h2 id="conclusionelasticobservabilityeasilyintegrateswithvpcflowlogsforanalyticsalertingandinsights">Conclusion: Elastic Observability easily integrates with VPC Flow Logs for analytics, alerting, and insights</h2>
<p>I hope you’ve gotten an appreciation for how Elastic Observability can help you manage AWS VPC Flow Logs. Here’s a quick recap of lessons and what you learned:</p>
<ul>
<li>A walk-through of how Elastic Observability provides enhanced analysis for VPC Flow Logs:</li>
<li>Using Elastic’s Analytics Discover capabilities to manually analyze the data</li>
<li>Leveraging Elastic Observability’s anomaly features to:<ul>
<li>Identify anomalies in the VPC flow logs</li>
<li>Detects anomalous log entry rates</li>
<li>Automatically categorizes log messages</li></ul></li>
<li>Using an OOTB dashboard to further analyze data</li>
<li>A more detailed walk-through of how to set up the Elastic Serverless Forwarder</li>
</ul>
<p>Start your own <a href="https://aws.amazon.com/marketplace/pp/prodview-voru33wi6xs7k?trk=5fbc596b-6d2a-433a-8333-0bd1f28e84da%E2%89%BBchannel=el">7-day free trial</a> by signing up via <a href="https://aws.amazon.com/marketplace/pp/prodview-voru33wi6xs7k?trk=d54b31eb-671c-49ba-88bb-7a1106421dfa%E2%89%BBchannel=el">AWS Marketplace</a> and quickly spin up a deployment in minutes on any of the <a href="https://www.elastic.co/guide/en/cloud/current/ec-reference-regions.html#ec_amazon_web_services_aws_regions">Elastic Cloud regions on AWS</a> around the world. Your AWS Marketplace purchase of Elastic will be included in your monthly consolidated billing statement and will draw against your committed spend with AWS.</p>
<h3 id="additionalloggingresources">Additional logging resources:</h3>
<ul>
<li><a href="https://www.elastic.co/getting-started/observability/collect-and-analyze-logs">Getting started with logging on Elastic (quickstart)</a></li>
<li><a href="https://www.elastic.co/guide/en/observability/current/logs-metrics-get-started.html">Ingesting common known logs via integrations (compute node example)</a></li>
<li><a href="https://docs.elastic.co/integrations">List of integrations</a></li>
<li><a href="https://www.elastic.co/blog/log-monitoring-management-enterprise">Ingesting custom application logs into Elastic</a></li>
<li><a href="https://www.elastic.co/blog/observability-logs-parsing-schema-read-write">Enriching logs in Elastic</a></li>
<li>Analyzing Logs with <a href="https://www.elastic.co/blog/reduce-mttd-ml-machine-learning-observability">Anomaly Detection (ML)</a> and <a href="https://www.elastic.co/blog/observability-logs-machine-learning-aiops">AIOps</a></li>
</ul>
<h3 id="commonusecaseexampleswithlogs">Common use case examples with logs:</h3>
<ul>
<li><a href="https://youtu.be/ax04ZFWqVCg">Nginx log management</a></li>
<li><a href="https://www.elastic.co/blog/vpc-flow-logs-monitoring-analytics-observability">AWS VPC Flow log management</a></li>
<li><a href="https://www.elastic.co/blog/kubernetes-errors-elastic-observability-logs-openai">Using OpenAI to analyze Kubernetes errors</a></li>
<li><a href="https://youtu.be/Li5TJAWbz8Q">PostgreSQL issue analysis with AIOps</a></li>
</ul>]]></content:encoded>
    <link>https://www.elastic.co/observability-labs/blog/vpc-flow-logs-monitoring-analytics-observability</link>
    <guid isPermaLink="false">vpc-flow-logs-monitoring-analytics-observability</guid>
    <category><![CDATA[Logs Analytics]]></category>
    <category><![CDATA[Metrics]]></category>
    <category><![CDATA[Infrastructure Monitoring]]></category>
    <dc:creator><![CDATA[Bahubali Shetti]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt29d963458983cce0/6a7f1c6ab4377022bc4d7157/patterns-midnight-background-no-logo-observability.png" length="0" type="image/png"/>
    <pubDate>Mon, 23 Jan 2023 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Independence with OpenTelemetry on Elastic]]></title>
    <description><![CDATA[OpenTelemetry has become a key component for observability given its open standards and developer-friendly tools. See how easily Elastic Observability integrates with OTel to provide a platform that minimizes vendor lock-in and maximizes flexibility.]]></description>
    <content:encoded><![CDATA[<p>The drive for faster, more scalable services is on the rise. Our day-to-day lives depend on apps, from a food delivery app to have your favorite meal delivered, to your banking app to manage your accounts, to even apps to schedule doctor’s appointments. These apps need to be able to grow from not only a features standpoint but also in terms of user capacity. The scale and need for global reach drives increasing complexity for these high-demand cloud applications.</p>
<p>In order to keep pace with demand, most of these online apps and services (for example, mobile applications, web pages, SaaS) are moving to a distributed microservice-based architecture and Kubernetes. Once you’ve migrated your app to the cloud, how do you manage and monitor production, scale, and availability of the service? <a href="https://opentelemetry.io/">OpenTelemetry</a> is quickly becoming the de facto standard for instrumentation and collecting application telemetry data for Kubernetes applications.</p>
<p><a href="https://www.elastic.co/what-is/opentelemetry">OpenTelemetry (OTel)</a> is an open source project providing a collection of tools, APIs, and SDKs that can be used to generate, collect, and export telemetry data (metrics, logs, and traces) to understand software performance and behavior. OpenTelemetry recently became a CNCF incubating project and has a significant amount of growing community and vendor support.</p>
<p>While OTel provides a standard way to instrument applications with a standard telemetry format, it doesn’t provide any backend or analytics components. Hence using OTel libraries in applications, infrastructure, and user experience monitoring provides flexibility in choosing the appropriate <a href="https://www.elastic.co/observability">observability tool</a> of choice. There is no longer any vendor lock-in for application performance monitoring (APM).</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt5ac0a045fdf76a37/6a7f193205b7b51e0118bd21/blog-elastic-otel-1.png" alt="" /></p>
<p>Elastic Observability natively supports OpenTelemetry and its OpenTelemetry protocol (OTLP) to ingest traces, metrics, and logs. All of Elastic Observability’s APM capabilities are available with OTel data. Hence the following capabilities (and more) are available for OTel data:</p>
<ul>
<li>Service maps</li>
<li>Service details (latency, throughput, failed transactions)</li>
<li>Dependencies between services</li>
<li>Transactions (traces)</li>
<li>ML correlations (specifically for latency)</li>
<li>Service logs</li>
</ul>
<p>In addition to Elastic’s APM and unified view of the telemetry data, you will now be able to use Elastic’s powerful machine learning capabilities to reduce the analysis, and alerting to help reduce MTTR.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blta01b4eb3f8dbfb6f/6a7f1935e02fac237a5d698d/blog-elastic-otel-2.png" alt="" /></p>
<p>Given its open source heritage, Elastic also supports other CNCF based projects, such as Prometheus, Fluentd, Fluent Bit, Istio, Kubernetes (K8S), and many more.</p>
<p>This blog will show:</p>
<ul>
<li>How to get a popular OTel instrumented demo app (Hipster Shop) configured to ingest into <a href="http://cloud.elastic.co">Elastic Cloud</a> through a few easy steps</li>
<li>Highlight some of the Elastic APM capabilities and features around OTel data and what you can do with this data once it’s in Elastic</li>
</ul>
<p>In follow-up blogs, we will detail how to use Elastic’s machine learning with OTel telemetry data, how to instrument OTel application metrics for specific languages, how we can support Prometheus ingest through the OTel collector, and more. Stay tuned!</p>
<h2 id="prerequisitesandconfig">Prerequisites and config</h2>
<p>If you plan on following this blog, here are some of the components and details we used to set up the configuration:</p>
<ul>
<li>Ensure you have an account on <a href="http://cloud.elastic.co">Elastic Cloud</a> and a deployed stack (<a href="https://www.elastic.co/guide/en/elastic-stack/current/installing-elastic-stack.html">see instructions here</a>).</li>
<li>We used the OpenTelemetry Demo. Directions for using Elastic with OpenTelemetry Demo are <a href="https://github.com/elastic/opentelemetry-demo">here</a>.</li>
<li>Make sure you have <a href="https://kubernetes.io/docs/reference/kubectl/">kubectl</a> and <a href="https://helm.sh/">helm</a> also installed locally.</li>
<li>Additionally, we are using an OTel manually instrumented version of the application. No OTel automatic instrumentation was used in this blog configuration.</li>
<li>Location of our clusters. While we used Google Kubernetes Engine (GKE), you can use any Kubernetes platform of your choice.</li>
<li>While Elastic can ingest telemetry directly from OTel instrumented services, we will focus on the more traditional deployment, which uses the OpenTelemetry Collector.</li>
<li>Prometheus and FluentD/Fluent Bit — traditionally used to pull all Kubernetes data — is not being used here versus Kubernetes Agents. Follow-up blogs will showcase this.</li>
</ul>
<p>Here is the configuration we will get set up in this blog:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt757221af75648dce/6a7f193896b5a66c5387b867/blog-elastic-otel-3.png" alt="Configuration to ingest OpenTelemetry data used in this blog" /></p>
<h2 id="settingitallup">Setting it all up</h2>
<p>Over the next few steps, I’ll walk through an <a href="https://www.elastic.co/observability/opentelemetry">Opentelemetry visualization</a>:</p>
<ul>
<li>Getting an account on Elastic Cloud</li>
<li>Bringing up a GKE cluster</li>
<li>Bringing up the application</li>
<li>Configuring Kubernetes OTel Collector configmap to point to Elastic Cloud</li>
<li>Using Elastic Observability APM with OTel data for improved visibility</li>
</ul>
<h3 id="step0createanaccountonelasticcloud">Step 0: Create an account on Elastic Cloud</h3>
<p>Follow the instructions to <a href="https://cloud.elastic.co/registration?fromURI=/home">get started on Elastic Cloud</a>.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt588fbb3e515933fa/6a7f193aea068d34baf0a2b1/blog-elastic-otel-4.png" alt="" /></p>
<h3 id="step1bringupak8scluster">Step 1: Bring up a K8S cluster</h3>
<p>We used Google Kubernetes Engine (GKE), but you can use any Kubernetes platform of your choice.</p>
<p>There are no special requirements for Elastic to collect OpenTelemetry data from a Kubernetes cluster. Any normal Kubernetes cluster on GKE, EKS, AKS, or Kubernetes compliant cluster (self-deployed and managed) works.</p>
<h3 id="step2loadtheopentelemetrydemoapplicationonthecluster">Step 2: Load the OpenTelemetry demo application on the cluster</h3>
<p>Get your application on a Kubernetes cluster in your cloud service of choice or local Kubernetes platform. The application I am using is available <a href="https://github.com/bshetti/opentelemetry-microservices-demo/tree/main/deploy-with-collector-k8s">here</a>.</p>
<p>First clone the directory locally:</p>
<pre><code>git clone https://github.com/elastic/opentelemetry-demo.git
</code></pre>
<p>(Make sure you have <a href="https://kubernetes.io/docs/reference/kubectl/">kubectl</a> and <a href="https://helm.sh/">helm</a> also installed locally.)</p>
<p>The instructions utilize a specific opentelemetry-collector configuration for Elastic. Essentially, the Elastic <a href="https://github.com/elastic/opentelemetry-demo/blob/main/kubernetes/elastic-helm/values.yaml">values.yaml</a> file specified in the elastic/opentelemetry-demo configure the opentelemetry-collector to point to the Elastic APM Server using two main values:</p>
<p>OTEL_EXPORTER_OTLP_ENDPOINT is Elastic’s APM Server<br />
OTEL_EXPORTER_OTLP_HEADERS Elastic Authorization</p>
<p>These two values can be found in the OpenTelemetry setup instructions under the APM integration instructions (Integrations-&gt;APM) in your Elastic cloud.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blte776fb3258454d4d/6a7f193d42a117a8cd95c2df/blog-elastic-apm-agents.png" alt="elastic apm agents" /></p>
<p>Once you obtain this, the first step is to create a secret key on the cluster with your Elastic APM server endpoint, and your APM Secret Token with the following instruction:</p>
<pre><code>kubectl create secret generic elastic-secret \
  --from-literal=elastic_apm_endpoint='YOUR_APM_ENDPOINT_WITHOUT_HTTPS_PREFIX' \
  --from-literal=elastic_apm_secret_token='YOUR_APM_SECRET_TOKEN'
</code></pre>
<p>Don't forget to replace:</p>
<ul>
<li>YOUR_APM_ENDPOINT_WITHOUT_HTTPS_PREFIX: your Elastic APM endpoint ( <strong>without https:// prefix</strong> ) with OTEL_EXPORTER_OTLP_ENDPOINT</li>
<li>YOUR_APM_SECRET_TOKEN: your Elastic APM secret token OTEL_EXPORTER_OTLP_HEADERS</li>
</ul>
<p>Now execute the following commands:</p>
<pre><code># switch to the kubernetes/elastic-helm directory
cd kubernetes/elastic-helm

# add the open-telemetry Helm repostiroy
helm repo add open-telemetry https://open-telemetry.github.io/opentelemetry-helm-charts

# deploy the demo through helm install
helm install -f values.yaml my-otel-demo open-telemetry/opentelemetry-demo
</code></pre>
<p>Once your application is up on Kubernetes, you will have the following pods (or some variant) running on the <strong>default</strong> namespace.</p>
<pre><code>kubectl get pods -n default
</code></pre>
<p>Output should be similar to the following:</p>
<pre><code>NAME                                                  READY   STATUS    RESTARTS      AGE
my-otel-demo-accountingservice-5c77754b4f-vwph6       1/1     Running   0             5d4h
my-otel-demo-adservice-6b8b7c7dc5-mb7j5               1/1     Running   0             5d4h
my-otel-demo-cartservice-76d94b7dcd-2g4lf             1/1     Running   0             5d4h
my-otel-demo-checkoutservice-988bbdb88-hmkrp          1/1     Running   0             5d4h
my-otel-demo-currencyservice-6cf4b5f9f6-vz9t2         1/1     Running   0             5d4h
my-otel-demo-emailservice-868c98fd4b-lpr7n            1/1     Running   6 (18h ago)   5d4h
my-otel-demo-featureflagservice-8446ff9c94-lzd4w      1/1     Running   0             5d4h
my-otel-demo-ffspostgres-867945d9cf-zzwd7             1/1     Running   0             5d4h
my-otel-demo-frauddetectionservice-5c97c589b9-z8fhz   1/1     Running   0             5d4h
my-otel-demo-frontend-d85ccf677-zg9fp                 1/1     Running   0             5d4h
my-otel-demo-frontendproxy-6c5c4fccf6-qmldp           1/1     Running   0             5d4h
my-otel-demo-kafka-68bcc66794-dsbr6                   1/1     Running   0             5d4h
my-otel-demo-loadgenerator-64c545b974-xfccq           1/1     Running   1 (36h ago)   5d4h
my-otel-demo-otelcol-fdfd9c7cf-6lr2w                  1/1     Running   0             5d4h
my-otel-demo-paymentservice-7955c68859-ff7zg          1/1     Running   0             5d4h
my-otel-demo-productcatalogservice-67c879657b-wn2wj   1/1     Running   0             5d4h
my-otel-demo-quoteservice-748d754ffc-qcwm4            1/1     Running   0             5d4h
my-otel-demo-recommendationservice-df78894c7-lwm5v    1/1     Running   0             5d4h
my-otel-demo-redis-7d48567546-h4p4t                   1/1     Running   0             5d4h
my-otel-demo-shippingservice-f6fc76ddd-2v7qv          1/1     Running   0             5d4h
</code></pre>
<h3 id="step3openkibanaandusetheapmservicemaptoviewyourotelinstrumentedservices">Step 3: Open Kibana and use the APM Service Map to view your OTel instrumented Services</h3>
<p>In the Elastic Observability UI under APM, select servicemap to see your services.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt5ec18e0b8fe27ba9/6a7f194033fa8a5adb202b64/blog-elastic-observability-APM.png" alt="elastic observability APM" /></p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt1d2c8e5136dc6f53/6a7f19426693f8101666435d/blog-elastic-observability-OTEL-service-map.png" alt="elastic observability OTEL service map" /></p>
<p>If you are seeing this, then the OpenTelemetry Collector is sending data into Elastic:</p>
<p><em>Congratulations,</em> <em>you've instrumented the OpenTelemetry demo application using and successfully ingested the telemetry data into the Elastic!</em></p>
<h3 id="step4whatcanelasticshowme">Step 4: What can Elastic show me?</h3>
<p>Now that the OpenTelemetry data is ingested into Elastic, what can you do?</p>
<p>First, you can view the APM service map (as shown in the previous step) — this will give you a full view of all the services and the transaction flows between services.</p>
<p>Next, you can now check out individual services and the transactions being collected.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltd116b5c740f25566/6a7f19456693f83ea1664361/blog-elastic-observability-frontend-overview.png" alt="elastic observability frontend overview" /></p>
<p>As you can see, the frontend details are listed. Everything from:</p>
<ul>
<li>Average service latency</li>
<li>Throughput</li>
<li>Main transactions</li>
<li>Failed traction rate</li>
<li>Errors</li>
<li>Dependencies</li>
</ul>
<p>Let’s get to the trace. In the Transactions tab, you can review all the types of transactions related to the frontend service:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltf1669d81336b9a3e/6a7f194873d9bdc7e029df3b/blog-elastic-observability-frontend-transactions.png" alt="elastic observability frontend transactions" /></p>
<p>Selecting the HTTP POST transaction, we can see the full trace with all the spans:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt7b4a0689cfa8ad76/6a7f194b33fa8a0360202b68/blog-elastic-observability-frontend-HTTP-POST.png" alt="Average latency for this transaction, throughput, any failures, and of course the trace!" /></p>
<p>Not only can you review the trace but you can also analyze what is related to higher than normal latency for HTTP POST .</p>
<p>Elastic uses machine learning to help identify any potential latency issues across the services from the trace. It’s as simple as selecting the Latency Correlations tab and running the correlation.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blta53b6a97f8d8cacf/6a7f194ee88c653c1c00bae0/blog-elastic-latency-correlations.png" alt="elastic observability latency correlations" /></p>
<p>This shows that the high latency transactions are occurring in checkout service with a medium correlation.</p>
<p>You can then drill down into logs directly from the trace view and review the logs associated with the trace to help identify and pinpoint potential issues.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt8ad97b9fd8b1ea36/6a7f19505967e50ea75dd69d/blog-elastic-latency-distribution.png" alt="elastic observability latency distribution" /></p>
<h3 id="analyzeyourdatawithelasticmachinelearningml">Analyze your data with Elastic machine learning (ML)</h3>
<p>Once OpenTelemetry metrics are in Elastic, start analyzing your data through Elastic’s ML capabilities.</p>
<p>A great review of these features can be found here: <a href="https://www.elastic.co/blog/apm-correlations-elastic-observability-root-cause-transactions">Correlating APM telemetry to determine root causes in transactions</a>. And there are many more videos and blogs on <a href="https://www.elastic.co/blog/">Elastic’s Blog</a>. We’ll follow up with additional blogs on leveraging Elastic’s machine learning capabilities for OpenTelemetry data.</p>
<h2 id="conclusion">Conclusion</h2>
<p>I hope you’ve gotten an appreciation for how Elastic Observability can help you ingest and analyze OpenTelemetry data with Elastic’s APM capabilities.</p>
<p>A quick recap of lessons and more specifically learned:</p>
<ul>
<li>How to get a popular OTel instrumented demo app (Hipster Shop) configured to ingest into <a href="http://cloud.elastic.co">Elastic Cloud</a>, through a few easy steps</li>
<li>Highlight some of the Elastic APM capabilities and features around OTel data and what you can do with this once it’s in Elastic</li>
</ul>
<p>Ready to get started? Sign up <a href="https://cloud.elastic.co/registration">for Elastic Cloud</a> and try out the features and capabilities I’ve outlined above to get the most value and visibility out of your OpenTelemetry data.</p>]]></content:encoded>
    <link>https://www.elastic.co/observability-labs/blog/opentelemetry-observability</link>
    <guid isPermaLink="false">opentelemetry-observability</guid>
    <category><![CDATA[OpenTelemetry]]></category>
    <category><![CDATA[APM]]></category>
    <category><![CDATA[Kubernetes]]></category>
    <category><![CDATA[Infrastructure Monitoring]]></category>
    <dc:creator><![CDATA[Bahubali Shetti]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt785e1bd8fa6dd28d/6a7f19532f00b2a466efef13/illustration-scalability-gear-1680x980_(1).jpg" length="0" type="image/jpeg"/>
    <pubDate>Tue, 15 Nov 2022 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Managing your Kubernetes cluster with Elastic Observability]]></title>
    <description><![CDATA[Unify all of your Kubernetes metrics, log, and trace data on a single platform and dashboard, Elastic. From the infrastructure to the application layer Elastic Observability makes it easier for you to understand how your cluster is performing.]]></description>
    <content:encoded><![CDATA[<p>As an operations engineer (SRE, IT manager, DevOps), you’re always struggling with how to manage technology and data sprawl. Kubernetes is becoming increasingly pervasive and a majority of these deployments will be in Amazon Elastic Kubernetes Service (EKS), Google Kubernetes Engine (GKE), or Azure Kubernetes Service (AKS). Some of you may be on a single cloud while others will have the added burden of managing clusters on multiple Kubernetes cloud services. In addition to cloud provider complexity, you also have to manage hundreds of deployed services generating more and more observability and telemetry data.</p>
<p>The day-to-day operations of understanding the status and health of your Kubernetes clusters and applications running on them, through the logs, metrics, and traces they generate, will likely be your biggest challenge. But as an operations engineer you will need all of that important data to help prevent, predict, and remediate issues. And you certainly don’t need that volume of metrics, logs and traces spread across multiple tools when you need to visualize and analyze Kubernetes telemetry data for troubleshooting and support.</p>
<p>Elastic Observability helps manage the sprawl of Kubernetes metrics and logs by providing extensive and centralized observability capabilities beyond just the logging that we are known for. Elastic Observability provides you with granular insights and context into the behavior of your Kubernetes clusters along with the applications running on them by unifying all of your metrics, log, and trace data through OpenTelemetry and APM agents.</p>
<p>Regardless of the cluster location (EKS, GKE, AKS, self-managed) or application, <a href="https://www.elastic.co/what-is/kubernetes-monitoring">Kubernetes monitoring</a> is made simple with Elastic Observability. All of the node, pod, container, application, and infrastructure (AWS, GCP, Azure) metrics, infrastructure and application logs, along with application traces are available in Elastic Observability.</p>
<p>In this blog we will show:</p>
<ul>
<li>How <a href="http://cloud.elastic.co">Elastic Cloud</a> can aggregate and ingest metrics and log data through the Elastic Agent (easily deployed on your cluster as a DaemonSet) to retrieve logs and metrics from the host (system metrics, container stats) along with logs from all services running on top of Kubernetes.</li>
<li>How Elastic Observability can bring a unified telemetry experience (logs, metrics,traces) across all your Kubernetes cluster components (pods, nodes, services, namespaces, and more).</li>
</ul>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt4dd4583086a7cbbc/6a7f0ba36c6eac5e44f1407f/ManagingKubernetes-ElasticAgentIntegration-1.png" alt="Elastic Agent with Kubernetes Integration" /></p>
<h2 id="prerequisitesandconfig">Prerequisites and config</h2>
<p>If you plan on following this blog, here are some of the components and details we used to set up this demonstration:</p>
<ul>
<li>Ensure you have an account on <a href="http://cloud.elastic.co">Elastic Cloud</a> and a deployed stack (<a href="https://www.elastic.co/guide/en/elastic-stack/current/installing-elastic-stack.html">see instructions here</a>).</li>
<li>While we used GKE, you can use any location for your Kubernetes cluster.</li>
<li>We used a variant of the ever so popular <a href="https://github.com/GoogleCloudPlatform/microservices-demo">HipsterShop</a> demo application. It was originally written by Google to showcase Kubernetes across a multitude of variants available such as the <a href="https://github.com/open-telemetry/opentelemetry-demo">OpenTelemetry Demo App</a>. To use the app, please go <a href="https://github.com/bshetti/opentelemetry-microservices-demo/tree/main/deploy-with-collector-k8s">here</a> and follow the instructions to deploy. You don’t need to deploy otelcollector for Kubernetes metrics to flow — we will cover this below.</li>
<li>Elastic supports native ingest from Prometheus and FluentD, but in this blog, we are showing a direct ingest from Kubernetes cluster via Elastic Agent. There will be a follow-up blog showing how Elastic can also pull in telemetry from Prometheus or FluentD/bit.</li>
</ul>
<h2 id="whatcanyouobserveandanalyzewithelastic">What can you observe and analyze with Elastic?</h2>
<p>Before we walk through the steps on getting Elastic set up to ingest and visualize Kubernetes cluster metrics and logs, let’s take a sneak peek at Elastic’s helpful dashboards.</p>
<p>As we noted, we ran a variant of HipsterShop on GKE and deployed Elastic Agents with Kubernetes integration as a DaemonSet on the GKE cluster. Upon deployment of the agents, Elastic starts ingesting metrics from the Kubernetes cluster (specifically from kube-state-metrics) and additionally Elastic will pull all log information from the cluster.</p>
<h3 id="visualizingkubernetesmetricsonelasticobservability">Visualizing Kubernetes metrics on Elastic Observability</h3>
<p>Here are a few Kubernetes dashboards that will be available out of the box (OOTB) on Elastic Observability.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt6296af433d4603be/6a7f0ba6e88c65225900b608/ManagingKubernetes-HipsterShopMetrics-2.png" alt="HipsterShop cluster metrics on Elastic Kubernetes overview dashboard " /></p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltdb67b0d9f1fb2b31/6a7f0ba9bd21989e18758039/ManagingKubernetes-HipsterShopDashboard-3.png" alt="HipsterShop default namespace pod dashboard on Elastic Observability" /></p>
<p>In addition to the cluster overview dashboard and pod dashboard, Elastic has several useful OOTB dashboards:</p>
<ul>
<li>Kubernetes overview dashboard (see above)</li>
<li>Kubernetes pod dashboard (see above)</li>
<li>Kubernetes nodes dashboard</li>
<li>Kubernetes deployments dashboard</li>
<li>Kubernetes DaemonSets dashboard</li>
<li>Kubernetes StatefulSets dashboards</li>
<li>Kubernetes CronJob &amp; Jobs dashboards</li>
<li>Kubernetes services dashboards</li>
<li>More being added regularly</li>
</ul>
<p>Additionally, you can either customize these dashboards or build out your own.</p>
<h3 id="workingwithlogsonelasticobservability">Working with logs on Elastic Observability</h3>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt6000b4853f85ac77/6a7f0bac1967ea5a663306c1/ManagingKubernetes-Logging-4.png" alt="Kubernetes container logs and Elastic Agent logs" /></p>
<p>As you can see from the screens above, not only can I get Kubernetes cluster metrics, but also all the Kubernetes logs simply by using the Elastic Agent in my Kubernetes cluster.</p>
<h3 id="preventpredictandremediateissues">Prevent, predict, and remediate issues</h3>
<p>In addition to helping manage metrics and logs, Elastic can help you detect and predict anomalies across your cluster telemetry. Simply turn on Machine Learning in Elastic against your data and watch it help you enhance your analysis work. As you can see below, Elastic is not only a unified observability location for your Kubernetes cluster logs and metrics, but it also provides extensive true machine learning capabilities to enhance your analysis and management.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltf8e368fc11775c13/6a7f0baffc63aba1ef64cbb7/ManagingKubernetes-AnomalyDetection-5.png" alt="Anomaly detection across logs on Elastic Observability" /></p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt0547380eaee99b10/6a7f0bb2ead8ec024cbaa7f9/ManagingKubernetes-PodIssues-6.png" alt="Analyzing issues on a Kubernetes pod with Elastic Observability " /></p>
<p>In the top graph, you see anomaly detection across logs and it shows something potentially wrong in the September 21 to 23 time period. Dig into the details on the bottom chart by analyzing a single kubernetes.pod.cpu.usage.node metric showing cpu issues early in September and again, later on in the month. You can do more complicated analyses on your cluster telemetry with Machine Learning using multi-metric analysis (versus the single metric issue I am showing above) along with population analysis.</p>
<p>Elastic gives you better machine learning capabilities to enhance your analysis of Kubernetes cluster telemetry. In the next section, let’s walk through how easy it is to get your telemetry data into Elastic.</p>
<h2 id="settingitallup">Setting it all up</h2>
<p>Let’s walk through the details of how to get metrics, logs, and traces into Elastic from a HipsterShop application deployed on GKE.</p>
<p>First, pick your favorite version of Hipstershop — as we noted above, we used a variant of the <a href="https://github.com/open-telemetry/opentelemetry-demo">OpenTelemetry-Demo</a> because it already has OTel. We slimmed it down for this blog, however (fewer services with some varied languages).</p>
<h3 id="step0getanaccountonelasticcloud">Step 0: Get an account on Elastic Cloud</h3>
<p>Follow the instructions to <a href="https://cloud.elastic.co/registration?fromURI=/home">get started on Elastic Cloud</a>.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt277a59dd6558e518/6a7f0bb5de2315150bfd7ba5/ManagingKubernetes-FreeElasticCloud-7.png" alt="" /></p>
<h3 id="step1getakubernetesclusterandloadyourkubernetesappintoyourcluster">Step 1: Get a Kubernetes cluster and load your Kubernetes app into your cluster</h3>
<p>Get your app on a Kubernetes cluster in your Cloud service of choice or local Kubernetes platform. Once your app is up on Kubernetes, you should have the following pods (or some variant) running on the default namespace.</p>
<pre><code>NAME                                    READY   STATUS    RESTARTS   AGE
adservice-8694798b7b-jbfxt              1/1     Running   0          4d3h
cartservice-67b598697c-hfsxv            1/1     Running   0          4d3h
checkoutservice-994ddc4c4-p9p2s         1/1     Running   0          4d3h
currencyservice-574f65d7f8-zc4bn        1/1     Running   0          4d3h
emailservice-6db78645b5-ppmdk           1/1     Running   0          4d3h
frontend-5778bfc56d-jjfxg               1/1     Running   0          4d3h
jaeger-686c775fbd-7d45d                 1/1     Running   0          4d3h
loadgenerator-c8f76d8db-gvrp7           1/1     Running   0          4d3h
otelcollector-5b87f4f484-4wbwn          1/1     Running   0          4d3h
paymentservice-6888bb469c-nblqj         1/1     Running   0          4d3h
productcatalogservice-66478c4b4-ff5qm   1/1     Running   0          4d3h
recommendationservice-648978746-8bzxc   1/1     Running   0          4d3h
redis-cart-96d48485f-gpgxd              1/1     Running   0          4d3h
shippingservice-67fddb767f-cq97d        1/1     Running   0          4d3h
</code></pre>
<h3 id="step2turnonahrefhttpsgithubcomkuberneteskubestatemetricstarget_selfkubestatemetricsa">Step 2: Turn on <a href="https://github.com/kubernetes/kube-state-metrics">kube-state-metrics</a></h3>
<p>Next you will need to turn on <a href="https://github.com/kubernetes/kube-state-metrics">kube-state-metrics</a>.</p>
<p>First:</p>
<pre><code>git clone https://github.com/kubernetes/kube-state-metrics.git
</code></pre>
<p>Next, in the kube-state-metrics directory under the examples directory, just apply the standard config.</p>
<pre><code>kubectl apply -f ./standard
</code></pre>
<p>This will turn on kube-state-metrics, and you should see a pod similar to this running in kube-system namespace.</p>
<pre><code>kube-state-metrics-5f9dc77c66-qjprz                    1/1     Running   0          4d4h
</code></pre>
<h3 id="step3installtheelasticagentwithkubernetesintegration">Step 3: Install the Elastic Agent with Kubernetes integration</h3>
<p><strong>Add Kubernetes Integration:</strong></p>
<ol>
<li><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltdd9b396ea440ab15/6a840f08c8ced91cb80528d0/Addk8sButton-8.jpg" alt="" /></li>
<li>In Elastic, go to integrations and select the Kubernetes Integration, and select to Add Kubernetes.</li>
<li>Select a name for the Kubernetes integration.</li>
<li>Turn on kube-state-metrics in the configuration screen.</li>
<li>Give the configuration a name in the new-agent-policy-name text box.</li>
<li>Save the configuration. The integration with a policy is now created.</li>
</ol>
<p>You can read up on the agent policies and how they are used on the Elastic Agent <a href="https://www.elastic.co/guide/en/fleet/current/agent-policy.html">here</a>.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltde684a9e67536da0/6a7f0bb79090b0f30084e95b/ManagingKubernetes-K8sIntegration-9.png" alt="" /></p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt9413c6c449235522/6a7f0bbaea068da442f09de7/ManagingKubernetes-FleetManagement-10.png" alt="" /></p>
<ol>
<li>Add Kubernetes integration.</li>
<li>Select the policy you just created in the second.</li>
<li>In the third step of Add Agent instructions, copy and paste or download the manifest.</li>
<li>Add manifest to the shell where you have kubectl running, save it as elastic-agent-managed-kubernetes.yaml, and run the following command.</li>
</ol>
<pre><code>kubectl apply -f elastic-agent-managed-kubernetes.yaml
</code></pre>
<p>You should see a number of agents come up as part of a DaemonSet in kube-system namespace.</p>
<pre><code>NAME                                                   READY   STATUS    RESTARTS   AGE
elastic-agent-qr6hj                                    1/1     Running   0          4d7h
elastic-agent-sctmz                                    1/1     Running   0          4d7h
elastic-agent-x6zkw                                    1/1     Running   0          4d7h
elastic-agent-zc64h                                    1/1     Running   0          4d7h
</code></pre>
<p>In my cluster, I have four nodes and four elastic-agents started as part of the DaemonSet.</p>
<h3 id="step4lookatelasticoutoftheboxdashboardsootbforkubernetesmetricsandstartdiscoveringkuberneteslogs">Step 4: Look at Elastic out of the box dashboards (OOTB) for Kubernetes metrics and start discovering Kubernetes logs</h3>
<p>That is it. You should see metrics flowing into all the dashboards. To view logs for specific pods, simply go into Discover in Kibana and search for a specific pod name.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt6296af433d4603be/6a7f0ba6e88c65225900b608/ManagingKubernetes-HipsterShopMetrics-2.png" alt="HipsterShop cluster metrics on Elastic Kubernetes overview dashboard" /></p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltdb67b0d9f1fb2b31/6a7f0ba9bd21989e18758039/ManagingKubernetes-HipsterShopDashboard-3.png" alt="Hipstershop default namespace pod dashboard on Elastic Observability" /></p>
<p>Additionally, you can browse all the pod logs directly in Elastic.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blta4498dff3b7ea4d0/6a7f0bbe63e959788f73dd60/ManagingKurbenetes-PodLogs-11.png" alt="frontendService and cartService logs" /></p>
<p>In the above example, I searched for frontendService and cartService logs.</p>
<h3 id="step5bonus">Step 5: Bonus!</h3>
<p>Because we were using an OTel based application, Elastic can even pull in the application traces. But that is a discussion for another blog.</p>
<p>Here is a quick peek at what Hipster Shop’s traces for a front end transaction look like in Elastic Observability.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt242e6a369d416a3b/6a7f0bc105b7b5347418b8ce/ManagingKubernetes-CheckOutTransaction-12.png" alt="Trace for Checkout transaction for HipsterShop" /></p>
<h2 id="conclusionelasticobservabilityrocksforkubernetesmonitoring">Conclusion: Elastic Observability rocks for Kubernetes monitoring</h2>
<p>I hope you’ve gotten an appreciation for how Elastic Observability can help you manage Kubernetes clusters along with the complexity of the metrics, log, and trace data it generates for even a simple deployment.</p>
<p>A quick recap of lessons and more specifically learned:</p>
<ul>
<li>How <a href="http://cloud.elastic.co">Elastic Cloud</a> can aggregate and ingest telemetry data through the Elastic Agent, which is easily deployed on your cluster as a DaemonSet and retrieves metrics from the host, such as system metrics, container stats, and metrics from all services running on top of Kubernetes</li>
<li>Show what Elastic brings from a unified telemetry experience (Kubernenetes logs, metrics, traces) across all your Kubernetes cluster components (pods, nodes, services, any namespace, and more).</li>
<li>Interest in exploring Elastic’s ML capabilities which will reduce your <strong>MTTHH</strong> (mean time to happy hour)</li>
</ul>
<p>Ready to get started? <a href="https://cloud.elastic.co/registration">Register</a> and try out the features and capabilities I’ve outlined above.</p>]]></content:encoded>
    <link>https://www.elastic.co/observability-labs/blog/kubernetes-cluster-metrics-logs-monitoring</link>
    <guid isPermaLink="false">kubernetes-cluster-metrics-logs-monitoring</guid>
    <category><![CDATA[Kubernetes]]></category>
    <category><![CDATA[Metrics]]></category>
    <category><![CDATA[Infrastructure Monitoring]]></category>
    <dc:creator><![CDATA[Bahubali Shetti]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt4dd4583086a7cbbc/6a7f0ba36c6eac5e44f1407f/ManagingKubernetes-ElasticAgentIntegration-1.png" length="0" type="image/png"/>
    <pubDate>Mon, 24 Oct 2022 00:00:00 GMT</pubDate>
  </item>
  </channel>
</rss>