<?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[David Hope - 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[David Hope - 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/author/david-hope</link>
    </image>
    <link>https://www.elastic.co/observability-labs/author/david-hope</link>
    <atom:link href="https://www.elastic.co/observability-labs/rss/author/david-hope.xml" rel="self" type="application/rss+xml"/>
    <language><![CDATA[en]]></language>
    <lastBuildDate>Sat, 12 Sep 2026 03:21:06 GMT</lastBuildDate>
  <item>
    <title><![CDATA[Windows Event Log Monitoring with OpenTelemetry & Elastic Streams]]></title>
    <description><![CDATA[Learn how to enhance Windows Event Log monitoring with OpenTelemetry for standardized ingestion and Elastic Streams for smart partitioning and analysis.]]></description>
    <content:encoded><![CDATA[<p>For system administrators and SREs, Windows Event Logs are both a goldmine and a graveyard. They contain the critical data needed to diagnose the root cause of a server crash or a security breach, but they are often buried under gigabytes of noise. Traditionally, extracting value from these logs required brittle regex parsers, manual rule creation, and a significant amount of human intuition.</p>
<p>However, the landscape of log management is shifting. By combining the industry-standard ingestion of OpenTelemetry (OTel) with the AI-driven capabilities of Elastic Streams, we can change how we monitor Windows infrastructure. This approach isn't just moving data. We are also using Large Language Models (LLMs) to understand it.</p>
<h2 id="thechallengewithtraditionalwindowslogging">The Challenge with Traditional Windows Logging</h2>
<p>Windows generates a massive variety of logs: System, Security, Application, Setup, and Forwarded Events. Within those categories, you have thousands of Event IDs. Historically, getting this data into an observability platform involved installing proprietary agents and configuring complex pipelines to strip out the XML headers and format the messages.</p>
<p>Once the data was ingested, we can try to figure out what "bad" looked like. You had to know in advance that Event ID 7031 indicated a service crash, and then write a specific alert for it. If you missed a specific Event ID or if the format changed, your monitoring went dark.</p>
<h2 id="step1ingestionviaopentelemetry">Step 1: Ingestion via OpenTelemetry</h2>
<p>The first step in modernizing this workflow is adopting OpenTelemetry. The OTel collector has matured significantly and now offers robust support for Windows environments. By installing the collector directly on Windows servers, you can configure receivers to tap into the event log subsystems.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt13a67d803cdc46b4/6a7f1cabb6b734b8c7e49216/otel-config.png" alt="OTel collector configuration for Windows Event Logs" /></p>
<p>The beauty of this approach is standardization. You aren't locked into a vendor-specific shipping agent. The OTel collector acts as a universal router, grabbing the logs and sending them to your observability backend in this case, the Elastic logs index designed to handle high-throughput streams.</p>
<p>The key thing to pay attention to in this configuration is how we add this transform statement:</p>
<pre><code>transform/logs-streams:
  log_statements:
    - context: resource
      statements:
        - set(attributes["elasticsearch.index"], "logs")
</code></pre>
<p>This works with the vanilla opentelemetry collector and when the data arrives in Elastic, it tells Elastic to use the new wired streams feature which enables all the downstream AI features we discuss in later steps.</p>
<p>Checkout my example configuration <a href="https://github.com/davidgeorgehope/otel-collector-windows/blob/main/config.yaml">here</a></p>
<h2 id="step2aidrivenpartitioning">Step 2: AI-Driven Partitioning</h2>
<p>Once the data arrives, the next challenge is organization. Dumping all Windows logs into a single <code>logs-*</code> index is a recipe for slow queries and confusion. In the past, we split indices based on hardcoded fields. Now, we can use AI to "fingerprint" the data.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt4de0a4697a52249b/6a7f1cafea068d714cf0a330/ai-partitioning.png" alt="AI-driven partitioning of Windows logs" /></p>
<p>This process involves analyzing the incoming stream to identify patterns. The system looks at the structure and content of the logs to determine their origin. For example, it can distinguish between a <code>Windows Security Audit</code> log and a <code>Service Control Manager</code> log purely based on the data shape.</p>
<p>The result is automatic partitioning. The system creates separate, optimized "buckets" or streams for each data type. You get a clean separation of concerns, Security logs go to one stream, File Manager logs to another, without having to write a single conditional routing rule. This partitioning is crucial for performance and for the next phase of the process: analysis.</p>
<h2 id="step3significanteventsandllmanalysis">Step 3: Significant Events and LLM Analysis</h2>
<p>Once your data is partitioned (e.g., into a dedicated <code>Service Control Manager</code> stream), you can apply GenAI models to analyze the semantic meaning of that stream.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt32645d1a432e8bfb/6a7f1cb3bdcff01f02c4331b/llm-analysis.png" alt="LLM analysis of log streams" /></p>
<p>In a traditional setup, the system sees text strings. In an AI-driven setup, the system understands context. When an LLM analyzes the <code>Service Control Manager</code> stream, it identifies what that system is responsible for. It knows that this specific component manages the starting and stopping of system services.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltf18fd4bedf526eb5/6a7f1cb6e3a219399f99f902/significant-events-suggestions.png" alt="Significant events suggestions from AI" /></p>
<p>Because the model understands the <em>purpose</em> of the log stream, it can generate suggestions for what constitutes a "Significant Event." It doesn't need you to tell it to look for crashes; it knows that for a Service Manager, a crash is a critical failure.</p>
<h3 id="frompassivestoragetoproactivesuggestions">From Passive Storage to Proactive Suggestions</h3>
<p>The workflow effectively automates the creation of detection rules. The LLM scans the logs and generates a list of potential problems relevant to that specific dataset, such as:</p>
<ul>
<li><strong>Service Crashes:</strong> High severity anomalies where background processes terminate unexpectedly.</li>
<li><strong>Startup/Boot Failures:</strong> Critical errors preventing the OS from reaching a stable state.</li>
<li><strong>Permission Denials:</strong> Security-relevant events regarding service interactions.</li>
</ul>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt1ec41d68fba309eb/6a7f1cba63e9593c6073e2bb/significant-events-list.png" alt="List of significant events detected" /></p>
<p>It bubbles these up as suggested observations. You can review a list of potential issues, see the severity the AI has assigned to them (e.g., Critical, Warning), and with a single click, generate the query required to find those logs.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt41d653eef46a3c18/6a7f1cbd9090b0c7ab84ee8b/query-generation.png" alt="Auto-generated query for significant events" /></p>
<h2 id="conclusion">Conclusion</h2>
<p>The combination of OpenTelemetry for standardized ingestion and AI-driven Streams for analysis turns the chaotic flood of Windows logs into a structured, actionable intelligence source. We are moving away from the era of "log everything, look at nothing" to an era where our tools understand our infrastructure as well as we do.</p>
<p>The barrier to effective monitoring is no longer technical complexity. Whether you are tracking security audits or debugging boot loops, leveraging LLMs to partition and analyze your streams is the new standard for observability.</p>
<p><a href="https://cloud.elastic.co/serverless-registration?onboarding_token=observability">Try Streams today</a></p>]]></content:encoded>
    <link>https://www.elastic.co/observability-labs/blog/windows-event-monitoring-with-opentelemetry-and-elastic-streams</link>
    <guid isPermaLink="false">windows-event-monitoring-with-opentelemetry-and-elastic-streams</guid>
    <category><![CDATA[Logs Analytics]]></category>
    <category><![CDATA[OpenTelemetry]]></category>
    <category><![CDATA[LLM Observability]]></category>
    <dc:creator><![CDATA[David Hope]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt4de0a4697a52249b/6a7f1cafea068d714cf0a330/ai-partitioning.png" length="0" type="image/png"/>
    <pubDate>Thu, 05 Feb 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[Getting more from your logs with OpenTelemetry]]></title>
    <description><![CDATA[Learn how to evolve beyond basic log ingest by leveraging OpenTelemetry for ingestion, structured logging, geographic enrichment, and ES|QL analytics. Transform raw log data into actionable intelligence with practical examples and proactive observability strategies.]]></description>
    <content:encoded><![CDATA[<p>Most people today use their logging tools mostly still in the same way we have for decades as a simple search lake, essentially still grepping for logs but from a centralized platform. There’s nothing wrong with this, you can get a lot of value by having a centralized logging platform but the question becomes how can I start to evolve beyond this basic log and search use case? Where can I start to be more effective with my incident investigations? In this blog we start from where most of our customers are today and give you some practical tips on how to move a little beyond this simple logging use case.</p>
<h2 id="ingestion">Ingestion</h2>
<p>Let's start at the beginning, ingest. Typically many of you are using older tools for ingestion today. If you want to be more forward thinking here, it’s time to introduce you to OpenTelemetry. OpenTelemetry was once not very mature or capable for logging but things have changed significantly. Elastic has been working particularly hard to improve the log capabilities resident in OpenTelemetry. So let's start by exploring how we can get started bringing logs into Elastic via the OpenTelemetry collector.</p>
<p>Firstly if you want to follow along simply create a host to run the log generator and OpenTelemetry collector.</p>
<p>Follow the instructions here to get the log generator running:</p>
<p><a href="https://github.com/davidgeorgehope/log-generator-bin/">https://github.com/davidgeorgehope/log-generator-bin/</a></p>
<p>To get the OpenTelemetry collector up and running in <a href="https://cloud.elastic.co/serverless-registration?onboarding_token=observability">Elastic Serverless</a>, you can click on Add Data from the bottom left, then 'host' and finally 'opentelemetry'</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt3381e49c8a41620d/6a7f0ad4b43770f66d4d6bb3/image14.png" alt="" /></p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt96238b357b7317b8/6a7f0ad7b6b7346243e48d14/image7.png" alt="" /></p>
<p>Follow the instructions but don’t start the collector just yet.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt9358dc05f0f9eea7/6a7f0ada05b7b546a018b870/image16.png" alt="" /></p>
<p>Our host here is running a 3 tier application with an Nginx frontend, backend and connected to a MySQL database. So let's start by bringing the logs into Elastic.</p>
<p>First we’ll install the Elastic Distributions for OpenTelemetry but before starting it, we will make a small change to the OpenTelemetry configuration file to expand the directories it will search for logs in.  Edit the otel.yml by simply using vi or your favorite editor:</p>
<pre><code>vi otel.yml
</code></pre>
<p>Instead of simply /var/log/.log we will add /var/log/*<em>/</em>.log to bring in all our log files.</p>
<pre><code>receivers:
&amp;nbsp; #&amp;nbsp;Receiver&amp;nbsp;for&amp;nbsp;platform&amp;nbsp;specific&amp;nbsp;log&amp;nbsp;files
&amp;nbsp; filelog/platformlogs:
&amp;nbsp; &amp;nbsp; include:&amp;nbsp;[&amp;nbsp;/var/log/**/*.log&amp;nbsp;]
&amp;nbsp; &amp;nbsp; retry_on_failure:
&amp;nbsp; &amp;nbsp; &amp;nbsp; enabled:&amp;nbsp;true
&amp;nbsp; &amp;nbsp; start_at:&amp;nbsp;end
&amp;nbsp; &amp;nbsp; storage:&amp;nbsp;file_storage
</code></pre>
<p>Start the otel collector</p>
<pre><code>sudo&amp;nbsp;./otelcol&amp;nbsp;--config&amp;nbsp;otel.yml
</code></pre>
<p>And we can see these are being brought in, in discover</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltfd1deda8fe0b550c/6a7f0add9090b0183e84e90b/image8.png" alt="" /></p>
<p>Now one thing that is immediately noticeable is that we automatically without changing anything get a bunch of useful additional information such as the os name and cpu information.  </p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt6bbd1e618839a3e5/6a7f0ae03ce8e2e95ccf52eb/image12.png" alt="" /></p>
<p>The OpenTelemetry collector has automatically, without any changes, started to enrich our logs, making it useful for additional processing, though we could do significantly better!</p>
<p>To start with we want to give our logs some structure. Lets edit that otel.yml file and add some OTTL to extract some key data from our NGINX logs.</p>
<pre><code>  transform/parse_nginx:
    trace_statements: []
    metric_statements: []
    log_statements:
      - context: log
        conditions:
          - 'attributes["log.file.name"] != nil and IsMatch(attributes["log.file.name"], "access.log")'
        statements:
          - merge_maps(attributes, ExtractPatterns(body, "^(?P&lt;client_ip&gt;\\S+)"), "upsert")
          - merge_maps(attributes, ExtractPatterns(body, "^\\S+ - (?P&lt;user&gt;\\S+)"), "upsert")
          - merge_maps(attributes, ExtractPatterns(body, "\\[(?P&lt;timestamp_raw&gt;[^\\]]+)\\]"), "upsert")
          - merge_maps(attributes, ExtractPatterns(body, "\"(?P&lt;method&gt;\\S+) "), "upsert")
          - merge_maps(attributes, ExtractPatterns(body, "\"\\S+ (?P&lt;path&gt;\\S+)\\?"), "upsert")
          - merge_maps(attributes, ExtractPatterns(body, "req_id=(?P&lt;req_id&gt;[^ ]+)"), "upsert")
          - merge_maps(attributes, ExtractPatterns(body, "\" (?P&lt;status&gt;\\d+) "), "upsert")
          - merge_maps(attributes, ExtractPatterns(body, "\" \\d+ (?P&lt;size&gt;\\d+)"), "upsert")
.....

   logs/platformlogs:
      receivers: [filelog/platformlogs]
      processors: [transform/parse_nginx,resourcedetection]
      exporters: [elasticsearch/otel]
</code></pre>
<p>Now when we start the Otel collector with this new configuration</p>
<pre><code>sudo&amp;nbsp;./otelcol&amp;nbsp;--config&amp;nbsp;otel.yml
</code></pre>
<p>We will see that we now have structured logs!!  </p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt47c182299d15924a/6a7f0ae2e02facaa035d649c/image17.png" alt="" /></p>
<h2 id="storeandoptimize">Store and Optimize</h2>
<p>To ensure you aren’t blowing your budget out with all this additional structured data there are few things you can do to help maximize storage efficiency.</p>
<p>You can use the filter processors in the Otel collector with granular filtering/dropping of irrelevant attributes to control volume going out of the collector for example.</p>
<pre><code>processors:
&amp;nbsp; filter/drop_logs_without_user_attributes:
&amp;nbsp; &amp;nbsp; logs:
&amp;nbsp; &amp;nbsp; &amp;nbsp; log_record:
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; - 'attributes["user"] == nil'
&amp;nbsp; filter/drop_200_logs:
&amp;nbsp; &amp;nbsp; logs:
&amp;nbsp; &amp;nbsp; &amp;nbsp; log_record:
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; - 'attributes["status"] == "200"'

service:
&amp;nbsp; pipelines:
&amp;nbsp; &amp;nbsp; logs/platformlogs:
&amp;nbsp; &amp;nbsp; &amp;nbsp; receivers: [filelog/platformlogs]
&amp;nbsp; &amp;nbsp; &amp;nbsp; processors: [transform/parse_nginx, filter/drop_logs_without_user_attributes, filter/drop_200_logs, resourcedetection]
&amp;nbsp; &amp;nbsp; &amp;nbsp; exporters: [elasticsearch/otel]
</code></pre>
<p>The filter processor will help reduce the noise for example if you wanted to drop the debug logs or logs from a noisy service. Great ways to keep a lid on your observability spend.</p>
<p>Additionally for your most critical flows and logs where you don’t want to drop any data, Elastic has you covered. In version 9.x of Elastic you now have LogsDB switched on by default.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blta4e371aaa99d613f/6a7f0ae505b7b5841418b878/image15.png" alt="" /></p>
<p>With LogsDB, Elastic has reduced the storage footprint of log data in Elasticsearch by up to 65% allowing you to store more observability and security data without exceeding your budget, while keeping all data accessible and searchable.</p>
<p>LogsDB reduces log storage by up to 65%. This dramatically minimizes storage footprints by leveraging advanced compression techniques like ZSTD, delta encoding, and run-length encoding, and it also reconstructs the _source field on demand, saving about 40% more storage by not retaining the original JSON document. Synthetic _source represents the introduction of columnar storage within Elasticsearch.</p>
<h2 id="analytics">Analytics</h2>
<p>So we have our data in Elastic, it’s structured, it conforms to the idea of a wide-event log since it has lots of good context, user ids, request ids and the data is captured at the start of a request Next we’re going to look at the analytics part of this. First let's take a stab at looking at the number of Errors for each user transaction in our application.</p>
<pre><code>FROM logs-generic.otel-default
| WHERE log.file.name == "access.log"
| WHERE attributes.status &gt;= "400"
| STATS error_count = COUNT(*) BY attributes.user
| SORT error_count DESC
</code></pre>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt8a0e48cb2af89732/6a7f0ae8e02fac183f5d64a0/image9.png" alt="" /></p>
<p>It’s pretty easy now to save this and put it on a dashboard, we just click the save button:  </p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt00d60b2300e53b92/6a7f0aeae02facc6b15d64a6/image1.png" alt="" /></p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt28e12818ef1840e6/6a7f0aedead8ec22c6baa797/image5.png" alt="" />  </p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt706bb2fa95790310/6a7f0af096b5a6804f87b38b/image6.png" alt="" /></p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt978c1b0402be9a78/6a7f0af36c6eac03d9f1404b/image3.png" alt="" />  </p>
<p>Next let's look at putting something together to show the global impact, first we will update our collector config to enrich our log data with geo location.</p>
<p>Update the OTTL configuration with this new line:</p>
<pre><code>   log_statements:
      - context: log
        conditions:
          - 'attributes["log.file.name"] != nil and IsMatch(attributes["log.file.name"], "access.log")'
        statements:
          - merge_maps(attributes, ExtractPatterns(body, "^(?P&lt;client_ip&gt;\\S+)"), "upsert")
          - merge_maps(attributes, ExtractPatterns(body, "^\\S+ - (?P&lt;user&gt;\\S+)"), "upsert")
          - merge_maps(attributes, ExtractPatterns(body, "\\[(?P&lt;timestamp_raw&gt;[^\\]]+)\\]"), "upsert")
          - merge_maps(attributes, ExtractPatterns(body, "\"(?P&lt;method&gt;\\S+) "), "upsert")
          - merge_maps(attributes, ExtractPatterns(body, "\"\\S+ (?P&lt;path&gt;\\S+)\\?"), "upsert")
          - merge_maps(attributes, ExtractPatterns(body, "req_id=(?P&lt;req_id&gt;[^ ]+)"), "upsert")
          - merge_maps(attributes, ExtractPatterns(body, "\" (?P&lt;status&gt;\\d+) "), "upsert")
          - merge_maps(attributes, ExtractPatterns(body, "\" \\d+ (?P&lt;size&gt;\\d+)"), "upsert")
          - set(attributes["source.address"], attributes["client_ip"]) where attributes["client_ip"] != nil
</code></pre>
<p>Next add a new processor (you will need to download the GeoIP database from MaxMind)</p>
<pre><code>geoip:
&amp;nbsp; context: record
&amp;nbsp; source:
&amp;nbsp; &amp;nbsp; from: attributes
&amp;nbsp; providers:
&amp;nbsp; &amp;nbsp; maxmind:
&amp;nbsp; &amp;nbsp; &amp;nbsp; database_path: /opt/geoip/GeoLite2-City.mmdb
</code></pre>
<p>And add this to the log pipeline after the parse_nginx</p>
<pre><code>service:
&amp;nbsp; pipelines:
&amp;nbsp; &amp;nbsp; logs/platformlogs:
&amp;nbsp; &amp;nbsp; &amp;nbsp; receivers: [filelog/platformlogs]
&amp;nbsp; &amp;nbsp; &amp;nbsp; processors: [transform/parse_nginx, geoip, resourcedetection]
&amp;nbsp; &amp;nbsp; &amp;nbsp; exporters: [elasticsearch/otel]
</code></pre>
<p>Start the otel collector</p>
<pre><code>sudo ./otelcol --config otel.yml
</code></pre>
<p>Once the data starts flowing we can add a map visualization:  </p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt0dafa7a946db893e/6a7f0af6eab5be207620a601/image2.png" alt="" /></p>
<p>Add a layer:  </p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltc441c72d57a5688e/6a7f0af833fa8a5a96202602/image4.png" alt="" /></p>
<p>Use ES|QL</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltf3b6e46d97b19516/6a7f0afb1967ea5020330667/image10.png" alt="" /></p>
<p>Use the following ES|QL  </p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltda5110536c01fd38/6a7f0afebdcff0321ac42d4d/image13.png" alt="" /></p>
<p>And this should give you a map showing the locations of all your NGINX server requests!  </p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blta7f3df34f68ddf9e/6a7f0b01ead8ec80e4baa7a1/image11.png" alt="" /></p>
<p>As you can see, analytics is a breeze with your new Otel data collection pipeline.</p>
<h2 id="conclusionbeyondlogaggregationtooperationalintelligence">Conclusion: Beyond log aggregation to operational intelligence</h2>
<p>The journey from basic log aggregation to structured, enriched observability represents more than a technical upgrade, it's a shift in how organizations approach system understanding and incident response. By adopting OpenTelemetry for ingestion, implementing intelligent filtering to manage costs, and leveraging LogsDB's storage optimizations, you're not just modernizing your ELK stack; you're building the foundation for proactive system management.</p>
<p>The structured logs, geographic enrichment, and analytical capabilities demonstrated here transform raw log data into actionable intelligence with ES|QL. Instead of reactive grepping through logs during incidents, you now have the infrastructure to identify patterns, track user journeys, and correlate issues across your entire stack before they become critical problems.</p>
<p>But here's the key question: Are you prepared to act on these insights? Having rich, structured data is only valuable if your organization can shift from a reactive "find and fix" mentality to a proactive "predict and prevent" approach. The real evolution isn't in your logging stack, it's in your operational culture.</p>
<p>Get started with this today in <a href="https://cloud.elastic.co/serverless-registration?onboarding_token=observability">Elastic Serverless</a></p>]]></content:encoded>
    <link>https://www.elastic.co/observability-labs/blog/getting-more-from-your-logs-with-opentelemetry</link>
    <guid isPermaLink="false">getting-more-from-your-logs-with-opentelemetry</guid>
    <category><![CDATA[Logs Analytics]]></category>
    <category><![CDATA[OpenTelemetry]]></category>
    <dc:creator><![CDATA[David Hope]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt0efb83b4eb43624c/6a7f0b0473d9bd7e2b29da4d/getting-more-from-your-logs-with-opentelemetry.png" length="0" type="image/png"/>
    <pubDate>Thu, 11 Sep 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[The observability gap: Why your monitoring strategy isn't ready for what's coming next]]></title>
    <description><![CDATA[The increasing complexity of distributed applications and the observability data they generate creates challenges for SREs and IT Operations teams. Take a look at how you can close this observability gap with OpenTelemetry and the right strategy.]]></description>
    <content:encoded><![CDATA[<p>Anyone that’s been to London knows the announcements at the Tube to “Mind the gap” but what about the gap that’s developing in our monitoring and observability strategies? I’ve been through this toil before, and have run a distributed system that was humming along perfectly. My alerts were manageable, my dashboards made sense, and when things broke, I could usually track down the issue in a reasonable amount of time. </p>
<p>Fast forward 3-5 years and things have changed, we added Kubernetes, embraced microservices, maybe these days you might have even sprinkled in some AI-powered features. Suddenly, you're drowning in telemetry data, your alert fatigue is real, and correlating issues across your distributed architecture feels stressful.</p>
<p>You're experiencing what I call the "observability gap", where system complexity rockets ahead while our monitoring maturity crawls behind. Today, we're going to explore why this gap exists, what's driving it wider, and most importantly, how to close it using modern observability practices.</p>
<h2 id="thecomplexityrocketshiphasleftthestation">The complexity rocket ship has left the station</h2>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt4dfb1949be1b3004/6a7f0dd1b6b7346bd2e48e4a/ObservabilityGapBlog-Image2.jpg" alt="Observability Gap" /></p>
<p>Let's be honest about what we're dealing with. The scale and complexity of our infrastructure isn't growing linearly, it's exponential. We've gone from monolithic applications running on physical servers to container orchestration platforms managing hundreds of microservices, with AI algorithms now starting to make scaling decisions autonomously.</p>
<p>This trajectory shows no signs of slowing down. With AI-assisted coding accelerating development cycles and intelligent orchestration systems like Kubernetes evolving toward predictive scaling, we're looking at infrastructure that's not just complex, but dynamically complex.</p>
<p>Meanwhile, our observability tooling? It's stuck in the past, designed for a world where you knew exactly how many servers you had and could manually correlate logs with metrics by cross-referencing timestamps.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt8fa2bca48e8d082d/6a7f0dd46c6eac494ef14183/ObservabilityGapBlog-Image3.jpg" alt="Observability Gap part 2" /></p>
<h2 id="thetelemetrydataexplosionandwhysamplingisnttheanswer">The telemetry data explosion (and why sampling isn't the answer)</h2>
<p>One of the first things teams notice as they scale is their observability bill climbing faster than their infrastructure costs. The knee-jerk reaction is often to start sampling data downsample metrics, head-sample traces, deduplicate logs. While these techniques have their place, they're fundamentally at odds with where we're heading.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blteb610051de00eee0/6a7f0dd7c2e914712f016c42/ObservabilityGapBlog-Image4.jpg" alt="Data Management: Reduce fidelity of data" /></p>
<p>Here's the thing: ML and AI systems thrive on rich, contextual data. When you sample away the "noise," you're often discarding the very signals that could help you understand system behavior patterns or predict failures. Instead of asking "how can we collect less data?", the better question is "how can we store and process all this data cost-effectively?"</p>
<p>Modern storage architectures, particularly those leveraging object storage and advanced compression techniques like ZStandard, can achieve remarkable cost-to-value ratios. The secret is organizing related data together and moving it to cheaper storage tiers quickly. This approach lets you have your cake and eat it too, full fidelity data retention without breaking the bank.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt495ce5512c1f01b6/6a7f0ddaead8ec9612baa8e8/ObservabilityGapBlog-Image5.jpg" alt="Data Management: Make Storage Cheaper" /></p>
<p>Now of course there is a balance to this and not all your applications are equal, so as a first step you should look at all your most critical flows and applications and ensure that they have the richest telemetry. Do not use a sledge hammer approach and sample all your data just to reduce bills when a scalpel is best. </p>
<h2 id="opentelemetryotelthefoundationeverythingelsebuildson">OpenTelemetry (OTel): the foundation everything else builds on</h2>
<p>If I had to pick the single most transformative change in observability during my career, it would be OpenTelemetry. Not because it's flashy or revolutionary in concept, but because it solves fundamental problems that have plagued us for years.</p>
<p>Before OTel, instrumenting applications meant vendor lock-in. Want to switch from vendor A to vendor B? Good luck re-instrumenting your entire codebase. Want to send the same telemetry to multiple backends? Hope you enjoy maintaining multiple agent configurations.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt73b2fa6ffdc6a577/6a7f0ddd3ce8e21185cf53e1/ObservabilityGapBlog-Image6.jpg" alt="What is OpenTelemetry" /></p>
<p>OpenTelemetry changes things completely. Here's the three main reasons why.</p>
<p><strong>Vendor Neutrality:</strong> Your instrumentation code becomes portable. The same OTEL SDK can send data to any compliant backend.</p>
<p><strong>OpenTelemetry Semantic Conventions:</strong> All your telemetry (logs, metrics, traces, profiles, wide-events) shares common metadata like service names, resource attributes, and trace context.</p>
<p><strong>Auto-Instrumentation:</strong> For most popular languages and frameworks, you get rich telemetry with zero code changes.</p>
<p>OTEL also makes manual instrumentation incredibly valuable with minimal effort. Adding a single line like this</p>
<p><code>baggage.set_baggage("customer.id", "alice123")</code></p>
<p>In your authentication service means that customer ID automatically flows through every downstream service call, every database query, every log message. Suddenly, you can search all your telemetry data by customer ID across your entire distributed system.</p>
<p>The trajectory is clear: within a few years, OTel will be as ubiquitous and invisible as Kubernetes is becoming today. Runtimes will include it by default, cloud providers will offer OTel collectors at the edge, and frameworks will come pre-instrumented.</p>
<h2 id="correlationthesecretsaucethatmakeseverythingclick">Correlation: the secret sauce that makes everything click</h2>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blte3d2151ee8113d0d/6a7f0de0e3a219329f99f510/ObservabilityGapBlog-Image7.jpg" alt="Why do we need correlation?" /></p>
<p>You get an alert about high latency. You check your metrics dashboard yep, 95th percentile is spiking. You switch to your tracing system and you can see some slow requests. You hop over to your logging system and there are some error messages around the same time. Now comes the fun part: figuring out which logs correspond to which traces and whether they're related to the metric that alerted you.</p>
<p>This context-switching nightmare is exactly what proper correlation eliminates. When your telemetry data shares common identifiers for example, trace IDs in logs, consistent service names, synchronized timestamps or even customer IDs you can seamlessly pivot between different signal types without losing context.</p>
<p>But correlation goes beyond just technical convenience. When you can search all your logs by customer.id and immediately see the traces and metrics for that customer's journey through your system, you transform how you approach support and debugging. When you can filter your entire observability stack by deployment version and instantly understand the impact of a release, you change how you think about deployments. </p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt98aa8a4169ff243b/6a7f0de35967e539c65dd337/ObservabilityGapBlog-Image8.jpg" alt="How does this work?" /></p>
<p>Metrics? Yes, even metrics can be correlated by using OpenTelemetry exemplars, for example using python you would turn on exemplars as follows.</p>
<pre><code># Setup metrics with exemplars enabled

exemplar_filter = ExemplarFilter(trace_based=True)  

exemplar_reservoir = ExemplarReservoir(

    exemplar_filter=exemplar_filter,`

    max_exemplars=5
)
</code></pre>
<p>This would then associate metrics with a trace that happens to be occurring so you get some metrics correlated to your traces.</p>
<h2 id="thenagainwhycorrelateatall">Then again, why correlate at all?</h2>
<p>So you may be thinking, this is great and I can see this being a useful strategy. It is especially useful when you have metrics, logs and traces in separate systems, however, pretty soon you realize that it's a lot of effort when you could just combine all this data together in a single data structure and avoid the need to correlate at all. The observability industry agrees and has recently been espousing the benefits of a new signal type called wide-events. </p>
<p>Wide-events are just really structured logs, the idea is to put metric data, trace data and log data all into the same wide data structure which can make analysis much easier. Think about it, if you have a single data structure you can very quickly run queries and aggregations without having to join any data which can get pretty expensive. </p>
<p>Additionally you are increasing the information density per log record which is particularly great for AI applications.  AI gets a context-rich dataset to do analysis on with minimal latency, a single record with enough descriptive capability to quickly find the root cause of your issue without having to dig around in other data stores and try to figure out whatever schema those data stores are using. </p>
<p>LLMs especially LOVE context and if you can give them all the context they need without having them try to find it, your investigation time will significantly reduce. </p>
<p>This isn't just about making SRE life easier (though it does that). It's about creating the rich, interconnected dataset that AI and ML systems need to understand your infrastructure's behavior patterns.</p>
<h2 id="aidriveninvestigations">AI-driven investigations</h2>
<p>Observability tools today have been pretty good at solving the alerting fatigue and dashboarding problems, things have gotten quite mature there. Alert correlation and other techniques drastically reduce the noise in these domains, not to mention a focus on being alerted by SLOs instead of pure technical metrics. Life has gotten better over the past few years for SREs here. </p>
<p>Now alerts are one piece of the puzzle but the latest AI techniques using LLMs and agentic AI can unlock time savings in a different spot, during investigations. Think about it, investigations are typically what drags on when you have an outage, the cognitive overload while the pressure is on is very real and pretty stressful for SREs. </p>
<p>The good news is that when we get our data in good shape with correlation, enrichment and adopting wide-events and we store the data in full fidelity we now have the tools to help us drive faster investigations. </p>
<p>LLMs can take all that rich data and do some very powerful analysis that can cut down your investigation time. Let's walk through an example.</p>
<p>Imagine we have the following basic log. We only have a limited amount of data for an LLM to reason about. All it can tell is that a database failed. </p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltbb49613601ff570b/6a7f0de6fc63abacbc64ccc7/ObservabilityGapBlog-Image9.jpg" alt="What is a basic log" /></p>
<p>Let's see what this looks like when we use a wide-event, notice that already we can see some significant benefits, firstly we only had to visit the log from a single node, the node that serviced the request. We didn’t have to dig into downstream logs. This already makes life easier for the LLM; it doesn't have to figure out how to correlate multiple log lines and traces and metrics though we do still have correlation IDs if we desperately need to look in downstream systems.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltcbe9c352d4cc7afa/6a7f0de9bdcff02561c42ebf/ObservabilityGapBlog-Image10.jpg" alt="App Log" /></p>
<p>Next we have all this additional rich data that an LLM can use to reason about what happened. LLMs work best with context and if you can feed them as much context as possible they will work more effectively to reduce your investigation time.</p>
<p>| Field | How an LLM uses it |
| ----- | ----- |
| <code>trace_id</code>, <code>parent_span_id</code> | Thread every hop together without parsing free-text |
| <code>status.code</code>, <code>error.*</code> | Precise failure class; no NLP guess-work |
| <code>db.*</code> | Root-cause surface ("postgres isn't provisioned") |
| <code>user.id</code>, <code>cloud.region</code> | Instant blast-radius queries |
| <code>deployment.version</code> | Correlation with new releases |</p>
<p>Notice that we didn’t get rid of the unstructured error message, this is still useful context! LLMs are great at processing unstructured text so this textual description helps it understand the problem even further. </p>
<p>Large language models shine when they’re handed complete, context-rich evidence, exactly what wide-event logging supplies. Invest once in richer logs, and every downstream AI workflow (summaries, anomaly detection, natural-language queries) becomes simpler, cheaper, and far more reliable.</p>
<h2 id="buildingtowardthefuture">Building toward the future</h2>
<p>As I look ahead, three trends seem inevitable:</p>
<ol>
<li><p><strong>OpenTelemetry semantic conventions powers wide-events:</strong> OTel semantic conventions will become as standard as logging is today to create wide-events. Cloud providers, runtimes, and frameworks will use it by default.</p></li>
<li><p><strong>Making sense of logs with LLMs:</strong> Both improving the richness of your data and having LLMs automatically improve the richness of your existing logs will become essential for shortening investigation times.</p></li>
<li><p><strong>AI will be essential</strong>: As system complexity outpaces human cognitive ability to understand it, AI assistance will become necessary for maintaining reasonable investigation times.</p></li>
</ol>
<p>The organizations that start building toward this future now, adopting OpenTelemetry, investing in richer observability, and beginning to experiment with AI-assisted debugging will have a significant advantage as these trends accelerate.</p>
<h2 id="yournextsteps">Your next steps</h2>
<p>If you're dealing with the observability gap in your own environment, here's where I'd start</p>
<ol>
<li><p><strong>Evaluate your logs:</strong> Do your logs have the richness of data you need to shorten investigation times? Can LLMs help provide additional context?</p></li>
<li><p><strong>Start experimenting with OpenTelemetry:</strong> Even if you can't migrate everything immediately, instrumenting new services with OTel and using semantic conventions to produce wide-events gives you experience with the technology and starts building your enriched dataset.</p></li>
<li><p><strong>Add high-value context:</strong> Customer IDs, session IDs, deployment versions even small amounts of contextual metadata can dramatically improve your debugging capabilities.</p></li>
<li><p><strong>Think beyond storage costs:</strong> Instead of sampling data away, investigate modern storage architectures that let you keep everything at a reasonable cost for your most critical services.</p></li>
</ol>
<p>The complexity rocket ship has left the station, and it's not slowing down. The question isn't whether your observability strategy needs to evolve; it's whether you'll evolve it proactively or reactively. I know which approach leads to better sleep at night.</p>
<h2 id="additionalresources">Additional resources</h2>
<ul>
<li><a href="https://www.elastic.co/virtual-events/getting-started-logging">Getting started with logging on the ELK Stack webinar</a>  </li>
<li><a href="https://www.elastic.co/observability-labs/blog/the-next-evolution-of-observability-unifying-data-with-opentelemetry-and-generative-ai">The next evolution of observability: unifying data with OpenTelemetry and generative AI blog</a>  </li>
<li><a href="https://www.elastic.co/observability-labs/blog/elastic-agent-pivot-opentelemetry">Pivoting Elastic's Data Ingestion to OpenTelemetry blog</a></li>
</ul>]]></content:encoded>
    <link>https://www.elastic.co/observability-labs/blog/modern-observability-opentelemetry-correlation-ai</link>
    <guid isPermaLink="false">modern-observability-opentelemetry-correlation-ai</guid>
    <category><![CDATA[Logs Analytics]]></category>
    <dc:creator><![CDATA[David Hope]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt73b0c0a781e473b3/6a7f0deb2f00b26088efebd0/ObservabilityGapBlog-Image1.jpg" length="0" type="image/jpeg"/>
    <pubDate>Mon, 25 Aug 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[The next evolution of observability: unifying data with OpenTelemetry and generative AI]]></title>
    <description><![CDATA[Generative AI and machine learning are revolutionizing observability, but siloed data hinders their true potential. This article explores how to break down data silos by unifying logs, metrics, and traces with OpenTelemetry, unlocking the full power of GenAI for natural language investigations, automated root cause analysis, and proactive issue resolution.]]></description>
    <content:encoded><![CDATA[<p>The Observability industry today stands at a critical juncture. While our applications generate more telemetry data than ever before, this wealth of information typically exists in siloed tools, separate systems for logs, metrics, and traces. Meanwhile, Generative AI is hurtling toward us like an asteroid about to make a tremendous impact on our industry.</p>
<p>As SREs, we've grown accustomed to jumping between dashboards, log aggregators, and trace visualizers when troubleshooting issues. But what if there was a better way? What if AI could analyze all your observability data holistically, answering complex questions in natural language, and identifying root causes automatically?</p>
<p>This is the next evolution of observability. But to harness this power, we need to rethink how we collect, store, and analyze our telemetry data.</p>
<h2 id="theproblemsiloeddatalimitsaieffectiveness">The problem: siloed data limits AI effectiveness</h2>
<p>Traditional observability setups separate data into distinct types:</p>
<ul>
<li>Metrics: Numeric measurements over time (CPU, memory, request rates)</li>
<li>Logs: Detailed event records with timestamps and context</li>
<li>Traces: Request journeys through distributed systems</li>
<li>Profiles: Code-level execution patterns showing resource consumption and performance bottlenecks at the function/line level</li>
</ul>
<p>This separation made sense historically due to the way the industry evolved. Different data types have traditionally had different cardinality, structure, access patterns and volume characteristics. However, this approach creates significant challenges for AI-powered analysis:</p>
<pre><code>Metrics (Prometheus) → "CPU spiked at 09:17:00"
Logs (ELK) → "Exception in checkout service at 09:17:32" 
Traces (Jaeger) → "Slow DB queries in order-service at 09:17:28"
Profiles (pyroscope) -&gt; "calculate_discount() is taking 75% of CPU time"
</code></pre>
<p>When these data sources live in separate systems, AI tools must either:</p>
<ol>
<li>Work with an incomplete picture (seeing only metrics but not the related logs)</li>
<li>Rely on complex, brittle integrations that often introduce timing skew</li>
<li>Force developers to manually correlate information across tools</li>
</ol>
<p>Imagine asking an AI, "Why did checkout latency spike at 09:17?" To answer comprehensively, it needs access to logs (to see the stack trace), traces (to understand the service path), and metrics (to identify resource strain). With siloed tools, the AI either sees only fragments of the story or requires complex ETL jobs that are slower than the incident itself.</p>
<h2 id="whytraditionalmachinelearningmlfallsshort">Why traditional machine learning (ML) falls short</h2>
<p>Traditional machine learning for observability typically focuses on anomaly detection within a single data dimension. It can tell you when metrics deviate from normal patterns, but struggles to provide context or root cause.</p>
<p>ML models trained on metrics alone might flag a latency spike, but can't connect it to a recent deployment (found in logs) or identify that it only affects requests to a specific database endpoint (found in traces). They behave like humans with extreme tunnel vision, seeing only a fraction of the relevant information and only the information that a specific vendor has given you an opinionated view into.</p>
<p>This limitation becomes particularly problematic in modern microservice architectures where problems frequently cascade across services. Without a unified view, traditional ML can detect symptoms but struggles to identify the underlying cause.</p>
<h2 id="thesolutionunifieddatawithenrichedlogs">The solution: unified data with enriched logs</h2>
<p>The solution is conceptually simple but transformative: unify metrics, logs, and traces into a single data store, ideally with enriched logs that contain all signals about a request in a single JSON document. We're about to see a merging of signals.</p>
<p>Think of traditional logs as simple text lines:</p>
<pre><code>[2025-05-19 09:17:32] ERROR OrderService - Failed to process checkout for user 12345
</code></pre>
<p>Now imagine an enriched log that contains not just the error message, but also:</p>
<ul>
<li>The complete distributed trace context</li>
<li>Related metrics at that moment</li>
<li>System environment details</li>
<li>Business context (user ID, cart value, etc.)</li>
</ul>
<p>This approach creates a holistic view where every signal about the same event sits side-by-side, perfect for AI analysis.</p>
<h2 id="howgenerativeaichangesthings">How generative AI changes things</h2>
<p>Generative AI differs fundamentally from traditional ML in its ability to:</p>
<ol>
<li>Process unstructured data: Understanding free-form log messages and error text</li>
<li>Maintain context: Connecting related events across time and services</li>
<li>Answer natural language queries: Translating human questions into complex data analysis</li>
<li>Generate explanations: Providing reasoning alongside conclusions</li>
<li>Surface hidden patterns: Discovering correlations and anomalies in log data that would be impractical to find through manual analysis or traditional querying</li>
</ol>
<p>With access to unified observability data, GenAI can analyze complete system behavior patterns and correlate across previously disconnected signals.</p>
<p>For example, when asked "Why is our checkout service slow?" a GenAI model with access to unified data can:</p>
<ul>
<li>Analyze unified enriched logs to identify which specific operations are slow and to find errors or warnings in those components</li>
<li>Check attached metrics to understand resource utilization</li>
<li>Correlate all these signals with deployment events or configuration changes</li>
<li>Present a coherent explanation in natural language with supporting graphs and visualizations</li>
</ul>
<h2 id="implementingunifiedobservabilitywithopentelemetry">Implementing unified observability with OpenTelemetry</h2>
<p>OpenTelemetry provides the perfect foundation for unified observability with its consistent schema across metrics, logs, and traces. Here's how to implement enriched logs in a Java application:</p>
<pre><code>import io.opentelemetry.api.OpenTelemetry;
import io.opentelemetry.api.metrics.Meter;
import io.opentelemetry.api.metrics.DoubleHistogram;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.api.trace.Tracer;
import io.opentelemetry.context.Scope;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;
import java.lang.management.ManagementFactory;
import java.lang.management.OperatingSystemMXBean;

public class OrderProcessor {
    private static final Logger logger = LoggerFactory.getLogger(OrderProcessor.class);
    private final Tracer tracer;
    private final DoubleHistogram cpuUsageHistogram;
    private final OperatingSystemMXBean osBean;

    public OrderProcessor(OpenTelemetry openTelemetry) {
        this.tracer = openTelemetry.getTracer("order-processor");
        Meter meter = openTelemetry.getMeter("order-processor");
        this.cpuUsageHistogram = meter.histogramBuilder("system.cpu.load")
                                      .setDescription("System CPU load")
                                      .setUnit("1")
                                      .build();
        this.osBean = ManagementFactory.getOperatingSystemMXBean();
    }

    public void processOrder(String orderId, double amount, String userId) {
        Span span = tracer.spanBuilder("processOrder").startSpan();
        try (Scope scope = span.makeCurrent()) {
            // Add attributes to the span
            span.setAttribute("order.id", orderId);
            span.setAttribute("order.amount", amount);
            span.setAttribute("user.id", userId);
            // Populate MDC for structured logging
            MDC.put("trace_id", span.getSpanContext().getTraceId());
            MDC.put("span_id", span.getSpanContext().getSpanId());
            MDC.put("order_id", orderId);
            MDC.put("order_amount", String.valueOf(amount));
            MDC.put("user_id", userId);
            // Record CPU usage metric associated with the current trace context
            double cpuLoad = osBean.getSystemLoadAverage();
            if (cpuLoad &gt;= 0) {
                cpuUsageHistogram.record(cpuLoad);
                MDC.put("cpu_load", String.valueOf(cpuLoad));
            }
            // Log a structured message
            logger.info("Processing order");
            // Simulate business logic
            // ...
            span.setAttribute("order.status", "completed");
            logger.info("Order processed successfully");
        } catch (Exception e) {
            span.recordException(e);
            span.setAttribute("order.status", "failed");
            logger.error("Order processing failed", e);
        } finally {
            MDC.clear();
            span.end();
        }
    }
}
</code></pre>
<p>This code demonstrates how to:</p>
<ol>
<li>Create a span for the operation</li>
<li>Add business attributes</li>
<li>Add current CPU usage</li>
<li>Link everything with consistent IDs</li>
<li>Record exceptions and outcomes in the backend system</li>
</ol>
<p>When configured with an appropriate exporter, this creates enriched logs that contain both application events and their complete context.</p>
<h2 id="powerfulqueriesacrosspreviouslyseparatedata">Powerful queries across previously separate data</h2>
<p>With data that has not yet been enriched, there is still hope. Firstly with GenAI powered ingestion it is possible to extract key fields to help correlate data such as a session id's. This will help you enrich your logs so they get the structure they need to behave like other signals. Below we can see Elastic's Auto Import mechanism that will automatically generate ingest pipelines and pull unstructured information from logs into a structured format perfect for analytics.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt49ea53cd2cb13c82/6a7f1b8cea068d2deaf0a2df/image4.png" alt="" /></p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt51470fc777215fdd/6a7f1b8f6c6eac7075f145bd/image2.png" alt="" /></p>
<p>Once you have this data in the same data store, you can perform powerful join queries that were previously impossible. For example, finding slow database queries that affected specific API endpoints:</p>
<pre><code>FROM logs-nginx.access-default 
| LOOKUP JOIN .ds-logs-mysql.slowlog-default-2025.05.01-000002 ON request_id 
| KEEP request_id, mysql.slowlog.query, url.query 
| WHERE mysql.slowlog.query IS NOT NULL
</code></pre>
<p>This query joins web server logs with database slow query logs, allowing you to directly correlate user-facing performance with database operations.</p>
<p>For GenAI interfaces, these complex queries can be generated automatically from natural language questions:</p>
<p>"Show me all checkout failures that coincided with slow database queries"</p>
<p>The AI translates this into appropriate queries across your unified data store, correlating application errors with database performance.</p>
<h2 id="realworldapplicationsandusecases">Real-world applications and use cases</h2>
<h3 id="naturallanguageinvestigation">Natural language investigation</h3>
<p>Imagine asking your observability system:</p>
<p>"Why did checkout latency spike at 09:17 yesterday?"</p>
<p>A GenAI-powered system with unified data could respond:</p>
<p>"Checkout latency increased by 230% at 09:17:32 following deployment v2.4.1 at 09:15. The root cause appears to be increased MySQL query times in the inventory-service. Specifically, queries to the 'product_availability' table are taking an average of 2300ms compared to the normal 95ms. This coincides with a CPU spike on database host db-03 and 24 'Lock wait timeout' errors in the inventory service logs."</p>
<p>Here's an example of Claude Desktop connected to <a href="https://github.com/elastic/mcp-server-elasticsearch">Elastic's MCP (Model Context Protocol) Server</a> which demonstrates how powerful natural language investigations can be. Here we ask Claude "analyze my web traffic patterns" and as you can see it has correctly identified that this is in our demo environment.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt5f22b476cc3e5cb4/6a7f1b9263e959e91b73e281/image3.png" alt="" /></p>
<h3 id="unknownproblemdetection">Unknown problem detection</h3>
<p>GenAI can identify subtle patterns by correlating signals that would be missed in siloed systems. For example, it might notice that a specific customer ID appears in error logs only when a particular network path is taken through your microservices—indicating a data corruption issue affecting only certain user flows.</p>
<h3 id="predictivemaintenance">Predictive maintenance</h3>
<p>By analyzing the unified historical patterns leading up to previous incidents, GenAI can identify emerging problems before they cause outages:</p>
<p>"Warning: Current load pattern on authentication-service combined with increasing error rates in user-profile-service matches 87% of the signature that preceded the April 3rd outage. Recommend scaling user-profile-service pods immediately."</p>
<h2 id="thefutureagenticaiforobservability">The future: agentic AI for observability</h2>
<p>The next frontier is agentic AI, systems that not only analyze but take action automatically.</p>
<p>These AI agents could:</p>
<ol>
<li>Continuously monitor all observability signals</li>
<li>Autonomously investigate anomalies</li>
<li>Implement fixes for known patterns</li>
<li>Learn from the effectiveness of previous interventions</li>
</ol>
<p>For example, an observability agent might:</p>
<ul>
<li>Detect increased error rates in a service</li>
<li>Analyze logs and traces to identify a memory leak</li>
<li>Correlate with recent code changes</li>
<li>Increase the memory limit temporarily</li>
<li>Create a detailed ticket with the root cause analysis</li>
<li>Monitor the fix effectiveness</li>
</ul>
<p>This is about creating systems that understand your application's behavior patterns deeply enough to maintain them proactively. See how this works in Elastic Observability, in the screenshot at the end of the RCA we are sending an email summary but this could trigger any action.  </p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltd7066e2bb8f06113/6a7f1b959090b0011b84ee37/image1.png" alt="" /></p>
<h2 id="businessoutcomes">Business outcomes</h2>
<p>Unifying observability data for GenAI analysis delivers concrete benefits:</p>
<ul>
<li>Faster resolution times: Problems that previously required hours of manual correlation can be diagnosed in seconds</li>
<li>Fewer escalations: Junior engineers can leverage AI to investigate complex issues before involving specialists</li>
<li>Improved system reliability: Earlier detection and resolution of emerging issues</li>
<li>Better developer experience: Less time spent context-switching between tools</li>
<li>Enhanced capacity planning: More accurate prediction of resource needs</li>
</ul>
<h2 id="implementationsteps">Implementation steps</h2>
<p>Ready to start your observability transformation? Here's a practical roadmap:</p>
<ol>
<li>Adopt OpenTelemetry: Standardize on OpenTelemetry for all telemetry data collection and use it to generate enriched logs.</li>
<li>Choose a unified storage solution: Select a platform that can efficiently store and query metrics, logs, traces and enriched logs together</li>
<li>Enrich your telemetry: Update application instrumentation to include relevant context</li>
<li>Create correlation IDs: Ensure every request has identifiers</li>
<li>Implement semantic conventions: Follow consistent naming patterns across your telemetry data</li>
<li>Start with focused use cases: Begin with high-value scenarios like checkout flows or critical APIs</li>
<li>Leverage GenAI tools: Integrate tools that can analyze your unified data and respond to natural language queries</li>
</ol>
<p>Remember, AI can only be as smart as the data you feed it. The quality and completeness of your telemetry data will determine the effectiveness of your AI-powered observability.</p>
<h2 id="generativeaianevolutionarycatalystforobservability">Generative AI: an evolutionary catalyst for observability</h2>
<p>The unification of observability data for GenAI analysis represents an evolutionary leap forward comparable to the transition from Internet 1.0 to 2.0. Early adopters will gain a significant competitive advantage through faster problem resolution, improved system reliability, and more efficient operations. GAI is a huge step for increasing observability maturity and moving your team to a more proactive stance.</p>
<p>Think of traditional observability as a doctor trying to diagnose a patient while only able to see their heart rate. Unified observability with GenAI is like giving that doctor a complete health picture, vital signs, lab results, medical history, and genetic data all accessible through natural conversation.</p>
<p>As SREs, we stand at the threshold of a new era in system observability. The asteroid of GenAI isn't a threat to be feared, it's an opportunity to evolve our practices and tools to build more reliable, understandable systems. The question isn't whether this transformation will happen, but who will lead it.</p>
<p>Will you?</p>]]></content:encoded>
    <link>https://www.elastic.co/observability-labs/blog/the-next-evolution-of-observability-unifying-data-with-opentelemetry-and-generative-ai</link>
    <guid isPermaLink="false">the-next-evolution-of-observability-unifying-data-with-opentelemetry-and-generative-ai</guid>
    <category><![CDATA[OpenTelemetry]]></category>
    <category><![CDATA[Agentic Observability]]></category>
    <category><![CDATA[Machine Learning]]></category>
    <dc:creator><![CDATA[David Hope]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltccc14cece0d58b74/6a7f1b99bdcff0587cc432c3/title.png" length="0" type="image/png"/>
    <pubDate>Wed, 11 Jun 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[2025 observability trends: Maturing beyond the hype]]></title>
    <description><![CDATA[Discover what 500+ decision-makers revealed about OpenTelemetry adoption, GenAI integration, and LLM monitoring—insights that separate innovators from followers in Elastic's 2025 observability survey.]]></description>
    <content:encoded><![CDATA[<p>Our latest survey of over 500 observability decision-makers reveals how dramatically the landscape has evolved as we move through 2025. What strikes me most is how observability has moved beyond its technical roots to become a true business imperative. Let’s dive into what we're seeing in the industry.</p>
<h2 id="theinvestmentparadoxofobservabilityin2025">The investment paradox of observability in 2025</h2>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltcd28ab8962baff2e/6a7f0a4dbd2198132e757fb9/image5.png" alt="" /></p>
<p>Here's something fascinating: 96% of executives in our survey expect observability to remain a key investment area. Yet almost all of them (97%) are hitting roadblocks in realizing full value. And surprisingly, the primary hurdles for observability are not technical or complicated in nature, can you guess what they might be?</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt01eaab94507227dc/6a7f0a50bdcff03a43c42d0b/image10.png" alt="" /></p>
<p>For 2025, IT leaders are challenged with financial hurdles for their observability. I'm seeing this tension play out constantly in conversations with leaders - they know they need to invest, but they're grappling with budget constraints, licensing costs, and proving ROI for their organizations. This creates an interesting dynamic where organizations must carefully balance increasing investment with rigorous cost optimization and business metrics.</p>
<p>What's particularly interesting is how this paradox is forcing organizations to become more strategic about their investments. Leaders are no longer just throwing money at the problem - they're thinking carefully about how to maximize value from every dollar spent.</p>
<h2 id="whyobservabilitymaturityismakingallthedifference">Why observability maturity is making all the difference</h2>
<p>The data really jumps out at me here. The gap between observability experts and newcomers tells a compelling story that I wasn't expecting to see. Expert organizations are significantly outperforming their peers across every key metric:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt05166add0381eda7/6a7f0a543ce8e231bacf52b5/image9.png" alt="" /></p>
<ul>
<li>91% of expert organizations are deploying applications and infrastructure faster (compared to just 34% of those in early stages)</li>
</ul>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt22f788515e7afd45/6a7f0a57ead8ecd41cbaa75d/image11.png" alt="" /></p>
<ul>
<li>82% are successfully reducing operational costs (versus 56% of early-stage organizations)</li>
</ul>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt35ca13bf87e472be/6a7f0a5aea068d31caf09d63/image4.png" alt="" /></p>
<ul>
<li>71% achieve better MTTR for incidents (while only 40% of early-stage organizations do)</li>
</ul>
<p>What I find particularly fascinating is how some benefits go beyond just maturity levels. About 80% of organizations report better customer issue response times regardless of their maturity stage. It tells me that even basic observability delivers immediate customer-facing value. This is crucial information for organizations just starting their observability journey - they can expect to see tangible benefits right from the start. But the overarching story may be that observability maturity leads teams from reactive to proactive and allows them to focus on higher level, value-add activities.</p>
<h2 id="costmanagementthenewimperative">Cost management: the new imperative</h2>
<p>The numbers around cost management paint a clear picture of where the industry is heading - 97% of IT decision-makers are actively managing observability costs, and 86% feel personally responsible for business outcomes.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt47754196fbd6a537/6a7f0a5d33fa8a2ff82025c2/image2.png" alt="" /></p>
<p>I'm seeing a clear trend where leaders are taking concrete steps in their day to day work:</p>
<ul>
<li>Consolidating their observability toolset while maintaining capabilities, they don’t want to lose anything</li>
<li>Implementing usage-based pricing models</li>
<li>Establishing clear ROI metrics</li>
<li>Creating cross-functional teams to optimize spending</li>
</ul>
<p>This isn't just about cutting costs - it's about being smarter with resources. Organizations are learning that more tools don't necessarily mean better observability.</p>
<h2 id="twotechnologiesreshapingtheobservabilitylandscape">Two technologies reshaping the observability landscape</h2>
<h3 id="aisgrowingimpact">AI's growing impact</h3>
<p>The enthusiasm for AI is remarkable - 94% of respondents see its tremendous potential. What fascinates me is how concerns about Generative AI reliability have actually decreased from 64% to 55% over the past year.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltf5138bff0bb780b6/6a7f0a5fe88c6544d100b58e/image7.png" alt="" /></p>
<p>Leaders are particularly excited about:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt92708af419f7bb2e/6a7f0a62bd21987622757fc9/image1.png" alt="" /></p>
<ul>
<li>Automated correlation of logs, metrics, and traces (72% of respondents)</li>
<li>Predictive analytics for preventing outages</li>
<li>Natural language interfaces for querying observability data</li>
<li>Automated root cause analysis</li>
</ul>
<p>The key shift I'm seeing for the upcoming year is the move from AI as a buzzword to AI as a practical tool delivering real value in observability workflows.  </p>
<p>Generative AI capabilities paired with retrieval augmented generation (RAG) capabilities allow organizations to leverage the power of LLMs and private data (e.g., runbooks, alerts, business data) to deliver relevant and meaningful results and identify and solve problems faster while reducing noise.</p>
<h3 id="opentelemetryscontinuedmomentum">OpenTelemetry's continued momentum</h3>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltcb6abc9011647535/6a7f0a64e3a219169e99f37e/image3.png" alt="" /></p>
<p>Looking at expert organizations, 80% are either experimenting with or have deployed OpenTelemetry. This isn't just about technology adoption - it's about building for the future with open standards. The correlation between OpenTelemetry adoption and overall observability maturity is correlated and unmistakable.</p>
<p>What's particularly interesting is how OpenTelemetry is changing the vendor landscape. Organizations are increasingly demanding OpenTelemetry support from their vendors, seeing it as a way to future-proof their observability investments and avoid vendor lock-in. Thinking back to how Linux shifted the server landscape, can we expect to see the same in the observability domain?</p>
<h2 id="businessintegrationandinsightsdeepens">Business integration and insights deepens</h2>
<hr />
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt91f467debde60ec5/6a7f0a67c2cc0922c0249464/image8.png" alt="" /></p>
<p>Here's what I find most compelling: 64% of expert organizations are frequently correlating operational data with business outcomes, while only 9% of early-stage organizations do the same. This represents a fundamental shift from technical monitoring to business observability.</p>
<p>This isn't just about uptime anymore - organizations are increasingly using observability data to:</p>
<ul>
<li>Make informed business decisions</li>
<li>Improve customer experience</li>
<li>Optimize resource allocation</li>
<li>Drive innovation</li>
</ul>
<h2 id="lookingahead">Looking ahead</h2>
<p>As we continue through 2025, I'm seeing observability mature beyond its initial promise. Organizations are focusing less on basic implementation and more on delivering real business value through:</p>
<ul>
<li>Deeper business integration, like mapping system performance directly to revenue metrics</li>
<li>Optimized cost management through new data lake technology, efficient storage and intelligent retention</li>
<li>AI-enhanced capabilities powered by LLMs and Agentic AI</li>
<li>Standardized instrumentation through OpenTelemetry, reducing vendor lock-in</li>
</ul>
<p>The path to success in 2025 isn't just about having the right tools - it's about building mature practices that deliver measurable business value while managing costs effectively. The organizations that can balance these competing demands while maintaining focus on business outcomes are the ones pulling ahead.</p>
<p>What are you seeing in your organization's observability journey? Are these trends aligning with your experience? </p>
<p>If you would like to dig in deeper on emerging observability trends, download <a href="https://www.elastic.co/resources/observability/report/landscape-observability-report">our full report</a> or watch the on-demand webinar, <a href="https://www.elastic.co/virtual-events/observability-trends-2025">2025 Observability trends: Maturing beyond the hype and delivering results</a>!</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/emerging-trends-in-observability-2025</link>
    <guid isPermaLink="false">emerging-trends-in-observability-2025</guid>
    <category><![CDATA[OpenTelemetry]]></category>
    <category><![CDATA[LLM Observability]]></category>
    <dc:creator><![CDATA[David Hope]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blta5afa8cac1d6450e/6a7f0a6b77b03421db3ff3c7/trends.png" length="0" type="image/png"/>
    <pubDate>Thu, 27 Feb 2025 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[Adding free and open Elastic APM as part of your Elastic Observability deployment]]></title>
    <description><![CDATA[Learn how to gather application trace data and store it alongside the logs and metrics from your applications and infrastructure with Elastic Observability and Elastic APM.]]></description>
    <content:encoded><![CDATA[<p>In a recent post, we showed you <a href="https://www.elastic.co/blog/getting-started-with-free-and-open-elastic-observability">how to get started with the free and open tier of Elastic Observability</a>. Below, we'll walk through what you need to do to expand your deployment so you can start gathering metrics from application performance monitoring (APM) or "tracing" data in your observability cluster, for free.</p>
<h2 id="whatisapm">What is APM?</h2>
<p>Application performance monitoring lets you see where your applications spend their time, what they are doing, what other applications or services they are calling, and what errors or exceptions they are encountering.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt75d1516cb178adb6/6a85c74c501a8561c6fbb28a/screenshot-serverless-distributed-trace.png" alt="" /></p>
<p>In addition, APM also lets you see history and trends for key performance indicators, such as latency and throughput, as well as transaction and dependency information:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt886c0c626ef5bb89/6a85c74f80984c6d8e668f32/ruby-overview.png" alt="" /></p>
<p>Whether you're setting up alerts for SLA breaches, trying to gauge the impact of your latest release, or deciding where to make the next improvement, APM can help with your root-cause analysis to help improve your users' experience and drive your mean time to resolution (MTTR) toward zero.</p>
<h2 id="logicalarchitecture">Logical architecture</h2>
<p>Elastic APM relies on the APM Integration inside Elastic Agent, which forwards application trace and metric data from applications instrumented with APM agents to an Elastic Observability cluster. Elastic APM supports multiple agent flavors:</p>
<ul>
<li>Native Elastic APM Agents, available for <a href="https://www.elastic.co/guide/en/apm/agent/index.html">multiple languages</a>, including Java, .NET, Go, Ruby, Python, Node.js, PHP, and client-side JavaScript</li>
<li>Code instrumented with <a href="https://www.elastic.co/guide/en/apm/get-started/current/open-telemetry-elastic.html">OpenTelemetry</a></li>
<li>Code instrumented with <a href="https://www.elastic.co/guide/en/apm/get-started/current/opentracing.html">OpenTracing</a></li>
<li>Code instrumented with <a href="https://www.elastic.co/guide/en/apm/server/current/jaeger.html">Jaeger</a></li>
</ul>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt0218d3e17c29a3b2/6a85c7515c27902af0f59a71/blog-elastic-observability-instrumented-services.png" alt="" /></p>
<p>In this blog, we'll provide a quick example of how to instrument code with the native Elastic APM Python agent, but the overall steps are similar for other languages.</p>
<p>Please note that there is a strong distinction between the <strong>Elastic APM Agent</strong> and the <strong>Elastic Agent</strong>. These are very different components, as you can see in the diagram above, so it's important not to confuse them.</p>
<h2 id="installtheelasticagent">Install the Elastic Agent</h2>
<p>The first step is to install the Elastic Agent. You either need Fleet <a href="https://www.elastic.co/guide/en/fleet/current/add-a-fleet-server.html">installed first</a>, or you can install the Elastic Agent standalone. Install the Elastic Agent somewhere by <a href="https://www.elastic.co/guide/en/fleet/master/elastic-agent-installation.html">following this guide</a>. This will give you an APM Integration endpoint you can hit. Note that this step is not necessary in Elastic Cloud, as we host the APM Integration for you. Check Elastic Agent is up by running:</p>
<pre><code>curl &lt;ELASTIC_AGENT_HOSTNAME&gt;:8200
</code></pre>
<h2 id="instrumentingsamplecodewithanelasticapmagent">Instrumenting sample code with an Elastic APM agent</h2>
<p>The instructions for the various language agents differ based on the programming language, but at a high level they have a similar flow. First, you add the dependency for the agent in the language's native spec, then you configure the agent to let it know how to find the APM Integration.</p>
<p>You can try out any flavor you'd like, but I am going to walk through the Python instructions using this Python example that <a href="https://github.com/davidgeorgehope/PythonElasticAPMExample">I created</a>.</p>
<h3 id="getthesamplecodeoruseyourown">Get the sample code (or use your own)</h3>
<p>To get started, I clone the GitHub repository then change to the directory:</p>
<pre><code>git clone https://github.com/davidgeorgehope/PythonElasticAPMExample
cd PythonElasticAPMExample
</code></pre>
<h3 id="howtoaddthedependency">How to add the dependency</h3>
<p>Adding the Elastic APM Dependency is simple — check the app.py file from <a href="https://github.com/davidgeorgehope/PythonElasticAPMExample/blob/main/app.py">the github repo</a> and you will notice the following lines of code.</p>
<pre><code>import elasticapm
from elasticapm import Client

app = Flask(__name__)
app.config["ELASTIC_APM"] = {    "SERVICE_NAME": os.environ.get("APM_SERVICE_NAME", "flask-app"),    "SECRET_TOKEN": os.environ.get("APM_SECRET_TOKEN", ""),    "SERVER_URL": os.environ.get("APM_SERVER_URL", "http://localhost:8200"),}
elasticapm.instrumentation.control.instrument()
client = Client(app.config["ELASTIC_APM"])
</code></pre>
<p>The Python library for Flask is capable of auto detecting transactions, but you can also start transactions in code as per the following, as we have done in this example:</p>
<pre><code>@app.route("/")
def hello():
    client.begin_transaction('demo-transaction')
    client.end_transaction('demo-transaction', 'success')
</code></pre>
<h3 id="configuretheagent">Configure the agent</h3>
<p>The agents need to send application trace data to the APM Integration, and to do this it has to be reachable. I configured the Elastic Agent to listen on my local host's IP, so anything in my subnet can send data to it. As you can see from the code below, we use docker-compose.yml to pass in the config via environment variables. Please edit these variables for your own Elastic installation.</p>
<pre><code># docker-compose.yml
version: "3.9"
services:
  flask_app:
    build: .
    ports:
      - "5001:5001"
    environment:
      - PORT=5001
      - APM_SERVICE_NAME=flask-app
      - APM_SECRET_TOKEN=your_secret_token
      - APM_SERVER_URL=http://host.docker.internal:8200
</code></pre>
<p>Some commentary on the above:</p>
<ul>
<li><strong>service_name:</strong> If you leave this out it will just default to the application's name, but you can override that here.</li>
<li><strong>secret_token:</strong> <a href="https://www.elastic.co/guide/en/apm/server/current/secret-token.html">Secret tokens</a> allow you to authorize requests to the APM Server, but they require that the APM Server is set up with SSL/TLS and that a secret token has been set up. We're not using HTTPS between the agents and the APM Server, so we'll comment this one out.</li>
<li><strong>server_url:</strong> This is how the agent can reach the APM Integration inside Elastic Agent. Replace this with the name or IP of your host running Elastic Agent.</li>
</ul>
<p>Now that the Elastic APM side of the configuration is done, we simply follow the steps from the <a href="https://github.com/davidgeorgehope/PythonElasticAPMExample/blob/main/README.md">README</a> to start up.</p>
<pre><code>docker-compose up --build -d
</code></pre>
<p>The build step will take several minutes.</p>
<p>You can navigate to the running sample application by visiting http://localhost:5001. There's not a lot to the sample, but it does generate some APM data. To generate a bit of a load, you can reload them a few times or run a quick little script:</p>
<pre><code>#!/bin/bash
# load_test.sh
url="http://localhost:5001"
for i in {1..1000}
do
  curl -s -o /dev/null $url
  sleep 1
done
</code></pre>
<p>This will just reload the pages every second.</p>
<p>Back in Kibana, navigate back to the APM app (hamburger icon, then select <strong>APM</strong> ) and you should see our new flask-app service (I let mine run so it shows a bit more history):</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltc0b22219b36a9336/6a85c7549d2b718e27f938c4/blog-elastic-observability-services.png" alt="" /></p>
<p>The Service Overview page provides an at-a-glance summary of the health of a service in one place. If you're a developer or an SRE, this is the page that will help you answer questions like:</p>
<ul>
<li>How did a new deployment impact performance?</li>
<li>What are the top impacted transactions?</li>
<li>How does performance correlate with underlying infrastructure?</li>
</ul>
<p>This view provides a list of all of the applications that have sent application trace data to Elastic APM in the specified period of time (in this case, the last 15 minutes). There are also sparklines showing mini graphs of latency, throughput, and error rate. Clicking on <strong>flask-app</strong> takes us to the <strong>service overview</strong> page, which shows the various transactions within the service (recall that my script is hitting the / endpoint, as seen in the <strong>Transactions</strong> section). We get bigger graphs for <strong>Latency</strong> , <strong>Throughput</strong> , <strong>Errors</strong> , and <strong>Error Rates</strong>.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt4d432bbc41e408db/6a85c75768266682891eab66/blog-elastic-observability-flask-app.png" alt="" /></p>
<p>When you're instrumenting real applications, under real load, you'll see a lot more connectivity (and errors!)</p>
<p>Clicking on a transaction in the transaction view, in this case, our sample app's demo-transaction transaction, we can see exactly what operations were called:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltb4f05eae471a702c/6a85c75a342d69fd7c21b03f/blog-elastic-observability-flask-app-demo-transaction.png" alt="" /></p>
<p>This includes detailed information about calls to external services, such as database queries:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt897018765d4cb3af/6a85c75d342d69678e21b043/blog-elastic-observability-span-details.png" alt="" /></p>
<h2 id="whatsnext">What's next?</h2>
<p>Now that you've got your Elastic Observability cluster up and running and collecting out-of-the-box application trace data, explore the public APIs for the languages that your applications are using, which allow you to take your APM data to the next level. The APIs allow you to add custom metadata, define business transactions, create custom spans, and more. You can find the public API specs for the various APM agents (such as <a href="https://www.elastic.co/guide/en/apm/agent/java/current/public-api.html">Java</a>, <a href="https://www.elastic.co/guide/en/apm/agent/ruby/current/api.html">Ruby</a>, <a href="https://www.elastic.co/guide/en/apm/agent/python/current/index.html">Python</a>, and more) on the APM agent <a href="https://www.elastic.co/guide/en/apm/agent/index.html">documentation pages</a>.</p>
<p>If you'd like to learn more about Elastic APM, check out <a href="https://www.elastic.co/webinars/introduction-to-elastic-apm-in-the-shift-to-cloud-native">our webinar on Elastic APM in the shift to cloud native</a> to see other ways that Elastic APM can help you in your ecosystem.</p>
<p>If you decide that you'd rather have us host your observability cluster, you can sign up for a free trial of the <a href="https://www.elastic.co/cloud/">Elasticsearch Service on Elastic Cloud</a> and change your agents to point to your new cluster.</p>
<p><em>Originally published May 5, 2021; updated April 6, 2023.</em></p>]]></content:encoded>
    <link>https://www.elastic.co/observability-labs/blog/free-open-elastic-apm-observability-deployment</link>
    <guid isPermaLink="false">free-open-elastic-apm-observability-deployment</guid>
    <category><![CDATA[APM]]></category>
    <category><![CDATA[OpenTelemetry]]></category>
    <category><![CDATA[Metrics]]></category>
    <dc:creator><![CDATA[David Hope]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltb8a4183daa602b2a/6a85c760bc5bb342fdf81a2d/blog-thumb-release-apm.png" length="0" type="image/png"/>
    <pubDate>Wed, 28 Feb 2024 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 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[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[Understanding APM: How to add extensions to the OpenTelemetry Java Agent]]></title>
    <description><![CDATA[This blog post provides a comprehensive guide for Site Reliability Engineers (SREs) and IT Operations to gain visibility and traceability into applications, especially those written with non-standard frameworks or without access to the source code.]]></description>
    <content:encoded><![CDATA[<h2 id="withoutcodeaccesssresanditoperationscannotalwaysgetthevisibilitytheyneed">Without code access, SREs and IT Operations cannot always get the visibility they need</h2>
<p>As an SRE, have you ever had a situation where you were working on an application that was written with non-standard frameworks, or you wanted to get some interesting business data from an application (number of orders processed for example) but you didn’t have access to the source code?</p>
<p>We all know this can be a challenging scenario resulting in visibility gaps, inability to fully trace code end to end, and missing critical business monitoring data that is useful for understanding the true impact of issues.</p>
<p>How can we solve this? One way we discussed in the following three blogs:</p>
<ul>
<li><a href="https://www.elastic.co/blog/create-your-own-instrumentation-with-the-java-agent-plugin">Create your own instrumentation with the Java Agent Plugin</a></li>
<li><a href="https://www.elastic.co/blog/custom-metrics-app-code-java-agent-plugin">How to capture custom metrics without app code changes using the Java Agent Plugin</a></li>
<li><a href="https://www.elastic.co/blog/regression-testing-your-java-agent-plugin">Regression testing your Java Agent Plugin</a></li>
</ul>
<p>This is where we develop a plugin for the Elastic<sup>®</sup> APM Agent to help get access to critical business data for monitoring and add tracing where none exists.</p>
<p>What we will discuss in this blog is how you can do the same with the <a href="https://opentelemetry.io/docs/instrumentation/java/automatic/">OpenTelemetry Java Agent</a> using the Extensions framework.</p>
<h2 id="basicconceptshowapmworks">Basic concepts: How APM works</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/blt374de29a3dcd79d9/6a85cb1af61d6e245b9c2b1b/elastic-blog-1-flowchart-process.png" alt="flowchart process" /></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="applicationprerequisitesandconfig">Application, prerequisites, and config</h2>
<p>There is a really simple application in <a href="https://github.com/davidgeorgehope/custom-instrumentation-examples">this GitHub repository</a> that is used throughout this blog. What it does is it simply asks you to input some text and then it counts the number of words.</p>
<p>It’s also listed below:</p>
<pre><code>package org.davidgeorgehope;
import java.util.Scanner;
import java.util.logging.Logger;

public class Main {
    private static Logger logger = Logger.getLogger(Main.class.getName());

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        while (true) {
            System.out.println("Please enter your sentence:");
            String input = scanner.nextLine();
            Main main = new Main();
            int wordCount = main.countWords(input);
            System.out.println("The input contains " + wordCount + " word(s).");
        }
    }
    public int countWords(String input) {

        try {
            Thread.sleep(10000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }

        if (input == null || input.isEmpty()) {
            return 0;
        }

        String[] words = input.split("\s+");
        return words.length;
    }
}
</code></pre>
<p>For the purposes of this blog, we will be using Elastic Cloud to capture the data generated by OpenTelemetry — <a href="https://www.elastic.co/getting-started/observability/collect-and-analyze-logs#create-an-elastic-cloud-account">follow the instructions here</a> to <a href="https://cloud.elastic.co/registration?fromURI=/home">get started on Elastic Cloud</a>.</p>
<p>Once you are started with Elastic Cloud, go grab the OpenTelemetry config from the APM pages:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt618b544db96971cd/6a85cb1d9a32f145f8a7dfec/elastic-blog-2-apm-agents.png" alt="apm agents" /></p>
<p>You will need this later.</p>
<p>Finally, <a href="https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases">download the OpenTelemetry Agent</a>.</p>
<h2 id="firinguptheapplicationandopentelemetry">Firing up the application and OpenTelemetry</h2>
<p>If you start out with this simple application, build it and run it like so with the OpenTelemetry Agent, filling in the appropriate variables with those you got from earlier.</p>
<pre><code>java -javaagent:opentelemetry-javaagent.jar -Dotel.exporter.otlp.endpoint=XX -Dotel.exporter.otlp.headers=XX -Dotel.metrics.exporter=otlp -Dotel.logs.exporter=otlp -Dotel.resource.attributes=XX -Dotel.service.name=your-service-name -jar simple-java-1.0-SNAPSHOT.jar
</code></pre>
<p>You will find nothing happens. The reason for this is that the OpenTelemetry Agent has no way of knowing what to monitor. The way that APM with automatic instrumentation works is that it “knows” about standard frameworks, like Spring or HTTPClient, and is able to get visibility by “injecting” trace code into those standard frameworks automatically.</p>
<p>It has no knowledge of org.davidgeorgehope.Main from our simple Java application.</p>
<p>Luckily, there is a way we can add this using the <a href="https://opentelemetry.io/docs/instrumentation/java/automatic/extensions/">OpenTelemetry Extensions framework</a>.</p>
<h2 id="theopentelemetryextension">The OpenTelemetry Extension</h2>
<p>In the repository above, aside from the simple-java application, there is also a plugin for Elastic APM and an extension for OpenTelemetry. The relevant files for OpenTelemetry Extension are located <a href="https://github.com/davidgeorgehope/custom-instrumentation-examples/tree/main/opentelemetry-custom-instrumentation/src/main/java/org/davidgeorgehope">here</a> — WordCountInstrumentation.java and WordCountInstrumentationModule.java .</p>
<p>You’ll notice that OpenTelemetry Extensions and Elastic APM Plugins both make use of Byte Buddy, which is a common library for code instrumentation. There are some key differences in the way the code is bootstrapped, though.</p>
<p>The WordCountInstrumentationModule class extends an OpenTelemtry specific class InstrumentationModule, whose purpose is to describe a set of TypeInstrumentation that need to be applied together to correctly instrument a specific library. The WordCountInstrumentation class is one such instance of a TypeInstrumentation.</p>
<p>Type instrumentations grouped in a module share helper classes, muzzle runtime checks, and applicable class loader criteria, and can only be enabled or disabled as a set.</p>
<p>This is a little bit different from how the Elastic APM Plugin works because the default method to to inject code with OpenTelemetry is inline (which is the default) with OpenTelemetry, and you can inject dependencies into the core application classloader using the InstrumentationModule configurations (as shown below). The Elastic APM method is safer as it allows isolation of helper classes and makes it easier to debug with normal IDEs we are contributing this method to OpenTelemetry. Here we inject the TypeInstrumentation class and the WordCountInstrumentation class into the classloader.</p>
<pre><code>@Override
    public List&lt;String&gt; getAdditionalHelperClassNames() {
        return List.of(WordCountInstrumentation.class.getName(),"io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation");
    }
</code></pre>
<p>The other interesting part of the TypeInstrumentation class is the setup.</p>
<p>Here we give our instrumentation “group” a name. An InstrumentationModule needs to have at least one name. The user of the javaagent can suppress a chosen instrumentation by referring to it by one of its names. The instrumentation module names use kebab-case.</p>
<pre><code>public WordCountInstrumentationModule() {
        super("wordcount-demo", "wordcount");
    }
</code></pre>
<p>Apart from this, we see methods in this class to specify the order of loading this relative to other instrumentation if needed, and we specify the class that extends TypeInstrumention and are responsible for the main bulk of the instrumentation work.</p>
<p>Let's take a look at that WordCountInstrumention class, which extends TypeInstrumention now:</p>
<pre><code>// The WordCountInstrumentation class implements the TypeInstrumentation interface.
// This allows us to specify which types of classes (based on some matching criteria) will have their methods instrumented.

public class WordCountInstrumentation implements TypeInstrumentation {

    // The typeMatcher method is used to define which classes the instrumentation should apply to.
    // In this case, it's the "org.davidgeorgehope.Main" class.
    @Override
    public ElementMatcher&lt;TypeDescription&gt; typeMatcher() {
        logger.info("TEST typeMatcher");
        return ElementMatchers.named("org.davidgeorgehope.Main");
    }

    // In the transform method, we specify which methods of the classes matched above will be instrumented,
    // and also the advice (a piece of code) that will be added to these methods.
    @Override
    public void transform(TypeTransformer typeTransformer) {
        logger.info("TEST transform");
        typeTransformer.applyAdviceToMethod(namedOneOf("countWords"),this.getClass().getName() + "$WordCountAdvice");
    }

    // The WordCountAdvice class contains the actual pieces of code (advices) that will be added to the instrumented methods.
    @SuppressWarnings("unused")
    public static class WordCountAdvice {
        // This advice is added at the beginning of the instrumented method (OnMethodEnter).
        // It creates and starts a new span, and makes it active.
        @Advice.OnMethodEnter(suppress = Throwable.class)
        public static Scope onEnter(@Advice.Argument(value = 0) String input, @Advice.Local("otelSpan") Span span) {
            // Get a Tracer instance from OpenTelemetry.
            Tracer tracer = GlobalOpenTelemetry.getTracer("instrumentation-library-name","semver:1.0.0");
            System.out.print("Entering method");

            // Start a new span with the name "mySpan".
            span = tracer.spanBuilder("mySpan").startSpan();

            // Make this new span the current active span.
            Scope scope = span.makeCurrent();

            // Return the Scope instance. This will be used in the exit advice to end the span's scope.
            return scope;
        }

        // This advice is added at the end of the instrumented method (OnMethodExit).
        // It first closes the span's scope, then checks if any exception was thrown during the method's execution.
        // If an exception was thrown, it sets the span's status to ERROR and ends the span.
        // If no exception was thrown, it sets a custom attribute "wordCount" on the span, and ends the span.
        @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
        public static void onExit(@Advice.Return(readOnly = false) int wordCount,
                                  @Advice.Thrown Throwable throwable,
                                  @Advice.Local("otelSpan") Span span,
                                  @Advice.Enter Scope scope) {
            // Close the scope to end it.
            scope.close();

            // If an exception was thrown during the method's execution, set the span's status to ERROR.
            if (throwable != null) {
                span.setStatus(StatusCode.ERROR, "Exception thrown in method");
            } else {
                // If no exception was thrown, set a custom attribute "wordCount" on the span.
                span.setAttribute("wordCount", wordCount);
            }

            // End the span. This makes it ready to be exported to the configured exporter (e.g. Elastic).
            span.end();
        }
    }
}
</code></pre>
<p>The target class for our instrumentation is defined in the typeMatch method, and the method we want to instrument is defined in the transform method. We are targeting the Main class and the countWords method.</p>
<p>As you can see, we have an inner class here that does most of the work of defining an onEnter and onExit method, which tells us what to do when we enter the countWords method and when we exit the countWords method.</p>
<p>In the onEnter method, we set up a new OpenTelemetry span, and in the onExit method, we end the span. If the method successfully ends, we also grab the wordcount and append that to the attribute.</p>
<p>Now let's take a look at what happens when we run this. The good news is that we have made this extremely simple by providing a dockerfile for your use to do all the work for you.</p>
<h2 id="pullingthisalltogether">Pulling this all together</h2>
<p><a href="https://github.com/davidgeorgehope/custom-instrumentation-examples/tree/main">Clone the GitHub repository</a> if you have not already done so, and before continuing, let’s take a quick look at the dockerfile we are using.</p>
<pre><code># Build stage
FROM maven:3.8.7-openjdk-18 as build

COPY simple-java /home/app/simple-java
COPY opentelemetry-custom-instrumentation /home/app/opentelemetry-custom-instrumentation

WORKDIR /home/app/simple-java
RUN mvn install

WORKDIR /home/app/opentelemetry-custom-instrumentation
RUN mvn install

# Package stage
FROM maven:3.8.7-openjdk-18
COPY --from=build /home/app/simple-java/target/simple-java-1.0-SNAPSHOT.jar /usr/local/lib/simple-java-1.0-SNAPSHOT.jar
COPY --from=build /home/app/opentelemetry-custom-instrumentation/target/opentelemetry-custom-instrumentation-1.0-SNAPSHOT.jar /usr/local/lib/opentelemetry-custom-instrumentation-1.0-SNAPSHOT.jar

WORKDIR /

RUN curl -L -o opentelemetry-javaagent.jar https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/latest/download/opentelemetry-javaagent.jar

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

ENTRYPOINT ["/start.sh"]
</code></pre>
<p>This dockerfile works in two parts: during the docker build process, we build the simple-java application from source followed by the custom instrumentation. After this, we download the latest OpenTelemetry Java Agent. During runtime, we simple execute the start.sh file described below:</p>
<pre><code>#!/bin/sh
java \
-javaagent:/opentelemetry-javaagent.jar \
-Dotel.exporter.otlp.endpoint=${SERVER_URL} \
-Dotel.exporter.otlp.headers="Authorization=Bearer ${SECRET_KEY}" \
-Dotel.metrics.exporter=otlp \
-Dotel.logs.exporter=otlp \
-Dotel.resource.attributes=service.name=simple-java,service.version=1.0,deployment.environment=production \
-Dotel.service.name=your-service-name \
-Dotel.javaagent.extensions=/usr/local/lib/opentelemetry-custom-instrumentation-1.0-SNAPSHOT.jar \
-Dotel.javaagent.debug=true \
-jar /usr/local/lib/simple-java-1.0-SNAPSHOT.jar
</code></pre>
<p>There are two important things to note with this script: the first is that we start the javaagent parameter set to the opentelemetry-javaagent.jar — this will start the OpenTelemetry javaagent running, which starts before any code is executed.</p>
<p>Inside this jar there has to be a class with a premain method which the JVM will look for. This bootstraps the java agent. As described above, any bytecode that is compiled is essentially filtered through the javaagent code so it can modify the class before being executed.</p>
<p>The second important thing here is the configuration of the javaagent.extensions, which loads our extension that we built to add instrumentation for our simple-java application.</p>
<p>Now run the following commands:</p>
<pre><code>docker build -t djhope99/custom-otel-instrumentation:1 .
docker run -it -e 'SERVER_URL=XXX' -e 'SECRET_KEY=XX djhope99/custom-otel-instrumentation:1
</code></pre>
<p>If you use the SERVER_URL and SECRET_KEY you got earlier in here, you should see this connect to Elastic.</p>
<p>When it starts up, it will ask you to enter a sentence, enter a few sentences, and press enter. Do this a few times — there is a sleep in here to force a long running transaction:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt155731d7462ebeb8/6a85cb20eaf245a1ada49f61/elastic-blog-3-codeblack.png" alt="code" /></p>
<p>Eventually you will see the service show up in the service map:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt209a74d642d0278a/6a85cb239bf994bab90a056f/elastic-blog-4-services.png" alt="services" /></p>
<p>Traces will appear:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt912f14b090cdde8c/6a85cb26f61d6e7e8d9c2b21/elastic-blog-5-your-service-name.png" alt="service name" /></p>
<p>And in the span you will see the wordcount attribute we collected:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt7c3de41db6310bb9/6a85cb299d2b716bd4f93984/elastic-blog-6-transaction-details.png" alt="transaction details" /></p>
<p>This can be used for further dashboarding and AI/ML, including anomaly detection if you need, which is easy to do, as you can see below.</p>
<p>First click on the burger on the left side and select <strong>Dashboard</strong> to create a new dashboard:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blteeec7457390a809c/6a85cb2b2d64d53d02081d40/elastic-blog-7-manage-deployment-analytics.png" alt="analytics" /></p>
<p>From here, click <strong>Create Visualization</strong>.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltbabf439d0dac824b/6a85cb2e9bf994451b0a0573/elastic-blog-8-visualization.png" alt="visualization" /></p>
<p>Search for the wordcount label in the APM index as shown below:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltb7703450e4ce97d8/6a85cb3111893c4072a7ab96/elastic-blog-9-dashboard-word.png" alt="dashboard" /></p>
<p>As you can see, because we created this attribute in the Span code as below with wordCount as a type “Integer,” we were able to automatically assign it as a numeric field in Elastic:</p>
<pre><code>span.setAttribute("wordCount", wordCount);
</code></pre>
<p>From here we can drag and drop it into the visualization for display on our Dashboard! Super easy.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt7da2d84e050356a3/6a85cb34501a85781efbb327/elastic-blog-10-drag-drop.png" alt="dra and drop" /></p>
<h2 id="inconclusion">In conclusion</h2>
<p>This blog elucidates the invaluable role of OpenTelemetry Java Agent in filling the visibility gaps and obtaining crucial business monitoring data, especially when access to the source code is not feasible.</p>
<p>The blog unraveled the basic understanding of Java Agent, Bytecode, and Byte Buddy, followed by a comprehensive examination of the automatic instrumentation process with Byte Buddy.</p>
<p>The implementation of the OpenTelemetry Java Agent, using the Extensions framework, was demonstrated with the aid of a simple Java application, which underscored the agent's ability to inject trace code into the application to facilitate monitoring.</p>
<p>It detailed how to configure the agent and integrate OpenTelemetry Extension, and it outlined the operation of a sample application to help users comprehend the practical application of the information discussed. This instructive blog post is an excellent resource for SREs and IT Operations seeking to optimize their work with applications using OpenTelemetry's automatic instrumentation feature.</p>
<blockquote>
  <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/monitor-openai-api-gpt-models-opentelemetry-elastic">Monitor OpenAI API and GPT models with OpenTelemetry and 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>
  </ul>
</blockquote>
<p>Don’t have an Elastic Cloud account yet? Sign up <a href="https://cloud.elastic.co/registration">for Elastic Cloud</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/extensions-opentelemetry-java-agent</link>
    <guid isPermaLink="false">extensions-opentelemetry-java-agent</guid>
    <category><![CDATA[OpenTelemetry]]></category>
    <category><![CDATA[APM]]></category>
    <dc:creator><![CDATA[David Hope]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt1c9c06c4bc5e5cbd/6a85cb37bc5bb3bb24f81b01/flexible-implementation-1680X980.png" length="0" type="image/png"/>
    <pubDate>Mon, 24 Jul 2023 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Gaining new perspectives beyond logging: An introduction to application performance monitoring]]></title>
    <description><![CDATA[Change is on the horizon for the world of logging. In this post, we’ll outline a recommended journey for moving from just logging to a fully integrated solution with logs, traces, and APM.]]></description>
    <content:encoded><![CDATA[<h2 id="prioritizecustomerexperiencewithapmandtracing">Prioritize customer experience with APM and tracing</h2>
<p>Enterprise software development and operations has become an interesting space. We have some incredibly powerful tools at our disposal, yet as an industry, we have failed to adopt many of these tools that can make our lives easier. One such tool that is currently underutilized is <a href="https://www.elastic.co/blog/adding-free-and-open-elastic-apm-as-part-of-your-elastic-observability-deployment">application performance monitoring</a> (APM) and tracing, despite the fact that OpenTelemetry has made it possible to adopt at low friction.</p>
<p>Logging, however, is ubiquitous. Every software application has logs of some kind, and the default workflow for troubleshooting (even today) is to go from exceptions experienced by customers and systems to the logs and start from there to find a solution.</p>
<p>There are various challenges with this, one of the main ones being that logs often do not give enough information to solve the problem. Many services today return ambiguous 500 errors with little or nothing to go on. What if there isn’t an error or log file at all or the problem is that the system is very slow? Logging alone cannot help solve these problems. This leaves users with half broken systems and poor user experiences. We’ve all been on the wrong side of this, and it can be incredibly frustrating.</p>
<p>The question I find myself asking is why does the customer experience often come second to errors? If the customer experience is a top priority, then a strategy should be in place to adopt tracing and APM and make this as important as logging. Users should stop going to logs by default and thinking primarily in logs, as many are doing today. This will also come with some required changes to mental models.</p>
<p>What’s the path to get there? That’s exactly what we will explore in this blog post. We will start by talking about supporting organizational changes, and then we’ll outline a recommended journey for moving from just logging to a fully integrated solution with logs, traces, and APM.</p>
<h2 id="cultivatinganewmonitoringmindsethowtodriveapmandtracingadoption">Cultivating a new monitoring mindset: How to drive APM and tracing adoption</h2>
<p>To get teams to shift their troubleshooting mindset, what organizational changes need to be made?</p>
<p>Initially, businesses should consider strategic priorities and goals that need to be shared broadly among the teams. One thing that can help drive this in a very large organization is to consider an entire product team devoted to Observability or a CoE (Center of Excellence) with its own roadmap and priorities.</p>
<p>This team (either virtual or permanent) should start with the customer in mind and work backward, starting with key questions like: What do I need to collect? What do I need to observe? How do I act? Once team members understand the answers to these questions, they can start to think about the technology decisions needed to drive those outcomes.</p>
<p>From a tracing and APM perspective, the areas of greatest concern are the customer experience, service level objectives, and service level outcomes. From here, organizations can start to implement programs of work to continuously improve and share knowledge across teams. This will help to align teams around a common framework with shared goals.</p>
<p>In the next few sections, we will go through a four step journey to help you maximize your success with APM and tracing. This journey will take you through the following key steps on your journey to successful APM adoption:</p>
<ol>
<li><strong>Ingest:</strong> What choices do you have to make to get tracing activated and start ingesting trace data into your observability tools?</li>
<li><strong>Integrate:</strong> How does tracing integrate with logs to enable full end-to-end observability, and what else beyond simple tracing can you utilize to get even better resolution on your data?</li>
<li><strong>Analytics and AIOPs:</strong> Improve the customer experience and reduce the noise through machine learning.</li>
<li><strong>Scale and total cost of ownership:</strong> Roll out enterprise-wide tracing and adopt strategies to deal with data volume.</li>
</ol>
<h2 id="1ingest">1. Ingest</h2>
<p>Ingesting data for APM purposes generally involves “instrumenting” the application. In this section, we will explore methods for instrumenting applications, talk a little bit about sampling, and finally wrap up with a note on using common schemas for data representation.</p>
<h3 id="gettingstartedwithinstrumentation">Getting started with instrumentation</h3>
<p>What options do we have for ingesting APM and trace data? There are many, many options we will discuss to help guide you, but first let's take a step back. APM has a deep history — in very first implementations of APM, people were concerned mainly with timing methods, like this below:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt1b8ff9b268b8a134/6a85cc2799083f8a0c40f9f1/blog-elastic-timing-methods.png" alt="timing methods" /></p>
<p>Usually you had a configuration file to specify which methods you wanted to time, and the APM implementation would instrument the specified code with method timings.</p>
<p>From here things started to evolve, and one of the first additions to APM was to add in tracing.</p>
<p>For Java, it’s fairly trivial to implement a system to do this by using what's known as a Java agent. You just specify -javagent command line argument, and the agent code gets access to the dynamic compilation routines within Java so it can modify the code before it is compiled into machine code, allowing you to “wrap” specific methods with timing or tracing routines. So, auto instrumenting Java was one of the first things that the original APM vendors did.</p>
<p><a href="https://opentelemetry.io/docs/instrumentation/java/automatic/">OpenTelemetry has agents like this</a>, and most observability vendors that offer APM solutions have their own proprietary ways of doing this, often with more advanced and differing features from the open source tooling.</p>
<p>Things have moved on since then, and Node.JS and Python are now popular.</p>
<p>As a result, ways of auto instrumenting these language runtimes have appeared, which mostly work by injecting the libraries into the code before starting them up. OpenTelemetry has a way of doing this on Kubernetes with an Operator and sidecar <a href="https://github.com/open-telemetry/opentelemetry-operator/blob/main/README.md">here</a>, which supports Python, Node.JS, Java, and DotNet.</p>
<p>The other alternative is to start adding APM and tracing API calls into your own code, which is not dissimilar to adding logging functionality. You may even wish to create an abstraction in your code to deal with this cross-cutting concern, although this is less of a problem now that there are open standards with which you can implement this.</p>
<p>You can see an example of how to add OpenTelemetry spans and attributes to your code for manual instrumentation below and <a href="https://github.com/davidgeorgehope/ChatGPTMonitoringWithOtel/blob/main/monitor.py">here</a>.</p>
<pre><code>from flask import Flask
import monitor  # Import the module
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter
import urllib
import os

from opentelemetry import trace
from opentelemetry.sdk.resources import SERVICE_NAME, Resource
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor
from opentelemetry.instrumentation.requests import RequestsInstrumentor


# Service name is required for most backends
resource = Resource(attributes={
    SERVICE_NAME: "your-service-name"
})

provider = TracerProvider(resource=resource)
processor = BatchSpanProcessor(OTLPSpanExporter(endpoint=os.getenv('OTEL_EXPORTER_OTLP_ENDPOINT'),
        headers="Authorization=Bearer%20"+os.getenv('OTEL_EXPORTER_OTLP_AUTH_HEADER')))

provider.add_span_processor(processor)
trace.set_tracer_provider(provider)
tracer = trace.get_tracer(__name__)
RequestsInstrumentor().instrument()

# Initialize Flask app and instrument it
app = Flask(__name__)

@app.route("/completion")
@tracer.start_as_current_span("do_work")
def completion():
        span = trace.get_current_span()
        if span:
            span.set_attribute("completion_count",1)
</code></pre>
<p>By implementing APM in this way, you could even eliminate the need to do any logging by storing all your required logging information within span attributes, exceptions, and metrics. The downside is that you can only do this with code that you own, so you will not be able to remove all logs this way.</p>
<h3 id="sampling">Sampling</h3>
<p>Many people don’t realize that APM is an expensive process. It adds a lot of CPU cycles and memory to your applications, and although there is a lot of value to be had, there are certainly trade-offs to be made.</p>
<p>Should you sample everything 100% and eat the cost? Or should you think about an intelligent trade-off with fewer samples or even tail-based sampling, which many products commonly support? Here, we will talk about the two most common sampling techniques — head-based sampling and tail-based sampling — to help you decide.</p>
<p><strong>Head-based sampling</strong><br />
In this approach, sampling decisions are made at the beginning of a trace, typically at the entry point of a service or application. A fixed rate of traces is sampled, and this decision propagates through all the services involved in a distributed trace.</p>
<p>With head-based sampling, you can control the rate using a configuration, allowing you to control the percentage of requests that are sampled and reported to the APM server. For instance, a sampling rate of 0.5 means that only 50% of requests are sampled and sent to the server. This is useful for reducing the amount of collected data while still maintaining a representative sample of your application's performance.</p>
<p><strong>Tail-based sampling</strong><br />
Unlike head-based sampling, tail-based sampling makes sampling decisions after the entire trace has been completed. This allows for more intelligent sampling decisions based on the actual trace data, such as only reporting traces with errors or traces that exceed a certain latency threshold.</p>
<p>We recommend tail-based sampling because it has the highest likelihood of reducing the noise and helping you focus on the most important issues. It also helps keep costs down on the data store side. A downside of tail-based sampling, however, is that it results in more data being generated from APM agents. This could use more CPU and memory on your application.</p>
<h3 id="opentelemetrysemanticconventionsandelasticcommonschema">OpenTelemetry Semantic Conventions and Elastic Common Schema</h3>
<p>OpenTelemetry prescribes Semantic Conventions, or Semantic Attributes, to establish uniform names for various operations and data types. Adhering to these conventions fosters standardization across codebases, libraries, and platforms, ultimately streamlining the monitoring process.</p>
<p>Creating OpenTelemetry spans for tracing is flexible, allowing implementers to annotate them with operation-specific attributes. These spans represent particular operations within and between systems, often involving widely recognized protocols like HTTP or database calls. To effectively represent and analyze a span in monitoring systems, supplementary information is necessary, contingent upon the protocol and operation type.</p>
<p>Unifying attribution methods across different languages is essential for operators to easily correlate and cross-analyze telemetry from polyglot microservices without needing to grasp language-specific nuances.</p>
<p>Elastic's recent contribution of the Elastic Common Schema to OpenTelemetry enhances Semantic Conventions to encompass logs and security.</p>
<p>Abiding by a shared schema yields considerable benefits, enabling operators to rapidly identify intricate interactions and correlate logs, metrics, and traces, thereby expediting root cause analysis and reducing time spent searching for logs and pinpointing specific time frames.</p>
<p>We advocate for adhering to established schemas such as ECS when defining trace, metrics, and log data in your applications, particularly when developing new code. This practice will conserve time and effort when addressing issues.</p>
<h2 id="2integrate">2. Integrate</h2>
<p>Integrations are very important for APM. How well your solution can integrate with other tools and technologies such as cloud, as well as its ability to integrate logs and metrics into your tracing data, is critical to fully understand the customer experience. In addition, most APM vendors have adjacent solutions for <a href="https://www.elastic.co/observability/synthetic-monitoring">synthetic monitoring</a> and profiling to gain deeper perspectives to supercharge your APM. We will explore these topics in the following section.</p>
<h3 id="apmlogssuperpowers">APM + logs = superpowers!</h3>
<p>Because APM agents can instrument code, they can also instrument code that is being used for logging. This way, you can capture log lines directly within APM. <a href="https://www.elastic.co/guide/en/observability/master/logs-send-application.html">This is normally simple to enable</a>.</p>
<p>With this enabled, you will also get automated injection of useful fields like these:</p>
<ul>
<li>service.name, service.version, service.environment</li>
<li>trace.id, transaction.id, error.id</li>
</ul>
<p>This means log messages will be automatically correlated with transactions as shown below, making it far easier to reduce mean time to resolution (MTTR) and find the needle in the haystack:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltce6112922800419b/6a85cc2af61d6e9f579c2b43/blog-elastic-latency-distribution.png" alt="latency distribution" /></p>
<p>If this is available to you, we highly recommend turning it on.</p>
<h3 id="deployingapminsidekubernetes">Deploying APM inside Kubernetes</h3>
<p>It is common for people to want to deploy APM inside a Kubernetes environment, and tracing is critical for monitoring applications in cloud-native environments. There are three different ways you can tackle this.</p>
<p><strong>1. Auto instrumentation using sidecars</strong><br />
With Kubernetes, it is possible to use an init container and something that will modify Kubernetes manifests on the fly to auto instrument your applications.</p>
<p>The init container will be used simply to copy the required library or jar file into the container at startup that you need to the main Kubernetes pod. Then, you can use <a href="https://kustomize.io/">Kustomize</a> to add the required command line arguments to bootstrap your agents.</p>
<p>If you are not familiar with it, Kustomize adds, removes, or modifies Kubernetes manifests on the fly. It is even available as a flag to the Kubernetes CLI — simply execute kubectl -k.</p>
<p>OpenTelemetry has an <a href="https://github.com/open-telemetry/opentelemetry-operator/blob/main/README.md">operator</a> that does all this for you automatically (without the need for Kustomize) for Java, DotNet, Python, and Node.JS, and many vendors also have their own operator or <a href="https://www.elastic.co/guide/en/apm/attacher/current/apm-attacher.html">helm charts</a> that can achieve the same result.</p>
<p><strong>2. Baking APM into containers or code</strong><br />
A second option for deploying out APM in Kubernetes — and indeed any containerized environment — is using Docker to bake the APM agents and configuration into a dockerfile.</p>
<p>Have a look at an example here using the OpenTelemetry Java Agent:</p>
<pre><code># Use the official OpenJDK image as the base image
FROM openjdk:11-jre-slim

# Set up environment variables
ENV APP_HOME /app
ENV OTEL_VERSION 1.7.0-alpha
ENV OTEL_JAVAAGENT_URL https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/download/v${OTEL_VERSION}/opentelemetry-javaagent-${OTEL_VERSION}-all.jar

# Create the application directory
RUN mkdir $APP_HOME
WORKDIR $APP_HOME

# Download the OpenTelemetry Java agent
ADD ${OTEL_JAVAAGENT_URL} /otel-javaagent.jar

# Add your Java application JAR file
COPY your-java-app.jar $APP_HOME/your-java-app.jar

# Expose the application port (e.g. 8080)
EXPOSE 8080

# Configure the OpenTelemetry Java agent and run the application
CMD java -javaagent:/otel-javaagent.jar \
      -Dotel.resource.attributes=service.name=your-service-name \
      -Dotel.exporter.otlp.endpoint=your-otlp-endpoint:4317 \
      -Dotel.exporter.otlp.insecure=true \
      -jar your-java-app.jar
</code></pre>
<p><strong>3. Tracing using a service mesh (Envoy/Istio)</strong><br />
The final option you have here is if you are using a service mesh. A service mesh is a dedicated infrastructure layer for handling service-to-service communication in a microservices architecture. It provides a transparent, scalable, and efficient way to manage and control the communication between services, enabling developers to focus on building application features without worrying about inter-service communication complexities.</p>
<p>The great thing about this is that we can activate tracing within the proxy and therefore get visibility into requests between services. We don’t have to change any code or even run APM agents for this; we simply turn on the OpenTelemetry collector that exists within the proxy — therefore this is likely the lowest overhead solution. <a href="https://www.envoyproxy.io/docs/envoy/latest/start/sandboxes/opentelemetry">Learn more about this option</a>.</p>
<h3 id="syntheticsuniversalprofiling">Synthetics Universal Profiling</h3>
<p>Most APM vendors have add ons to the primary APM use cases. Typically we see synthetics and <a href="https://www.elastic.co/observability/universal-profiling">continuous profiling</a> being added to APM solutions. APM can integrate with both, and there is some good value in bringing these technologies together to give even more insights into issues.</p>
<p><strong>Synthetics</strong><br />
Synthetic monitoring is a method used to measure the performance, availability, and reliability of web applications, websites, and APIs by simulating user interactions and traffic. It involves creating scripts or automated tests that mimic real user behavior, such as navigating through pages, filling out forms, or clicking buttons, and then running these tests periodically from different locations and devices.</p>
<p>This gives Development and Operations teams the ability to spot problems far earlier than they might otherwise, catching issues before real users do in many cases.</p>
<p>Synthetics can be integrated with APM — inject an APM agent into the website when the script runs, so even if you didn’t put end user monitoring into your website initially, it can be injected at run time. This usually happens without any input from the user. From there, a tracing id for each request can be passed down through the various layers of the system, allowing teams to follow the request all the way from the synthetics script to the lowest levels of the application stack such as the database.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt3da4b528c0a61b51/6a85cc2eba7acc5fe499216c/blog-elastic-rainbow-sandals.png" alt="observability rainbow sandals" /></p>
<p><strong>Universal profiling</strong><br />
“Profiling” is a dynamic method of analyzing the complexity of a program, such as CPU utilization or the frequency and duration of function calls. With profiling, you can locate exactly which parts of your application are consuming the most resources. <a href="https://www.elastic.co/observability/universal-profiling">“Continuous profiling”</a> is a more powerful version of profiling that adds the dimension of time. By understanding your system’s resources over time, you can then locate, debug, and fix issues related to performance.</p>
<p>Universal profiling is a further extension of this, which allows you to capture profile information about all of the code running in your system all the time. Using a technology like <a href="https://www.elastic.co/blog/ebpf-observability-security-workload-profiling">eBPF</a> can allow you to see <em>all</em> the function calls in your systems, including into things like the Kubernetes runtime. Doing this gives you the ability to finally see unknown unknowns — things you didn’t know were problems. This is very different from APM, which is really about tracking individual traces and requests and the overall customer experience. Universal profiling is about overcoming those issues you didn’t even know existed and even answering the question “What is my most expensive line of code?”</p>
<p>Universal profiling can be linked into APM, showing you profiles that occurred during a specific customer issue, for example, or by linking profiles directly to traces by looking at the global state that exists at the thread level. These technologies can work wonders when used together.</p>
<p>Typically, profiles are viewed as “flame graphs” shown below. The boxes represent the amount of “on-cpu” time spent executing a particular function.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltab04ef0b036f83a9/6a85cc31d7b2e71203fe8506/blog-elastic-universal-profiling.png" alt="observability universal profiling" /></p>
<h2 id="3analyticsandaiops">3. Analytics and AIOps</h2>
<p>The interesting thing about APM is it opens up a whole new world of analytics versus just logs. All of a sudden, you have access to the information flows from <em>inside</em> applications.</p>
<p>This allows you to easily capture things like the amount of money a specific customer is currently spending on your most critical ecommerce store, or look at failed trades in a brokerage app to see how much lost revenue those failures are impacting. You can even then apply machine learning algorithms to project future spend or look at anomalies occurring in this data, giving you a new window into how your business runs.</p>
<p>In this section, we will look at ways to do this and how to get the most out of this new world, as well as how to apply AIOps practices to this new data. We will also discuss getting SLIs and SLOs setup for APM data.</p>
<h3 id="gettingbusinessdataintoyourtraces">Getting business data into your traces</h3>
<p>There are generally two ways of getting business data into your traces. You can modify code and add in Span attributes, an example of which is available <a href="https://github.com/davidgeorgehope/ChatGPTMonitoringWithOtel/blob/main/monitor.py">here</a> and shown below. Or you can write an extension or a plugin, which has the benefit of avoiding code changes. OpenTelemetry supports <a href="https://opentelemetry.io/docs/instrumentation/java/extensions/">adding extensions in its auto-instrumentation agents</a>. Most other APM vendors usually have something similar.</p>
<pre><code>def count_completion_requests_and_tokens(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        counters['completion_count'] += 1
        response = func(*args, **kwargs)

        token_count = response.usage.total_tokens
        prompt_tokens = response.usage.prompt_tokens
        completion_tokens = response.usage.completion_tokens
        cost = calculate_cost(response)
        strResponse = json.dumps(response)

        # Set OpenTelemetry attributes
        span = trace.get_current_span()
        if span:
            span.set_attribute("completion_count", counters['completion_count'])
            span.set_attribute("token_count", token_count)
            span.set_attribute("prompt_tokens", prompt_tokens)
            span.set_attribute("completion_tokens", completion_tokens)
            span.set_attribute("model", response.model)
            span.set_attribute("cost", cost)
            span.set_attribute("response", strResponse)
        return response
    return wrapper
</code></pre>
<h3 id="usingbusinessdataforfunandprofit">Using business data for fun and profit</h3>
<p>Once you have the business data in your traces, you can start to have some fun with it. Take a look at the example below for a financial services fraud team. Here we are tracking transactions — average transaction value for our larger business customers. Crucially, we can see if there are any unusual transactions.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt968f966ddbe0150e/6a85cc332d64d515ef081d5c/blog-elastic-customer-count.png" alt="customer count" /></p>
<p>A lot of this is powered by machine learning, which can classify transactions or do <a href="https://www.elastic.co/blog/reduce-mttd-ml-machine-learning-observability">anomaly detection</a>. Once you start capturing the data, it is possible to do a lot of useful things like this, and with a flexible platform, integrating machine learning models into this process becomes a breeze.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltc046307a571de036/6a85cc3733f244b66149f524/blog-elastic-fraud-12h.png" alt="fraud 12-h" /></p>
<h3 id="slisandslos">SLIs and SLOs</h3>
<p>Service level indicators (SLIs) and service level objectives (SLOs) serve as critical components for maintaining and enhancing application performance. SLIs, which represent key performance metrics such as latency, error rate, and throughput, help quantify an application's performance, while SLOs establish target performance levels to meet user expectations.</p>
<p>By selecting relevant SLIs and setting achievable SLOs, organizations can better monitor their application's performance using APM tools. Continually evaluating and adjusting SLIs and SLOs in response to changes in application requirements, user expectations, or the competitive landscape ensures that the application remains competitive and delivers an exceptional user experience.</p>
<p>In order to define and track SLIs and SLOs, APM becomes a critical perspective that is needed for understanding the user experience. Once APM is implemented, we recommend that organizations perform the following steps.</p>
<ul>
<li>Define SLOs and SLIs required to track them.</li>
<li>Define SLO budgets and how they are calculated. Reflect business’ perspective and set realistic targets.</li>
<li>Define SLIs to be measured from a user experience perspective.</li>
<li>Define different alerting and paging rules, page only on customer facing SLO degradations, record symptomatic alerts, notify on critical symptomatic alerts.</li>
</ul>
<p>Synthetic monitoring and end user monitoring (EUM) can also help with getting even more data required to understand latency, throughput, and error rate from the user’s perspective, where it is critical to get good business focused metrics and data from.</p>
<h2 id="4scaleandtotalcostofownership">4. Scale and total cost of ownership</h2>
<p>With increased perspectives, customers often run into scalability and total cost of ownership issues. All this new data can be overwhelming. Luckily there are various techniques you can use to deal with this. Tracing itself can actually help with volume challenges because you can decompose unstructured logs and combine them with traces, which leads to additional efficiency. You can also use different sampling methods to deal with scale challenges (i.e., both techniques we previously mentioned).</p>
<p>In addition to this, for large enterprise scale, we can use streaming pipelines like Kafka or Pulsar to manage the data volumes. This has an additional benefit that you get for free: if you take down the systems consuming the data or they face outages, it is less likely you will lose data.</p>
<p>With this configuration in place, your “Observability pipeline” architecture would look like this:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltd4444d4d2f26f46f/6a85cc3a4710c62b50d3cb73/blog-elastic-opentelemetry-collector.png" alt="opentelemetry collector" /></p>
<p>This completely decouples your sources of data from your chosen observability solution, which will future proof your observability stack going forward, enable you to reach massive scale, and make you less reliant on specific vendor code for collection of data.</p>
<p>Another thing we recommend doing is being intelligent about instrumentation. This will serve two benefits: you will get some CPU cycles back in the instrumented application, and your backend data collection systems will have less data to process. If you know, for example, that you have no interest in tracking calls to a specific endpoint, you can exclude those classes and methods from instrumentation.</p>
<p>And finally, data tiering is a transformative approach for managing data storage that can significantly reduce the total cost of ownership (TCO) for businesses. Primarily, it allows organizations to store data across different types of storage mediums based on their accessibility needs and the value of the data. For instance, frequently accessed, high-value data can be stored in expensive, high-speed storage, while less frequently accessed, lower-value data can be stored in cheaper, slower storage.</p>
<p>This approach, often incorporated in cloud storage solutions, enables cost optimization by ensuring that businesses only pay for the storage they need at any given time. Furthermore, it provides the flexibility to scale up or down based on demand, eliminating the need for large capital expenditures on storage infrastructure. This scalability also reduces the need for costly over-provisioning to handle potential future demand.</p>
<h2 id="conclusion">Conclusion</h2>
<p>In today's highly competitive and fast-paced software development landscape, simply relying on logging is no longer sufficient to ensure top-notch customer experiences. By adopting APM and distributed tracing, organizations can gain deeper insights into their systems, proactively detect and resolve issues, and maintain a robust user experience.</p>
<p>In this blog, we have explored the journey of moving from a logging-only approach to a comprehensive observability strategy that integrates logs, traces, and APM. We discussed the importance of cultivating a new monitoring mindset that prioritizes customer experience, and the necessary organizational changes required to drive APM and tracing adoption. We also delved into the various stages of the journey, including data ingestion, integration, analytics, and scaling.</p>
<p>By understanding and implementing these concepts, organizations can optimize their monitoring efforts, reduce MTTR, and keep their customers satisfied. Ultimately, prioritizing customer experience through APM and tracing can lead to a more successful and resilient enterprise in today's challenging environment.</p>
<p><a href="https://www.elastic.co/observability/application-performance-monitoring">Learn more about APM at Elastic</a>.</p>]]></content:encoded>
    <link>https://www.elastic.co/observability-labs/blog/introduction-apm-tracing-logging</link>
    <guid isPermaLink="false">introduction-apm-tracing-logging</guid>
    <category><![CDATA[APM]]></category>
    <category><![CDATA[OpenTelemetry]]></category>
    <category><![CDATA[Logs Analytics]]></category>
    <dc:creator><![CDATA[David Hope]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt7bda210049148e3b/6a85cc3dd7b2e756d4fe850a/log-management-720x420_(2).jpeg" length="0" type="image/jpeg"/>
    <pubDate>Tue, 30 May 2023 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Monitor OpenAI API and GPT models with OpenTelemetry and Elastic]]></title>
    <description><![CDATA[Get ready to be blown away by this game-changing approach to monitoring cutting-edge ChatGPT applications! As the ChatGPT phenomenon takes the world by storm, it's time to supercharge your monitoring game with OpenTelemetry and Elastic Observability.]]></description>
    <content:encoded><![CDATA[<p>ChatGPT is so hot right now, it broke the internet. As an avid user of ChatGPT and a developer of ChatGPT applications, I am incredibly excited by the possibilities of this technology. What I see happening is that there will be exponential growth of ChatGPT-based solutions, and people are going to need to monitor those solutions.</p>
<p>Since this is a pretty new technology, we wouldn’t want to burden our shiny new code with proprietary technology, would we? No, we would not, and that is why we are going to use OpenTelemetry to monitor our ChatGPT code in this blog. This is particularly relevant for me as I recently created a service to generate meeting notes from Zoom calls. If I am to release this into the wild, how much is it going to cost me and how do I make sure it is available?</p>
<h2 id="openaiapistotherescue">OpenAI APIs to the rescue</h2>
<p>The OpenAI API is pretty awesome, there is no doubt. It also gives us the information shown below in each response to each API call, which can help us with understanding what we are being charged. By using the token counts, the model, and the pricing that OpenAI has put up on its website, we can calculate the cost. The question is, how do we get this information into our monitoring tools?</p>
<pre><code>{
  "choices": [
    {
      "finish_reason": "length",
      "index": 0,
      "logprobs": null,
      "text": "\n\nElastic is an amazing observability tool because it provides a comprehensive set of features for monitoring"
    }
  ],
  "created": 1680281710,
  "id": "cmpl-70CJq07gibupTcSM8xOWekOTV5FRF",
  "model": "text-davinci-003",
  "object": "text_completion",
  "usage": {
    "completion_tokens": 20,
    "prompt_tokens": 9,
    "total_tokens": 29
  }
}
</code></pre>
<h2 id="opentelemetrytotherescue">OpenTelemetry to the rescue</h2>
<p><a href="https://www.elastic.co/blog/opentelemetry-observability">OpenTelemetry</a> is truly a fantastic piece of work. It has had so much adoption and work committed to it over the years, and it seems to really be getting to the point where we can call it the Linux of Observability. We can use it to record logs, metrics, and traces and get those in a vendor neutral way into our favorite observability tool — in this case, Elastic Observability.</p>
<p>With the latest and greatest otel libraries in Python, we can auto-instrument external calls, and this will help us understand how OpenAI calls are performing. Let's take a sneak peek at our sample Python application, which implements Flask and the ChatGPT API and also has OpenTelemetry. If you want to try this yourself, take a look at the GitHub link at the end of this blog and follow these steps.</p>
<h3 id="setupelasticcloudaccountifyoualreadydonthaveone">Set up Elastic Cloud account (if you already don’t have one)</h3>
<ol>
<li>Sign up for a two-week free trial at <a href="https://www.elastic.co/cloud/elasticsearch-service/signup">https://www.elastic.co/cloud/elasticsearch-service/signup</a>.</li>
<li>Create a deployment.</li>
</ol>
<p>Once you are logged in, click <strong>Add integrations</strong>.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt2a530a6a1d8ae18c/6a85cd3eeaf2458371a49f8f/blog-elastic-cloud-deployment-add-integrations.png" alt="elastic cloud deployment add integrations" /></p>
<p>Click on <strong>APM Integration</strong>.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt12f670bb3d7aad2c/6a85cd411aa1e1660eff8da3/blog-elastic-apm-integration.png" alt="elastic apm integration" /></p>
<p>Then scroll down to get the details you need for this blog:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltfa14098df2f3aab7/6a85cd44d6cf2912dcbb0925/blog-elastic-opentelemetry-download.png" alt="elastic opentelemetry download" /></p>
<p>Be sure to set the following Environment variables, replacing the variables with data you get from Elastic as above and OpenAI from <a href="https://platform.openai.com/account/api-keys">here</a>, and then run these export commands on the command line.</p>
<pre><code>export OPEN_AI_KEY=sk-abcdefgh5ijk2l173mnop3qrstuvwxyzab2cde47fP2g9jij
export OTEL_EXPORTER_OTLP_AUTH_HEADER=abc9ldeofghij3klmn
export OTEL_EXPORTER_OTLP_ENDPOINT=https://123456abcdef.apm.us-west2.gcp.elastic-cloud.com:443
</code></pre>
<p>And install the following Python libraries:</p>
<pre><code>pip3 install opentelemetry-api
pip3 install opentelemetry-sdk
pip3 install opentelemetry-exporter-otlp
pip3 install opentelemetry-instrumentation
pip3 install opentelemetry-instrumentation-requests
pip3 install openai
pip3 install flask
</code></pre>
<p>Here is a look at the code we are using for the example application. In the real world, this would be your own code. All this does is call OpenAI APIs with the following message: “Why is Elastic an amazing observability tool?”</p>
<pre><code>import openai
from flask import Flask
import monitor  # Import the module
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter
import urllib
import os
from opentelemetry import trace
from opentelemetry.sdk.resources import SERVICE_NAME, Resource
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor
from opentelemetry.instrumentation.requests import RequestsInstrumentor

# OpenTelemetry setup up code here, feel free to replace the “your-service-name” attribute here.
resource = Resource(attributes={
    SERVICE_NAME: "your-service-name"
})
provider = TracerProvider(resource=resource)
processor = BatchSpanProcessor(OTLPSpanExporter(endpoint=os.getenv('OTEL_EXPORTER_OTLP_ENDPOINT'),
        headers="Authorization=Bearer%20"+os.getenv('OTEL_EXPORTER_OTLP_AUTH_HEADER')))
provider.add_span_processor(processor)
trace.set_tracer_provider(provider)
tracer = trace.get_tracer(__name__)
RequestsInstrumentor().instrument()



# Initialize Flask app and instrument it

app = Flask(__name__)
# Set OpenAI API key
openai.api_key = os.getenv('OPEN_AI_KEY')


@app.route("/completion")
@tracer.start_as_current_span("do_work")
def completion():
    response = openai.Completion.create(
        model="text-davinci-003",
        prompt="Why is Elastic an amazing observability tool?",
        max_tokens=20,
        temperature=0
    )
    return response.choices[0].text.strip()

if __name__ == "__main__":
    app.run()
</code></pre>
<p>This code should be fairly familiar to anyone who has implemented OpenTelemetry with Python here — there is no specific magic. The magic happens inside the “monitor” code that you can use freely to instrument your own OpenAI applications.</p>
<h2 id="monkeyingaround">Monkeying around</h2>
<p>Inside the monitor.py code, you will see we do something called “Monkey Patching.” Monkey patching is a technique in Python where you dynamically modify the behavior of a class or module at runtime by modifying its attributes or methods. Monkey patching allows you to change the functionality of a class or module without having to modify its source code. It can be useful in situations where you need to modify the behavior of an existing class or module that you don't have control over or cannot modify directly.</p>
<p>What we want to do here is modify the behavior of the “Completion” call so we can steal the response metrics and add them to our OpenTelemetry spans. You can see how we do that below:</p>
<pre><code>def count_completion_requests_and_tokens(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        counters['completion_count'] += 1
        response = func(*args, **kwargs)
        token_count = response.usage.total_tokens
        prompt_tokens = response.usage.prompt_tokens
        completion_tokens = response.usage.completion_tokens
        cost = calculate_cost(response)
        strResponse = json.dumps(response)
        # Set OpenTelemetry attributes
        span = trace.get_current_span()
        if span:
            span.set_attribute("completion_count", counters['completion_count'])
            span.set_attribute("token_count", token_count)
            span.set_attribute("prompt_tokens", prompt_tokens)
            span.set_attribute("completion_tokens", completion_tokens)
            span.set_attribute("model", response.model)
            span.set_attribute("cost", cost)
            span.set_attribute("response", strResponse)
        return response
    return wrapper
# Monkey-patch the openai.Completion.create function
openai.Completion.create = count_completion_requests_and_tokens(openai.Completion.create)
</code></pre>
<p>By adding all this data to our Span, we can actually send it to our OpenTelemetry OTLP endpoint (in this case it will be Elastic). The benefit of doing this is that you can easily use the data for search or to build dashboards and visualizations. In the final step, we also want to calculate the cost. We do this by implementing the following function, which will calculate the cost of a single request to the OpenAI APIs.</p>
<pre><code>def calculate_cost(response):
    if response.model in ['gpt-4', 'gpt-4-0314']:
        cost = (response.usage.prompt_tokens * 0.03 + response.usage.completion_tokens * 0.06) / 1000
    elif response.model in ['gpt-4-32k', 'gpt-4-32k-0314']:
        cost = (response.usage.prompt_tokens * 0.06 + response.usage.completion_tokens * 0.12) / 1000
    elif 'gpt-3.5-turbo' in response.model:
        cost = response.usage.total_tokens * 0.002 / 1000
    elif 'davinci' in response.model:
        cost = response.usage.total_tokens * 0.02 / 1000
    elif 'curie' in response.model:
        cost = response.usage.total_tokens * 0.002 / 1000
    elif 'babbage' in response.model:
        cost = response.usage.total_tokens * 0.0005 / 1000
    elif 'ada' in response.model:
        cost = response.usage.total_tokens * 0.0004 / 1000
    else:
        cost = 0
    return cost
</code></pre>
<h2 id="elastictotherescue">Elastic to the rescue</h2>
<p>Once we are capturing all this data, it’s time to have some fun with it in Elastic. In Discover, we can see all the data points we sent over using the OpenTelemetry library:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltfce6ddd6aa2ec67b/6a85cd460782905a9f3217aa/blog-elastic-discover-apm.png" alt="elastic discover apm" /></p>
<p>With these labels in place, it is very easy to build a dashboard. Take a look at this one I built earlier (<a href="https://github.com/davidgeorgehope/ChatGPTMonitoringWithOtel/blob/main/chatGPTDashboard.ndjson">which is also checked into my GitHub Repository</a>):</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt771ed8e0409e9e81/6a85cd4907829032893217ae/blog-elastic-labels-dashboard.png" alt="elastic labels dashboard" /></p>
<p>We can also see Transactions, Latency of the OpenAI service, and all the spans related to our ChatGPT service calls.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt090652b31aa8510a/6a85cd4c4710c62948d3cba0/blog-elastic-observability-service-name.png" alt="observability service name" /></p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blta8534a667cecc5f1/6a85cd4f18249c222918f803/blog-elastic-your-service-name.png" alt="elastic your service name" /></p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltfc32738277650edf/6a85cd529bf994220f0a05b5/blog-elastic-api-openai.png" alt="elastic api openai" /></p>
<p>In the transaction view, we can also see how long specific OpenAI calls have taken:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt0101e66241fca1b4/6a85cd54f9373dad1d96f5de/blog-elastic-latency-distribution.png" alt="elastic latency distribution" /></p>
<p>Some requests to OpenAI here have taken over 3 seconds. ChatGPT can be very slow, so it’s important for us to understand how slow this is and if users are becoming frustrated.</p>
<h2 id="summary">Summary</h2>
<p>We looked at monitoring ChatGPT with OpenTelemetry with Elastic. ChatGPT is a worldwide phenomenon and it’s going to no doubt grow and grow, and pretty soon everyone will be using it. Because it can be slow to get responses out, it is critical that people are able to understand the performance of any code that is using this service.</p>
<p>There is also the issue of cost, since it’s incredibly important to understand if this service is eating into your margins and if what you are asking for is profitable for your business. With the current economic environment, we have to keep an eye on profitability.</p>
<p>Take a look at the code for this solution <a href="https://github.com/davidgeorgehope/ChatGPTMonitoringWithOtel">here</a>. And please feel free to use the “monitor” library to instrument your own OpenAI code.</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>
<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>]]></content:encoded>
    <link>https://www.elastic.co/observability-labs/blog/monitor-openai-api-gpt-models-opentelemetry</link>
    <guid isPermaLink="false">monitor-openai-api-gpt-models-opentelemetry</guid>
    <category><![CDATA[LLM Observability]]></category>
    <category><![CDATA[OpenTelemetry]]></category>
    <category><![CDATA[APM]]></category>
    <dc:creator><![CDATA[David Hope]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltc8ce30804f9f5a2b/6a85cd5743c0b79e872f0666/opentelemetry-graphic-ad-2-1920x1080.png" length="0" type="image/png"/>
    <pubDate>Tue, 04 Apr 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>
  </channel>
</rss>