<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/">
    <channel>
        <title>Elastic Observability Labs - Machine Learning</title>
        <link>https://www.elastic.co/observability-labs</link>
        <description>Trusted security news &amp; research from the team at Elastic.</description>
        <lastBuildDate>Thu, 16 Apr 2026 16:08:47 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>Elastic Observability Labs - Machine Learning</title>
            <url>https://www.elastic.co/observability-labs/assets/observability-labs-thumbnail.png</url>
            <link>https://www.elastic.co/observability-labs</link>
        </image>
        <copyright>© 2026. Elasticsearch B.V. All Rights Reserved</copyright>
        <item>
            <title><![CDATA[Using Anomaly Detection in Elastic Cloud to Identify Fraud]]></title>
            <link>https://www.elastic.co/observability-labs/blog/anomaly-detection-to-identify-fraud</link>
            <guid isPermaLink="false">anomaly-detection-to-identify-fraud</guid>
            <pubDate>Thu, 30 Jan 2025 00:00:00 GMT</pubDate>
            <description><![CDATA[Follow the step-by-step process of using Elastic Cloud’s anomaly detection to analyze example credit card transactions to detect potential fraud.]]></description>
            <content:encoded><![CDATA[<p><strong>Fraud detection is one of the most pressing challenges facing the financial services industry today.</strong> With the rise of digital payments, app-based banking, and online financial services, the volume and sophistication of fraudulent activity have grown significantly. In recent years, high-profile incidents like the <a href="https://www.justice.gov/usao-nj/pr/eighteen-people-charged-international-200-million-credit-card-fraud-scam">$200 million credit card fraud scheme</a> uncovered by the U.S. Department of Justice, which involved the creation of thousands of fake identities, have highlighted just how advanced fraud operations have become. These threats pose serious risks to financial institutions and their customers, making real-time fraud prevention an absolute necessity.</p>
<p>Elastic Cloud provides a powerful solution to meet these challenges. Its scalable, high-performance platform enables organizations to ingest and analyze all data types efficiently (from transactional data to customers’ personal information to claims data), delivering actionable insights that empower fraud prevention teams to detect anomalies and stop fraud before it occurs. From identifying unusual spending patterns to uncovering hidden threats, Elastic Cloud offers the speed and flexibility needed to safeguard assets in an increasingly digital economy.</p>
<p>In this blog, we’ll walk you through how Elastic Cloud can be used to identify fraud within credit card transactions—a key area of focus due to the high volume of data and the significant potential for fraudulent activity.</p>
<p>We’ll use a <code>Node.js</code> code example to generate an example set of credit card transactions. The generated transactions include a data anomaly similar to an anomaly that might occur as a result of fraudulent activity known as “Card Testing”, which is when a malicious actor tests to see if stolen credit card data can be used to make fraudulent transactions. We’ll then import the credit card transactions into an Elastic Cloud index and use Elastic Observability’s Anomaly Detection feature to analyze the transactions to detect potential signs of “Card Testing”.</p>
<h2>Performing fraud detection with Elastic Cloud</h2>
<h3>Generate example credit card transactions</h3>
<p>Begin the process by using a terminal on your local computer to run a <a href="https://github.com/elastic/observability-examples/tree/main/anomaly-detection">Node.js code example</a> that will generate some example credit card transaction data.</p>
<p>Within your terminal window, run the following <strong>git clone</strong> command to clone the Github repository containing the Node.js code example:</p>
<pre><code>git clone https://github.com/elastic/observability-examples
</code></pre>
<p>Run the following <strong>cd</strong> command to change directory to the code example folder:</p>
<pre><code>cd observability-examples/anomaly-detection
</code></pre>
<p>Run the following npm install command to install the code example’s dependencies:</p>
<pre><code>npm install
</code></pre>
<p>Enter the following <strong>node</strong> command to run the code example which will generate a JSON file named transactions.ndjson containing 1000 example credit card transactions:</p>
<pre><code>node generate-transactions.js 
</code></pre>
<p>Now that we've got some credit card transaction data, we can import the transactions into Elastic Cloud to analyze the data.</p>
<h3>Import transactions data into an Elastic Cloud index</h3>
<p>We’ll start the import process in <a href="https://cloud.elastic.co/">Elastic Cloud</a>. Create an Elastic Serverless project in which we can import and analyze the transaction data. Click <strong>Create project</strong>.</p>
<p><img src="https://www.elastic.co/observability-labs/assets/images/anomaly-detection-to-identify-fraud/create-serverless-project.png" alt="Create Elastic serverless project" /></p>
<p>Click <strong>Next</strong> in the <strong>Elastic for Observability</strong> project type tile.</p>
<p><img src="https://www.elastic.co/observability-labs/assets/images/anomaly-detection-to-identify-fraud/create-serverless-observability-project.png" alt="Create Elastic Observability serverless project" /></p>
<p>Click <strong>Create project</strong>.</p>
<p><img src="https://www.elastic.co/observability-labs/assets/images/anomaly-detection-to-identify-fraud/create-serverless-observability-project-confirm.png" alt="Create Elastic Observability serverless project confirm" /></p>
<p>Click <strong>Continue</strong>.</p>
<p><img src="https://www.elastic.co/observability-labs/assets/images/anomaly-detection-to-identify-fraud/create-serverless-observability-project-continue.png" alt="Create Elastic Observability serverless project continue" /></p>
<p>Select the <strong>Application</strong> tile.</p>
<p><img src="https://www.elastic.co/observability-labs/assets/images/anomaly-detection-to-identify-fraud/select-application-data-import.png" alt="Select application data import" /></p>
<p>Enter the text “Upload” into the search box.</p>
<p><img src="https://www.elastic.co/observability-labs/assets/images/anomaly-detection-to-identify-fraud/data-import-search-for-upload-option.png" alt="Data import search for upload option" /></p>
<p>Select the <strong>Upload a file</strong> tile.</p>
<p><img src="https://www.elastic.co/observability-labs/assets/images/anomaly-detection-to-identify-fraud/data-import-select-upload-tile.png" alt="Data import select upload tile" /></p>
<p>Click <strong>Select or drag and drop a file.</strong></p>
<p><img src="https://www.elastic.co/observability-labs/assets/images/anomaly-detection-to-identify-fraud/data-import-select-upload-file-selector.png" alt="Data import select upload file selector" /></p>
<p>Select the <strong>transactions.ndjson</strong> file on your local computer that was created from running the Node.js code example in a previous step.</p>
<p><img src="https://www.elastic.co/observability-labs/assets/images/anomaly-detection-to-identify-fraud/data-import-select-local-file.png" alt="Data import select local file" /></p>
<p>Click <strong>Import</strong>.</p>
<p><img src="https://www.elastic.co/observability-labs/assets/images/anomaly-detection-to-identify-fraud/data-import-select-local-file-import.png" alt="Data import select local file import" /></p>
<p>Enter an <strong>Index</strong> <strong>name</strong> and click <strong>Import</strong>.</p>
<p><img src="https://www.elastic.co/observability-labs/assets/images/anomaly-detection-to-identify-fraud/data-import-select-local-file-import-enter-index.png" alt="Data import select local file import enter index" /></p>
<p>You’ll see a confirmation when the import process completes and the new index is successfully created.</p>
<h3>Use Anomaly Detection to analyze credit card transactions</h3>
<p>Anomaly Detection is a powerful tool that can analyze your data to find unusual patterns that would otherwise be difficult, if not impossible, to manually uncover. Now that we've got transaction data loaded into an index, let's use anomaly detection to analyze it. Click <strong>Machine learning</strong> in the navigation menu.</p>
<p><img src="https://www.elastic.co/observability-labs/assets/images/anomaly-detection-to-identify-fraud/select-machine-learning.png" alt="Select-machine-learning" /></p>
<p>Select <strong>Anomaly Detection Jobs</strong></p>
<p><img src="https://www.elastic.co/observability-labs/assets/images/anomaly-detection-to-identify-fraud/select-machine-learning-anomaly-detection-jobs.png" alt="Select machine learning anomaly detection jobs" /></p>
<p>Click <strong>Create anomaly detection job</strong>.</p>
<p><img src="https://www.elastic.co/observability-labs/assets/images/anomaly-detection-to-identify-fraud/select-machine-learning-create-anomaly-detection-job.png" alt="Select machine learning create anomaly detection job" /></p>
<p>Select the Index containing the imported transactions as the data source of the anomaly detection job.</p>
<p><img src="https://www.elastic.co/observability-labs/assets/images/anomaly-detection-to-identify-fraud/select-machine-learning-index-for-anomaly-detection-job.png" alt="Select machine learning index for anomaly-detection-job" /></p>
<p>As mentioned above, one form of credit card fraud is called “Card Testing” where a malicious actor tests a batch of credit cards to determine if they are still valid.</p>
<p>We can analyze the transaction data in our index to detect fraudulent “Card Testing” by using the anomaly detection <strong>Population</strong> wizard. Select the <strong>Population</strong> wizard tile.</p>
<p><img src="https://www.elastic.co/observability-labs/assets/images/anomaly-detection-to-identify-fraud/select-population-wizard-for-anomaly-detection-job.png" alt="Select population wizard for anomaly detection job" /></p>
<p>Click <strong>Use full data</strong>.</p>
<p><img src="https://www.elastic.co/observability-labs/assets/images/anomaly-detection-to-identify-fraud/select-population-wizard-use-full-data-anomaly-detection-job.png" alt="Select population wizard use full data anomaly detection job" /></p>
<p>Click <strong>Next</strong>.</p>
<p><img src="https://www.elastic.co/observability-labs/assets/images/anomaly-detection-to-identify-fraud/select-population-wizard-use-full-data-anomaly-detection-job-next.png" alt="Select population wizard use full data anomaly detection job next" /></p>
<p>Click the <strong>Population field</strong> selector and select <strong>IPAddress</strong>.</p>
<p><img src="https://www.elastic.co/observability-labs/assets/images/anomaly-detection-to-identify-fraud/configure-anomaly-detection-job-population.png" alt="Configure anomaly detection job population" /></p>
<p>Click the <strong>Add metric</strong> option.</p>
<p><img src="https://www.elastic.co/observability-labs/assets/images/anomaly-detection-to-identify-fraud/configure-anomaly-detection-job-population-select-count.png" alt="Configure anomaly detection job population select count" /></p>
<p>Select <strong>Count(Event rate)</strong> as the metric to be added.</p>
<p><img src="https://www.elastic.co/observability-labs/assets/images/anomaly-detection-to-identify-fraud/configure-anomaly-detection-job-population-select-add-metric.png" alt="Configure anomaly detection job population select add metric" /></p>
<p>Click <strong>Next</strong>.</p>
<p><img src="https://www.elastic.co/observability-labs/assets/images/anomaly-detection-to-identify-fraud/configure-anomaly-detection-job-population-create-next.png" alt="Configure anomaly detection job population create next" /></p>
<p>Enter a <strong>Job ID</strong> and click <strong>Next.</strong></p>
<p><img src="https://www.elastic.co/observability-labs/assets/images/anomaly-detection-to-identify-fraud/configure-anomaly-detection-job-population-enter-job-id-next.png" alt="Configure anomaly detection job population enter job id next" /></p>
<p>Click <strong>Next</strong>.</p>
<p><img src="https://www.elastic.co/observability-labs/assets/images/anomaly-detection-to-identify-fraud/configure-anomaly-detection-job-population-confirm-create-next.png" alt="Configure anomaly detection job population confirm create next" /></p>
<p>Click <strong>Create job</strong>.</p>
<p><img src="https://www.elastic.co/observability-labs/assets/images/anomaly-detection-to-identify-fraud/anomaly-detection-job-create-job.png" alt="Anomaly detection job create job" /></p>
<p>Once the job completes, click <strong>View results</strong>.</p>
<p><img src="https://www.elastic.co/observability-labs/assets/images/anomaly-detection-to-identify-fraud/anomaly-detection-job-view-results.png" alt="Anomaly detection job view results" /></p>
<p>You should see that an anomaly has been detected. It looks like a specific IP Address has been identified performing an exceedingly high number of transactions with multiple credit cards on a single day.</p>
<p><img src="https://www.elastic.co/observability-labs/assets/images/anomaly-detection-to-identify-fraud/anomaly-detection-job-anomaly-detected.png" alt="Anomaly detection job anomaly detected" /></p>
<p>You can click the red highlighted segments in the timeline to see more details to assist you with evaluating possible remediation actions to implement.</p>
<p><img src="https://www.elastic.co/observability-labs/assets/images/anomaly-detection-to-identify-fraud/anomaly-detection-job-anomaly-detected-details.png" alt="Anomaly detection job anomaly detected details" /></p>
<p>In just a few steps, we were able to create a machine learning job that grouped all the transactions by the IP address that sent them and identified slices of time where one IP sent an unusually large number of requests compared to other IPs. Our fraudster!</p>
<h2>Take the next step in fraud prevention</h2>
<p>Fraud detection is an ongoing battle for organizations across industries, and the stakes are higher than ever. As digital payments, insurance claims, and online banking continue to dominate, the need for robust, real-time solutions to detect and prevent fraud is critical. In this blog, we demonstrated how Elastic Cloud empowers organizations to address this challenge effectively.</p>
<p>By using Elastic Cloud’s powerful capabilities, we ingested and analyzed a dataset of credit card transactions to detect potential fraudulent activity, such as “Card Testing.” From ingesting data into an Elastic index to leveraging machine learning-powered anomaly detection, this step-by-step process highlighted how Elastic Cloud can uncover hidden patterns and provide actionable insights to fraud prevention teams.</p>
<p>This example is just the beginning of what Elastic Cloud can do. Its scalable architecture, flexible tools, and powerful analytics make it an invaluable asset for any organization looking to protect their customers and assets from fraud. Whether it's detecting unusual spending patterns, identifying compromised accounts, or monitoring large-scale operations, Elastic Cloud provides the speed, precision, and efficiency financial services organizations need to stay one step ahead of fraudsters.</p>
<p>As fraud continues to evolve, so must the tools we use to combat it. Elastic Cloud gives you the power to meet these challenges head-on, enabling your institution to provide a safer, more secure experience for your customers.</p>
<p>Ready to explore more? View a <a href="https://elastic.navattic.com/fraud-detection">guided tour</a> of all the steps in this blog post or create an <a href="https://cloud.elastic.co/projects">Elastic Serverless Observability project</a> and start analyzing your data for anomalies today.</p>
<p><strong>Related resources:</strong></p>
<ul>
<li><strong>Overview:</strong> <a href="https://www.elastic.co/accelerate-fraud-detection-and-prevention-with-elastic">Accelerate fraud detection and prevention with Elastic</a></li>
<li><strong>Blog:</strong> <a href="https://www.elastic.co/blog/elastic-ai-fraud-detection-financial-services">AI-powered fraud detection: Protecting financial services with Elastic</a></li>
<li><strong>Blog:</strong> <a href="https://www.elastic.co/blog/financial-services-fraud-generative-ai-attack-surface">Fraud in financial services: Leaning on generative AI to protect a rapidly expanding attack surface</a></li>
</ul>
]]></content:encoded>
            <category>observability-labs</category>
            <enclosure url="https://www.elastic.co/observability-labs/assets/images/anomaly-detection-to-identify-fraud/anomaly-detection-to-identify-fraud.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[How Streams Generates a Log Pipeline in Seconds]]></title>
            <link>https://www.elastic.co/observability-labs/blog/elastic-streams-ai-pipeline-generation</link>
            <guid isPermaLink="false">elastic-streams-ai-pipeline-generation</guid>
            <pubDate>Thu, 16 Apr 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Streams generates a complete, tested log processing pipeline from a single click. Here's the two-stage mechanism behind it: deterministic fingerprinting, a reasoning agent that iterates against real data, and hard validation thresholds that enforce quality before you see the result.]]></description>
            <content:encoded><![CDATA[<p>Just click the Suggest pipeline button in Kibana's Processing tab and within a few seconds you're looking at a complete pipeline (Grok pattern, date normalization, type conversions) with a preview of how your actual log documents parse through it.</p>
<p>The alternative is doing this by hand: write a Grok pattern, testing it, fixing the edge cases, realizing the field names don't match ECS, renaming them, adding a date processor. And all that is just the work for a single service.</p>
<p><img src="https://www.elastic.co/observability-labs/assets/images/elastic-streams-ai-pipeline-generation/architecture-overview@2x.png" alt="Two-stage pipeline generation architecture" /></p>
<h2>The three jobs every log pipeline has</h2>
<p>Every log processing pipeline does the same three things: Things usually start with extracting fields from raw log messages, normalizing them to a consistent schema, and cleaning up whatever you don't need. Most teams would build and maintain these by hand, which can be challenging as log formats change and you realize that the person who wrote the Grok pattern moved teams, and nothing about the pipeline is documented except the pattern itself.</p>
<p>Every new service now means doing it again from scratch, with a different format, different edge cases, and eventually a different person maintaining a pattern they didn't write.</p>
<p>For the initial pipeline, Streams handles all three jobs automatically and validates the result before anything touches your production data.</p>
<h2>What happens when you click &quot;Suggest pipeline&quot;</h2>
<p>Open the Processing tab for a stream in Kibana. Click the button. Within seconds, the panel populates with a proposed pipeline (typically a parsing step, date normalization, type conversions, and field cleanup) along with a live preview showing what your most recent documents look like after the pipeline runs.</p>
<p><img src="https://www.elastic.co/observability-labs/assets/images/elastic-streams-ai-pipeline-generation/pipeline.gif" alt="Streams pipeline generation in Kibana" /></p>
<p>n this view you can see the exact fields that will be extracted, their types, and how many of your sample documents parsed successfully. If a field name is off, you can also edit it inline; if a step is adding noise, just remove it. And if the parse rate needs work, you can easily adjust and re-run generation. Nothing is written to the stream until you explicitly confirm. For now at least this is an important step for the human to be in the loop with these changes. As systems like these mature more, this may not be necessary in the future.</p>
<p>Let's walk through the steps in more detail.</p>
<h2>Stage 1: Log grouping and pattern extraction</h2>
<p>The first stage of our process doesn't involve a reasoning model. It's actually deterministic: the same input always produces the same output, with no variance from a model. It also scopes down what Stage 2 has to figure out.</p>
<p>Before any extraction runs, Streams clusters the messages by log format fingerprint. The algorithm is really simple too: digits map to <code>0</code>, letters map to <code>a</code>, and punctuation is preserved as-is. Two messages that produce the same fingerprint land in the same group.</p>
<pre><code># two entries from the same nginx stream
2026-03-30 14:22:31 192.168.1.100 - james &quot;GET /api/v1/health&quot; 200
2026-03-30 08:01:05 10.0.0.5      - alice &quot;GET /api/v2/status&quot; 404

# fingerprint
0-0-0 0:0:0 0.0.0.0 - a     &quot;a /a/a0/a&quot; 0
0-0-0 0:0:0 0.0.0.0 - a     &quot;a /a/a0/a&quot; 0
</code></pre>
<p>A stream with mixed log formats produces multiple groups, one per distinct format in the batch. This is a fairly simple but really effective way for us to cluster similar logs together and it makes all the other steps much more reliable.</p>
<p><img src="https://www.elastic.co/observability-labs/assets/images/elastic-streams-ai-pipeline-generation/stage1-parallel-extraction@2x.png" alt="Stage 1: log grouping and per-group pattern extraction" /></p>
<p>Both Grok and Dissect run on the same input, though they work differently. Grok runs per group, as it supports multiple patterns and handles each distinct format independently. Dissect uses a single pattern, so it targets only the largest group in the batch.</p>
<p>For each candidate, a heuristic algorithm analyzes the messages and identifies field boundaries: what's fixed text and what varies. It generates a pattern with positional placeholder names. An LLM then reviews the extracted field positions against a sample of up to 10 messages and renames the placeholders to human-readable, schema-compliant names.</p>
<pre><code># grok heuristic output (positional placeholders)
%{IPV4:field_0} - %{USER:field_1} \[%{HTTPDATE:field_2}\] &quot;%{WORD:field_3} %{URIPATHPARAM:field_4}...&quot;

# after LLM field naming (ECS-aligned)
%{IPV4:source.ip} - %{USER:user.name} \[%{HTTPDATE:@timestamp}\] &quot;%{WORD:http.request.method} %{URIPATHPARAM:url.path}...&quot;

# dissect heuristic output (positional placeholders)
%{field_0} - %{field_1} [%{field_2}] &quot;%{field_3} %{field_4} %{?field_5}&quot; %{field_6} %{field_7}

# after LLM field naming (ECS-aligned)
%{source.ip} - %{user.name} [%{@timestamp}] &quot;%{http.request.method} %{url.path} %{?http_version}&quot; %{http.response.status_code} %{http.response.body.bytes}
</code></pre>
<p>The resulting processor is simulated against your submitted documents to measure its parse rate. Grok is a little more expressive, with typed fields, named captures, multiple sub-patterns. The big downside is that it's also slower. Dissect on the other hand is faster but limited to fixed-position splits. Simple log formats tend to parse cleanly with dissect; complex ones need grok.</p>
<p>The candidate with the higher parse rate becomes that group's parsing processor. This runs for every group in the batch. Stage 1 hands Stage 2 one parsing processor per group found.</p>
<p>For a batch of nginx access logs, the extraction produces two candidates for the one format group present:</p>
<pre><code># input (sampled from 300 submitted documents)
192.168.1.100 - james [30/Mar/2026:14:22:31 +0000] &quot;GET /api/v1/health HTTP/1.1&quot; 200 1234

# grok candidate → parse rate 94% (282/300)
%{IPV4:source.ip} - %{USER:user.name} \[%{HTTPDATE:@timestamp}\] &quot;%{WORD:http.request.method} %{URIPATHPARAM:url.path} HTTP/%{NUMBER:http.version}&quot; %{NUMBER:http.response.status_code:int} %{NUMBER:http.response.body.bytes:int}

# dissect candidate → parse rate 71% (213/300)
%{source.ip} - %{user.name} [%{@timestamp}] &quot;%{http.request.method} %{url.path} %{?http_version}&quot; %{http.response.status_code} %{http.response.body.bytes}

# winner: grok
</code></pre>
<p>Grok wins here because <code>%{HTTPDATE}</code> handles the bracketed timestamp format; Dissect tries to split on fixed positions and fails on the surrounding brackets. Both run in parallel; comparing their results adds negligible time since this intial simulation is only done on a sample of documents.</p>
<h2>Stage 2: The reasoning agent</h2>
<p>Stage 1 produces a parsing processor; Stage 2 turns it into a complete, validated pipeline.</p>
<p>This stage uses a reasoning agent that iterates through a loop with two tools, running up to six iterations.</p>
<p><img src="https://www.elastic.co/observability-labs/assets/images/elastic-streams-ai-pipeline-generation/stage2-agent-loop@2x.png" alt="Stage 2 reasoning agent loop with hard validation thresholds" /></p>
<p>The loop:</p>
<ol>
<li>The agent takes the Stage 1 parsing processor and proposes additional steps: date normalization, type conversions, field cleanup, and PII masking for fields it identifies as sensitive.</li>
<li>It runs the complete proposed pipeline against your original documents (the raw data, not pre-processed) and returns validation results.</li>
<li>If the simulation fails, the agent reads the error messages and adjusts. The failures are very specific, and we're making good use of the LLMs capabilities to understand them: which processor failed, on what percentage of documents, with what error type. When the parse rate drops below 80%, the tool returns:</li>
</ol>
<pre><code>Parse rate is too low: 67.00% (minimum required: 80%). The pipeline is not
extracting fields from enough documents. Review the processors and ensure
they handle the document structure correctly.

Processor &quot;grok[0]&quot; has a failure rate of 33.00% (maximum allowed: 20%).
This processor is failing on too many documents.
</code></pre>
<p>The agent now reads the processor name, the failure rate, and the threshold, then adjusts the pattern on the next iteration. It can't commit until the errors resolve.</p>
<ol start="4">
<li>This repeats until the pipeline passes, then commits and sends for user approval in the UI.</li>
</ol>
<p>To ensure quality we enforce two hard thresholds at the tool level, not by the agent's judgment:</p>
<ul>
<li>If fewer than 80% of documents parse successfully, the simulation returns an error. The agent must fix this before proceeding.</li>
<li>If any individual processor fails on more than 20% of documents, the simulation is invalid.</li>
</ul>
<p>Validation is also embedded in the tool: the model sees an error message and must resolve it before proceeding. It can't commit a pipeline that fails these checks.</p>
<p>Under the hood we're steering the agent in a spefific direction. The system prompt here includes: &quot;Simplify first. Remove problematic processors rather than adding workarounds. A pipeline that handles 95% of documents perfectly is better than one that attempts 100% but fails unpredictably.&quot;</p>
<p>If your data is already well-structured (proper <code>@timestamp</code>, correct field types, no raw text that needs parsing), the agent detects this and commits an empty pipeline. It doesn't add processors for the sake of it.</p>
<h2>The output is Streamlang</h2>
<p>The agent writes Streamlang DSL, Elastic's processing language for streams, which compiles to ingest pipelines behind the scenes.</p>
<p>The field schema, the processor types, the step format: all expressed in Streamlang. Here's what the user-approved pipeline looks like for the nginx example above, targeting an ECS stream:</p>
<pre><code class="language-yaml">steps:
  - action: grok
    from: message
    patterns:
      - &quot;%{IPV4:source.ip} - %{USER:user.name} \\[%{HTTPDATE:@timestamp}\\] \&quot;%{WORD:http.request.method} %{URIPATHPARAM:url.path} HTTP/%{NUMBER:http.version}\&quot; %{NUMBER:http.response.status_code:int} %{NUMBER:http.response.body.bytes:int}&quot;
  - action: date
    from: &quot;@timestamp&quot;
    formats:
      - &quot;dd/MMM/yyyy:HH:mm:ss Z&quot;
  - action: convert
    from: http.response.status_code
    type: integer
  - action: remove
    from: message
</code></pre>
<h2>Two schemas, one generator</h2>
<p>Not everyone lands logs in the same shape, and Elastic needs to support a variety of formats. Teams running OpenTelemetry collectors want their data in OTel-native fields. Teams on Elastic's traditional stack expect ECS. Both are valid, and forcing everyone onto one schema would mean asking half our users to restructure their pipelines before they can even get started.</p>
<p>So Streams supports both, and the generator handles both. We automatically detect if we should use OTel or ECS here. For this we mostly look at the name of the stream and check if it contains <code>otel</code>, as that's what the current naming in our stack defaults to.</p>
<p>The pipeline looks different for each because the canonical field names differ:</p>
<table>
<thead>
<tr>
<th></th>
<th>OTel</th>
<th>ECS</th>
</tr>
</thead>
<tbody>
<tr>
<td>Log body</td>
<td><code>body.text</code></td>
<td><code>message</code></td>
</tr>
<tr>
<td>Log level</td>
<td><code>severity_text</code></td>
<td><code>log.level</code></td>
</tr>
<tr>
<td>Service name</td>
<td><code>resource.attributes.service.name</code></td>
<td><code>service.name</code></td>
</tr>
<tr>
<td>Host name</td>
<td><code>resource.attributes.host.name</code></td>
<td><code>host.name</code></td>
</tr>
</tbody>
</table>
<p><img src="https://www.elastic.co/observability-labs/assets/images/elastic-streams-ai-pipeline-generation/otel-vs-ecs@2x.png" alt="OTel vs ECS stream pipeline comparison with alias layer" /></p>
<p>An OTel stream gets a grok processor that reads from <code>body.text</code>:</p>
<pre><code class="language-json">{ &quot;action&quot;: &quot;grok&quot;, &quot;from&quot;: &quot;body.text&quot;, &quot;patterns&quot;: [&quot;...&quot;] }
</code></pre>
<p>An ECS stream reads from <code>message</code>:</p>
<pre><code class="language-json">{ &quot;action&quot;: &quot;grok&quot;, &quot;from&quot;: &quot;message&quot;, &quot;patterns&quot;: [&quot;...&quot;] }
</code></pre>
<p>OTel streams alias the ECS field names to their OTel equivalents. <code>log.level</code> is an alias for <code>severity_text</code>. <code>message</code> is an alias for <code>body.text</code>. A query written for ECS works on an OTel stream without changes, since the alias layer handles the translation.</p>
<pre><code class="language-json">{
  &quot;message&quot;:    { &quot;path&quot;: &quot;body.text&quot;,     &quot;type&quot;: &quot;alias&quot; },
  &quot;log.level&quot;:  { &quot;path&quot;: &quot;severity_text&quot;, &quot;type&quot;: &quot;alias&quot; }
}
</code></pre>
<p>The agent is aware of which side of this it's on. It doesn't add a rename step for <code>severity_text</code> → <code>log.level</code> on an OTel stream because the alias already provides that mapping. On an ECS stream, it adds the normalization explicitly.</p>
<h2>Schema normalization</h2>
<p>Field extraction is the most important and obvious part, but our fields also need to align.</p>
<p>If two services both log HTTP requests but call the status code field differently (<code>response_status</code> in one, <code>http_code</code> in another), a query for <code>http.response.status_code: 5*</code> returns nothing for either of them. Schema normalization maps both to the standard name:</p>
<pre><code># before: extracted field names from two different services
{ &quot;response_status&quot;: 500 }    # service A
{ &quot;http_code&quot;: 500 }           # service B

# after: ECS normalization
{ &quot;http.response.status_code&quot;: 500 }
</code></pre>
<p>Now every service uses <code>http.response.status_code</code>, and the query works across all of them.</p>
<p><img src="https://www.elastic.co/observability-labs/assets/images/elastic-streams-ai-pipeline-generation/schema-normalization@2x.png" alt="Schema normalization: two services with different field names mapped to a single ECS field" /></p>
<p>During simulation, the agent checks ECS and OTel metadata for every field it generates. Fields that already have standard names are left alone. Fields that map to a known ECS field get renamed. The simulation metrics surface this explicitly: each field in the results includes its ECS or OTel type indicator, so you can see at a glance what's been normalized.</p>
<h2>The bar the agent must clear</h2>
<p>The system prompt sets explicit acceptance criteria for a user-approved pipeline:</p>
<ul>
<li>99% of documents must have a valid <code>@timestamp</code></li>
<li>All fields must have the correct types for the target schema</li>
<li>The overall failure rate must be below 0.5%</li>
</ul>
<p>If the agent can't satisfy all of these within six iterations, the generation fails.</p>
<h2>To summarize</h2>
<p>Pipeline generation takes seconds where the manual process takes hours. The time savings come from automating the validation loop you'd otherwise run by hand: write a pattern, test it against real documents, read the failures, adjust, and try again. The agent does this in up to six cycles against the last documents your stream actually received.</p>
<h2>What's coming next in Streams and processing</h2>
<p>The most user-facing change in progress is the refinement loop. Right now, if the suggestion is close but not exactly right, you edit steps manually and that's it. The next version lets you adjust the proposed pipeline and send it back through the agent with your changes as context, so it builds from where you left off rather than starting from scratch.</p>
<p>Two other things are in progress: generation going async (currently it blocks the UI for a few seconds; soon it runs in the background), and support for streams that already have a pipeline. For now, it only handles streams without existing processing steps.</p>
<p>The same capabilities are also being exposed as callable tools in the Streams agent builder and as APIs for third-party agent frameworks. An agent can run a full pipeline generation as part of a broader onboarding workflow, without the UI.</p>
]]></content:encoded>
            <category>observability-labs</category>
            <enclosure url="https://www.elastic.co/observability-labs/assets/images/elastic-streams-ai-pipeline-generation/og-image@2x.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[ML and AI Ops Observability with OpenTelemetry and Elastic]]></title>
            <link>https://www.elastic.co/observability-labs/blog/ml-ai-ops-observability-opentelemetry-elastic</link>
            <guid isPermaLink="false">ml-ai-ops-observability-opentelemetry-elastic</guid>
            <pubDate>Tue, 31 Mar 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Learn how to instrument ML and AI pipelines with OpenTelemetry and Elastic to correlate traces, logs, and metrics from notebooks to production inference services.]]></description>
            <content:encoded><![CDATA[<p>While isolated execution logs might work for local experiments, they are no longer enough for the new era of complex, production-ready Machine Learning (ML) pipelines and Artificial Intelligence (AI) agents. Modern ML and AI systems present three unique challenges:</p>
<ul>
<li><strong>Distributed components</strong>: A single request might hit an API gateway, retrieve data from a feature store, evaluate a predictive model in a Python inference service, query a vector database, and call an external LLM.</li>
<li><strong>Non-determinism</strong>: AI agents make autonomous decisions and tool calls. If an agent fails, you need a full trace to understand its reasoning loop and what external tools it tried to invoke.</li>
<li><strong>Context dependence</strong>: You don't just care <em>that</em> an error happened; you need to know <em>what model version</em> was running, <em>what hyperparameters</em> were used, <em>what the input data looked like</em>, <em>what</em> was the commit that made that change. Many of these attributes are custom to your app, and you need an Observability environment that has the flexibility of creating new parameters on the fly and use them to find and fix issues.</li>
</ul>
<p>On top of that, with the increased use of AI agents to generate code and make autonomous decisions, Observability becomes key to understanding what is working and what is not. It creates a critical feedback loop to quickly fix problems. More than ever, ML and AI applications need to adopt the best practices of mature software engineering systems to succeed.</p>
<p>This guide shows how to use OpenTelemetry and Elastic to correlate traces, logs, and metrics to track runs, compare model behavior, and trace requests across Python and Go services with one shared context.</p>
<h2>Problem context: why AI systems are harder to debug</h2>
<p>Traditional services already have distributed failure modes, but ML and AI systems add more moving parts:</p>
<ul>
<li>notebook experiments and ad hoc jobs</li>
<li>batch training and evaluation pipelines</li>
<li>online inference services</li>
<li>external API calls, including LLM providers</li>
<li>changing model versions and hyperparameters</li>
</ul>
<p>When one prediction path gets slower or starts failing, plain isolated logs do not answer enough questions. You need to correlate:</p>
<ul>
<li><strong>what ran</strong> (run ID, model version, parameters)</li>
<li><strong>where time was spent</strong> (pipeline stage latencies)</li>
<li><strong>what was the result</strong> (model stats, predictions, API calls, compare with other runs)</li>
<li><strong>what changed</strong> (code, data, dependencies)</li>
</ul>
<p>In a future blog post, we'll show you how to set up automatic RCA and remediations with <a href="https://github.com/elastic/workflows/">Elastic Workflows</a> and our AI integrations. But as a first step, ML and AI pipelines need a robust Observability framework, which is very easy to set up with OpenTelemetry and Elastic.</p>
<h2>Solution overview</h2>
<p>OpenTelemetry gives you a standard way to emit traces, metrics, and logs. Elastic provides full OpenTelemetry ingestion, giving you a single place to store and query that telemetry. Kibana's UI is fully integrated with OpenTelemetry, allowing you to explore your services, service dependencies, service latencies, spans, and metrics out-of-the-box.</p>
<p>You can start with two deployment options:</p>
<ul>
<li><strong>Cloud</strong>: send OpenTelemetry data directly to Elastic Cloud Managed OTLP Endpoint (<a href="https://www.elastic.co/docs/reference/opentelemetry/motlp">mOTLP docs</a>), without the overhead of managing collectors</li>
<li><strong>Local</strong>: run Elastic and the EDOT Collector with <a href="https://github.com/elastic/start-local?tab=readme-ov-file#install-the-elastic-distribution-of-opentelemetry-edot-collector">start-local</a>, the EDOT Collector will be automatically listening for OTLP data in <code>localhost:4317</code></li>
</ul>
<p>Both options let you keep your application code unchanged for the initial implementation.</p>
<h2>Step 1: zero-code baseline for Python services</h2>
<p>Start by just installing the Elastic Distribution of OpenTelemetry Python (<a href="https://github.com/elastic/elastic-otel-python">EDOT Python</a>) package and using the <code>opentelemetry-instrument</code> wrapper to run your script. By simply running your script with this wrapper—without modifying your application code—your Python services begin emitting standard telemetry right away. This includes any logs exported via <code>logging</code>, alongside metrics and traces for auto-instrumented libraries. This data can be routed directly to Elastic's managed OTLP endpoint or a local EDOT collector.</p>
<pre><code class="language-bash">pip install elastic-opentelemetry
edot-bootstrap --action=install
</code></pre>
<p>Export the OpenTelemetry environment variables, then run <code>opentelemetry-instrument</code> on your script to enable auto-instrumentation.</p>
<pre><code class="language-bash">export OTEL_EXPORTER_OTLP_ENDPOINT=&quot;https://&lt;motlp-endpoint&gt;&quot; # No need when using start-local with EDOT
export OTEL_EXPORTER_OTLP_HEADERS=&quot;Authorization=ApiKey &lt;key&gt;&quot; # No need when using start-local with EDOT
export OTEL_RESOURCE_ATTRIBUTES=&quot;deployment.environment=prod,service.version=1.0.0&quot; # Set the environment and version for your app
export OTEL_PYTHON_LOGGING_AUTO_INSTRUMENTATION_ENABLED=true
export ELASTIC_OTEL_SYSTEM_METRICS_ENABLED=true
export OTEL_METRIC_EXPORT_INTERVAL=5000 # Choose the interval for your application metrics

opentelemetry-instrument --service_name=&lt;pipeline-name&gt; python3 &lt;your_python_script&gt;.py # Set your chosen name for your service
</code></pre>
<p>With this baseline, you can quickly get:</p>
<ul>
<li>Centralized logs with trace context. Any logs exported via <code>logging</code> will be searchable in Elastic and Kibana, with the ability to perform full-text search on your logs</li>
<li>Set alerting on log errors</li>
<li>Process and system metrics. System and process metrics from the execution will be automatically exported to Elastic. You can visualize them, and analyse memory usage (leaks, OOM errors), CPU utilization (Bottlenecks / Spikes), thread counts, disk I/O bottlenecks or network I/O saturation.</li>
<li>Set alerting on metrics</li>
<li>Spans for auto instrumented libraries</li>
<li>Service latency baselines and error trends</li>
<li>Set manual or Anomaly detection alerting on error rates, latencies or throughput</li>
<li>Correlate logs, metrics, and traces in a single shared context to quickly find the root cause of issues, using OpenTelemetry for instrumentation and Elastic for analysis.</li>
</ul>
<p>Once ingested, Kibana immediately populates out-of-the-box dashboards. You can explore full-text searchable logs, monitor system and process metrics, investigate auto-instrumented trace waterfalls, map out your ML dependencies with service maps, and easily set up alerts for latency spikes, memory or CPU usage or log errors.</p>
<p><img src="https://www.elastic.co/observability-labs/assets/images/ml-ai-ops-observability-opentelemetry-elastic/step-1-logs.png" alt="Logs in Elastic" />
<img src="https://www.elastic.co/observability-labs/assets/images/ml-ai-ops-observability-opentelemetry-elastic/step-1-log-errors.png" alt="Log errors in Elastic" />
<img src="https://www.elastic.co/observability-labs/assets/images/ml-ai-ops-observability-opentelemetry-elastic/step-1-alerts-on-log-errors.png" alt="Alerts on log errors" />
<img src="https://www.elastic.co/observability-labs/assets/images/ml-ai-ops-observability-opentelemetry-elastic/step-1-metrics.png" alt="System and process metrics" />
<img src="https://www.elastic.co/observability-labs/assets/images/ml-ai-ops-observability-opentelemetry-elastic/step-1-auto-instrumented-traces.png" alt="Auto instrumented traces" />
<img src="https://www.elastic.co/observability-labs/assets/images/ml-ai-ops-observability-opentelemetry-elastic/step-1-service-map.png" alt="Service map in Elastic" />
<img src="https://www.elastic.co/observability-labs/assets/images/ml-ai-ops-observability-opentelemetry-elastic/step-1-alerts-on-latencies.png" alt="Alerts on latencies" /></p>
<p>For LLM-specific observability, OpenTelemetry provides official <a href="https://opentelemetry.io/docs/specs/semconv/gen-ai/">Semantic Conventions for Generative AI</a> to standardize how you track token usage, model names, and prompts. These semantic conventions are still in development and not stable yet. Some instrumentations for the most used libraries in this space are being developed as part of the <a href="https://github.com/open-telemetry/opentelemetry-python-contrib/tree/main/instrumentation-genai">OpenTelemetry Python Contrib repository</a>.
Alternatively you can implement these conventions manually in your custom spans. LLM related OpenTelemetry logs, metrics and traces sent to Elastic will be in context and automatically correlated with the rest of your application or stack of applications.</p>
<h2>Step 2: add ML-specific context with custom spans and log fields</h2>
<p>Auto-instrumentation is a starting point. For ML and AI Ops, add explicit spans around business stages and attach run metadata. Elastic's schema flexibility and dynamic mappings make it a perfect fit for custom attributes or metrics that are exclusive to your pipelines or specific experiments. There is no need to know what the data will look like before writing it. You have the flexibility of creating new parameters on the fly, Elastic maps them automatically, and you can track them instantly.</p>
<p>Add custom fields and metric-like values as structured log fields so you can chart and alert on them later:</p>
<pre><code class="language-python">logger.info(&quot;training metrics&quot;, extra={
    &quot;ml.run_id&quot;: run_id,
    &quot;ml.training_accuracy&quot;: train_accuracy,
    &quot;ml.validation_accuracy&quot;: val_accuracy,
    &quot;ml.drift_detected&quot;: drift_detected,
})
</code></pre>
<p>Because Elastic handles dynamic mapping, any custom metrics or attributes you log, like model ids, training accuracy or drift detection, are instantly indexed and available to search in Discover or visualize via Dashboards.</p>
<p><img src="https://www.elastic.co/observability-labs/assets/images/ml-ai-ops-observability-opentelemetry-elastic/step-2-custom-log-attributes.png" alt="Custom log attributes" /></p>
<p>This makes dashboards and rules practical:</p>
<ul>
<li>alert when <code>ml.validation_accuracy &lt; 0.8</code></li>
<li>alert when <code>ml.drift_detected == true</code></li>
<li>compare stage latency by <code>ml.model_version</code></li>
</ul>
<p>You can use these custom attributes to build targeted visualizations, and trigger alerts when ML-specific metrics like validation accuracy drop below a critical threshold.</p>
<p><img src="https://www.elastic.co/observability-labs/assets/images/ml-ai-ops-observability-opentelemetry-elastic/step-2-charts-from-custom-log-attributes.png" alt="Charts from custom log attributes" /></p>
<p>Adding custom spans allows you to break down the specific stages of your ML pipelines, such as data loading and model training, wrapping them in their own measurable execution blocks, and analyze average latency or error rates for specific pipeline stages.</p>
<p><img src="https://www.elastic.co/observability-labs/assets/images/ml-ai-ops-observability-opentelemetry-elastic/step-2-custom-spans.png" alt="Custom spans in code" /></p>
<pre><code class="language-python">from opentelemetry import trace

tracer = trace.get_tracer(&quot;ml.pipeline&quot;)

with tracer.start_as_current_span(&quot;load_data&quot;) as span:
    span.set_attribute(&quot;ml.run_id&quot;, run_id)
    span.set_attribute(&quot;ml.dataset&quot;, dataset_source)
    load_data()

with tracer.start_as_current_span(&quot;train_model&quot;) as span:
    span.set_attribute(&quot;ml.model_version&quot;, model_version)
    span.set_attribute(&quot;ml.learning_rate&quot;, learning_rate)
    train_model()
</code></pre>
<p>Custom spans will be reflected in the APM UI alongside your traces. So you can explore their latency, impact in total execution, stack traces, error rates.</p>
<p><img src="https://www.elastic.co/observability-labs/assets/images/ml-ai-ops-observability-opentelemetry-elastic/step-2-custom-spans-ui-in-elastic.png" alt="Custom spans UI in Elastic" />
<img src="https://www.elastic.co/observability-labs/assets/images/ml-ai-ops-observability-opentelemetry-elastic/step-2-analysing-spans.png" alt="Analysing spans" />
<img src="https://www.elastic.co/observability-labs/assets/images/ml-ai-ops-observability-opentelemetry-elastic/step-2-latency-and-avg-latency-of-spans.png" alt="Latency and avg latency of spans" />
<img src="https://www.elastic.co/observability-labs/assets/images/ml-ai-ops-observability-opentelemetry-elastic/step-2-alerts-on-custom-log-metrics.png" alt="Alerts on custom log metrics" /></p>
<h2>Step 3: trace across Python and Go in production</h2>
<p>Real inference paths often cross service boundaries. For example:</p>
<p>In a production environment, a user request might pass through a Go-based API before hitting your Python ML inference service. OpenTelemetry ensures tracing context is preserved seamlessly across these boundaries.</p>
<p><img src="https://www.elastic.co/observability-labs/assets/images/ml-ai-ops-observability-opentelemetry-elastic/step-3-service-map-with-multiple-services.png" alt="Service map with multiple services" /></p>
<p>In our example, we have a simple Go HTTP service that acts as the entry point and demonstrates OpenTelemetry instrumentation in Go. This REST API service stores and retrieves ML predictions by querying Elasticsearch based on data IDs from the source dataset. All of its endpoints are natively instrumented with OTel spans.</p>
<p>The full request lifecycle looks like this:</p>
<ol>
<li>The Go API receives the client request.</li>
<li>It searches Elasticsearch for an existing prediction or calls the Python model service to run inference.</li>
<li>The Python service loads features, runs the model, and returns predictions.</li>
</ol>
<p>When both services use OpenTelemetry, trace context is propagated automatically through headers. In Elastic, you can inspect one end-to-end trace and locate latency or errors by service and span.</p>
<p>The resulting distributed trace in Elastic pieces the entire journey together. You can see the exact breakdown of time spent in the Go API versus the Python model, and correlate logs from both services in a single unified view.</p>
<p><img src="https://www.elastic.co/observability-labs/assets/images/ml-ai-ops-observability-opentelemetry-elastic/step-3-multiple-services.png" alt="Multiple services request flow" />
<img src="https://www.elastic.co/observability-labs/assets/images/ml-ai-ops-observability-opentelemetry-elastic/step-3-spans-per-service.png" alt="Spans per service" />
<img src="https://www.elastic.co/observability-labs/assets/images/ml-ai-ops-observability-opentelemetry-elastic/step-3-go-service-logs.png" alt="Go service logs" />
<img src="https://www.elastic.co/observability-labs/assets/images/ml-ai-ops-observability-opentelemetry-elastic/step-3-go-traces-in-discover.png" alt="Go traces in discover" /></p>
<h2>Validation checklist</h2>
<p>After instrumentation, validate with a short runbook:</p>
<ol>
<li>Confirm logs, metrics, and traces arrive for each service.</li>
<li>Verify your custom attributes (e.g. <code>run_id</code>, <code>model_version</code>, <code>llm_ground_truth_score</code>) are present in traces and logs.</li>
<li>Compare p95 latency per stage (<code>load_data</code>, <code>train_model</code>, <code>predict</code>).</li>
<li>Trigger a controlled failure and confirm error traces include stack context.</li>
<li>Test one rule for errors, one rule for latency spikes, and one rule for model-quality fields. Set up a connector and attach it to the rule to reach you in Slack, email, or trigger an auto-remediation workflow.</li>
</ol>
<h2>Conclusion and next steps</h2>
<p>OpenTelemetry gives ML and AI teams a unified telemetry layer, while Elastic makes that data instantly queryable and actionable across your entire lifecycle—from notebook experiments to production inference. By starting with zero-code instrumentation and incrementally adding ML-specific attributes and cross-language tracing, your team can easily adopt the Observability best practices of mature software engineering systems and succeed in the new era of complex AI operations.</p>
<p>Try this setup in <a href="https://cloud.elastic.co/registration">Elastic Cloud</a>, and use <a href="https://www.elastic.co/docs/reference/opentelemetry/motlp">mOTLP</a> for a managed ingest path. If you want a local sandbox first, start with <a href="https://github.com/elastic/start-local?tab=readme-ov-file#install-the-elastic-distribution-of-opentelemetry-edot-collector">Elastic start-local + EDOT Collector</a>.</p>
]]></content:encoded>
            <category>observability-labs</category>
            <enclosure url="https://www.elastic.co/observability-labs/assets/images/ml-ai-ops-observability-opentelemetry-elastic/header.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[Monitor your Python data pipelines with OTEL]]></title>
            <link>https://www.elastic.co/observability-labs/blog/monitor-your-python-data-pipelines-with-otel</link>
            <guid isPermaLink="false">monitor-your-python-data-pipelines-with-otel</guid>
            <pubDate>Thu, 08 Aug 2024 00:00:00 GMT</pubDate>
            <description><![CDATA[Learn how to configure OTEL for your data pipelines, detect any anomalies, analyze performance, and set up corresponding alerts with Elastic.]]></description>
            <content:encoded><![CDATA[<p>This article delves into how to implement observability practices, particularly using <a href="https://opentelemetry.io/">OpenTelemetry (OTEL)</a> in Python, to enhance the monitoring and quality control of data pipelines using Elastic. While the primary focus of the examples presented in the article is ETL (Extract, Transform, Load) processes to ensure the accuracy and reliability of data pipelines that is crucial for Business Intelligence (BI), the strategies and tools discussed are equally applicable to Python processes used for Machine Learning (ML) models or other data processing tasks.</p>
<h2>Introduction</h2>
<p>Data pipelines, particularly ETL processes, form the backbone of modern data architectures. These pipelines are responsible for extracting raw data from various sources, transforming it into meaningful information, and loading it into data warehouses or data lakes for analysis and reporting.</p>
<p>In our organization, we have Python-based ETL scripts that play a pivotal role in exporting and processing data from Elasticsearch (ES) clusters and loading it into <a href="https://cloud.google.com/bigquery">Google BigQuery (BQ)</a>. This processed data then feeds into <a href="https://www.getdbt.com">DBT (Data Build Tool)</a> models, which further refine the data and make it available for analytics and reporting. To see the full architecture and learn how we monitor our DBT pipelines with Elastic see <a href="https://www.elastic.co/observability-labs/blog/monitor-dbt-pipelines-with-elastic-observability">Monitor your DBT pipelines with Elastic Observability</a>. In this article we focus on the ETL scripts. Given the critical nature of these scripts, it is imperative to set up mechanisms to control and ensure the quality of the data they generate.</p>
<p>The strategies discussed here can be extended to any script or application that handles data processing or machine learning models, regardless of the programming language used as long as there exists a corresponding agent that supports OTEL instrumentation.</p>
<h2>Motivation</h2>
<p>Observability in data pipelines involves monitoring the entire lifecycle of data processing to ensure that everything works as expected. It includes:</p>
<ol>
<li>Data Quality Control:</li>
</ol>
<ul>
<li>Detecting anomalies in the data, such as unexpected drops in record counts.</li>
<li>Verifying that data transformations are applied correctly and consistently.</li>
<li>Ensuring the integrity and accuracy of the data loaded into the data warehouse.</li>
</ul>
<ol start="2">
<li>Performance Monitoring:</li>
</ol>
<ul>
<li>Tracking the execution time of ETL scripts to identify bottlenecks and optimize performance.</li>
<li>Monitoring resource usage, such as memory and CPU consumption, to ensure efficient use of infrastructure.</li>
</ul>
<ol start="3">
<li>Real-time Alerting:</li>
</ol>
<ul>
<li>Setting up alerts for immediate notification of issues such as failed ETL jobs, data quality issues, or performance degradation.</li>
<li>Identify the root case of such incidents</li>
<li>Proactively addressing incidents to minimize downtime and impact on business operations</li>
</ul>
<p>Issues such as failed ETL jobs, can even point to larger infrastructure or data source data quality issues.</p>
<h2>Steps for Instrumentation</h2>
<p>Here are the steps to automatically instrument your Python script for exporting OTEL traces, metrics, and logs.</p>
<h3>Step 1: Import Required Libraries</h3>
<p>We first need to install the following libraries.</p>
<pre><code class="language-sh">pip install elastic-opentelemetry google-cloud-bigquery[opentelemetry]
</code></pre>
<p>You can also them to your project's <code>requirements.txt</code> file and install them with <code>pip install -r requirements.txt</code>.</p>
<h4>Explanation of Dependencies</h4>
<ol>
<li>
<p><strong>elastic-opentelemetry</strong>: This package is the Elastic Distribution for OpenTelemetry Python. Under the hood it will install the following packages:</p>
<ul>
<li>
<p><strong>opentelemetry-distro</strong>: This package is a convenience distribution of OpenTelemetry, which includes the OpenTelemetry SDK, APIs, and various instrumentation packages. It simplifies the setup and configuration of OpenTelemetry in your application.</p>
</li>
<li>
<p><strong>opentelemetry-exporter-otlp</strong>: This package provides an exporter that sends telemetry data to the OpenTelemetry Collector or any other endpoint that supports the OpenTelemetry Protocol (OTLP). This includes traces, metrics, and logs.</p>
</li>
<li>
<p><strong>opentelemetry-instrumentation-system-metrics</strong>: This package provides instrumentation for collecting system metrics, such as CPU usage, memory usage, and other system-level metrics.</p>
</li>
</ul>
</li>
<li>
<p><strong>google-cloud-bigquery[opentelemetry]</strong>: This package integrates Google Cloud BigQuery with OpenTelemetry, allowing you to trace and monitor BigQuery operations.</p>
</li>
</ol>
<h3>Step 2: Export OTEL Variables</h3>
<p>Set the necessary OpenTelemetry (OTEL) variables by getting the configuration from APM OTEL from Elastic.</p>
<p>Go to APM -&gt; Services -&gt; Add data (top left corner).</p>
<p><img src="https://www.elastic.co/observability-labs/assets/images/monitor-your-python-data-pipelines-with-otel/otel-variables-1.png" alt="1 - Get OTEL variables step 1" /></p>
<p>In this section you will find the steps how to configure various APM agents. Navigate to OpenTelemetry to find the variables that you need to export.</p>
<p><img src="https://www.elastic.co/observability-labs/assets/images/monitor-your-python-data-pipelines-with-otel/otel-variables-2.png" alt="2 - Get OTEL variables step 2" /></p>
<p><strong>Find OTLP Endpoint</strong>:</p>
<ul>
<li>Look for the section related to OpenTelemetry or OTLP configuration.</li>
<li>The <code>OTEL_EXPORTER_OTLP_ENDPOINT</code> is typically provided as part of the setup instructions for integrating OpenTelemetry with Elastic APM. It might look something like <code>https://&lt;your-apm-server&gt;/otlp</code>.</li>
</ul>
<p><strong>Obtain OTLP Headers</strong>:</p>
<ul>
<li>In the same section, you should find instructions or a field for OTLP headers. These headers are often used for authentication purposes.</li>
<li>Copy the necessary headers provided by the interface. They might look like <code>Authorization: Bearer &lt;your-token&gt;</code>.</li>
</ul>
<p>Note: Notice you need to replace the whitespace between <code>Bearer</code> and your token with <code>%20</code> in the <code>OTEL_EXPORTER_OTLP_HEADERS</code> variable when using Python.</p>
<p>Alternatively you can use a different approach for authentication using API keys (see <a href="https://github.com/elastic/elastic-otel-python?tab=readme-ov-file#authentication">instructions</a>). If you are using our <a href="https://www.elastic.co/docs/current/serverless/general/what-is-serverless-elastic">serverless offering</a> you will need to use this approach instead.</p>
<p><strong>Set up the variables</strong>:</p>
<ul>
<li>Replace the placeholders in your script with the actual values obtained from the Elastic APM interface and execute it in your shell via the source command <code>source env.sh</code>.</li>
</ul>
<p>Below is a script to set these variables:</p>
<pre><code class="language-sh">#!/bin/bash
echo &quot;--- :otel: Setting OTEL variables&quot;
export OTEL_EXPORTER_OTLP_ENDPOINT='https://your-apm-server/otlp:443'
export OTEL_EXPORTER_OTLP_HEADERS='Authorization=Bearer%20your-token'
export OTEL_PYTHON_LOGGING_AUTO_INSTRUMENTATION_ENABLED=true
export OTEL_PYTHON_LOG_CORRELATION=true
export ELASTIC_OTEL_SYSTEM_METRICS_ENABLED=true
export OTEL_METRIC_EXPORT_INTERVAL=5000
export OTEL_LOGS_EXPORTER=&quot;otlp,console&quot;
</code></pre>
<p>With these variables set, we are ready for auto-instrumentation without needing to add anything to the code.</p>
<h4>Explanation of Variables</h4>
<ul>
<li>
<p><strong>OTEL_EXPORTER_OTLP_ENDPOINT</strong>: This variable specifies the endpoint to which OTLP data (traces, metrics, logs) will be sent. Replace <code>placeholder</code> with your actual OTLP endpoint.</p>
</li>
<li>
<p><strong>OTEL_EXPORTER_OTLP_HEADERS</strong>: This variable specifies any headers required for authentication or other purposes when sending OTLP data. Replace <code>placeholder</code> with your actual OTLP headers.</p>
</li>
<li>
<p><strong>OTEL_PYTHON_LOGGING_AUTO_INSTRUMENTATION_ENABLED</strong>: This variable enables auto-instrumentation for logging in Python, allowing logs to be automatically enriched with trace context.</p>
</li>
<li>
<p><strong>OTEL_PYTHON_LOG_CORRELATION</strong>: This variable enables log correlation, which includes trace context in log entries to correlate logs with traces.</p>
</li>
<li>
<p><strong>OTEL_METRIC_EXPORT_INTERVAL</strong>: This variable specifies the metric export interval in milliseconds, in this case 5s.</p>
</li>
<li>
<p><strong>OTEL_LOGS_EXPORTER</strong>: This variable specifies the exporter to use for logs. Setting it to &quot;otlp&quot; means that logs will be exported using the OTLP protocol. Adding &quot;console&quot; specifies that logs should be exported to both the OTLP endpoint and the console. In our case for better visibility on the infa side, we choose to export to console as well.</p>
</li>
<li>
<p><strong>ELASTIC_OTEL_SYSTEM_METRICS_ENABLED</strong>: It is needed to use this variable when using the Elastic distribution as by default it is set to false.</p>
</li>
</ul>
<p>Note: <strong>OTEL_METRICS_EXPORTER</strong> and <strong>OTEL_TRACES_EXPORTER</strong>: This variables specify the exporter to use for metrics/traces, and are set to &quot;otlp&quot; by default, which means that metrics and traces will be exported using the OTLP protocol.</p>
<h3>Running Python ETLs</h3>
<p>We run Python ETLs with the following command:</p>
<pre><code class="language-sh">OTEL_RESOURCE_ATTRIBUTES=&quot;service.name=x-ETL,service.version=1.0,deployment.environment=production&quot; &amp;&amp; opentelemetry-instrument python3 X_ETL.py 
</code></pre>
<h4>Explanation of the Command</h4>
<ul>
<li>
<p><strong>OTEL_RESOURCE_ATTRIBUTES</strong>: This variable specifies additional resource attributes, such as <a href="https://www.elastic.co/guide/en/observability/current/apm.html">service name</a>, service version and deployment environment, that will be included in all telemetry data, you can customize these values per your needs. You can use a different service name for each script.</p>
</li>
<li>
<p><strong>opentelemetry-instrument</strong>: This command auto-instruments the specified Python script for OpenTelemetry. It sets up the necessary hooks to collect traces, metrics, and logs.</p>
</li>
<li>
<p><strong>python3 X_ETL.py</strong>: This runs the specified Python script (<code>X_ETL.py</code>).</p>
</li>
</ul>
<h3>Tracing</h3>
<p>We export the traces via the default OTLP protocol.</p>
<p>Tracing is a key aspect of monitoring and understanding the performance of applications. <a href="https://www.elastic.co/guide/en/observability/current/apm-data-model-spans.html">Spans</a> form the building blocks of tracing. They encapsulate detailed information about the execution of specific code paths. They record the start and end times of activities and can have hierarchical relationships with other spans, forming a parent/child structure.</p>
<p>Spans include essential attributes such as transaction IDs, parent IDs, start times, durations, names, types, subtypes, and actions. Additionally, spans may contain stack traces, which provide a detailed view of function calls, including attributes like function name, file path, and line number, which is especially useful for debugging. These attributes help us analyze the script's execution flow, identify performance issues, and enhance optimization efforts.</p>
<p>With the default instrumentation, the whole Python script would be a single span. In our case we have decided to manually add specific spans per the different phases of the Python process, to be able to measure their latency, throughput, error rate, etc individually. This is how we define spans manually:</p>
<pre><code class="language-python">from opentelemetry import trace

if __name__ == &quot;__main__&quot;:

    tracer = trace.get_tracer(&quot;main&quot;)
    with tracer.start_as_current_span(&quot;initialization&quot;) as span:
            # Init code
            … 
    with tracer.start_as_current_span(&quot;search&quot;) as span:
            # Step 1 - Search code
            …
   with tracer.start_as_current_span(&quot;transform&quot;) as span:
           # Step 2 - Transform code
           …
   with tracer.start_as_current_span(&quot;load&quot;) as span:
           # Step 3 - Load code
           …
</code></pre>
<p>You can explore traces in the APM interface as shown below.</p>
<p><img src="https://www.elastic.co/observability-labs/assets/images/monitor-your-python-data-pipelines-with-otel/Traces-APM-Observability-Elastic.png" alt="3 - APM Traces view" /></p>
<h3>Metrics</h3>
<p>We export metrics via the default OTLP protocol as well, such as CPU usage and memory. No extra code needs to be added in the script itself.</p>
<p>Note: Remember to set <code>ELASTIC_OTEL_SYSTEM_METRICS_ENABLED</code> to true.</p>
<p><img src="https://www.elastic.co/observability-labs/assets/images/monitor-your-python-data-pipelines-with-otel/otel-metrics-apm-view.png" alt="4 - APM Metrics view" /></p>
<h3>Logging</h3>
<p>We export logs via the default OTLP protocol as well.</p>
<p>For logging, we modify the logging calls to add extra fields using a dictionary structure (bq_fields) as shown below:</p>
<pre><code class="language-python">        job.result()  # Waits for table load to complete
        job_details = client.get_job(job.job_id)  # Get job details

        # Extract job information
        bq_fields = {
            # &quot;slot_time_ms&quot;: job_details.slot_ms,
            &quot;job_id&quot;: job_details.job_id,
            &quot;job_type&quot;: job_details.job_type,
            &quot;state&quot;: job_details.state,
            &quot;path&quot;: job_details.path,
            &quot;job_created&quot;: job_details.created.isoformat(),
            &quot;job_ended&quot;: job_details.ended.isoformat(),
            &quot;execution_time_ms&quot;: (
                job_details.ended - job_details.created
            ).total_seconds()
            * 1000,
            &quot;bytes_processed&quot;: job_details.output_bytes,
            &quot;rows_affected&quot;: job_details.output_rows,
            &quot;destination_table&quot;: job_details.destination.table_id,
            &quot;event&quot;: &quot;BigQuery Load Job&quot;, # Custom event type
            &quot;status&quot;: &quot;success&quot;, # Status of the step (success/error)
            &quot;category&quot;: category # ETL category tag 
        }

        logging.info(&quot;BigQuery load operation successful&quot;, extra=bq_fields)
</code></pre>
<p>This code shows how to extract BQ job stats, execution time, bytes processed, rows affected and destination table among them. You can add other metadata like we do such as custom event type, status, and category.</p>
<p>Any calls to logging (of all levels above the set threshold, in this case INFO <code>logging.getLogger().setLevel(logging.INFO)</code>) will create a log that will be exported to Elastic. This means that in Python scripts that already use <code>logging</code> there is no need to make any changes to export logs to Elastic.</p>
<p><img src="https://www.elastic.co/observability-labs/assets/images/monitor-your-python-data-pipelines-with-otel/otel-logs-apm-view.png" alt="5 - APM Logs view" /></p>
<p>For each of the log messages, you can go into the details view (click on the <code>…</code> when you hover over the log line and go into <code>View details</code>) to examine the metadata attached to the log message. You can also explore the logs in <a href="https://www.elastic.co/guide/en/kibana/8.14/discover.html">Discover</a>.</p>
<h4>Explanation of Logging Modification</h4>
<ul>
<li>
<p><strong>logging.info</strong>: This logs an informational message. The message &quot;BigQuery load operation successful&quot; is logged.</p>
</li>
<li>
<p><strong>extra=bq_fields</strong>: This adds additional context to the log entry using the <code>bq_fields</code> dictionary. This context can include details making the log entries more informative and easier to analyze. This data will be later used to set up alerts and data anomaly detection jobs.</p>
</li>
</ul>
<h2>Monitoring in Elastic's APM</h2>
<p>As shown, we can examine traces, metrics, and logs in the APM interface. To make the most out of this data, we make use on top of nearly the whole suit of features in Elastic Observability alongside Elastic Analytic's ML capabilities.</p>
<h3>Rules and Alerts</h3>
<p>We can set up rules and alerts to detect anomalies, errors, and performance issues in our scripts.</p>
<p>The <a href="https://www.elastic.co/guide/en/kibana/current/apm-alerts.html#apm-create-error-alert"><code>error count threshold</code> rule</a> is used to create a trigger when the number of errors in a service exceeds a defined threshold.</p>
<p>To create the rule go to Alerts and Insights -&gt; Rules -&gt; Create Rule -&gt; Error count threshold, set the error count threshold, the service or environment you want to monitor (you can also set an error grouping key across services), how often to run the check, and choose a connector.</p>
<p><img src="https://www.elastic.co/observability-labs/assets/images/monitor-your-python-data-pipelines-with-otel/error-count-threshold.png" alt="6 - ETL Status Error Rule" /></p>
<p>Next, we create a rule of type <code>custom threshold</code> on a given ETL logs <a href="https://www.elastic.co/guide/en/kibana/current/data-views.html">data view</a> (create one for your index) filtering on &quot;labels.status: error&quot; to get all the logs with status error from any of the steps of the ETL which have failed. The rule condition is set to document count &gt; 0. In our case, in the last section of the rule config, we also set up Slack <a href="https://www.elastic.co/guide/en/kibana/current/alerting-getting-started.html">alerts</a> every time the rule is activated. You can pick from a long list of <a href="https://www.elastic.co/guide/en/kibana/current/action-types.html">connectors</a> Elastic supports.</p>
<p><img src="https://www.elastic.co/observability-labs/assets/images/monitor-your-python-data-pipelines-with-otel/etl-fail-status-rule.png" alt="7 - ETL Status Error Rule" /></p>
<p>Then we can set up alerts for failures. We add status to the logs metadata as shown in the code sample below for each of the steps in the ETLs. It then becomes available in ES via <code>labels.status</code>.</p>
<pre><code class="language-python">logging.info(
            &quot;Elasticsearch search operation successful&quot;,
            extra={
                &quot;event&quot;: &quot;Elasticsearch Search&quot;,
                &quot;status&quot;: &quot;success&quot;,
                &quot;category&quot;: category,
                &quot;index&quot;: index,
            },
        )
</code></pre>
<h3>More Rules</h3>
<p>We could also add rules to detect anomalies in the execution time of the different spans we define. This is done by selecting transaction/span -&gt; Alerts and rules -&gt; Custom threshold rule -&gt; Latency. In the example below, we want to generate an alert whenever the search step takes more than 25s.</p>
<p><img src="https://www.elastic.co/observability-labs/assets/images/monitor-your-python-data-pipelines-with-otel/apm_custom_threshold_latency.png" alt="8 - APM Custom Threshold - Latency" /></p>
<p><img src="https://www.elastic.co/observability-labs/assets/images/monitor-your-python-data-pipelines-with-otel/apm_custom_threshold_latency_2.png" alt="9 - APM Custom Threshold - Config" /></p>
<p>Alternatively, for finer-grained control, you can go with Alerts and rules -&gt; Anomaly rule, set up an anomaly job, and pick a threshold severity level.</p>
<p><img src="https://www.elastic.co/observability-labs/assets/images/monitor-your-python-data-pipelines-with-otel/apm_anomaly_rule_config.png" alt="10 - APM Anomaly Rule - Config" /></p>
<h3>Anomaly detection job</h3>
<p>In this example we set an anomaly detection job on the number of documents before transform.</p>
<p>We set up an <a href="https://www.elastic.co/guide/en/machine-learning/current/ml-ad-run-jobs.html">Anomaly Detection jobs</a> on the number of document before the transform using the [Single metric job] (<a href="https://www.elastic.co/guide/en/machine-learning/current/ml-anomaly-detection-job-types.html#multi-metric-jobs">https://www.elastic.co/guide/en/machine-learning/current/ml-anomaly-detection-job-types.html#multi-metric-jobs</a>) to detect any anomalies with the incoming data source.</p>
<p><img src="https://www.elastic.co/observability-labs/assets/images/monitor-your-python-data-pipelines-with-otel/single-metrics.png" alt="11 - Single Metrics" /></p>
<p>In the last step, you can create alerting similarly to what we did before to receive alerts whenever there is an anomaly detected, by setting up a severity level threshold. Using the anomaly score which is assigned to every anomaly, every anomaly is characterized by a severity level.</p>
<p><img src="https://www.elastic.co/observability-labs/assets/images/monitor-your-python-data-pipelines-with-otel/anomaly-detection-alerting-1.png" alt="12 - Anomaly detection Alerting - Severity" /></p>
<p>Similarly to the previous example, we set up a Slack connector to receive alerts whenever an anomaly is detected.</p>
<p><img src="https://www.elastic.co/observability-labs/assets/images/monitor-your-python-data-pipelines-with-otel/anomaly-detection-alerting-connectors.png" alt="13 - Anomaly detection Alerting - Connectors" /></p>
<p>You can go to your custom dashboard by going to Add Panel -&gt; ML -&gt; Anomaly Swim Lane -&gt; Pick your job.</p>
<p>Similarly, we add jobs for the number of documents after the transform, and a Multi-Metric one on the <code>execution_time_ms</code>, <code>bytes_processed</code> and <code>rows_affected</code> similarly to how it was done in <a href="https://www.elastic.co/observability-labs/blog/monitor-dbt-pipelines-with-elastic-observability">Monitor your DBT pipelines with Elastic Observability</a>.</p>
<h2>Custom Dashboard</h2>
<p>Now that your logs, metrics, and traces are in Elastic, you can use the full potential of our Kibana dashboards to extract the most from them. We can create a custom dashboard like the following one: a pie chart based on <code>labels.event</code> (category field for every type of step in the ETLs), a chart for every type of step broken down by status, a timeline of steps broken down by status, BQ stats for the ETL, and anomaly detection swim lane panels for the various anomaly jobs.</p>
<p><img src="https://www.elastic.co/observability-labs/assets/images/monitor-your-python-data-pipelines-with-otel/custom_dashboard.png" alt="14 - Custom Dashboard" /></p>
<h2>Conclusion</h2>
<p>Elastic’s APM, in combination with other Observability and ML features, provides a unified view of our data pipelines, allowing us to bring a lot of value with minimal code changes:</p>
<ul>
<li>Logging of new logs (no need to add custom logging) alongside their execution context</li>
<li>Monitor the runtime behavior of our models</li>
<li>Track data quality issues</li>
<li>Identify and troubleshoot real-time incidents</li>
<li>Optimize performance bottlenecks and resource usage</li>
<li>Identify dependencies on other services and their latency</li>
<li>Optimize data transformation processes</li>
<li>Set up alerts on latency, data quality issues, error rates of transactions or CPU usage)</li>
</ul>
<p>With these capabilities, we can ensure the resilience and reliability of our data pipelines, leading to more robust and accurate BI system and reporting.</p>
<p>In conclusion, setting up OpenTelemetry (OTEL) in Python for data pipeline observability has significantly improved our ability to monitor, detect, and resolve issues proactively. This has led to more reliable data transformations, better resource management, and enhanced overall performance of our data transformation, BI and Machine Learning systems.</p>
]]></content:encoded>
            <category>observability-labs</category>
            <enclosure url="https://www.elastic.co/observability-labs/assets/images/monitor-your-python-data-pipelines-with-otel/main_image.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[The next evolution of observability: unifying data with OpenTelemetry and generative AI]]></title>
            <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>
            <pubDate>Wed, 11 Jun 2025 00:00:00 GMT</pubDate>
            <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>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 class="language-text">Metrics (Prometheus) → &quot;CPU spiked at 09:17:00&quot;
Logs (ELK) → &quot;Exception in checkout service at 09:17:32&quot; 
Traces (Jaeger) → &quot;Slow DB queries in order-service at 09:17:28&quot;
Profiles (pyroscope) -&gt; &quot;calculate_discount() is taking 75% of CPU time&quot;
</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, &quot;Why did checkout latency spike at 09:17?&quot; 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>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>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 class="language-text">[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>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 &quot;Why is our checkout service slow?&quot; 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>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 class="language-java">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(&quot;order-processor&quot;);
        Meter meter = openTelemetry.getMeter(&quot;order-processor&quot;);
        this.cpuUsageHistogram = meter.histogramBuilder(&quot;system.cpu.load&quot;)
                                      .setDescription(&quot;System CPU load&quot;)
                                      .setUnit(&quot;1&quot;)
                                      .build();
        this.osBean = ManagementFactory.getOperatingSystemMXBean();
    }

    public void processOrder(String orderId, double amount, String userId) {
        Span span = tracer.spanBuilder(&quot;processOrder&quot;).startSpan();
        try (Scope scope = span.makeCurrent()) {
            // Add attributes to the span
            span.setAttribute(&quot;order.id&quot;, orderId);
            span.setAttribute(&quot;order.amount&quot;, amount);
            span.setAttribute(&quot;user.id&quot;, userId);
            // Populate MDC for structured logging
            MDC.put(&quot;trace_id&quot;, span.getSpanContext().getTraceId());
            MDC.put(&quot;span_id&quot;, span.getSpanContext().getSpanId());
            MDC.put(&quot;order_id&quot;, orderId);
            MDC.put(&quot;order_amount&quot;, String.valueOf(amount));
            MDC.put(&quot;user_id&quot;, userId);
            // Record CPU usage metric associated with the current trace context
            double cpuLoad = osBean.getSystemLoadAverage();
            if (cpuLoad &gt;= 0) {
                cpuUsageHistogram.record(cpuLoad);
                MDC.put(&quot;cpu_load&quot;, String.valueOf(cpuLoad));
            }
            // Log a structured message
            logger.info(&quot;Processing order&quot;);
            // Simulate business logic
            // ...
            span.setAttribute(&quot;order.status&quot;, &quot;completed&quot;);
            logger.info(&quot;Order processed successfully&quot;);
        } catch (Exception e) {
            span.recordException(e);
            span.setAttribute(&quot;order.status&quot;, &quot;failed&quot;);
            logger.error(&quot;Order processing failed&quot;, 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>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://www.elastic.co/observability-labs/assets/images/the-next-evolution-of-observability-unifying-data-with-opentelemetry-and-generative-ai/image4.png" alt="" /></p>
<p><img src="https://www.elastic.co/observability-labs/assets/images/the-next-evolution-of-observability-unifying-data-with-opentelemetry-and-generative-ai/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 class="language-sql">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>&quot;Show me all checkout failures that coincided with slow database queries&quot;</p>
<p>The AI translates this into appropriate queries across your unified data store, correlating application errors with database performance.</p>
<h2>Real-world applications and use cases</h2>
<h3>Natural language investigation</h3>
<p>Imagine asking your observability system:</p>
<p>&quot;Why did checkout latency spike at 09:17 yesterday?&quot;</p>
<p>A GenAI-powered system with unified data could respond:</p>
<p>&quot;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.&quot;</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 &quot;analyze my web traffic patterns&quot; and as you can see it has correctly identified that this is in our demo environment.</p>
<p><img src="https://www.elastic.co/observability-labs/assets/images/the-next-evolution-of-observability-unifying-data-with-opentelemetry-and-generative-ai/image3.png" alt="" /></p>
<h3>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>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>&quot;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.&quot;</p>
<h2>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://www.elastic.co/observability-labs/assets/images/the-next-evolution-of-observability-unifying-data-with-opentelemetry-and-generative-ai/image1.png" alt="" /></p>
<h2>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>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>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>
            <category>observability-labs</category>
            <enclosure url="https://www.elastic.co/observability-labs/assets/images/the-next-evolution-of-observability-unifying-data-with-opentelemetry-and-generative-ai/title.png" length="0" type="image/png"/>
        </item>
    </channel>
</rss>