<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0">
  <channel>
    <title><![CDATA[APM - Elastic Observability Labs]]></title>
    <description><![CDATA[Trusted security news & research from the team at Elastic.]]></description>
    <copyright><![CDATA[© 2026. Elasticsearch B.V. All Rights Reserved]]></copyright>
    <image>
      <title><![CDATA[APM - Elastic Observability Labs]]></title>
      <url>https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltad972c1c27dbefc6/6a88d9782904ea5e8511d473/observability-labs-thumbnail.png</url>
      <link>https://www.elastic.co/observability-labs/blog/category/apm</link>
    </image>
    <link>https://www.elastic.co/observability-labs/blog/category/apm</link>
    <atom:link href="https://www.elastic.co/observability-labs/rss/category/apm.xml" rel="self" type="application/rss+xml"/>
    <language><![CDATA[en]]></language>
    <lastBuildDate>Mon, 14 Sep 2026 00:18:55 GMT</lastBuildDate>
  <item>
    <title><![CDATA[LLM tracing in Elastic APM: prompts, responses, and token counts in the span view]]></title>
    <description><![CDATA[In a twenty-call agentic trace, you can see which span is using the most tokens and read the prompt that caused it. Both live in Elastic APM, so there is no second tool to run.]]></description>
    <content:encoded><![CDATA[<p>Elastic APM now does LLM tracing in the trace view. The GenAI tab in the span flyout has the whole conversation, so you can read the system prompt, the user messages and the model response, and copy any of them. Every GenAI span row in the waterfall shows input and output token counts, so in an agentic trace with twenty LLM calls you can find the span using the most tokens without opening any of them. Your LLM calls are now in the same waterfall as your database queries and HTTP spans.</p>
<p>Both features follow the <a href="https://github.com/open-telemetry/semantic-conventions-genai/tree/main/docs/gen-ai">OTel GenAI semantic conventions</a> and work with any OTel-instrumented provider. If your framework already emits OTel GenAI span attributes, there is nothing to change.</p>
<h2 id="howotelgenaispansarestructured">How OTel GenAI spans are structured</h2>
<p>A GenAI span stores everything as span attributes. A typical chat span includes:</p>
<ul>
<li><code>gen_ai.provider.name</code>: the provider (<code>openai</code>, <code>anthropic</code>, <code>aws.bedrock</code>, etc.); <code>gen_ai.system</code> is supported as a fallback for older instrumentation.</li>
<li><code>gen_ai.operation.name</code>: the operation type (<code>chat</code>, <code>embeddings</code>, etc.).</li>
<li><code>gen_ai.request.model</code>: the model being called.</li>
<li><code>gen_ai.usage.input_tokens</code>: tokens consumed by the prompt.</li>
<li><code>gen_ai.usage.output_tokens</code>: tokens generated in the response.</li>
<li><code>gen_ai.input.messages</code>, <code>gen_ai.output.messages</code>: conversation messages.</li>
<li><code>gen_ai.system_instructions</code>: the system prompt.</li>
</ul>
<p>Both features read from these attributes:</p>
<p>| Feature | What it shows | Where it appears | Attributes it reads |
| --- | --- | --- | --- |
| <strong>GenAI tab</strong> | Details (operation type, request model, provider, input and output token counts, response model, response ID) and Conversation (system prompt, user messages, model response) | Span flyout in the APM trace view, and the span flyout in Discover | Appears with any <code>gen_ai.*</code> attribute. Conversation needs <code>gen_ai.system_instructions</code>, <code>gen_ai.input.messages</code>, and <code>gen_ai.output.messages</code> |
| <strong>Token count badges</strong> | Input and output token counts for each GenAI span | Every GenAI span row in the trace waterfall | <code>gen_ai.usage.input_tokens</code>, <code>gen_ai.usage.output_tokens</code> |</p>
<h2 id="howtoreadllmpromptsandresponsesinthegenaitab">How to read LLM prompts and responses in the GenAI tab</h2>
<p>When any <code>gen_ai.*</code> attribute is present on a span, the span flyout shows a dedicated <strong>GenAI</strong> tab next to <strong>Metadata</strong>.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltaeedbb89fd9c6871/6a968cd2144a15728fde3efd/genai-tab-span-details.png" alt="GenAI tab in span details flyout" /></p>
<p>The Details section shows model metadata from the span attributes: operation type, request model, provider, input and output token counts, response model, and response ID. The Conversation section shows the full exchange, populated from <code>gen_ai.system_instructions</code> (system prompt), <code>gen_ai.input.messages</code> (user messages), and <code>gen_ai.output.messages</code> (model response), each with a copy button so you can pull the exact prompt or response out of the trace without scraping text from a formatted table.</p>
<p>All raw span attributes remain accessible on the <strong>Metadata</strong> tab.</p>
<p>The <strong>GenAI</strong> tab is also available in the span flyout in <strong>Discover</strong>, so you can inspect LLM prompts and responses directly alongside your log and trace data without switching to the APM view.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltd1ccc4e30c48f8ed/6a968ced36a7416fdf27288b/genai-tab-discover.png" alt="GenAI tab in Discover span flyout" /></p>
<h2 id="whatinstrumentationdoesllmtracingrequire">What instrumentation does LLM tracing require?</h2>
<p>No Kibana-side configuration is needed. The GenAI tab appears automatically when any <code>gen_ai.*</code> attribute is present on a span. Full Conversation support requires the <a href="https://github.com/open-telemetry/semantic-conventions-genai/blob/main/docs/gen-ai/gen-ai-spans.md">OTel GenAI v1.37.0 span-attribute model</a>: <code>gen_ai.input.messages</code>, <code>gen_ai.output.messages</code>, and <code>gen_ai.system_instructions</code>.</p>
<p>Frameworks that emit the older span-events model (<code>gen_ai.user.message</code>, <code>gen_ai.assistant.message</code>, <code>gen_ai.choice</code>) will show the Details metadata section but will not populate the Conversation section. For a current list of compatible instrumentations, see the <a href="https://github.com/open-telemetry/opentelemetry-python-genai/#released-instrumentations">OTel GenAI semantic conventions</a>.</p>
<p>To verify, open the span in Discover, check that <code>gen_ai.input.messages</code> and <code>gen_ai.output.messages</code> are present, and confirm the Conversation section renders.</p>
<p>If your application already sends APM data to Elastic from a GenAI workload, open any GenAI span in the trace view and check for the GenAI tab.</p>
<h2 id="llmtokenusageinthetracewaterfall">LLM token usage in the trace waterfall</h2>
<p>Token count badges now appear on each GenAI span row in the waterfall, so you can scan the full trace without drilling in. In agentic traces with ten or twenty LLM calls, this lets you identify which span is driving token consumption before opening any span.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt2d47691c65ef8799/6a968d055f9db7338e560d85/genai-waterfall-tokens.png" alt="Trace waterfall with token counts on GenAI spans" /></p>
<p>Each row shows input and output token count badges sourced from <code>gen_ai.usage.input_tokens</code> and <code>gen_ai.usage.output_tokens</code>. The row label is the span name, which instrumentation frameworks typically set to something like <code>chat gpt-4o-mini</code>.</p>
<p>Waterfall-level token counts are most useful in agentic traces where a chain of LLM calls uses different models or the same model with varying context sizes.</p>
<h2 id="whichllmprovidersdoeselasticapmsupportforgenaitracing">Which LLM providers does Elastic APM support for GenAI tracing?</h2>
<p>Elastic APM's LLM tracing works with any OTel-instrumented provider: the GenAI tab and waterfall token counts use the same OTel attribute schema regardless of which provider your application uses. Provider is read from <code>gen_ai.provider.name</code>, falling back to <code>gen_ai.system</code> for older instrumentation.</p>
<p>The <a href="https://github.com/open-telemetry/semantic-conventions-genai/tree/main/docs/gen-ai">OTel GenAI semantic conventions</a> that enable this provider detection are currently in a <code>Development</code> lifecycle. Check the <a href="https://github.com/open-telemetry/semantic-conventions-genai/releases">release notes</a> before upgrading instrumentation.</p>
<h2 id="howtoenablellmtracinginelasticapm">How to enable LLM tracing in Elastic APM</h2>
<blockquote>
  <p><strong>Availability:</strong> Both features are available as a Technical Preview on Elastic Serverless and will be available as a Technical Preview in Elastic Stack 9.6.</p>
</blockquote>
<p>To try these features:</p>
<ol>
<li>Instrument your GenAI application with an OTel SDK that follows the <a href="https://github.com/open-telemetry/semantic-conventions-genai/tree/main/docs/gen-ai">OTel GenAI semantic conventions</a> (v1.37.0 or later for full Conversation support).</li>
<li>Send traces to <a href="https://www.elastic.co/observability">Elastic Observability</a> using OTLP, the Elastic APM agent, or an EDOT SDK.</li>
<li>Open the <strong>APM</strong> section in Kibana, navigate to a service that makes LLM calls, and open the trace waterfall for any transaction.</li>
</ol>
<p>The GenAI tab appears on any span with at least one <code>gen_ai.*</code> attribute set; token count badges appear when <code>gen_ai.usage.input_tokens</code> or <code>gen_ai.usage.output_tokens</code> are present.</p>
<p>If you don't have a GenAI application to test with, the <a href="https://github.com/jennypavlova/otel-genai-chat-app">otel-genai-chat-app</a> repository is a minimal OpenAI chat app pre-instrumented with EDOT. Set <code>OPENAI_API_KEY</code> and follow the EDOT commands in the <a href="https://github.com/jennypavlova/otel-genai-chat-app#otel-genai-chat-app">README</a> to send traces to Elastic and see both features in action.</p>
<h2 id="whatsnextforllmobservabilityinelasticapm">What's next for LLM observability in Elastic APM</h2>
<p>We're exploring cost estimation per span (estimated spend based on model pricing and token counts, surfaced in the waterfall) and tool call rendering (structured display of tool/function call inputs and outputs for agentic spans).</p>
<p>If you are building GenAI applications and want early access or to share feedback, reach out through the <a href="https://discuss.elastic.co/c/observability">Elastic community forums</a> or open an issue in the <a href="https://github.com/elastic/kibana/issues">kibana repository</a>.</p>]]></content:encoded>
    <link>https://www.elastic.co/observability-labs/blog/llm-tracing-elastic-apm-genai-spans</link>
    <guid isPermaLink="false">llm-tracing-elastic-apm-genai-spans</guid>
    <category><![CDATA[AI]]></category>
    <category><![CDATA[APM]]></category>
    <category><![CDATA[OpenTelemetry]]></category>
    <category><![CDATA[What's New]]></category>
    <dc:creator><![CDATA[Jenny Pavlova,Miriam Aparicio,Costas Pipilas]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt7b5da648bcacf4aa/6a968bfc5c312610fa43eee4/header.png" length="0" type="image/png"/>
    <pubDate>Tue, 01 Sep 2026 15:22:01 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Android application monitoring with OpenTelemetry: distributed tracing from tap to backend]]></title>
    <description><![CDATA[EDOT Android adds mobile APM to your Android app with one Gradle dependency: crash reporting, session tracking and distributed tracing visible in Kibana.]]></description>
    <content:encoded><![CDATA[<p>People are handling more and more matters on their smartphones through mobile apps, both privately and professionally. With thousands or even millions of users, ensuring great performance and reliability is a key challenge for mobile app teams and the backend services they depend on. Understanding real user impact, crash patterns, and the root causes of slow response times is fundamental to managing mobile app quality.</p>
<p>The challenge deepens when something goes wrong. A crash on the device, a slow screen, or an error response might originate in the Android app itself, in a backend service, or somewhere in the network path between them. Debugging these problems without a connected, E2E view from the mobile client to the backend is time-consuming and frustrating. And without a standard instrumentation format, mobile teams often end up maintaining separate tooling that doesn't integrate with what the backend and infrastructure teams already use.</p>
<p><a href="https://opentelemetry.io/">OpenTelemetry</a> offers a way out: a unified, open-standard instrumentation model that works across platforms and languages, backed by a large community. The Elastic Distribution of OpenTelemetry Android, or EDOT Android, is an APM agent for native Android applications built on top of OpenTelemetry. It gives Android teams a practical path to observe mobile app behavior in Elastic, providing them with distributed tracing, crash reporting, session tracking, disk buffering, and automatic instrumentation, with as little code as possible while staying grounded in open standards.</p>
<p>To see what it all looks like, we will instrument a demo Android weather application end to end. You will run Elasticsearch, Kibana, and the Elastic Agent locally. The Elastic Agent provides the OTLP endpoint that receives telemetry from the Android app and backend. You will then generate distributed traces, custom spans, logs, and Android crashes from the app, and explore the results in Kibana using the Android OpenTelemetry dashboards.</p>
<p>This article focuses on a hands-on experiment to explore the E2E experience of observing Android apps with Elastic, using the EDOT Android agent. For more specific details on the EDOT Android agent, such as a list of supported features and a setup guide for your own Android project, take a look at <a href="https://www.elastic.co/docs/reference/opentelemetry/edot-sdks/android">EDOT Android docs</a>.</p>
<h2 id="settingupedotandroidwithelasticsearchandkibana">Setting up EDOT Android with Elasticsearch and Kibana</h2>
<p>We will use the <a href="https://github.com/elastic/android-agent-demo">EDOT Android demo application</a>. The demo is intentionally small but covers the main workflows you need when evaluating mobile observability with Elastic.</p>
<p>The demo has two main components: an <strong>Android app</strong>, and a <strong>Spring Boot backend</strong>. Additionally, you'll need an <strong>Elastic Stack</strong> environment up and running; we'll explain more about how to get one later in this guide.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltd2e9cece7b3e7427/6a85cd5c18249cdfba18f809/blog-demo-project-components.png" alt="Demo app components" /></p>
<h3 id="prerequisites">Prerequisites</h3>
<ul>
<li>Java 17 or higher.</li>
<li><a href="https://www.docker.com/">Docker</a>.</li>
<li><a href="https://developer.android.com/studio">Android Studio</a>.</li>
<li>An <a href="https://developer.android.com/studio/run/emulator">Android emulator</a>.</li>
<li>On Windows, use <a href="https://learn.microsoft.com/en-us/windows/wsl/install">Windows Subsystem for Linux (WSL)</a> to run the demo scripts.</li>
</ul>
<h3 id="step1clonethedemoappsrepository">Step 1: Clone the demo app's repository</h3>
<p>We'll start by cloning the <a href="https://github.com/elastic/android-agent-demo">EDOT Android demo application</a>:</p>
<pre><code>git clone git@github.com:elastic/android-agent-demo.git
</code></pre>
<h3 id="step2starttheelasticstack">Step 2: Start the Elastic Stack</h3>
<p>The demo uses <a href="https://github.com/elastic/start-local/">start-local</a> to run Elasticsearch, Kibana, and the <a href="https://www.elastic.co/docs/reference/fleet/elastic-agent-as-otel-collector">Elastic Agent</a> with a single command. In this setup, the Elastic Agent provides the OTLP endpoint that receives telemetry from the application and backend. Run this from the directory where you want the local Elastic files to be created:</p>
<pre><code>curl -fsSL https://elastic.co/start-local | sh -s -- --edot
</code></pre>
<p>For more information on this step, take a look at the <a href="https://github.com/elastic/android-agent-demo#step-1-setting-up-elasticsearch-kibana-and-the-elastic-agent">demo app's instructions</a>.</p>
<h3 id="step3startthelocalbackend">Step 3: Start the local backend</h3>
<p>The demo backend is a Spring Boot service instrumented with the <a href="https://github.com/elastic/elastic-otel-java/">EDOT Java agent</a>. It handles the app's weather requests and calls the <a href="https://open-meteo.com/">Open-Meteo</a> public API for weather data.</p>
<pre><code>./backend-manager start
</code></pre>
<p>For more information on managing the backend service, take a look at the <a href="https://github.com/elastic/android-agent-demo#step-2-launching-the-backend-service">demo app's instructions</a>.</p>
<h3 id="step4launchtheandroidapplication">Step 4: Launch the Android application</h3>
<p>Use <a href="https://developer.android.com/studio/intro">Android Studio</a> to open up the <a href="https://github.com/elastic/android-agent-demo">EDOT Android demo application</a> repo and run the application in your emulator. More info on how to run Android apps from Android Studio <a href="https://developer.android.com/studio/run">here</a>.</p>
<h2 id="generatingdistributedtraceserrorsandcrashesfromanandroidapp">Generating distributed traces, errors and crashes from an Android app</h2>
<p>The Android app has two screens: a city selector and a weather display screen that shows the current weather for the selected city on the previous screen. It includes two intentional failure paths: the first one is reached by selecting <strong>New York</strong>, which causes the backend to reject the request (the demo backend only supports European cities), and tapping the floating crash button intentionally crashes the app so you can review crash reporting in Kibana after relaunch. We'll take a look at those use cases in more detail below.</p>
<h3 id="tracingasuccessfulrequestendtoend">Tracing a successful request end to end</h3>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltb358eb3f6e58dbf0/6a85cd5f43c0b77b1e2f066c/blog-android-app-selecting-paris.png" alt="Selecting Paris" /></p>
<p>In the EDOT Android demo app, selecting "Paris" as the city triggers a successful backend request on the second screen, for which a span will be automatically generated using EDOT Android's <a href="https://www.elastic.co/docs/reference/opentelemetry/edot-sdks/android/automatic-instrumentation#okhttp">OkHttp auto-instrumentation</a>, which supports all OkHttp-generated HTTP requests and tools using it, such as Retrofit. Aside from the Android HTTP span, the successful city request continues e2e and creates a backend HTTP client span to Open-Meteo.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt0ed067b86aa2fba2/6a85cd61eaf24566fea49f99/blog-trace-waterfall-view.png" alt="Trace waterfall" /></p>
<h3 id="howbackenderrorsappearintheandroiddistributedtrace">How backend errors appear in the Android distributed trace</h3>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt5f4b20a6e5ab1e55/6a85cd65abdc29c09f122542/blog-android-app-selecting-new-york.png" alt="Selecting New York" /></p>
<p>The demo backend only supports European cities, so selecting "New York" causes it to fail, which in turn automatically creates an error associated with our Android app's HTTP span. This is done automatically. We'll see later how to find and inspect these issues from Kibana.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blte19bebcdcb1c6b55/6a85cd67abdc295b7d12254a/blog-error-trace-waterfall-view.png" alt="Error trace waterfall" /></p>
<h3 id="howedotandroidcapturesandreportsappcrashes">How EDOT Android captures and reports app crashes</h3>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt2075b1b23db04111/6a85cd6b33f244a7ec49f557/blog-android-app-selecting-crash.png" alt="Application crash" /></p>
<p>The crash button creates a crash event that appears in Kibana after the app is reopened. This event contains session information that will help us narrow down its root cause from Kibana, as we'll see later.</p>
<p>Note: EDOT Android automatically attaches Android session context to spans and logs. That means that any span or log created before the crash can be reviewed together with the crash event and nearby spans from the same session, giving you a complete picture of what the user was doing. This even applies to <a href="https://www.elastic.co/docs/reference/opentelemetry/edot-sdks/android/manual-instrumentation">manually created spans and logs</a>.</p>
<h2 id="visualizingandroidapplicationmonitoringdatainkibana">Visualizing Android application monitoring data in Kibana</h2>
<p>To see the whole story from our Android app in a single place, we'll install Kibana's <a href="https://www.elastic.co/docs/reference/integrations/otel_android_dashboards">Android OpenTelemetry Assets</a> package by following the steps below.</p>
<ol>
<li>In Kibana, search for "Android OpenTelemetry Assets" in the <a href="https://www.elastic.co/docs/explore-analyze/find-and-organize/find-apps-and-objects">global search field</a>.</li>
<li>Open it and click <strong>Install</strong> to add the Android dashboards to your Kibana instance.</li>
</ol>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blte095f0fc9464311c/6a85cd6d5c2790e893f59b59/blog-content-pack-search.png" alt="Searching content pack" /></p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt1c2ec15025a351d8/6a85cd70682666f73c1eac55/blog-content-pack-install.png" alt="Installing content pack" /></p>
<h3 id="exploringandroidapplicationmonitoringdashboardsinkibana">Exploring Android application monitoring dashboards in Kibana</h3>
<p>Once the content package is installed, open the [Android OTel] Application Overview dashboard:</p>
<ol>
<li>In Kibana, search for Dashboards in the <a href="https://www.elastic.co/docs/explore-analyze/find-and-organize/find-apps-and-objects">global search field</a> or in Kibana's menu.</li>
<li>In Dashboards, search for Android OTel and open the "[Android OTel] Application Overview" dashboard.</li>
<li>Select your application from the Applications panel at the top of the dashboard.</li>
</ol>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt01e558c291c033e4/6a85cd735c2790eef2f59b5d/blog-dashboard-list.png" alt="Dashboard list" /></p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt7cd63db3287bbe1c/6a85cd7618249c7e3918f817/blog-dashboard-android-overview.png" alt="Android overview dashboard" /></p>
<p>The dashboard provides a set of metric panels for an overview of your app's health, performance, and RUM, as well as a set of panels that can be further explored either in Discover or the Exception dashboard, as explained below.</p>
<h2 id="howtoinspectthedistributedtracingwaterfallinkibana">How to inspect the distributed tracing waterfall in Kibana</h2>
<p>From the Application Overview dashboard, go to one of the span tables (either <strong>All spans</strong> or <strong>Failed spans</strong>) and click its <strong>Explore in Discover</strong> button.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt583214104943a381/6a85cd7811893c26cea7abc6/blog-dashboard-android-overview-explore-spans.png" alt="Explore spans" /></p>
<p>In Discover, click the expand icon on the left side of any span row to open its details panel. The trace waterfall UI appears inside, showing the full span hierarchy and timing for that trace. You can expand the waterfall to full screen and drill down from there.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltdfb4a277fa1ca84a/6a85cd7b0782902c153217be/blog-discover-span-dialog-open.png" alt="Discover open span dialog" /></p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt763fe1ec0c488c93/6a85cd7d80984c6d40669020/blog-discover-span-dialog-view.png" alt="Discover span dialog" /></p>
<h3 id="analyzingfailedspansandbackenderrorsinkibana">Analyzing failed spans and backend errors in Kibana</h3>
<p>While you can find all kinds of spans in the dashboard's <strong>All spans</strong> panel, you can narrow them down to failed ones only by exploring the <strong>Failed spans</strong> panel instead.</p>
<p>For the New York path, find a failed span and expand it. The trace waterfall highlights the backend error, and the exception details show the intentional backend rule that only supports European cities.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltd3515f83ee7c1ce3/6a85cd8018249c1b6b18f81b/blog-dashboard-android-overview-explore-failed-spans.png" alt="Explore failed spans" /></p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt8075553525ac7f75/6a85cd834710c61975d3cbb6/blog-discover-failed-span-dialog-open.png" alt="Discover failed span dialog open" /></p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blte5dcfeb3df0e6c34/6a85cd86f5f1a06eb82ec95f/blog-discover-failed-span-dialog.png" alt="Discover failed span dialog" /></p>
<h2 id="reviewingcrashdetailsandstacktracesintheexceptiondashboard">Reviewing crash details and stacktraces in the exception dashboard</h2>
<p>Crash reporting is provided by the <a href="https://www.elastic.co/docs/reference/opentelemetry/edot-sdks/android/automatic-instrumentation#crash-reporting">crash automatic instrumentation plugin</a>. When an unhandled exception crashes the app, EDOT Android stores the crash event on disk. The event is exported the next time the app starts. Disk buffering ensures the crash event is not lost even if the network was unavailable at the time of the crash.</p>
<p>In the Application Overview dashboard, scroll to the <strong>Crashes</strong> section. You will see crash groups listed by a computed stacktrace group ID. Select a group and click <strong>View crash details</strong> to open the Exception Details dashboard.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt5facd8c29dd053d6/6a85cd8927c5cdb9ee5f7444/blog-dashboard-android-overview-crash-list.png" alt="Crash list" /></p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt0be9ec5b582ea0be/6a85cd8c9d2b71fe05f939fc/blog-dashboard-android-overview-crash-view-details.png" alt="View crash details" /></p>
<p>The Exception Details dashboard shows a set of metrics to better understand the impact of the selected crash, as well as its full stacktrace. Crash events are grouped based on their stacktrace, which helps ensure that the same crash is counted and aggregated in this dashboard to better understand a single crash's impact on your application.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt36c7aefc29888240/6a85cd8f1aa1e11536ff8dab/blog-dashboard-android-exceptions.png" alt="Exception dashboard view" /></p>
<p>For this demo, the stacktrace points to the intentional crash in <code>MainActivity</code>. The nearby session events should also include the custom <code>Crash button click</code> log created just before the crash, which helps explain how the crash was triggered. We'll take a look at how to inspect a session to get an idea of the user's journey within your application that led them to a crash.</p>
<h2 id="usingsessionstounderstanduserflowinedotandroid">Using sessions to understand user flow in EDOT Android</h2>
<p>Mobile troubleshooting often starts with a single bad outcome (a crash, an error, a slow UX), but the useful question is what happened before that outcome. EDOT Android helps answer that by attaching <code>session.id</code> to every span and log emitted by the application, even for manually created ones.</p>
<p>A session is meant to cover a single user interaction with your application. A new one is created when there is no previous active session or when the previous session has expired. Sessions expire after 30 minutes of inactivity. If the app stays active, a session can last up to 4 hours.</p>
<p>This lets you query all the telemetry from a single session and review it in order. After finding a crash group, drill into one affected session from the <strong>Top affected sessions</strong> panel and review the event timeline. You can see the custom logs, app startup spans, HTTP request spans, and crash data together in one investigation path.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt4afad0b2945a3c77/6a85cd92f61d6ea4129c2b6d/blog-dashboard-android-exceptions-view-session-details.png" alt="Exception dashboard view session details" /></p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt239ba5bb7095356b/6a85cd9418249c055218f81f/blog-dashboard-android-overview-with-session-filter.png" alt="Overview dashboard with session filter" /></p>
<p>The <strong>Event timeline</strong> panel on the Application Overview dashboard is also useful here: select a session from the dashboard's top filters, and the timeline shows the full sequence of spans and logs in that session chronologically.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt13d994d2109d0253/6a85cd978c2944d923b8909f/blog-dashboard-android-overview-event-timeline.png" alt="Event timeline with session filter" /></p>
<h2 id="whatedotandroidaddsasamobileapmforproductionapps">What EDOT Android adds as a mobile APM for production apps</h2>
<p>The demo uses a local stack and simple code, but the same agent features apply to production apps.</p>
<p><strong>Disk buffering</strong> stores telemetry locally before export. This reduces data loss when the device has poor connectivity or the app is temporarily offline.</p>
<p><strong>Automatic instrumentation</strong> creates telemetry for supported targets without adding code around every call. Today that includes OkHttp, crash reporting, and an adapter for <a href="https://github.com/open-telemetry/opentelemetry-android">OpenTelemetry Android</a> instrumentation.</p>
<p><strong>Manual instrumentation</strong> lets you add spans, logs, and metrics for app-specific workflows. This is useful for screen loading times, checkout flows, login steps, feature usage, or any area where framework-level telemetry alone is not enough.</p>
<p><strong>Central configuration</strong> can remotely adjust selected EDOT Android behavior through Kibana when the OpAMP endpoint is configured. At the time of writing, central configuration for EDOT Android is in preview and supports settings such as recording and session sample rate.</p>
<p><strong>Distributed tracing</strong> connects Android app requests to backend service spans so you can trace the full path of any user action, from the tap on the screen to the database query on the server. EDOT Android ensures that your application's telemetry timestamps are in sync with the <a href="https://en.wikipedia.org/wiki/Coordinated_Universal_Time">coordinated universal time</a>. This ensures a proper trace waterfall hierarchy later on in Kibana, where different components are properly coordinated in time.</p>
<h2 id="cleanupthedemo">Clean up the demo</h2>
<p>When you are finished, stop the backend in case you're planning to restart it later, or uninstall it otherwise:</p>
<pre><code>./backend-manager stop
# ./backend-manager uninstall
</code></pre>
<p>Then stop or uninstall the local Elastic Stack:</p>
<pre><code>cd elastic-start-local
./stop.sh
# ./uninstall.sh
</code></pre>
<h2 id="gettingstartedwithedotandroidinyourownapp">Getting started with EDOT Android in your own app</h2>
<p>EDOT Android gives native Android teams an OpenTelemetry-based path for mobile APM in Elastic. With a small Gradle setup and one early initialization call, you get automatic HTTP spans, crash reporting, session tracking, and direct access to the OpenTelemetry SDK for custom telemetry, and you can see it all tied together in Kibana's Android dashboards.</p>
<p>Observability is a crucial part of modern mobile development. Crashes, slow screens, and backend errors all impact real users, and the sooner you can identify root causes across the full request path, from the device to the database, the better. The demo app is a good first step because it exercises the complete workflow without requiring a production deployment. After that, the same setup model applies to your own app with production endpoints, API key authentication, and custom spans and logs tailored to your use cases.</p>
<p>Developer resources:</p>
<ul>
<li><a href="https://github.com/elastic/android-agent-demo">EDOT Android demo application</a></li>
<li><a href="https://www.elastic.co/docs/reference/opentelemetry/edot-sdks/android">EDOT Android documentation</a></li>
<li><a href="https://www.elastic.co/docs/reference/opentelemetry/edot-sdks/android/getting-started">EDOT Android getting started guide</a></li>
<li><a href="https://www.elastic.co/docs/reference/opentelemetry/edot-sdks/android/automatic-instrumentation">EDOT Android automatic instrumentation</a></li>
<li><a href="https://www.elastic.co/docs/reference/opentelemetry/edot-sdks/android/manual-instrumentation">EDOT Android manual instrumentation</a></li>
<li><a href="https://www.elastic.co/docs/reference/opentelemetry/edot-sdks/android/configuration">EDOT Android configuration</a></li>
<li><a href="https://www.elastic.co/docs/troubleshoot/ingest/opentelemetry/edot-sdks/android">EDOT Android troubleshooting</a> </li>
<li><a href="https://www.elastic.co/docs/reference/integrations/otel_android_dashboards">Android OpenTelemetry Assets dashboard docs</a></li>
</ul>
<p>Don't have an Elastic Cloud account yet? <a href="https://cloud.elastic.co/registration">Sign up for Elastic Cloud</a> and try out mobile observability with EDOT Android as described in this guide. We'd love to hear about your experience gaining visibility into your Android application stack with Elastic.</p>]]></content:encoded>
    <link>https://www.elastic.co/observability-labs/blog/distributed-tracing-android-mobile-apm-opentelemetry</link>
    <guid isPermaLink="false">distributed-tracing-android-mobile-apm-opentelemetry</guid>
    <category><![CDATA[APM]]></category>
    <category><![CDATA[OpenTelemetry]]></category>
    <category><![CDATA[What's New]]></category>
    <dc:creator><![CDATA[Cesar Munoz]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt9a224bedfc4c93ec/6a85cd9a682666089e1eac5f/header-image.png" length="0" type="image/png"/>
    <pubDate>Wed, 12 Aug 2026 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Your SLO is on fire; here's how to find the arsonist in Elastic Observability]]></title>
    <description><![CDATA[When SLO alerting flags a burn rate spike, follow the SLI from the alert detail page through bad event spans and trace waterfalls to find the exact dependency burning your SLO error budget, all without leaving the investigation.]]></description>
    <content:encoded><![CDATA[<p>The redesigned <a href="https://www.elastic.co/docs/solutions/observability/incident-management/create-an-slo-burn-rate-rule">SLO burn rate alert</a> detail page in Elastic Observability connects the alert to the SLI, the events behind it, and the traces that show which dependency is burning your <a href="https://www.elastic.co/docs/solutions/observability/incident-management/service-level-objectives-slos">service-level objective (SLO)</a> error budget.
You see when burn rate rose, compare good and bad event spans side by side, then follow the bad ones into the trace waterfall to find the failing hop, all without switching tools.</p>
<p>This walkthrough shows the full path using the <a href="https://github.com/elastic/opentelemetry-demo">OpenTelemetry Demo</a> (Astronomy Shop), where a failing shipping dependency drives every checkout SLI miss.
You can reproduce it by running the demo against Elastic Observability, defining an APM availability SLO on checkout, and introducing a failing dependency.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt4bc4f3056185ce56/6a7f1ae0448e4e7e625c0b70/slo-burn-rate-3.gif" alt="Comparing good and bad event spans for an SLO SLI" /></p>
<p><strong>Availability</strong></p>
<p>SLO burn rate analysis is available in Elastic Observability serverless today and is coming to Elastic Cloud Hosted and self-managed deployments in 9.5.</p>
<h2 id="prerequisitesforsloburnratealertinginelasticobservability">Prerequisites for SLO burn rate alerting in Elastic Observability</h2>
<p>You need an instrumented service and an SLO defined on its APM data in Elastic Observability.</p>
<ul>
<li><strong>Application instrumentation:</strong> one of the following:</li>
<li><strong><a href="https://www.elastic.co/docs/solutions/observability/apm/apm-agents">Elastic APM agents</a></strong> for Java, .NET, Node.js, Python, PHP, Ruby, Go, and other supported languages</li>
<li><strong><a href="https://www.elastic.co/docs/reference/opentelemetry">Elastic Distributions of OpenTelemetry (EDOT)</a></strong> language SDKs</li>
<li><strong><a href="https://www.elastic.co/docs/solutions/observability/apm/opentelemetry">OpenTelemetry SDKs</a></strong> sending OTLP via the EDOT Collector, Elastic Agent, APM Server, or the Managed OTLP endpoint.
If you run a custom upstream Collector pipeline, include both the <a href="https://www.elastic.co/docs/reference/edot-collector/components/elasticapmconnector"><code>elasticapm</code> connector</a> and the <a href="https://www.elastic.co/docs/reference/edot-collector/components/elasticapmprocessor"><code>elasticapm</code> processor</a>.
Those components ship with the EDOT Collector (or a custom EDOT-like build); they are not part of the standard OpenTelemetry Collector Contrib distribution.
For wiring details, see the <a href="https://www.elastic.co/docs/solutions/observability/get-started/opentelemetry/use-cases/upstream-collector">upstream collector setup</a>.</li>
<li><strong>SLO definition:</strong> an SLO based on <a href="https://www.elastic.co/docs/solutions/observability/incident-management/create-an-slo">APM latency or APM availability</a> for the service you want to protect.
Elastic creates a default burn rate alert rule when you save the SLO.</li>
<li><strong>Backend:</strong> Elastic Observability serverless today, or Elastic Stack 9.5 on Elastic Cloud Hosted and self-managed when 9.5 releases.</li>
</ul>
<h2 id="sloburnrateanalysiswalkthroughalerttofailingdependency">SLO burn rate analysis walkthrough: alert to failing dependency</h2>
<h3 id="step1reviewsloburnrateonthealertdetailpage">Step 1: Review SLO burn rate on the alert detail page</h3>
<p>Open the SLO burn rate alert detail page from the notification.</p>
<p>The chart shows when burn rate rose and how much error budget remains for the rolling period.
From there, open the SLI linked to the alert and decide whether you are looking at a short spike or sustained degradation before you dig into events.</p>
<p>In this example, the availability SLO for checkout is burning budget quickly, and the rise is recent, so the change likely landed in the last few hours rather than as long-running drift:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt81cb47bc3eea7e6f/6a7f1ae273d9bd661329df65/slo-burn-rate-1.gif" alt="SLO burn rate alert detail showing when burn rate rose" /></p>
<h3 id="step2checktheslierrorratedrivingsloerrorbudgetconsumption">Step 2: Check the SLI error rate driving SLO error budget consumption</h3>
<p>On the SLI view, check the error rate that feeds the SLO.</p>
<p>You can look at the SLI as <a href="https://www.elastic.co/docs/solutions/observability/apm/metrics">RED metrics</a> in the APM UI for a quick service-level picture, or open <strong>Traces in Discover</strong> when you want to filter, compare, or break down the spans that count toward the SLO.</p>
<p>Here, error rate on checkout has climbed, which matches the jump in availability SLO burn rate:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltab8b81391074958a/6a7f1ae63ce8e234c8cf57b6/slo-burn-rate-2.gif" alt="SLI error rate with RED metrics and Traces in Discover" /></p>
<h3 id="step3comparegoodandbadeventspansforthesli">Step 3: Compare good and bad event spans for the SLI</h3>
<p>Open the events for the SLI and switch between spans for good events and bad events.</p>
<p>Differences between those sets usually show up before you open individual traces.
In this case, bad events share a pattern that good events do not, which narrows where to look next:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt4bc4f3056185ce56/6a7f1ae0448e4e7e625c0b70/slo-burn-rate-3.gif" alt="Good and bad event spans for the SLO SLI" /></p>
<h3 id="step4findthefailingspaninthetracewaterfall">Step 4: Find the failing span in the trace waterfall</h3>
<p>Open a few sample spans for bad events and view each trace in the waterfall.</p>
<p>That puts the failing span in the request path so you can see which hop is causing the SLI miss.
In this example, the failing span is a downstream call, not application logic inside checkout itself:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt1344742cca793033/6a7f1aebb43770167c4d7112/slo-burn-rate-4.gif" alt="Trace waterfall showing where bad events occur in the request path" /></p>
<h3 id="step5verifywhichdependencyisburningyoursloerrorbudget">Step 5: Verify which dependency is burning your SLO error budget</h3>
<p>Open the service that owns the bad span and check its dependencies.</p>
<p>Here the waterfall points to <strong>shipping</strong> and a failing call to <strong>quote-old</strong>.
Requests to that dependency fail on every attempt, so the SLO burn is driven by an external dependency rather than checkout code:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt7900bf577690e2ea/6a7f1aee3ce8e24fa8cf57ba/slo-burn-rate-5.gif" alt="Trace timeline and dependency view showing 100% failed requests to quote-old" /></p>
<h2 id="sloburnrateinvestigationfromalerttorootcause">SLO burn rate investigation: from alert to root cause</h2>
<p>From an SLO burn rate alert in Elastic Observability, you can see when burn rate rose, open the linked SLI, compare good and bad event spans, inspect bad spans in the trace waterfall, and verify failing dependencies to find what is burning error budget.</p>]]></content:encoded>
    <link>https://www.elastic.co/observability-labs/blog/slo-burn-rate-analysis-trace-investigation</link>
    <guid isPermaLink="false">slo-burn-rate-analysis-trace-investigation</guid>
    <category><![CDATA[Incident Management]]></category>
    <category><![CDATA[APM]]></category>
    <dc:creator><![CDATA[Roshan Gonsalkorale]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt2c266fed417e9cd1/6a7f1af1b6b7347d70e491ca/header.jpg" length="0" type="image/jpeg"/>
    <pubDate>Tue, 28 Jul 2026 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[6x faster at 500 services: how we rebuilt the Kibana APM service map from canvas to React DOM]]></title>
    <description><![CDATA[Each service node shows alert, SLO and anomaly health so you can filter to breaching services only and embed the result on any Kibana dashboard, with full keyboard navigation across the topology.]]></description>
    <content:encoded><![CDATA[<p>We rebuilt the Kibana <a href="https://www.elastic.co/docs/solutions/observability/apm/service-map">APM service map in Elastic Observability</a> on <a href="https://reactflow.dev/">React Flow</a>. At 500 services it renders in 64ms, roughly 6x faster than the previous Cytoscape.js implementation, and ships 60% less JavaScript (69 KiB vs 172 KiB). Service nodes now show alert, <a href="https://www.elastic.co/docs/solutions/observability/incident-management/service-level-objectives-slos">SLO</a>, and anomaly health so you can filter to breaching services only and embed the result on any Kibana dashboard, with full keyboard navigation across the topology. <a href="https://www.elastic.co/observability-labs/blog/service-map-apm-dependency-analysis">From alert to failing dependency in four clicks: Elastic APM's embedded service map</a> covers the alert panel, SLO badges, and dashboard embedding in more depth.</p>
<p>Here's the map before this rebuild, and after:</p>
<p>| Before | After |
|---|---|
| <img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt1133c510816a5d38/6a7f02ccde2315758afd7718/before.png" alt="Service map before this rebuild" /> | <img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltcb0fcb2a3c84c7bc/6a7f02cffc63ab34e164c804/after.png" alt="Service map after this rebuild" /> |</p>
<h2 id="whytheapmservicemapmovedfromcytoscapejstoreactflowgraphvisualization">Why the APM service map moved from Cytoscape.js to React Flow graph visualization</h2>
<p>The payoff is concrete: <a href="https://reactflow.dev">React Flow</a> renders nodes as real React components instead of a canvas draw loop, so panning and zooming stay smooth on large topologies, our Elastic UI (EUI) nodes and badges render natively (no more re-rendering the full canvas on every update), and DOM-based rendering makes nodes naturally visible to screen readers, unlike canvas-based rendering.</p>
<p>It's lighter, too: the graph library dropped from 172.4 KiB (<code>cytoscape.js</code>) to 69 KiB (<code>@xyflow/react</code>), about 103 KiB less JavaScript to ship, <a href="https://github.com/elastic/kibana/issues/248470">#248470</a>.</p>
<h3 id="howmuchfasteristheapmservicemapwithreactflow">How much faster is the APM service map with React Flow?</h3>
<p>Before committing to the APM service map migration from Cytoscape.js to React Flow, we benchmarked both libraries at 100, 200, and 500 services (a synthetic chain topology, measured via Lighthouse and component-level timings averaged over multiple runs, <a href="https://github.com/elastic/kibana/issues/248470">#248470</a>):</p>
<p>| Services | Cytoscape.js render | React Flow render | Faster by |
|---|---|---|---|
| 100 | 61.6 ms | 15.0 ms | ~76% |
| 200 | 102.5 ms | 28.1 ms | ~73% |
| 500 | 392.5 ms | 64.1 ms | ~84% |</p>
<p>At 500 services, React Flow draws the map in ~64 ms versus Cytoscape.js's ~393 ms, roughly <strong>6x faster</strong>.
The underlying graph layout step alone got ~70-78% faster.
Total main-thread blocking time dropped ~20%, and peak memory was slightly lower despite rendering everything as real DOM. Those results and more are available in the <a href="https://github.com/elastic/kibana/issues/248470#issuecomment-3744191001">benchmark results comment</a>.</p>
<h3 id="keyboardnavigationandaccessibilityintheapmservicemap">Keyboard navigation and accessibility in the APM service map</h3>
<p>The map now announces your position in the topology as you navigate, with live screen-reader context for every interaction (<a href="https://github.com/elastic/kibana/pull/251444">#251444</a>).</p>
<ul>
<li><p><strong>Spatial arrow-key navigation:</strong> arrow keys navigate based on visual proximity rather than logical document order.
Focus moves to the nearest service in the visual direction you press, even through complex serpentine folds.</p></li>
<li><p><strong>Direct shortcuts:</strong> launch flyouts with Enter or Space, and dismiss them with Escape.</p></li>
<li><p><strong>Live announcements:</strong> every interaction is backed by a screen-reader announcement, such as "Selected connection from A to B."</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt2ee3cef26ace23b2/6a7f02d31967ea0aa13302c0/sm_a11y_nav.gif" alt="Keyboard navigation inside service map" /></p></li>
</ul>
<h2 id="howtheapmservicemaplayoutalgorithmworks">How the APM service map layout algorithm works</h2>
<p>The APM service map uses Dagre for hierarchical graph layout, then applies serpentine folding to prevent long dependency chains from producing unreadable high-aspect-ratio strips:</p>
<ol>
<li><p><strong>Dagre hierarchical layout:</strong> we use <a href="https://github.com/dagrejs/dagre">Dagre</a>, a graph layout engine, with a direction toggle between horizontal and vertical that you can change from the options panel.
If Dagre fails to compute a layout, the map falls back to a deterministic grid so it stays interactive and error-free.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltf5d7ab1ff062a077/6a7f02d6227b1c8484598184/layout_d.gif" alt="Layout switching using Dagre" /></p></li>
<li><p><strong>Serpentine folding for long chains:</strong> long dependency pipelines produce thin, unreadable strips that require heavy zooming.
When the aspect ratio becomes too extreme, we wrap ranks into stacked bands that snake back and forth, so "fit view" can zoom in much tighter on the actual services.
If the topology is already compact or has too many cross-band edges, we skip folding.
(<a href="https://github.com/elastic/kibana/pull/272900">#272900</a>).</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt7721116f45ff8890/6a7f02da05b7b564d518b4bb/serpentine.png" alt="Serpentine-folded layout for a long dependency chain" /></p></li>
</ol>
<h2 id="servicedependencymappingandkibanadashboardembeddingunderthehood">Service dependency mapping and Kibana dashboard embedding under the hood</h2>
<p>The visual refresh is the easy part to see. Some of the architecture changes that underpin the rebuilt map are:</p>
<h3 id="unifiedresourcenodesforcleanerservicedependencymapping">Unified resource nodes for cleaner service dependency mapping</h3>
<p>We now group external dependencies into unified resource nodes, reducing visual noise.
We also fixed message-queue span grouping so those patterns no longer create orphaned nodes (<a href="https://github.com/elastic/kibana/pull/252713">#252713</a>).</p>
<h3 id="servicemapflyoutchartspoweredbyesqlandlens">Service map flyout charts powered by ES|QL and Lens</h3>
<p>The service flyout's infrastructure and <a href="https://www.elastic.co/docs/solutions/observability/apm/metrics">RED metrics</a> use <a href="https://www.elastic.co/docs/explore-analyze/query-filter/languages/esql">ES|QL</a> and Kibana's core <a href="https://www.elastic.co/docs/explore-analyze/visualize/lens">Lens</a> visualization engine, giving the map the same charting experience used across Kibana (<a href="https://github.com/elastic/kibana/pull/273713">#273713</a>).</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt0d3bb963b1a7677e/6a7f02dc63e959297c73d937/sm_flyout.png" alt="The service flyout in APM service map" /></p>
<h3 id="embeddingtheservicemaponanykibanadashboard">Embedding the service map on any Kibana dashboard</h3>
<p>Adding a service map to a dashboard takes one click.
Open the "Copy to dashboard" menu from any map view in APM, and the panel carries your current environment, service filter, KQL query, and filter chips directly across.
Relative time ranges like <code>now-15m</code> transfer as-is rather than freezing to an absolute timestamp, so the panel stays live on the dashboard (<a href="https://github.com/elastic/kibana/pull/272277">#272277</a>).</p>
<p>Once embedded, the panel adapts to where it lives.
It shows or hides controls based on view mode, and respects global time settings without overwriting your relative ranges (<a href="https://github.com/elastic/kibana/pull/274551">#274551</a>).</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltfcaec905ec3f3339/6a7f02e1b437707cfd4d6863/dashboard_embed.gif" alt="Adding a service map to a dashboard from APM and navigating in full-screen mode" /></p>
<h2 id="whatsnextfortheapmservicemap">What's next for the APM service map</h2>
<p>The service map rebuild is a foundation, not a finish line.
Beyond what's available in 9.5, some of what's coming next is already on the <a href="https://github.com/orgs/elastic/projects/2066/views/2?sliceBy%5Bvalue%5D=APM+%2F+RUM">APM service map roadmap</a>.</p>
<h3 id="contributors">Contributors</h3>
<p>I led this migration and built these features alongside a great team.
Thanks to Samuel Brito, Gonçalo Rica Pais da Silva, Irene Blanco Fabregat, Carlos Crespo, Miriam Aparicio Garcia, Sandra G, and Nathan Smith for the engineering, and to Karolina Kurstak and Roshan Gonsalkorale for the design and product work.</p>]]></content:encoded>
    <link>https://www.elastic.co/observability-labs/blog/apm-service-map-react-flow-migration</link>
    <guid isPermaLink="false">apm-service-map-react-flow-migration</guid>
    <category><![CDATA[APM]]></category>
    <dc:creator><![CDATA[Jenny Pavlova]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt594f6736c6660717/6a7f02e46693f80e62663b2f/header.png" length="0" type="image/png"/>
    <pubDate>Tue, 28 Jul 2026 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Three clicks from alert to error log: breaking down RED metrics by any span attribute in Elastic Observability]]></title>
    <description><![CDATA[See which pod, deployment or version is driving a RED metrics change by breaking down span attributes in Discover, then trace a failing span to the error log behind it.]]></description>
    <content:encoded><![CDATA[<p>Elastic Observability now lets you break down <a href="https://www.elastic.co/docs/solutions/observability/apm/metrics">RED metrics</a> in Discover by any span attribute on your traces. Split by pod, deployment, service version or any custom dimension to see which values moved the metric. From there, you can open a failing span's trace waterfall and follow it through to the linked error log in a few clicks, no query needed.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt1cf0a6474d8514d8/6a7f1a2d96b5a66aff87b881/metric-drivers-2.gif" alt="Breaking down RED metrics by span attribute in Discover" /></p>
<p><strong>Availability</strong></p>
<p>This is available in serverless today and is coming to Elastic Cloud Hosted and self-managed deployments in 9.5.</p>
<h2 id="whatyouneedforredmetricsbreakdowninelasticobservability">What you need for RED metrics breakdown in Elastic Observability</h2>
<p>You need trace data from a service instrumented with any method <a href="https://www.elastic.co/docs/solutions/observability/apm/ingest">Elastic APM supports</a>.</p>
<ul>
<li><strong>Application instrumentation:</strong> one of the following:</li>
<li><strong><a href="https://www.elastic.co/docs/solutions/observability/apm/apm-agents">Elastic APM agents</a></strong> for Java, .NET, Node.js, Python, PHP, Ruby, Go, and other supported languages.</li>
<li><strong><a href="https://www.elastic.co/docs/solutions/observability/apm/opentelemetry">OpenTelemetry SDKs</a></strong> sending OTLP via Elastic Agent or an <a href="https://www.elastic.co/docs/solutions/observability/get-started/opentelemetry/use-cases/upstream-collector">upstream OpenTelemetry Collector</a> with the <a href="https://www.elastic.co/docs/reference/edot-collector/components/elasticapmconnector"><code>elasticapm</code> connector</a> under <strong>connectors</strong> (not processors).</li>
<li><strong>Useful attributes:</strong> breakdown works best when spans include the dimensions you want to compare (<code>k8s.pod.name</code>, <code>k8s.deployment.name</code>, <code>service.version</code>, and others). You can also declare custom attributes on spans: add <a href="https://www.elastic.co/docs/solutions/observability/apm/metadata">labels</a> to transactions and spans with Elastic APM agents, or set <a href="https://www.elastic.co/docs/solutions/observability/apm/opentelemetry/attributes">OpenTelemetry attributes</a> on spans and resources with OpenTelemetry SDKs. Those custom fields work as breakdown dimensions too.</li>
<li><strong>Backend:</strong> Observability serverless today, or Elastic Stack 9.5 on Elastic Cloud Hosted and self-managed when 9.5 releases.</li>
</ul>
<h2 id="howtogofromaredmetricsalerttotherootcauseerrorlog">How to go from a RED metrics alert to the root-cause error log</h2>
<h3 id="step1reviewredmetricsonthealertdetailpage">Step 1: Review RED metrics on the alert detail page</h3>
<p>When you receive a notification for a RED metric threshold breach, if you open the <a href="https://www.elastic.co/docs/solutions/observability/apm/create-apm-rules-alerts">alert detail page</a>, you can review the symptoms for the impacted service on one page.</p>
<p>In our example, failed transactions have clearly increased for the cart service, so we want to understand what is driving that RED metric change:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltd60e8db89d43d27f/6a7f1a31eab5be222b20aaf4/metric-drivers-1.gif" alt="Alert detail showing RED symptoms for the cart service" /></p>
<h3 id="step2breakdownredmetricsbyspanattributesindiscover">Step 2: Break down RED metrics by span attributes in Discover</h3>
<p>To investigate why a RED metric changed, open <strong>Traces in Discover</strong> and use the new <strong>breakdown</strong> feature to split RED metrics by any attribute on your spans.
In our example, we're going to check Kubernetes attributes and service version, but you could break down by any span attribute you send (e.g. <code>cloud.region</code>, <code>cloud.availability_zone</code>, or <code>container.id</code>).</p>
<p>Each breakdown shows which attribute values moved the metric, so you can see whether the problem is isolated to one pod, deployment, version, or whatever dimension you split on.</p>
<p>In our example, error rate clusters on a single Kubernetes deployment, which points the investigation at a release. We will break down by <code>service.version</code> to validate our hypothesis:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt1cf0a6474d8514d8/6a7f1a2d96b5a66aff87b881/metric-drivers-2.gif" alt="Breaking down RED metrics by span attribute in Discover" /></p>
<h3 id="step3openthetracewaterfallandreadthelinkederrorlog">Step 3: Open the trace waterfall and read the linked error log</h3>
<p>Once trace breakdown has identified a specific service version as the likely cause, we can filter by that <code>service.version</code> and look at sample failing spans to see if they explain why the version is causing failures.</p>
<p>Open the trace waterfall for one failing span and follow through to the linked error log.</p>
<p>In our example, the error log points to bad configuration that could be causing the issue. Either way, we have narrowed the investigation to a solid hypothesis we can act on in just a few clicks:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt9ef23df1d803c870/6a7f1a356c6eac6b6ef14598/metric-drivers-3.gif" alt="Trace waterfall and error log for a sample failing span" /></p>
<h2 id="fromredmetricsalerttoerrorloginelasticobservability">From RED metrics alert to error log in Elastic Observability</h2>
<p>From a RED metric alert, you can review the symptomatic service, break down <strong>Traces</strong> in Discover by any attribute on your spans, and open a failing span's trace waterfall to reach the error log in just a few clicks.</p>]]></content:encoded>
    <link>https://www.elastic.co/observability-labs/blog/red-metrics-trace-breakdown-discover</link>
    <guid isPermaLink="false">red-metrics-trace-breakdown-discover</guid>
    <category><![CDATA[APM]]></category>
    <category><![CDATA[OpenTelemetry]]></category>
    <category><![CDATA[What's New]]></category>
    <dc:creator><![CDATA[Roshan Gonsalkorale,Irene Blanco Fabregat]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blte55244369ae6789c/6a7f1a3896b5a6329d87b885/header.jpg" length="0" type="image/jpeg"/>
    <pubDate>Mon, 27 Jul 2026 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[From alert to failing dependency in four clicks: Elastic APM's embedded service map]]></title>
    <description><![CDATA[The APM service map is now embedded on alert pages with filters, connection metrics and a service fly-out so you can analyse dependencies and find the root cause without leaving the alert.]]></description>
    <content:encoded><![CDATA[<p>Elastic APM 9.5 embeds the <a href="https://www.elastic.co/docs/solutions/observability/apm/service-map">service map</a> on every alert detail page, in the APM UI and on Kibana dashboards.
Open a <a href="https://www.elastic.co/docs/solutions/observability/apm/metrics">RED metric</a> alert and start dependency analysis without leaving the page.
The map now includes text search, health filters, connection RED metrics with drill-through to Traces in Discover, and a service fly-out that previews any node's health right from the map.
This walkthrough uses the <a href="https://github.com/elastic/opentelemetry-demo">OpenTelemetry Demo</a> (Astronomy Shop) to go from a RED metric alert on a checkout service to a failing shipping dependency in four steps.</p>
<div>
    
</div>
<p><em>From a checkout RED metric alert to a failing shipping dependency on the APM service map (OpenTelemetry Demo).</em></p>
<h2 id="wheretheapmservicemapisavailable">Where the APM service map is available</h2>
<p>This is available in Elastic Observability serverless today and is coming to Elastic Cloud Hosted and self-managed deployments in 9.5.</p>
<h2 id="prerequisitesforapmservicemapdependencyanalysis">Prerequisites for APM service map dependency analysis</h2>
<p>You need trace data from services instrumented with any method <a href="https://www.elastic.co/docs/solutions/observability/apm/ingest">Elastic APM supports</a>.</p>
<ul>
<li><strong>Application instrumentation:</strong> one of the following:</li>
<li><strong><a href="https://www.elastic.co/docs/solutions/observability/apm/apm-agents">Elastic APM agents</a></strong> for Java, .NET, Node.js, Python, PHP, Ruby, Go, and other supported languages</li>
<li><strong><a href="https://www.elastic.co/docs/reference/opentelemetry">Elastic Distributions of OpenTelemetry (EDOT)</a></strong> language SDKs</li>
<li><strong><a href="https://www.elastic.co/docs/solutions/observability/apm/opentelemetry">OpenTelemetry SDKs</a></strong> sending OTLP via the EDOT Collector, Elastic Agent, APM Server, or the Managed OTLP endpoint.
If you run a custom upstream Collector pipeline, include both the <a href="https://www.elastic.co/docs/reference/edot-collector/components/elasticapmconnector"><code>elasticapm</code> connector</a> and the <a href="https://www.elastic.co/docs/reference/edot-collector/components/elasticapmprocessor"><code>elasticapm</code> processor</a>.
Those components ship with the EDOT Collector (or a custom EDOT-like build); they are not part of the standard OpenTelemetry Collector Contrib distribution.
For wiring details, see the <a href="https://www.elastic.co/docs/solutions/observability/get-started/opentelemetry/use-cases/upstream-collector">upstream collector setup</a>.</li>
<li><strong>Service map data:</strong> distributed traces that link the services in your architecture. The map draws connections from span parent-child relationships across instrumented services.</li>
<li><strong>Backend:</strong> Elastic Observability serverless today, or Elastic Stack 9.5 on Elastic Cloud Hosted and self-managed when 9.5 releases.</li>
</ul>
<h2 id="dependencyanalysiswalkthroughfromapmalerttoshippingdependency">Dependency analysis walkthrough: from APM alert to shipping dependency</h2>
<h3 id="step1apmservicemaponthealertdetailpage">Step 1: APM service map on the alert detail page</h3>
<p>When you receive a notification for a <a href="https://www.elastic.co/docs/solutions/observability/apm/create-apm-rules-alerts">RED metric threshold breach</a> on the checkout service, open the alert detail page.</p>
<p>The upgraded <a href="https://www.elastic.co/docs/solutions/observability/apm/service-map">service map</a> is embedded on the page, so you can start analysing dependencies the moment you land on the alert. You see checkout in context with its upstream and downstream connections without navigating away.</p>
<p>In our example, failed transactions have increased on checkout. The map is already scoped to the alert time range:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt38b23ac64f528167/6a7f1a956693f8002f664383/depedencies-walkthrough-1.gif" alt="RED metric alert for checkout with embedded service map" /></p>
<h3 id="step2spottingafailingdependencyontheservicemap">Step 2: Spotting a failing dependency on the service map</h3>
<p>When we scan through the downstream dependencies, we can see some indicators that there is a problem with the <strong>checkout</strong> and <strong>shipping</strong> services. When we open the shipping service fly-out to view more details, we can see the transactions for <code>/get-quote</code> have an elevated failure rate:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blte13f5353240dc962/6a7f1a99e88c65206c00bb04/depedencies-walkthrough-2.gif" alt="Identifying shipping as the failing downstream dependency on the service map" /></p>
<h3 id="step3drillingintofailingtransactionstoidentifyredmetricdrivers">Step 3: Drilling into failing transactions to identify RED metric drivers</h3>
<p>Click on the <code>/get-quote</code> transaction to drill into these transactions. We can see a release marker for version <code>2.3.0</code> that seems to correlate to increase in failures. To validate this is the main contributing factor, we want to analyse these transactions and check a few dimensions to isolate the behaviour to the release:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltfa07c9992b1e7ba0/6a7f1a9cea068d80f9f0a2d1/depedencies-walkthrough-3.gif" alt="Service fly-out preview for shipping with RED metrics and transaction breakdown" /></p>
<h3 id="step4comparingredmetricsbydimensionintracesindiscover">Step 4: Comparing RED metrics by dimension in Traces in Discover</h3>
<p>Using Traces in Discover, we can use the <strong>breakdown</strong> feature to compare various attributes to confirm the RED metric change is due to the release. As we break down by <code>service.version</code>, <code>k8s.deployment.name</code> and <code>k8s.pod.name</code>, we can see the failure rate is highly elevated for just this Kubernetes deployment.</p>
<p>These field names match the OpenTelemetry semantic conventions used by the demo (EDOT / OTel instrumentation). If you instrument with classic Elastic APM agents, use the ECS-style <code>kubernetes.*</code> equivalents instead (see <a href="https://www.elastic.co/docs/reference/ecs/ecs-otel-alignment-details">ECS ↔ OTel field alignment</a>).</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt0de3780b0fc2841b/6a7f1aa03cab1cd5d60e4ca1/depedencies-walkthrough-4.gif" alt="Service fly-out preview for shipping with RED metrics and transaction breakdown" /></p>
<p>Given we have clear indicators that this problem is almost certainly due to a problem caused by this Kubernetes deployment, we would next investigate the changes this deployment made so we can identify a mitigation strategy to return the system to a healthy status, most likely a rollback of the Kubernetes deployment.</p>
<h2 id="dependencyanalysiswiththeservicemaponcustomdashboards">Dependency analysis with the service map on custom dashboards</h2>
<p>The APM service map can also be added as a panel to custom Kibana dashboards.
If you have a custom Dashboard you would like to surface for a debugging problem with your service, you can <a href="https://www.elastic.co/docs/solutions/observability/incident-management/create-manage-rules#observability-create-manage-rules-add-investigation-resources">attach it to your alert rule</a> so users can use this to complement the default alert detail view.</p>
<p>When you receive the notification and land on the alert detail page, you can click <strong>Related Dashboards</strong> and open your custom dashboard. From here, you can use the same Service Map panel to quickly analyse dependencies just like the alert detail page:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltab1edeb5a3eadb02/6a7f1aa3de231529c6fd80a1/depedencies-variant-dashboards.gif" alt="Related dashboard with service map panel" /></p>
<h2 id="whatsnewintheapmservicemap">What's new in the APM service map</h2>
<p>Below is a list of the upgrades to the service map:</p>
<h3 id="servicemapembeddedonapmalertpagesanddashboards">Service map embedded on APM alert pages and dashboards</h3>
<ul>
<li>All APM alert detail pages will have the service map embedded to facilitate faster dependency analysis.</li>
<li>There is a new <strong>Service Map</strong> Dashboard panel that you can add to any Dashboard.</li>
</ul>
<h3 id="fullscreenservicemapforlargearchitectures">Full-screen service map for large architectures</h3>
<p>Open the map in full screen from the APM UI or from a dashboard panel. Full screen gives you more room on large architectures and works the same whether you opened the map from an alert, a service, or a dashboard.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltcad0dd14b294d020/6a7f1aa72f00b21ad9efef29/depedencies-fullscreen.gif" alt="Full screen service map mode" /></p>
<h3 id="servicemapcontrolssearchfiltersandorientation">Service map controls: search, filters, and orientation</h3>
<p>On the service map in the APM UI and on dashboard embeds, the map toolbar adds controls to move faster on busy environments:</p>
<ul>
<li><strong>Search</strong> — find services by text string</li>
</ul>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt22ba0bee13abadc2/6a7f1aaab4377067e84d710a/depedencies-quickfilter.gif" alt="Service map search, orientation, and filter controls" /></p>
<ul>
<li><strong>Orientation</strong> — switch layout direction when dense graphs are hard to read</li>
</ul>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt2a5aae3c8b2aa685/6a7f1aad42a1179b7495c30b/depedencies-orientation.gif" alt="Service map search, orientation, and filter controls" /></p>
<ul>
<li><strong>Filters</strong> — narrow the map by alert status, whether a service has dependencies, anomaly status, and SLO status</li>
</ul>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltd7a696138886669a/6a7f1ab1c2cc0937d52499be/depedencies-quickfilters.gif" alt="Service map search, orientation, and filter controls" /></p>
<h3 id="servicemaplegendnodeshapesconnectionsandanomalyscores">Service map legend: node shapes, connections, and anomaly scores</h3>
<p>A built-in legend explains node shapes, connection styles, and health indicators on the map. You spend less time decoding colours and icons when you are triaging under pressure.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt1a46daf232cc305b/6a7f1ab5ea068d282bf0a2d5/depedencies-legend.png" alt="Service map with legend open" /></p>
<p>The legend covers node shapes (instrumented services, databases and messaging, grouped resources), connection styles (one-way and two-way requests), and anomaly score colour rings from low through critical:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt6430ac89390b5e54/6a7f1ab805b7b5b71e18bd49/depedencies-legend-detail.png" alt="Service map legend detail: node shapes, connections, and anomaly scores" /></p>
<h3 id="minimapfornavigatinglargeservicemaps">Minimap for navigating large service maps</h3>
<p>A minimap helps you orient yourself on large maps. It also highlights anomalous services at a glance, so you can spot outliers without panning across the full graph.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt8711d8dc8fa6ac00/6a7f1abc6693f8048e664389/depedencies-minimap.gif" alt="Service map minimap showing anomalous services" /></p>
<h3 id="redmetricsonconnectionswithdrillthroughtotracesindiscover">RED metrics on connections with drill-through to Traces in Discover</h3>
<p>Select a connection between two services to view <a href="https://www.elastic.co/docs/solutions/observability/apm/metrics">RED metrics</a> for the requests between them: rate, errors, and duration for that specific dependency edge.</p>
<p>From there, open <strong>Traces in Discover</strong> in one click to analyse those requests with full query flexibility. That is how we confirmed shipping was failing the requests checkout sent to it in the walkthrough above.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt1997328c2e5e1f37/6a7f1abf1967ea8534330b80/depedencies-edgeanalysis.gif" alt="Connection RED metrics with one-click to Traces in Discover" /></p>
<h3 id="addingtheservicemaptoadashboardfromtheapmui">Adding the service map to a dashboard from the APM UI</h3>
<p>From the service map in the APM UI, add the current map view to a dashboard in one click. You do not need to rebuild filters or time range settings manually when you want the same map on a team dashboard.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt1c6854adfac03760/6a7f1ac33cab1c1f090e4ca7/depedencies-add-to-dashboard.gif" alt="One-click add service map to dashboard from APM UI" /></p>
<h2 id="serviceflyoutpreviewredmetricsanomaliesandslosfromthemap">Service fly-out: preview RED metrics, anomalies and SLOs from the map</h2>
<p>Wherever you view the service map in Kibana, you can open a service fly-out to preview a service without leaving the map.</p>
<p>The fly-out shows:</p>
<ul>
<li><strong>RED metrics</strong> for the service</li>
<li><strong>Anomaly status</strong> — whether machine learning has flagged unusual behaviour</li>
<li><strong>SLO status</strong> — whether the service is meeting its objectives</li>
<li><strong>Transaction breakdown</strong> — how rate, errors, and duration split across transaction types</li>
</ul>
<p>Use it to sanity-check a node before you open the full service page or follow a connection into <strong>Traces in Discover</strong>.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltd1844d9ad0008151/6a7f1ac6ead8ec2693baac5a/depedencies-fly-out.gif" alt="Service fly-out with RED metrics, anomaly and SLO status, and transaction breakdown" /></p>
<h2 id="summaryfromapmalerttorootcausewiththeservicemap">Summary: From APM alert to root cause with the service map</h2>
<p>From a RED metric alert on checkout, the embedded service map showed shipping as the failing downstream dependency. Map controls, connection RED metrics, dashboard embeds, and the service fly-out are available on every map view in Kibana, so you can start dependency analysis wherever you already work.</p>
<h2 id="furtherreading">Further Reading</h2>
<p>You can see how we implemented these changes in the following post from Jenny Pavlova, who led the technical implementation:</p>
<p><a href="https://ela.st/9-5-apm-service-map-update">6x faster at 500 services: how we rebuilt the Kibana APM service map from canvas to React DOM</a></p>
<h3 id="relatedposts">Related Posts</h3>
<p>See other recent improvements we've made for observing instrumented services in the blogs below:</p>
<ul>
<li><a href="https://ela.st/9-5-infrastructure-metric-analysis-instrumented-services">Four clicks from alert to root cause: how Elastic Observability links APM services to Kubernetes infrastructure</a></li>
<li><a href="https://ela.st/9-5-faster-slo-burn-rate-analysis-instrumented-services">Your SLO is on fire; here's how to find the arsonist in Elastic Observability</a></li>
<li><a href="https://ela.st/9-5-analyse-red-metric-drivers">Three clicks from alert to error log: breaking down RED metrics by any span attribute in Elastic Observability</a></li>
</ul>]]></content:encoded>
    <link>https://www.elastic.co/observability-labs/blog/service-map-apm-dependency-analysis</link>
    <guid isPermaLink="false">service-map-apm-dependency-analysis</guid>
    <category><![CDATA[APM]]></category>
    <category><![CDATA[OpenTelemetry]]></category>
    <dc:creator><![CDATA[Roshan Gonsalkorale,Jenny Pavlova,Karolina Kurstak]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltcb9c625d49e1272d/6a7f1ac9e88c656f9800bb0c/depedencies-header.png" length="0" type="image/png"/>
    <pubDate>Mon, 27 Jul 2026 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Four clicks from alert to root cause: how Elastic Observability links APM services to Kubernetes infrastructure]]></title>
    <description><![CDATA[Check service dependencies and compare per-pod CPU, memory and network trends on the Infrastructure tab to find which instance is causing trouble, all without leaving the alert investigation.]]></description>
    <content:encoded><![CDATA[<p>Elastic Observability links your <a href="https://www.elastic.co/docs/solutions/observability/apm/opentelemetry">OTel-instrumented services</a> to the Kubernetes hosts, containers, and pods they run on.
The <a href="https://www.elastic.co/docs/solutions/observability/apm/infrastructure">Infrastructure tab</a> in APM puts per-instance CPU, memory and network trends a few clicks away, so when a service degrades you can spot which pod lines up with when the problem started, all from inside the investigation.
This walkthrough follows a latency alert on a recommendation service from notification to the problematic pod in four steps.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blte83736c1f42a7f15/6a7f02af2f00b296c3efe6ef/step-04-check-infra-metric-trends.gif" alt="Correlating service latency with per-pod infrastructure metrics" /></p>
<h2 id="availability">Availability</h2>
<p>This is available in Elastic Observability serverless today and is coming to Elastic Cloud Hosted and self-managed deployments in 9.5.</p>
<h2 id="prerequisitesforlinkingapmservicestokubernetesinfrastructure">Prerequisites for linking APM services to Kubernetes infrastructure</h2>
<p>You need application traces and Kubernetes infrastructure metrics in the same Elastic Observability project.</p>
<ul>
<li><strong>Application instrumentation:</strong> EDOT-instrumented services sending traces via the EDOT Collector or an <a href="https://www.elastic.co/docs/solutions/observability/get-started/opentelemetry/use-cases/upstream-collector">upstream OpenTelemetry Collector</a> with both the <a href="https://www.elastic.co/docs/reference/edot-collector/components/elasticapmconnector"><code>elasticapm</code> connector</a> and the <a href="https://www.elastic.co/docs/reference/edot-collector/components/elasticapmprocessor"><code>elasticapm</code> processor</a>. The EDOT Collector includes both by default; for a custom upstream pipeline, see the <a href="https://www.elastic.co/docs/solutions/observability/get-started/opentelemetry/use-cases/upstream-collector">upstream collector setup</a>.</li>
<li><strong>Kubernetes observation:</strong> the cluster observed via OpenTelemetry with host and Kubernetes metrics from the EDOT Collector. See the <a href="https://www.elastic.co/docs/solutions/observability/get-started/opentelemetry/quickstart/serverless/k8s">Kubernetes quickstarts</a> and <a href="https://www.elastic.co/docs/solutions/observability/get-started/opentelemetry/use-cases/kubernetes">Kubernetes observability with EDOT</a> for setup.</li>
<li><strong>Backend:</strong> Observability serverless today, or Elastic Stack 9.5 on Elastic Cloud Hosted and self-managed when 9.5 releases.</li>
</ul>
<h2 id="apmalerttriagefromnotificationtoproblematicpod">APM alert triage: from notification to problematic pod</h2>
<h3 id="step1confirmtheservicedegradationontheapmalertdetailpage">Step 1: Confirm the service degradation on the APM alert detail page</h3>
<p>The redesigned <a href="https://www.elastic.co/docs/solutions/observability/apm/create-apm-rules-alerts">alert detail page</a> in Elastic Observability shows the impacted service, environment, endpoint and RED metrics in one view.
Open it from the alert notification.</p>
<p>You can clearly see which service is impacted, which environment it runs in, what endpoint is being affected and easily look for correlations in their RED metrics.
In this case, we can immediately rule out a spike in traffic as the throughput is clearly stable.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt9ee2e802b0b09fc9/6a7f02b34c4bfbf920ccd0fd/step-01-alert-detail.gif" alt="Alert showing high transaction latency on the recommendation service" /></p>
<h3 id="step2ruleoutservicedependencieswiththeembeddedservicemap">Step 2: Rule out service dependencies with the embedded service map</h3>
<p>The newly embedded <a href="https://www.elastic.co/docs/solutions/observability/apm/service-map">service map</a> preview on the alert detail page shows the health and RED metrics of every dependent service, so you can rule out upstream causes without navigating away.
In this case, we have been able to quickly rule out problems with other services causing the symptom with the symptomatic service:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt1711cc95500177fb/6a7f02b7448e4e15195c0268/step-02-check-dependencies.gif" alt="Service map showing healthy dependent services" /></p>
<h3 id="step3reviewkubernetesinfrastructuremetricsperpodcontainerandhost">Step 3: Review Kubernetes infrastructure metrics per pod, container and host</h3>
<p>After ruling out service dependencies, open the service's updated <a href="https://www.elastic.co/docs/solutions/observability/apm/infrastructure"><strong>Infrastructure</strong> tab</a> in Elastic Observability to check for infrastructure-level patterns.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blta5526498737af6af/6a7f02ba227b1cf310598174/step-03-view-service-check-infra.gif" alt="Infrastructure tab showing average metrics per instance for the symptomatic service" /></p>
<h3 id="step4compareperinstancemetrictrendstofindtherootcause">Step 4: Compare per-instance metric trends to find the root cause</h3>
<p>The <a href="https://www.elastic.co/docs/solutions/observability/apm/infrastructure">Infrastructure tab</a> shows the average metric values over the specified time period.
To really understand whether there is a problem with the infrastructure, we need to <strong>compare the pod, container and host metrics over time</strong>.
This allows us to easily spot differences between different entities that may correlate with when the service started showing symptoms.
In our example, we can clearly see a difference between some of the metrics between the pods that correlates with when the service symptoms began.
So we know there is something going on with the infrastructure that needs investigating:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blte83736c1f42a7f15/6a7f02af2f00b296c3efe6ef/step-04-check-infra-metric-trends.gif" alt="Infrastructure metric trends correlating latency with a change in CPU or network" /></p>
<h2 id="summaryfromapmalerttorootcauseinfourclicks">Summary: from APM alert to root cause in four clicks</h2>
<p>In just a few clicks from an alert in Elastic Observability, you can rule out healthy dependent services without leaving the alert detail page, then compare per-pod infrastructure metrics to see which instance correlates with when the symptoms started.</p>]]></content:encoded>
    <link>https://www.elastic.co/observability-labs/blog/apm-kubernetes-infrastructure-metrics-analysis</link>
    <guid isPermaLink="false">apm-kubernetes-infrastructure-metrics-analysis</guid>
    <category><![CDATA[APM]]></category>
    <category><![CDATA[OpenTelemetry]]></category>
    <category><![CDATA[Kubernetes]]></category>
    <dc:creator><![CDATA[Roshan Gonsalkorale,Miguel Sánchez Gómez]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blta9d1c405e47bfbf5/6a7f02beeab5be600a20a278/header.jpg" length="0" type="image/jpeg"/>
    <pubDate>Mon, 27 Jul 2026 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[How Elastic cut OpenTelemetry tail sampling memory by 65% with disk-backed trace storage]]></title>
    <description><![CDATA[Elastic contributed two features upstream to the OTel Collector's tail sampling processor. The span-ingest strategy lets sampling decisions happen earlier, and Pebble tail storage moves trace buffering to disk. It costs more CPU, but operators can raise decision_wait and num_traces without OOM kills.]]></description>
    <content:encoded><![CDATA[<p>Elastic contributed two upstream improvements to the OpenTelemetry Collector's tail sampling processor (<a href="https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/processor/tailsamplingprocessor"><code>tailsamplingprocessor</code></a>) that cut memory usage by up to 65%.
<code>sampling_strategy: span-ingest</code> lets sampling decisions happen at ingest time, releasing traces before <code>decision_wait</code> elapses.
<a href="https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/extension/tailstorage/pebbletailstorageextension"><code>pebbletailstorageextension</code></a> moves trace buffering to a Pebble LSM database on disk, so storage scales with disk capacity instead of RAM. That means operators can increase <code>decision_wait</code> and <code>num_traces</code> without OOM kills. The cost is roughly 2x CPU.</p>
<h2 id="whatistailsampling">What is Tail Sampling?</h2>
<p>Distributed tracing is useful for debugging, but at production scale it comes with processing overhead and storage costs, at which point sampling becomes a natural way to maintain the value of tracing while keeping costs under control. Tail-based sampling, or tail sampling, is a technique that makes a sampling decision conditionally at a later stage, so that high-value traces like errors or slow transactions are more likely to be sampled. The opposite is head sampling, which makes the decision at the start of a trace, before any such information is available.</p>
<h2 id="howdoesthetailsamplingprocessorwork">How does the tail sampling processor work?</h2>
<p>The tail sampling processor buffers 100% of incoming traces (or spans, used interchangeably), then forwards the sampled subset after applying the sampling policies.
Buffering is a major source of memory usage, and it scales proportionally to the volume of spans, a well known pain point in the community.</p>
<p>Memory usage is bounded by configuration parameters like <code>decision_wait</code> and <code>num_traces</code>.
Setting <code>decision_wait</code> to 1 minute means a sampling decision is made for a trace after 1 minute, during which all spans for that trace are expected to have arrived.
If a trace is slower than 1 minute, the decision is made with some spans missing.</p>
<p>As a side note, scaling out the tail sampling setup involves using the <a href="https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/exporter/loadbalancingexporter"><code>loadbalancingexporter</code></a> to satisfy the requirement that all spans for a trace must be routed to the same collector.
This introduces some operational complexity and potentially data loss during collector restarts.
But this post focuses on the memory usage of a single tail sampling processor instance, regardless of horizontal scaling.</p>
<h2 id="whydoestailsamplingcausememorypressure">Why does tail sampling cause memory pressure?</h2>
<p>These parameters introduce a tradeoff between data loss and memory usage, and they require assumptions about the shape of traces: how slow they can be, how many spans they contain, how large each span is. These assumptions can become stale as instrumentation evolves.</p>
<p>How much data loss is acceptable to limit memory usage, and can the tradeoff be improved? The following two contributions aim to give operators more flexibility.</p>
<h2 id="howspaningestreducestailsamplingmemorybyreleasingspansearly">How span-ingest reduces tail sampling memory by releasing spans early</h2>
<p><code>sampling_strategy</code> is a new configuration option added to the tail sampling processor in <a href="https://github.com/open-telemetry/opentelemetry-collector-contrib/releases/tag/v0.149.0"><code>v0.149.0</code></a>.</p>
<p><code>sampling_strategy</code> defaults to <code>trace-complete</code>, which matches the original behavior: sampling policies are only evaluated when <code>decision_wait</code> has elapsed, at which point the trace is considered complete.
(There is a similar config, <code>decision_wait_after_root_received</code>, for optimization, but it is excluded from this discussion for simplicity.)
This means all spans are buffered in memory for roughly <code>decision_wait</code> before being released, regardless of whether a decision could have been made earlier.
For example, health check spans that should always be dropped are still held in memory until policy evaluation time.</p>
<p>Alternatively, <code>sampling_strategy</code> can be set to <code>span-ingest</code>, where spans are evaluated individually at ingest time.
This allows terminal decisions, specifically <code>drop</code> or <code>sampled</code>, to be made earlier, freeing memory by dropping or exporting all spans buffered so far for that trace before <code>decision_wait</code> elapses.
In the health check example, a policy can be configured to drop the entire trace as soon as the root span belongs to a health check.
It is worth noting that an <code>unsampled</code> decision, unlike an explicit <code>drop</code>, is not terminal, as it can be overruled by a <code>sampled</code> or <code>drop</code> decision from another span in the same trace, so <code>unsampled</code> traces cannot be released early.</p>
<p>Switching from <code>trace-complete</code> to <code>span-ingest</code> will require policy adjustments, as policies can no longer assume all spans are available at evaluation time.
Moreover, not all policy types are supported with the <code>span-ingest</code> strategy.</p>
<h2 id="diskbackedtailsamplingstoragewithpebble">Disk-backed tail sampling storage with Pebble</h2>
<p>Even with <code>span-ingest</code>, all spans are still buffered in memory.
As <code>decision_wait</code> is increased to accommodate slow traces and <code>num_traces</code> is increased to limit data loss, the collector will eventually hit its memory limit and get OOM killed, resulting in further data loss.</p>
<p>What if traces were buffered on disk instead, where there is an order of magnitude more capacity?
The main drawback is performance: disk throughput and latency, even with SSDs, are at least an order of magnitude slower than memory, so disk writes need to be efficient.
For this reason, <a href="https://github.com/cockroachdb/pebble"><code>Pebble</code></a>, an LSM database, was chosen as the storage backend for its fast write performance.
Read performance is less of a concern, as reads only happen for the sampled subset of traces when <code>sampling_strategy</code> is set to <code>span-ingest</code>.</p>
<p>The implementation introduces a <code>TailStorage</code> interface for trace storage operations, and a new <code>tail_storage</code> option in <a href="https://github.com/open-telemetry/opentelemetry-collector-contrib/releases/tag/v0.150.0"><code>v0.150.0</code></a> (behind feature gate <code>processor.tailsamplingprocessor.tailstorageextension</code>) to configure the storage backend.
The default in-memory behavior is unchanged, but it is now possible to swap in a different storage backend, like the new <a href="https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/extension/tailstorage/pebbletailstorageextension"><code>pebbletailstorageextension</code></a> contributed to Collector Contrib.</p>
<h2 id="tailsamplingmemorybenchmarkstracecompletevsspaningestwithpebble">Tail sampling memory benchmarks: trace-complete vs span-ingest with Pebble</h2>
<h3 id="benchmarksetupopentelemetrydemowithfanoutcollectors">Benchmark setup: OpenTelemetry Demo with fan-out collectors</h3>
<p>The following benchmarks were produced by running <a href="https://github.com/open-telemetry/opentelemetry-demo"><code>OpenTelemetry Demo</code></a> with increased load against a pipe collector, which receives all spans and fans them out to two identical collectors under observation (<code>CUO-A</code> and <code>CUO-B</code>), differing only in their tail sampling configuration.
Measurements include pipe collector throughput, spans received, spans sent (sampled), CPU usage, and memory usage.</p>
<h3 id="benchmarksetupdiagram">Benchmark setup diagram</h3>
<pre><code>                      demo ns
     +----------------------------------------+
     |  opentelemetry-demo                     |
     |    loadgenerator (locust)               |
     |    services: frontend, cart, ...        |
     |    demo-collector                       |
     +----------------------------------------+
                          |  OTLP/gRPC
                          v
                     chamber ns
     +----------------------------------------+
     |             pipe-collector             |
     |         receive once, fan out          |
     |      exporters: [otlp/a, otlp/b]       |
     +----------------------------------------+
              | OTLP                  | OTLP
              v                       v
     +----------------+      +----------------+
     |     CUO-A      |      |     CUO-B      |
     | tail_sampling  |      | tail_sampling  |
     |   (config A)   |      |   (config B)   |
     +----------------+      +----------------+
</code></pre>
<h3 id="tailsamplingprocessorconfigurations">Tail sampling processor configurations</h3>
<h4 id="cuoa">CUO-A</h4>
<pre><code>config:
  processors:
    tail_sampling:
      sampling_strategy: trace-complete
      decision_wait: 5m
      num_traces: 5000000
      block_on_overflow: true
      decision_cache:
        sampled_cache_size: 10000
        non_sampled_cache_size: 200000
      policies:
        - name: root_1pct
          type: and
          and:
            and_sub_policy:
              - name: root_span_only
                type: ottl_condition
                ottl_condition:
                  error_mode: ignore
                  span:
                    - "IsRootSpan()"
              - name: root_probabilistic
                type: probabilistic
                probabilistic:
                  sampling_percentage: 1.0
</code></pre>
<h4 id="cuob">CUO-B</h4>
<p><code>CUO-B</code> uses the same tail sampling processor configuration as <code>CUO-A</code>, except it sets <code>sampling_strategy: span-ingest</code> and <code>tail_storage: pebble_tail_storage/main</code>, along with its corresponding <a href="https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/extension/tailstorage/pebbletailstorageextension"><code>pebbletailstorageextension</code></a> configuration.</p>
<pre><code>extensions:
  pebble_tail_storage/main:
    directory: /var/lib/otelcol/pebble
</code></pre>
<h3 id="memorycpuandthroughputresults">Memory, CPU and throughput results</h3>
<p>The following tables compare trace-complete (CUO-A) against span-ingest with Pebble disk storage (CUO-B) across memory, CPU and throughput.
The process RSS, Go heap allocation, and per-process CPU measurements come from OpenTelemetry Collector internal process and runtime metrics, while container working set and container CPU come from Kubernetes cgroup metrics scraped by kubelet/cAdvisor.</p>
<h4 id="memorypeakoverthewindow">Memory (peak over the window)</h4>
<p>| Metric | cuo-a | cuo-b | Δ (B vs A) |
| --- | ---: | ---: | ---: |
| process RSS | 916.4 MiB | 442.7 MiB | -51.7% |
| Go heap alloc | 699.3 MiB | 241.7 MiB | -65.4% |
| container working set | 763.0 MiB | 282.9 MiB | -62.9% |</p>
<h4 id="cputotaloverthewindow">CPU (total over the window)</h4>
<p>| Metric | cuo-a | cuo-b | Δ (B vs A) |
| --- | ---: | ---: | ---: |
| per-process CPU | 11.9 core-s | 22.7 core-s | +90.7% |
| container CPU | 11.9 core-s | 22.7 core-s | +90.1% |</p>
<h4 id="throughputtotaloverthewindow">Throughput (total over the window)</h4>
<p>| Metric | cuo-a | cuo-b | Δ (B vs A) |
| --- | ---: | ---: | ---: |
| spans received | 257,804 | 257,804 | 0.0% |
| spans sent | 2,477 | 2,477 | 0.0% |</p>
<h4 id="tailsampling">Tail sampling</h4>
<p>| Metric | cuo-a | cuo-b | Δ (B vs A) |
| --- | ---: | ---: | ---: |
| traces in memory peak | 29,284 | 29,245 | -0.1% |
| traces sampled by root_1pct policy | 496 | 496 | 0.0% |</p>
<ul>
<li><code>cuo-a</code> = <code>trace-complete</code>, <code>cuo-b</code> = <code>span-ingest-pebble</code></li>
<li>Window: 15m 39s (<code>t+0:00</code> start, <code>t+10:06</code> drain start, <code>t+15:39</code> drain end)</li>
</ul>
<p>The results show a significant memory reduction when using <a href="https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/extension/tailstorage/pebbletailstorageextension"><code>pebbletailstorageextension</code></a> with <code>span-ingest</code>, at the cost of increased CPU usage from event serialization and database overhead.</p>
<h2 id="whatsnextforopentelemetrytailsampling">What's next for OpenTelemetry tail sampling</h2>
<p>Both <code>sampling_strategy</code> and <a href="https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/extension/tailstorage/pebbletailstorageextension"><code>pebbletailstorageextension</code></a> are still in their early stages at the time of writing.
Feedback and contributions are welcome in the OpenTelemetry Collector Contrib repo.
Stay tuned for more improvements.</p>]]></content:encoded>
    <link>https://www.elastic.co/observability-labs/blog/tail-sampling-memory-opentelemetry</link>
    <guid isPermaLink="false">tail-sampling-memory-opentelemetry</guid>
    <category><![CDATA[OpenTelemetry]]></category>
    <category><![CDATA[APM]]></category>
    <category><![CDATA[What's New]]></category>
    <dc:creator><![CDATA[Carson Ip]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blteb738eca25e4e5c5/6a7f1b746693f828d066439f/header.png" length="0" type="image/png"/>
    <pubDate>Tue, 21 Jul 2026 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[From five dashboards to one prompt: how we built an APM health monitor with Elastic Agent Builder]]></title>
    <description><![CDATA[Five ES|QL tools score latency, errors, throughput and dependencies to find the root cause, so you don't dashboard-hop during an APM incident.]]></description>
    <content:encoded><![CDATA[<p>Using Elastic Agent Builder, we built an agent that answers one question: is your service healthy? </p>
<p>This agent fans out to five ES|QL queries over your existing traces-* data and answers Red, Yellow or Green with the root cause attached, in one response, without switching dashboards or correlating anything by hand. It runs on Elastic Agent Builder, tested on Elasticsearch and Kibana 9.3, and one deployment covers every service in your fleet with no per-service setup. </p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt45cee36cefac285f/6a7f02a0227b1cec34598160/image5.png" alt="" /></p>
<p>Here's how it's built</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt06ae6cd3234ce2f8/6a7f02a305b7b56bea18b4ab/image2.png" alt="" /></p>
<h2 id="ismyservicehealthyonequestiononeagentoneanswer">Is my service healthy? One question, one agent, one answer</h2>
<p>There is a question every engineer dreads during an incident: "Is the service healthy?"</p>
<p>It sounds simple. It is not. Answering it properly means switching between dashboards, firing off multiple queries, correlating latency spikes with error logs, and checking whether downstream dependencies are contributing to the problem, all under pressure and often at an inconvenient hour.</p>
<p>What if that entire investigation collapsed into a single conversation?</p>
<p>That is what we set out to build: an AI agent that behaves like a knowledgeable SRE sitting beside you. It knows which questions to ask, knows how to query your APM data, and returns a clear Red, Yellow, or Green health verdict with context and recommendations. We call it the APM Service Health Monitor, and it runs entirely on Elastic.</p>
<h2 id="whyapmhealthisaderivedsignalnotasinglemetric">Why APM health is a derived signal, not a single metric</h2>
<p>Here is the thing about APM data: you already have everything you need. Latency percentiles, error rates, throughput, and span-level dependency traces are all sitting in your <code>traces-*</code> indices, indexed and ready.</p>
<p>The missing piece is not data. It is the reasoning layer that connects those signals into a coherent picture.</p>
<p>A p95 latency of 890 ms means very little on its own. But a p95 latency of 890 ms that runs 38% above the 24-hour baseline, alongside a 6% error rate on your postgres-primary dependency? That is a story. That is a Red, and it tells an engineer exactly where to look.</p>
<p>The APM Service Health Monitor encodes that reasoning (threshold logic, trend comparison, and dependency blast-radius analysis) into an Elastic AI agent that runs it on demand, for any service, anytime.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt60839b56219ee418/6a7f02a6448e4e84155c0260/image4.png" alt="APM Service Health Monitor agent at the centre, fanning out to five ES|QL tools querying traces-*" />
<em>APM Service Health Monitor agent at the centre, fanning out to five ES|QL tools querying traces-*</em></p>
<h2 id="howdoestheapmservicehealthmonitorarchitecturework">How does the APM Service Health Monitor architecture work?</h2>
<p>The agent is registered in Elastic Agent Builder as a chat-type agent wired to five ES|QL-powered tools. Each tool does one thing precisely. The agent orchestrates, compares, and reasons across all five.</p>
<pre><code>POST kbn:/api/agent_builder/agents
{
  "id": "apm_service_health_agent",
  "type": "chat",
  "name": "APM Service Health Monitor",
  ...
}
</code></pre>
<p>The APM Service Health Monitor sits at the center, fanning out to five ES|QL tools that query <code>traces-*</code>.</p>
<p>Here is how the tool layer is composed.</p>
<h3 id="tool1apm_metrics_overview_toolthe24hourbaseline">Tool 1: apm_metrics_overview_tool, the 24-hour baseline</h3>
<p>Every health assessment starts with a baseline. This tool computes the aggregate picture for a service over the last 24 hours (average latency, p95 and p99 latency, error rate, and throughput) in a single ES|QL query:</p>
<pre><code>FROM traces-*
| WHERE service.name == ?service
| WHERE transaction.type == "request"
| WHERE @timestamp &gt;= NOW() - 24 hours
| EVAL is_error = CASE(event.outcome == "failure", 1, 0)
| STATS
    avg_latency_ms  = AVG(transaction.duration.us / 1000),
    p95_latency_ms  = PERCENTILE(transaction.duration.us / 1000, 95),
    p99_latency_ms  = PERCENTILE(transaction.duration.us / 1000, 99),
    error_rate      = 100.0 * SUM(is_error) / COUNT(*),
    throughput_rps  = COUNT(*) / (24*3600)
</code></pre>
<p>This snapshot becomes the anchor. Every trend tool that follows compares its latest readings back to these numbers, so the agent always has a reference point, not just a raw value.</p>
<h3 id="tool2apm_latency_trend_toolperformanceovertime">Tool 2: apm_latency_trend_tool, performance over time</h3>
<p>Averages hide inflection points. The latency trend tool buckets p95, p75, and average latency into 5-minute intervals across the last 24 hours, giving the agent a time-series view of performance:</p>
<pre><code>| STATS
    avg_latency_ms = AVG(transaction.duration.us / 1000),
    p95_latency_ms = PERCENTILE(transaction.duration.us / 1000, 95),
    p75_latency_ms = PERCENTILE(transaction.duration.us / 1000, 75)
  BY time_bucket = DATE_TRUNC(5 minutes, @timestamp)
| SORT time_bucket ASC
</code></pre>
<p>The agent takes the most recent 5-minute bucket's <code>p95_latency_ms</code> and compares it against the <code>p95_latency_ms</code> from <code>apm_metrics_overview_tool</code>, the 24-hour rolling average. That percentage difference is what gets scored Green, Yellow, or Red.</p>
<h3 id="tool3apm_error_trend_toolfailureshapedetection">Tool 3: apm_error_trend_tool, failure shape detection</h3>
<p>Error rate is directional. A gradual rise from 0.5% to 1.2% tells a different story than a sudden spike to 8%. The error trend tool captures this shape with 5-minute buckets:</p>
<pre><code>| EVAL is_error = CASE(event.outcome == "failure", 1, 0)
| STATS
    total_requests = COUNT(*),
    error_count    = SUM(is_error),
    error_rate     = 100.0 * SUM(is_error) / COUNT(*)
  BY time_bucket = DATE_TRUNC(5 minutes, @timestamp)
</code></pre>
<p>The agent reads the most recent bucket's <code>error_rate</code> from this trend and scores it directly against the fixed thresholds (below 1% Green, 1 to 5% Yellow, above 5% Red).</p>
<h3 id="tool4apm_throughput_trend_tooltrafficasahealthsignal">Tool 4: apm_throughput_trend_tool, traffic as a health signal</h3>
<p>Throughput is underrated as a health indicator. A 40% drop in requests per second is itself an incident. It can mean a deployment regression, a misconfigured load balancer, or a silent upstream failure.</p>
<pre><code>| STATS requests_count = COUNT(*)
  BY time_bucket = DATE_TRUNC(5 minutes, @timestamp)
| SORT time_bucket ASC
</code></pre>
<p>The agent normalizes each 5-minute bucket's request count to a per-second rate and compares it against <code>throughput_rps</code> from <code>apm_metrics_overview_tool.</code>. The same 10% and 30% drift thresholds apply. A service that is suspiciously quiet is flagged just as quickly as one that is on fire.</p>
<h3 id="tool5apm_dependency_health_tooltheblastradiusview">Tool 5: apm_dependency_health_tool, the blast-radius view</h3>
<p>The <code>apm_dependency_health_tool</code> is often the one that finds what the others cannot surface directly. A service can look internally healthy while a downstream database or external API silently accumulates failures. This tool maps every dependency via <code>span.destination.service.resource</code> and computes its error rate from span data:</p>
<pre><code>FROM traces-*
| WHERE processor.event == "span"
  AND span.destination.service.resource IS NOT NULL
| EVAL is_error = CASE(event.outcome == "failure", 1, 0)
| STATS
    total_calls  = COUNT(*),
    failed_calls = SUM(is_error),
    error_rate   = 100.0 * SUM(is_error) / COUNT(*)
  BY dependency_name = span.destination.service.resource
| SORT error_rate DESC
</code></pre>
<p>Unlike the other four tools, dependency health is scored independently. Each dependency's <code>error_rate</code> is compared directly against fixed thresholds (1% or below Green, above 1% Yellow, above 5% Red), with no baseline comparison needed.</p>
<h2 id="howthehealthscoringworks">How the health scoring works</h2>
<p>Once all five tools return their data, the agent computes a health state for each metric in a single reasoning step. There is no polling loop and no intermediate storage. Here is exactly what gets compared to what:</p>
<p>| Metric | Tool providing the value | Compared against | How scored |
| :---- | :---- | :---- | :---- |
| Latency (p95) | <code>apm_latency_trend_tool</code> | 24h p95 from <code>apm_metrics_overview_tool</code> | &lt; 10% Green · 10–30% Yellow · &gt; 30% Red|
| Error rate | <code>apm_error_trend_tool</code> | Fixed thresholds + 24h <code>error_rate</code> from overview | &lt; 1% Green · 1–5% Yellow · &gt; 5% Red |
| Throughput | <code>apm_throughput_trend_tool</code> | 24h <code>throughput_rps</code> from <code>apm_metrics_overview_tool</code> | &lt; 10% Green · 10–30% Yellow · &gt; 30% Red|
| Each dependency | <code>apm_dependency_health_tool</code> | Fixed thresholds only (no baseline) | All ≤1% Green · Any &gt;1% Yellow · Any &gt;5% Red |</p>
<p>The overall verdict follows a single rule: <strong>the worst individual metric wins</strong>.</p>
<pre><code>Any metric is Red                → Overall = 🔴 Red
Any metric is Yellow (no Red)   → Overall = 🟡 Yellow
All metrics are Green            → Overall = 🟢 Green
</code></pre>
<p>The Red/Yellow/Green scoring logic lives in the agent's natural-language instructions. It is readable, auditable, and adjustable without touching code or redeploying infrastructure.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blte61fab3108287137/6a7f02a9eab5be1d6e20a270/image1.png" alt="Four metric columns - Latency, Error Rate, Throughput, Dependencies - converging into Red/Yellow/Green outcome states" />
<em>Four metric columns (Latency, Error Rate, Throughput, Dependencies) converging into Red/Yellow/Green outcome states</em></p>
<h2 id="deployingtheapmservicehealthmonitorinagentbuilder">Deploying the APM Service Health Monitor in Agent Builder</h2>
<p>Registration follows Elastic's standard Agent Builder API pattern, tested against Elasticsearch 9.3 and Kibana 9.3. Register the tools first, then wire the agent to all five tool IDs:</p>
<pre><code># Step 1: Register the five ES|QL tools
POST kbn:/api/agent_builder/tools   # apm_metrics_overview_tool
POST kbn:/api/agent_builder/tools   # apm_latency_trend_tool
POST kbn:/api/agent_builder/tools   # apm_error_trend_tool
POST kbn:/api/agent_builder/tools   # apm_throughput_trend_tool
POST kbn:/api/agent_builder/tools   # apm_dependency_health_tool

# Step 2: Register the agent with tools wired in
POST kbn:/api/agent_builder/agents  # apm_service_health_agent

# Step 3: Verify the agent is live
GET kbn:/api/agent_builder/agents/apm_service_health_agent
</code></pre>
<p>Each tool is parameterized with a service binding, so the same agent serves every service in your fleet with no per-service configuration. The queries target <code>traces-*</code> with cross-cluster wildcard support, so a single deployment covers multi-cluster environments out of the box.</p>
<h2 id="tryitasktheagentaboutyourservice">Try it: ask the agent about your service</h2>
<p>Once deployed, open the agent in Kibana and type this:</p>
<pre><code>What is the health of my checkout-service in the last 24 hours?
</code></pre>
<p>The agent fans out to all five tools, computes the metric states, applies the health logic in one pass, and responds with a structured report.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blte61fab3108287137/6a7f02a9eab5be1d6e20a270/image1.png" alt="Kibana chat card: checkout-service · Last 24h · per-metric table with Red/Yellow/Green status pills · Overall: Red" />
<em>Kibana chat card: checkout-service · Last 24h · per-metric table with Red/Yellow/Green status pills · Overall: 🔴 Red</em></p>
<p>The agent identified the root cause without any manual correlation: postgres-primary is failing 6.1% of its calls, and that is cascading directly into the p95 latency spike. No dashboard pivoting, no manual ES|QL. One prompt, full situational awareness.</p>
<p>You can continue the conversation with follow-up questions in the same session:</p>
<ul>
<li>Which other services depend on postgres-primary?</li>
<li>How does this compare to yesterday's health?</li>
<li>Show me the error trend for the last 6 hours only.</li>
</ul>
<p>The agent invokes the appropriate tools for each follow-up, keeping the full context of the original health assessment in view.</p>
<h2 id="whyagentbuilderandesqlaretherightstackforthis">Why Agent Builder and ES|QL are the right stack for this</h2>
<p>A few deliberate choices made this design work cleanly.</p>
<h3 id="esqlasthequerylayer">ES|QL as the query layer</h3>
<p>ES|QL's pipe-based syntax makes each tool query readable, testable, and independently verifiable. The <code>DATE_TRUNC</code> bucketing for trends, the PERCENTILE aggregations for latency, and the span.destination.service.resource grouping for dependencies are precise, auditable queries. Run any of them directly in Kibana Dev Tools and you will see exactly what the agent sees.</p>
<h3 id="narrowstatelesstools">Narrow, stateless tools</h3>
<p>Each tool does one thing and returns structured data. The agent provides the orchestration and reasoning. Adding a new metric dimension means registering one new tool and updating the agent instructions. Nothing else changes.</p>
<h3 id="instructionsasarunbook">Instructions as a runbook</h3>
<p>The agent's health logic is expressed in natural language inside its configuration. Anyone on your team can read it, adjust it without a code deploy, and audit it in full from the Agent Builder UI in Kibana.</p>
<h2 id="whatsnextfortheapmservicehealthmonitor">What's next for the APM Service Health Monitor</h2>
<p>The APM Service Health Monitor is a foundation, not a ceiling. Natural extensions include:</p>
<ul>
<li><p><strong>Alert-triggered health checks:</strong> wire the agent to fire automatically when an anomaly detection rule triggers, attaching the health summary directly to the alert notification.</p></li>
<li><p><strong>Deployment correlation:</strong> integrate change event data so the agent can identify whether a Red status started after a specific deployment.</p></li>
<li><p><strong>SLO-aware thresholds:</strong> replace fixed percentage thresholds with per-service error budget consumption, so Red, Yellow, and Green reflect actual business impact against defined SLOs.</p></li>
<li><p><strong>Cross-service traversal:</strong> extend the dependency tool to recursively assess upstream and downstream services, building a full topology view from a single query.</p></li>
</ul>
<h2 id="requirementsanddeployment">Requirements and deployment</h2>
<p>Requirements:</p>
<ul>
<li>Elasticsearch 9.3 and Kibana 9.3 (tested version).</li>
<li><a href="https://www.elastic.co/docs/solutions/search/agent-builder/get-started">Agent Builder</a> enabled in Kibana.</li>
<li>APM data flowing into <code>traces-*</code> indices via any Elastic APM agent.</li>
<li><a href="https://www.elastic.co/subscriptions">Elastic Enterprise license</a> is required to utilize Agent Builder and <a href="https://www.elastic.co/search-labs/blog/esql-cross-cluster-search">ES|QL cross-cluster search</a>.</li>
</ul>
<p>The full agent and tool definitions are available in <a href="https://github.com/elastic/elasticsearch-labs/tree/main/supporting-blog-content/observability-labs/elastic-agent-apm-health-monitor">this repository</a>. Once your traces are indexed, all five ES|QL tools work against your data with no modification, parameterized by service name at query time, covering your entire service fleet from a single deployment.</p>
<p>The next time someone asks "is the service healthy?", you will have a precise, data-backed answer before the question finishes echoing in the incident channel.</p>
<p>Questions, extensions, or feedback? Join the conversation in the <a href="https://discuss.elastic.co/">Elastic community forums</a>.</p>
<h2 id="fulldeploymentreference">Full deployment reference</h2>
<p>Everything you need to deploy the APM Service Health Monitor, in execution order. Run the five tool registrations first, then the agent.</p>
<p><strong>Tool 1: Dependency health</strong></p>
<pre><code>POST kbn:/api/agent_builder/tools
{
  "id": "apm_dependency_health_tool",
  "type": "esql",
  "description": "Evaluates the health of external dependencies (DBs, APIs, caches)...",
  "configuration": {
    "query": "FROM traces-*\n| WHERE service.name == ?service\n| WHERE processor.event == \"span\"\n  AND span.destination.service.resource IS NOT NULL\n| WHERE @timestamp &gt;= NOW() - 24 hours\n| EVAL is_error = CASE(event.outcome == \"failure\", 1, 0)\n| STATS\n    total_calls = COUNT(*),\n    failed_calls = SUM(is_error),\n    error_rate = 100.0 * SUM(is_error)/COUNT(*)\n  BY dependency_name = span.destination.service.resource\n| SORT error_rate DESC | LIMIT 100"
  }
}
</code></pre>
<p><strong>Tool 2: Error trend</strong></p>
<pre><code>POST kbn:/api/agent_builder/tools
{
  "id": "apm_error_trend_tool",
  "type": "esql",
  "description": "Tracks 5-minute bucketed error rates for the service over 24 hours...",
  "configuration": {
    "query": "FROM traces-*\n| WHERE service.name == ?service\n| WHERE transaction.type == \"request\"\n| WHERE @timestamp &gt;= NOW() - 24 hours\n| EVAL is_error = CASE(event.outcome == \"failure\", 1, 0)\n| STATS\n    total_requests = COUNT(*),\n    error_count = SUM(is_error),\n    error_rate = 100.0 * SUM(is_error)/COUNT(*)\n  BY time_bucket = DATE_TRUNC(5 minutes, @timestamp)\n| SORT time_bucket ASC | LIMIT 1000"
  }
}
</code></pre>
<p><strong>Tool 3: Latency trend</strong></p>
<pre><code>POST kbn:/api/agent_builder/tools
{
  "id": "apm_latency_trend_tool",
  "type": "esql",
  "description": "Provides 5-minute bucketed latency trends (avg, p95, p75) for last 24 hours...",
  "configuration": {
    "query": "FROM traces-*\n| WHERE service.name == ?service\n| WHERE transaction.type == \"request\"\n| WHERE @timestamp &gt;= NOW() - 24 hours\n| STATS\n    avg_latency_ms = AVG(transaction.duration.us / 1000),\n    p95_latency_ms = PERCENTILE(transaction.duration.us / 1000, 95),\n    p75_latency_ms = PERCENTILE(transaction.duration.us / 1000, 75)\n  BY time_bucket = DATE_TRUNC(5 minutes, @timestamp)\n| SORT time_bucket ASC | LIMIT 1000"
  }
}
</code></pre>
<p><strong>Tool 4: Metrics overview</strong></p>
<pre><code>POST kbn:/api/agent_builder/tools
{
  "id": "apm_metrics_overview_tool",
  "type": "esql",
  "description": "Aggregates key service metrics over last 24 hours: avg latency, p95/p99, error rate, throughput...",
  "configuration": {
    "query": "FROM traces-*\n| WHERE service.name == ?service\n| WHERE transaction.type == \"request\"\n| WHERE @timestamp &gt;= NOW() - 24 hours\n| EVAL is_error = CASE(event.outcome == \"failure\", 1, 0)\n| STATS\n    avg_latency_ms = AVG(transaction.duration.us / 1000),\n    p95_latency_ms = PERCENTILE(transaction.duration.us / 1000, 95),\n    p99_latency_ms = PERCENTILE(transaction.duration.us / 1000, 99),\n    error_rate = 100.0 * SUM(is_error) / COUNT(*),\n    throughput_rps = COUNT(*) / (24*3600)"
  }
}
</code></pre>
<p><strong>Tool 5: Throughput trend</strong></p>
<pre><code>POST kbn:/api/agent_builder/tools
{
  "id": "apm_throughput_trend_tool",
  "type": "esql",
  "description": "Provides service throughput trends in 5-minute intervals over 24 hours...",
  "configuration": {
    "query": "FROM traces-*\n| WHERE service.name == ?service\n| WHERE transaction.type == \"request\"\n| WHERE @timestamp &gt;= NOW() - 24 hours\n| STATS requests_count = COUNT(*)\n  BY time_bucket = DATE_TRUNC(5 minutes, @timestamp)\n| SORT time_bucket ASC | LIMIT 1000"
  }
}
</code></pre>
<p><strong>Agent: APM service health monitor</strong></p>
<pre><code>POST kbn:/api/agent_builder/agents
{
  "id": "apm_service_health_agent",
  "name": "APM Service Health Monitor",
  "description": "Provides Red/Yellow/Green health status and trends for services over 24 hours.",
  "labels": ["apm", "health", "service", "monitoring"],
  "avatar_color": "#4CAF50",
  "configuration": {
    "instructions": "You are the Service Health Agent...",
    "tools": [{
      "tool_ids": [
        "apm_dependency_health_tool",
        "apm_error_trend_tool",
        "apm_latency_trend_tool",
        "apm_metrics_overview_tool",
        "apm_throughput_trend_tool"
      ]
    }]
  }
}
</code></pre>]]></content:encoded>
    <link>https://www.elastic.co/observability-labs/blog/apm-health-check-elastic-agent-builder</link>
    <guid isPermaLink="false">apm-health-check-elastic-agent-builder</guid>
    <category><![CDATA[APM]]></category>
    <dc:creator><![CDATA[Naga Putta,Stephen Brown]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt60839b56219ee418/6a7f02a6448e4e84155c0260/image4.png" length="0" type="image/png"/>
    <pubDate>Thu, 09 Jul 2026 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Contextual AI: Stop pinging the SRE: three MCP tools that turn Elastic Agent Builder into your team's runbook]]></title>
    <description><![CDATA[Build three MCP tools in Elastic Agent Builder that read endpoint health, recent deploys and SLO burn rate directly in your editor. Encode your platform team's runbook once; every developer gets self-serve production context without pinging an SRE.]]></description>
    <content:encoded><![CDATA[<p>A developer asks their editor, "Is it safe to merge this PR?" and gets a real answer in seconds, not a 10–15 minute dashboard hunt or a Slack ping to an SRE. This post shows how to build three MCP tools in Elastic Agent Builder that read endpoint health, recent deploys, and SLO burn rate, and encode the platform team's interpretation rules, error rate thresholds, deploy warm-up windows, and burn rate limits directly into the tool descriptions. The result is contextual AI: an agent that reasons over production signals using the runbook the platform team wrote once.</p>
<h2 id="prerequisitesforelasticagentbuildermcptools">Prerequisites for Elastic Agent Builder MCP tools</h2>
<ul>
<li>An <a href="https://cloud.elastic.co/registration">Elastic Cloud</a> deployment with Elastic Stack 9.3+ (or Elastic Cloud Serverless) with Agent Builder enabled.</li>
<li>An APM-ingested service. If your cluster does not already have APM data, the companion notebook includes instructions to generate synthetic traffic using <a href="https://github.com/elastic/apm-integration-testing">elastic/apm-integration-testing</a> with the <code>opbeans-node</code> demo app.</li>
<li>An MCP-compatible client: <a href="https://docs.anthropic.com/en/docs/claude-code/overview">Claude Code</a>, <a href="https://www.cursor.com/">Cursor</a>, or VS Code with an MCP extension.</li>
<li>Basic familiarity with <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/esql.html">ES|QL</a> syntax.</li>
<li><a href="https://nodejs.org/">Node.js</a> 18+ (for the <code>mcp-remote</code> bridge).</li>
</ul>
<p>If you are new to MCP or need to set up the Elastic MCP server for the first time, check out <a href="https://www.elastic.co/search-labs/blog/elastic-mcp-server-agent-builder-tools">Connect Agent Builder tools to any AI agent with Elastic MCP server</a> for the full setup walkthrough. This article assumes the MCP server is already configured.</p>
<h2 id="theproblemwhydevelopersflyblind">The problem: why developers fly blind</h2>
<p>A developer is about to merge a pull request. The change looks simple: increasing the timeout for the downstream <code>recommendations</code> service call from 2 seconds to 5 seconds. But before hitting the merge button, a question lingers: <em>is the service healthy enough to absorb this change right now?</em></p>
<p>To answer that question today, the developer has two options:</p>
<ol>
<li><strong>Check dashboards manually.</strong> Open the APM UI, look at error rates, scan latency charts, find the SLO page, and look for recent deploys. This takes 10-15 minutes and requires knowing what to look for and how to interpret it.</li>
<li><strong>Ask an SRE.</strong> Ping the platform team on Slack: "Hey, is checkout healthy? I want to merge something." This creates an interruption, adds latency to the decision, and doesn't scale.</li>
</ol>
<p>The core problem is not the data. Elastic already collects everything: traces, metrics, error logs, deploy markers, and SLO budgets. The problem is that <strong>correlating multiple signals requires mental overhead and domain knowledge that most developers don't have</strong>.</p>
<p>An SRE knows that a p99 spike after a deploy is normal for 5 minutes, that an error rate under 0.5% is acceptable during a release window, and that merging when the SLO budget is below 20% is risky. That knowledge lives in runbooks, tribal memory, and experience.</p>
<p>What if the platform engineer could encode that knowledge into tools that any developer can query from their editor?</p>
<h2 id="howmcptoolsinelasticagentbuilderencodeyourrunbook">How MCP tools in Elastic Agent Builder encode your runbook</h2>
<p>The key insight is this: <strong>a tool is not just a query; it is a query plus interpretation</strong>. A dashboard shows you a p99 of 450ms. A well-designed tool tells you "p99 is 450ms, which is within normal range for this service, and has been stable since the last deploy 2 hours ago."</p>
<p>The difference is that the tool description carries the domain knowledge. When a platform engineer creates a tool in <a href="https://www.elastic.co/search-labs/blog/elastic-ai-agent-builder-context-engineering-introduction">Agent Builder</a>, they write descriptions like: "Error rate above 1% typically indicates a regression. If this coincides with a recent deploy, the deploy is the likely cause." That description becomes part of the context the AI agent uses when reasoning across multiple tool results.</p>
<p>This is what we mean by <em>contextual AI</em>: the AI agent does not just fetch data; it reasons over it using the interpretation rules that the platform team encoded.</p>
<p>Here is the architecture:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltf9dab0ce1789f384/6a85c8599d2b71139df93915/image-02.png" alt="Architecture: developer editor with MCP client connecting to Elastic Agent Builder tools authored by the platform engineer" /></p>
<p>The platform engineer authors the tools once. Every developer on the team benefits from their own editor, without needing to learn ES|QL or understand APM data models.</p>
<h2 id="settinguptheelasticagentbuildersampleenvironment">Setting up the Elastic Agent Builder sample environment</h2>
<p>The full end-to-end setup (traffic generation with <a href="https://github.com/elastic/opbeans-node">opbeans-node</a>, deploy annotations, SLO creation, and the three Agent Builder tools) is available as a runnable notebook at this repository: <a href="https://github.com/Delacrobix/OART-Contextual-AI-Bridging-the-Gap-between-Platform-Engineering-and-Product-Development/blob/main/notebook.ipynb"><code>notebook.ipynb</code></a>. The sections below focus on the ES|QL queries and tool descriptions: the <em>why</em> behind each tool, not the mechanics of posting them.</p>
<h2 id="buildingtool1get_endpoint_health">Building Tool 1: get_endpoint_health</h2>
<p>This tool answers the question: "How is this endpoint performing right now?" It returns error rate, latency percentiles (p50, p95, p99), and throughput for a given service and endpoint within a time window.</p>
<p>Here is the full <a href="https://www.elastic.co/docs/explore-analyze/ai-features/agent-builder/tools/esql-tools">tool configuration</a> as created in Agent Builder:</p>
<pre><code>{
  "id": "get_endpoint_health",
  "type": "esql",
  "description": "Returns the current health of a service endpoint: error rate, latency percentiles (p50/p95/p99), and throughput. Use this tool to assess whether a service is healthy before making changes. Interpretation guide: error rate below 0.5% is healthy, 0.5-1% is elevated (check for recent deploys), above 1% indicates a problem. For latency, compare p99 against the service baseline: checkout is typically under 500ms, product-search under 200ms. A sudden p99 spike within 15 minutes of a deploy suggests the deploy caused a regression.",
  "tags": ["apm", "reliability", "health"],
  "configuration": {
    "query": "FROM traces-apm-* | WHERE service.name == ?serviceName AND @timestamp &gt;= NOW() - ?timeWindow AND transaction.duration.us IS NOT NULL | STATS total_transactions = COUNT(*), error_count = SUM(CASE(event.outcome == \"failure\", 1, 0)), p50_latency_ms = PERCENTILE(transaction.duration.us, 50) / 1000, p95_latency_ms = PERCENTILE(transaction.duration.us, 95) / 1000, p99_latency_ms = PERCENTILE(transaction.duration.us, 99) / 1000 BY service.name | EVAL error_rate_pct = ROUND(error_count / total_transactions * 100, 2) | EVAL throughput_per_min = ROUND(total_transactions / ?windowMinutes, 1)",
    "params": {
      "serviceName": {
        "type": "keyword",
        "description": "The APM service name to check (e.g., opbeans-node)"
      },
      "timeWindow": {
        "type": "keyword",
        "description": "Time window to analyze, in ES|QL duration format (e.g., 30 minutes, 1 hour, 6 hours)"
      },
      "windowMinutes": {
        "type": "integer",
        "description": "Time window in minutes, used to calculate throughput per minute"
      }
    }
  }
}
</code></pre>
<p>The query uses the <a href="https://www.elastic.co/observability-labs/blog/elastic-discover-traces-apm"><code>traces-apm-*</code></a> data stream, which contains raw transaction data. We filter with <code>transaction.duration.us IS NOT NULL</code> to select only transaction events (excluding spans). Using <code>traces-apm-*</code> is more portable than the pre-aggregated <code>metrics-apm.transaction.1m-*</code> stream, which only populates after sustained traffic.</p>
<p>Notice the <code>description</code> field. It is not just "returns health metrics." It includes <strong>interpretation rules</strong>: what error rate thresholds mean, what latency baselines look like, and how to correlate spikes with deploys. This is the runbook encoded in the tool.</p>
<h2 id="buildingtool2get_recent_deploys">Building Tool 2: get_recent_deploys</h2>
<p>This tool answers: "What has been deployed recently?" Deploy history is a critical context because most production issues correlate with code changes. The agent needs this to reason about whether current metrics are normal or reflect a recent deployment.</p>
<p>Deploy annotations are stored in the <code>observability-annotations</code> index. Here is the full tool configuration:</p>
<pre><code>{
  "id": "get_recent_deploys",
  "type": "esql",
  "description": "Returns the deployment history for a service over the last 24 hours, including version numbers, timestamps, and deploy messages. Use this tool to understand the deployment timeline when assessing service health. Key patterns: if a deploy happened within the last 15 minutes, elevated error rates or latency may be expected (warm-up period). If metrics degraded immediately after a deploy, the deploy is the likely cause. Multiple deploys in a short window (under 2 hours) increase risk because it becomes harder to isolate which change caused an issue.",
  "tags": ["apm", "deploys", "change-tracking"],
  "configuration": {
    "query": "FROM observability-annotations | WHERE service.name == ?serviceName AND @timestamp &gt;= NOW() - 24 hours | SORT @timestamp DESC | KEEP @timestamp, service.version, service.environment, message | LIMIT 10",
    "params": {
      "serviceName": {
        "type": "keyword",
        "description": "The APM service name to check deploy history for"
      }
    }
  }
}
</code></pre>
<p>Again, the <code>description</code> encodes domain knowledge: the 15-minute warm-up window, the correlation between deploys and metric changes, and the risk of multiple rapid deploys. This is how a platform engineer transfers their intuition into something an AI agent can reason with.</p>
<h2 id="buildingtool3get_slo_status">Building Tool 3: get_slo_status</h2>
<p>This tool answers: "How much error budget do we have left?" <a href="https://www.elastic.co/docs/solutions/observability/incident-management/service-level-objectives-slos">SLO budget</a> is the platform team's quantified way of expressing risk tolerance. If the budget is nearly spent, even a small change could cause a violation.</p>
<p>Unlike the previous tools that query APM data, this one queries the internal SLO indices where Elastic stores pre-computed SLI data. The query calculates the current burn rate, that is, how fast the service is consuming error budget relative to the allowed threshold:</p>
<pre><code>{
  "id": "get_slo_status",
  "type": "esql",
  "description": "Returns the current SLO burn rate for a service over the last hour. The response includes: SLI value (current performance), error budget target, and burn rate percentage. The burn rate tells you how fast the service is consuming error budget relative to the allowed threshold. Interpretation: a burn rate below 100% means the service is consuming budget slower than the limit (sustainable). Between 100-200%, the service is burning budget faster than planned (proceed with caution). Above 200%, the service is burning budget at double the allowed rate (delay non-critical changes). Above 500%, investigate immediately. Note: this measures the current burn rate over the last hour, not cumulative budget consumption over the full SLO window. A temporarily high burn rate does not mean the overall budget is exhausted.",
  "tags": ["slo", "reliability", "budget"],
  "configuration": {
    "query": "FROM .slo-observability.sli-v* | WHERE slo.id == ?sloId AND @timestamp &gt;= NOW() - 1 hour | STATS sli_value = AVG(slo.numerator) / AVG(slo.denominator) BY slo.id, slo.name | EVAL error_budget_target = 0.995 | EVAL burn_rate_pct = ROUND((1 - sli_value) / (1 - error_budget_target) * 100, 1)",
    "params": {
      "sloId": {
        "type": "keyword",
        "description": "The SLO identifier. Use the SLO ID for the service you are evaluating."
      }
    }
  }
}
</code></pre>
<blockquote>
  <p><strong>Note on the SLI index:</strong> the version suffix in <code>.slo-observability.sli-v*</code> depends on your Stack release (e.g., <code>v3.6</code> in Stack 9.3). Verify with <code>GET _cat/indices/.slo-observability.*?v</code> and adjust the pattern if your cluster uses a different version.</p>
</blockquote>
<p>The burn rate interpretation rules in the <code>description</code> are the most valuable part. A raw number like "burn rate 85%" means nothing to a developer without context. The tool description translates that into actionable guidance: "below 100% means sustainable, above 200% means delay non-critical changes."</p>
<h2 id="connectingtoyoureditorviamcp">Connecting to your editor via MCP</h2>
<p>With all three tools created in Agent Builder, they are automatically available through the <a href="https://www.elastic.co/docs/solutions/search/agent-builder/mcp-server">MCP server endpoint</a>. Configure your MCP client to connect.</p>
<h3 id="claudecodeconfiguration">Claude Code configuration</h3>
<p>Add the Elastic MCP server to your Claude Code settings:</p>
<pre><code>{
  "mcpServers": {
    "elastic-agent-builder": {
      "command": "npx",
      "args": [
        "mcp-remote",
        "https://your-kibana-url/api/agent_builder/mcp",
        "--header",
        "Authorization: ApiKey your-base64-api-key"
      ]
    }
  }
}
</code></pre>
<h3 id="cursorconfiguration">Cursor configuration</h3>
<p>For Cursor, add the server in <strong>Settings &gt; MCP Servers</strong>:</p>
<pre><code>{
  "mcpServers": {
    "elastic-agent-builder": {
      "command": "npx",
      "args": [
        "mcp-remote",
        "https://your-kibana-url/api/agent_builder/mcp",
        "--header",
        "Authorization: ApiKey your-base64-api-key"
      ]
    }
  }
}
</code></pre>
<p>Once connected, your editor's AI agent will discover all three tools automatically. You can verify by asking: "What Elastic tools do you have available?" The agent should list <code>get_endpoint_health</code>, <code>get_recent_deploys</code>, and <code>get_slo_status</code>.</p>
<p><strong>API key permissions:</strong> the API key needs the <a href="https://www.elastic.co/docs/solutions/search/agent-builder/kibana-api"><code>feature_agentBuilder.read</code></a> Kibana privilege and read access to the relevant indices (<code>traces-apm.*</code>, <code>observability-annotations</code>, <code>.slo-observability.*</code>). For production use, set the key expiry to 30-90 days and follow the principle of least privilege.</p>
<h2 id="thescenarioisitsafetomergethispr">The scenario: "Is it safe to merge this PR?"</h2>
<p>A developer on the team has a pull request that increases the timeout for the downstream <code>recommendations</code> service call from 2 seconds to 5 seconds in <code>opbeans-node</code>. Before merging, they ask the agent:</p>
<blockquote>
  <p><strong>Developer:</strong> "I'm about to merge PR #42, which increases the recommendations service timeout from 2s to 5s in opbeans-node. Is it safe to merge right now?"</p>
</blockquote>
<p>The agent begins its multi-signal reasoning chain. Here is what happens.</p>
<h3 id="step1theagentcallsget_endpoint_health">Step 1: the agent calls get_endpoint_health</h3>
<p>The agent checks the current health of the service:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt96106b17d8c1dfbf/6a85c85b27c5cd3f9f5f7394/image-03.png" alt="Agent calls get_endpoint_health and returns latency percentiles, error rate, and throughput" /></p>
<h3 id="step2theagentcallsget_recent_deploys">Step 2: the agent calls get_recent_deploys</h3>
<p>Next, it checks for recent deployments:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt87131c78a4a6a828/6a85c85e1aa1e11c92ff8cf9/image-04.png" alt="Agent calls get_recent_deploys and returns the recent deploy timeline for the service" /></p>
<h3 id="step3theagentcallsget_slo_status">Step 3: the agent calls get_slo_status</h3>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt33ce0ed438af263b/6a85c86168266647891eab97/image-05.png" alt="Agent calls get_slo_status and returns the current SLO burn rate" /></p>
<h3 id="theagentsresponse">The agent's response</h3>
<p>After correlating all three results, the agent produces a recommendation:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt55cae1213a8da2c9/6a85c8649d2b71f445f9391d/image-06.png" alt="Final agent recommendation correlating endpoint health, recent deploys, and SLO burn rate to flag the merge as risky" /></p>
<p>The agent pulled the current p99, checked recent deploys, and read the SLO burn rate. It combined those signals with the timeout change in the PR, flagged the merge as risky, and recommended next steps.</p>
<h2 id="conclusionwhentousemcptoolsinsteadofpingingansre">Conclusion: when to use MCP tools instead of pinging an SRE</h2>
<p>With Elasticsearch, Agent Builder, and MCP, a developer can answer questions like "is it safe to merge this PR?" from inside their editor, in seconds, without pinging an SRE. Elasticsearch holds the signals: traces, deploy markers, and SLO budgets. Agent Builder is where the platform team encodes how to read those signals: the thresholds, the warm-up windows, the correlation rules. MCP is what carries those tools into the developer's editor.</p>
<p>The query pulls the data. The description tells the agent how to read it. The platform engineer writes the runbook once, and every developer on the team gets to use it.</p>
<h2 id="nextstepsextendelasticagentbuildermcptoolstocicd">Next steps: extend Elastic Agent Builder MCP tools to CI/CD</h2>
<ul>
<li>Explore the <a href="https://www.elastic.co/docs/solutions/search/elastic-agent-builder">Elastic Agent Builder documentation</a> for more tool types and configuration options.</li>
<li>See <a href="https://www.elastic.co/observability-labs/blog/agentic-cicd-kubernetes-mcp-server">Agentic CI/CD: Kubernetes Deployment Gates with Elastic MCP Server</a> for extending this pattern into your CI/CD pipeline.</li>
<li>Check out <a href="https://www.elastic.co/observability-labs/blog/elastic-agent-skills-observability-workflows">Agent Skills for Elastic Observability</a> for a complementary approach using pre-packaged observability skills.</li>
</ul>]]></content:encoded>
    <link>https://www.elastic.co/observability-labs/blog/mcp-tools-elastic-agent-builder</link>
    <guid isPermaLink="false">mcp-tools-elastic-agent-builder</guid>
    <category><![CDATA[Agentic Observability]]></category>
    <category><![CDATA[APM]]></category>
    <dc:creator><![CDATA[Jeffrey Rengifo]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt5c071b863a97c979/6a85c867abdc29d3a612248a/header.png" length="0" type="image/png"/>
    <pubDate>Thu, 04 Jun 2026 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Self-Driving Observability: From Stacktraces to Profiling-Derived Metrics]]></title>
    <description><![CDATA[Profiling-derived metrics turn raw stacktraces into time-series KPIs, unlock continuous profiling for every user and lay the foundation for an observability system that detects, investigates, and acts on its own.]]></description>
    <content:encoded><![CDATA[<p>Continuous profiling has come a long way. With the <a href="https://opentelemetry.io/blog/2026/profiles-alpha/">OpenTelemetry Profiles signal entering Alpha</a> and the <a href="https://github.com/open-telemetry/opentelemetry-ebpf-profiler">OpenTelemetry eBPF profiler</a> — donated by Elastic — now operating as a first-class OpenTelemetry Collector receiver, low-overhead, whole-system profiling on Linux is finally available to every OpenTelemetry user. No instrumentation, no recompilation, no service restarts. Just deploy the profiler and get visibility from the kernel, through native code, all the way up into HotSpot, Python, V8, .NET, Go, PHP, Perl, BEAM Erlang and Ruby runtimes.</p>
<p>The processing pipeline is straightforward: The profiler samples every CPU core on the system at a fixed rate
(19Hz by default), unwinds execution stacks, symbolizes the resulting stacktraces and ships the profiles to
a backend like Elasticsearch.</p>
<p>And then… the user has to figure out what to do with them.</p>
<p>That last step is where continuous profiling has historically faced adoption challenges, as
the path from "profiling is on" to "profiling is useful" is steeper than it should be.</p>
<h2 id="fourbarrierstoadoption">Four barriers to adoption</h2>
<ul>
<li><p><strong>Storage cost:</strong> Full stacktraces, even after deduplication and clever storage schemas, are expensive to store at fleet scale. That cost makes continuous profiling an opt-in feature in practice: a lot of potential users never enable it, and the ones who do, tend to enable it only on a subset of hosts.</p></li>
<li><p><strong>Query friction:</strong> A normalized stacktrace schema is optimized for ingestion and storage but complicates ad-hoc questions. "How much CPU time does my service spend in TLS?" is a simple question that may require intricate ES|QL or custom code in order to be answered.</p></li>
<li><p><strong>AI-hostile data:</strong> Normalized stacktrace data (typically involving multiple levels of indirection) resists straightforward algorithmic analysis. LLMs in particular struggle with it and necessitate further data transformations into representations more amenable to LLM processing.</p></li>
<li><p><strong>UX barrier:</strong> Flamegraphs are extremely useful when you know how to read them but intimidating when you don't.</p></li>
</ul>
<p>These four barriers compound: storage cost limits coverage, the UX barrier limits who benefits from coverage, query friction limits what questions users can ask and the AI-hostile data representation limits what the system can do when users don't know what questions to ask.</p>
<h2 id="howprofilingderivedmetricsworkclassifyattheedge">How profiling-derived metrics work: classify at the edge</h2>
<p>The core idea is simple: instead of sending full stacktraces all the way to a backend and asking the user to make sense of them there, we classify and count at the edge, inside an OpenTelemetry Collector pipeline, and emit ordinary OpenTelemetry time-series counters. The profiling logic itself doesn't change; it's still the OpenTelemetry eBPF profiler running inside the OpenTelemetry Collector. All the new work happens in a stateless connector inside the Collector: the connector inspects each stacktrace produced by the profiler, classifies its frames into one or more categories and increments counters.</p>
<p>We've released <a href="https://github.com/elastic/opentelemetry-collector-components/tree/main/connector/profilingmetricsconnector"><code>profilingmetricsconnector</code></a> as part of Elastic's <code>opentelemetry-collector-components</code> repository. It sits between the OpenTelemetry eBPF profiler receiver and any metrics exporter, and turns symbolized stacktraces into named, aggregated counters with attributes.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt5bb4283e563187e2/6a7f197c33fa8a9fe8202b6c/profilingmetricsconnector-pipeline.svg" alt="profilingmetricsconnector pipeline" /></p>
<p>Because the profilingmetricsconnector lives inside the standard OpenTelemetry Collector pipeline, every metric it produces flows through the same processors as the rest of your telemetry. In the following example, the <a href="https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/main/processor/resourcedetectionprocessor/README.md"><code>resourcedetectionprocessor</code></a> enriches each counter with host-derived attributes.</p>
<pre><code>connectors:
  profilingmetrics:
    flush_interval: 30s

receivers:
  profiling: {}

exporters:
  elasticsearch:
    endpoints:
      - # ENDPOINT
    api_key: # API_KEY
    mapping:
      mode: otel

processors:
  resourcedetection:
    detectors: ["system"]
    system:
      hostname_sources: ["os"]
      resource_attributes:
        host.name:
          enabled: true
        host.id:
          enabled: false
        host.arch:
          enabled: true
        os.description:
          enabled: true
        os.type:
          enabled: true

service:
  pipelines:
    profiles:
      receivers: [ profiling ]
      exporters: [ profilingmetrics ]
    metrics:
      receivers: [ profilingmetrics ]
      processors: [resourcedetection]
      exporters: [ elasticsearch ]
</code></pre>
<h2 id="profilingderivedcpumetricswhatgetsemitted">Profiling-derived CPU metrics: what gets emitted</h2>
<p>The connector ships with a set of pre-baked counters built from useful classification rules. Each metric is a count of stacktrace samples whose leaf frame matched a particular category, with the frequency value standing in for CPU consumption.</p>
<p>| Metric | Classifies | Attached metadata |
|---|---|---|
| <code>kernel.count</code> | Kernel leaf frames | <code>syscall</code>, <code>category</code> (<code>disk/rw</code>, <code>ipc/rw</code>, <code>network/{tcp,udp,other}/rw</code>, <code>memory</code>, <code>synchronization</code>, …) |
| <code>native.count</code> | Native C/C++/Rust leaf frames | shared library name (<code>libcrypto</code>, <code>libclrjit</code>, <code>libsystemd</code>, …) |
| <code>hotspot.count</code>, <code>go.count</code>, <code>python.count</code>, … | Runtime-specific leaf frames | runtime-specific attributes |</p>
<p>The kernel categorization is worth a closer look as a modern Linux kernel has more than 400 system calls. However, most of what shows up in CPU stacktraces falls into a handful of subsystems: filesystem read/write, network read/write, memory management, scheduling, synchronization. Some syscalls (e.g. <code>read</code>, <code>write</code>) are ambiguous on their own and only become specific when one examines more frames down the stack: <code>ext4_file_read_iter</code> points to filesystem, <code>tcp_v4_rcv</code> to network. The connector handles this disambiguation as part of frame iteration.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt282d1eb8637b61fc/6a7f197fb4377095a24d70e6/kibana-kernel-cpu-by-category.png" alt="Kernel CPU breakdown by category in Kibana" /></p>
<p>Native frames typically lack symbolic information beyond shared library names, but those names are still informative: <code>libssl</code> and <code>libcrypto</code> mean cryptographic work as part of OpenSSL or one of its variants; <code>libz</code> means compression; <code>libclrjit</code> means the .NET JIT is busy. We don't need to enumerate libraries statically as the connector dynamically generates <code>shlib_name</code> attribute values using the trimmed library name (e.g. <code>libssl</code> not <code>libssl.so.3</code>) for clean cardinality.</p>
<p>Currently, for each stacktrace, the connector computes a <strong>Self CPU</strong> count (the leaf frame matched the category) corresponding to exclusive CPU usage. A complication exists for fine-grained kernel categories like <code>network/tcp/write</code> where the actual leaf frame is usually a device-driver call that we can't meaningfully match. We deal with that by trying to match frames further up the stack (e.g. <code>tcp_sendmsg</code> is enough to correctly classify the sample).</p>
<p>Users can also add their own categories by specifying a frame pattern (e.g. a function or package) and the connector will generate counters for them.</p>
<h2 id="benefitsofprofilingderivedmetricsforobservability">Benefits of profiling-derived metrics for observability</h2>
<p>This shift looks small from the outside — "we're emitting counters" — but it changes almost everything about how profiling fits into an observability stack.</p>
<ul>
<li><p><strong>Orders of magnitude less storage:</strong> A counter aggregated over a 5-second (or 30-second or one-minute) window is dramatically cheaper than the full stacktraces it distills. The pre-aggregation interval is configurable with the trade-off being time resolution rather than categorization fidelity. For most "where is my CPU being spent?" questions, 30 seconds is plenty.</p></li>
<li><p><strong>On by default:</strong> Because the storage cost is now in line with regular metrics, profiling-derived metrics can be on for everyone, on every host, from the moment the profiler is deployed. Users get a CPU breakdown by runtime, syscall, kernel category and shared library on day one.</p></li>
<li><p><strong>Standard dashboards:</strong> These are ordinary OpenTelemetry time-series counters and can be visualized ad-hoc using stacked bar graphs, pie charts, top-N panels or any other visualization Kibana supports out of the box. The same Lens and TSDB-backed views for application metrics work here.</p></li>
</ul>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blte715bd8eb945ab75/6a7f198296b5a6391f87b86b/kibana-user-cpu-over-time.png" alt="User CPU by frame type over time in Kibana" /></p>
<ul>
<li><p><strong>AI and query-friendly:</strong> Standard time-series data is trivially consumable by ES|QL, ML jobs, anomaly detectors and by LLMs. "Show me the top services by <code>network/udp/write</code> time, filtered to the payments namespace, over the last six hours" is one query that is not only simple for the system to answer but also simple for an LLM to generate.</p></li>
<li><p><strong>Cross-signal correlation:</strong> Because the metrics flow through the standard OpenTelemetry Collector pipeline, they pick up the same resource attributes (e.g. <code>service.name</code>, <code>k8s.pod.name</code>, <code>host.name</code>, <code>deployment.environment</code>) that logs, other metrics and traces already carry.</p></li>
<li><p><strong>Instant value, with a path to more detail:</strong> A user who just wants to know "what's burning my CPU?" gets a meaningful answer without ever opening a flamegraph. A user who wants to dig deeper still has the full eBPF profiler underneath, ready to hand back complete stacktraces when they're warranted.</p></li>
</ul>
<h2 id="userprogrammableprofilingandadaptivesampling">User-programmable profiling and adaptive sampling</h2>
<p>The longer-term direction is for the profiler to stop being something users <em>consume</em> and start being something they <em>program</em>. User-defined metrics are the first step in this direction, complemented by on-demand (full) profiling and adaptive sampling.</p>
<p>Profiling-derived metrics or other signals can act as a trigger for on-demand profiling where the system enables full profiling on a specific host or service to capture complete stacktraces. In that way, the full profiling processing and storage cost is paid only when it matters.</p>
<p>We can apply the same idea to the sampling rate. 19Hz is a sensible baseline for steady state but when the metrics signal an interesting event or an anomaly, the system can automatically ramp to 100Hz or higher to capture high-fidelity data for the time window during which it's relevant. It can then ramp down to baseline.</p>
<h2 id="howprofilingderivedmetricsenableselfdrivingobservability">How profiling-derived metrics enable self-driving observability</h2>
<p>Most observability stacks today use an open-loop model: the profiler emits data with a fixed configuration. Then a human looks at flamegraphs and dashboards, potentially correlates with logs, other metrics and traces, forms a hypothesis and triggers a deeper investigation. Every link in this chain requires a human decision. Nothing feeds back into the profiler at speed and the system cannot act on its own observations.</p>
<p>Profiling-derived metrics close that loop.</p>
<ol>
<li><p>A "significant host events" metric, an anomaly on <code>network/udp/write</code> or a spike in <code>native.count/libz</code>: something crosses a threshold.</p></li>
<li><p>The profiler adjusts in response: sampling rate increases, full profiling turns on for the affected hosts.</p></li>
<li><p>The richer data is correlated against logs, traces, and other metrics by an LLM, by a human or both. The same resource attributes that make cross-signal correlation easy for the user make it easy for the system.</p></li>
<li><p>A root cause is identified. A remediation is suggested or applied. The metric returns to baseline and the loop continues.</p></li>
</ol>
<p>This is what we mean when we talk about <em>self-driving observability</em>. The profiler is no longer just an instrument that someone wields. It is the sensory organ of an autonomous feedback loop: a system that observes itself, decides what to look at more closely and adjusts its own configuration in response to what it sees.</p>
<h2 id="whatsnextinclusivecpuoffcpumetricsandruntimespecificprofiling">What's next: inclusive CPU, off-CPU metrics, and runtime-specific profiling</h2>
<p>Any piece of data visible in a stacktrace can be a metric source and several extensions are already on the roadmap.</p>
<ul>
<li><p><strong>Inclusive-CPU metrics:</strong> Today's pre-baked counters attribute CPU at the leaf frame (exclusive-CPU). Inclusive-CPU metrics will attribute the entire call chain which is useful when you care about the total cost of a function call — the function plus everything it transitively calls — not just the work done directly in its own body.</p></li>
<li><p><strong>Runtime-specific metrics:</strong> GC time per runtime, JSON/Protobuf serialization, RPC frameworks, FFI boundaries. The kinds of questions every team eventually asks about their language runtime, answered by default.</p></li>
<li><p><strong>Off-CPU metrics:</strong> On-CPU profiling tells you where you're spending CPU but Off-CPU profiling tells you where you're <em>not</em> (e.g. blocked on I/O, locks). The same classification logic applies, with the only change being the source signal.</p></li>
</ul>
<p>Profiling-derived metrics are an active area of work within Elastic and the <a href="https://github.com/elastic/opentelemetry-collector-components/tree/main/connector/profilingmetricsconnector">profilingmetricsconnector</a> is the place to start if you want to play with this today. A ready-made <a href="https://www.elastic.co/docs/reference/integrations/profilingmetrics_otel">Kibana integration</a> ships dashboards for all the metrics described above.</p>
<p>If you're already using Elastic's continuous profiling, expect these metrics to show up as first-class citizens in the Elastic stack. If you're not, this is a very low-friction way in as no flamegraph expertise is required and storage
cost is minimal.</p>
<p>The flamegraph isn't going anywhere, but for the first time, it isn't the <em>only</em> way profiling yields results.</p>]]></content:encoded>
    <link>https://www.elastic.co/observability-labs/blog/otel-profiling-metrics</link>
    <guid isPermaLink="false">otel-profiling-metrics</guid>
    <category><![CDATA[OpenTelemetry]]></category>
    <category><![CDATA[Metrics]]></category>
    <category><![CDATA[APM]]></category>
    <dc:creator><![CDATA[Christos Kalkanis,Roger Coll]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt418826f669e25898/6a7f19859090b02bc984ee13/header.jpg" length="0" type="image/jpeg"/>
    <pubDate>Mon, 01 Jun 2026 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[How to trace MCP server tool calls with OpenTelemetry and Elastic APM]]></title>
    <description><![CDATA[Add OpenTelemetry tracing to an MCP server, visualize tool call performance in Elastic APM, and query the trace data from Claude Desktop using the Agent Builder MCP.]]></description>
    <content:encoded><![CDATA[<p>An MCP server is just a Node process, which means OpenTelemetry instrumentation is one <code>--import</code> flag away. What is new is what happens after the traces land in <a href="https://www.elastic.co/docs/solutions/observability/apm">Elastic APM</a>. The same Claude Desktop session that produced them can query them back through the <a href="https://www.elastic.co/docs/explore-analyze/ai-features/agent-builder/mcp-server">Elastic Agent Builder MCP</a>. The agent analyzes its own tool-call latency, identifies slow tools, and explains failures without leaving the chat. Observability stops being a dashboard that the human checks after the fact and becomes the context the agent uses while working. This post walks through the OTel semantic conventions for MCP, the wrapper pattern for tool spans, and how the loop closes on the Elastic side.</p>
<h2 id="prerequisites">Prerequisites</h2>
<ul>
<li><p>Elastic Cloud hosted (9.3+) or <a href="https://www.elastic.co/docs/deploy-manage/deploy/elastic-cloud/serverless">serverless</a></p></li>
<li><p><a href="https://claude.com/download">Claude Desktop</a></p></li>
<li><p>An MCP server instrumented with the Elastic Distribution of OpenTelemetry (EDOT). We cover how to instrument one below.</p></li>
</ul>
<h2 id="theobservabilitygapinmcpservers">The observability gap in MCP servers</h2>
<p>MCP servers ship with no built-in observability, which means tool-call latency, errors, and performance baselines are invisible to developers.
<a href="https://modelcontextprotocol.io/docs/getting-started/intro">MCP (Model Context Protocol) servers</a> are increasingly used as infrastructure for AI-powered applications, giving AI models access to databases, APIs, internal <a href="https://modelcontextprotocol.io/specification/2025-06-18/server/tools">tools</a>, and business data.
The MCP SDK does not instrument any of it.</p>
<p>The gap shows up in three concrete ways:</p>
<ul>
<li>When a tool call takes 3 seconds, you don't know if the bottleneck is in your business logic, a downstream API, or the data layer.</li>
<li>When a tool call fails, you get the error message but no context about what the server was doing before it failed.</li>
<li>When you add a new tool, you have no baseline to compare its performance against.</li>
</ul>
<p>These are the same problems any backend service faces.
The answer is the same one backend developers have used for years: distributed tracing with <a href="https://www.elastic.co/docs/solutions/observability/apm/opentelemetry">OpenTelemetry</a>.</p>
<p>MCP servers are standard programmatic processes.
There is nothing special about them from an instrumentation perspective.
You add the <a href="https://opentelemetry.io/docs/languages/js/getting-started/nodejs/">OTel SDK</a>, define spans around your tool handlers, and ship traces to your backend.
The only new part is knowing which span names and attributes to use so your traces are meaningful and consistent.</p>
<h2 id="whatwebuilt">What we built</h2>
<p>For this article, we use the <a href="https://www.npmjs.com/package/@modelcontextprotocol/server-everything"><code>@modelcontextprotocol/server-everything</code></a> package, the official reference MCP server published by Anthropic.
It ships with a set of tools that cover the common patterns you will find in real-world MCP servers: simple request/response, parameterized calls, long-running operations, and calls that return structured data.</p>
<p>The server exposes several tools.
In this article we use three of them:</p>
<ul>
<li><p><code>echo</code>: receives a string and returns it unchanged. A minimal request/response tool, useful for verifying that the instrumentation pipeline works end to end.</p></li>
<li><p><code>get-sum</code>: receives two numbers and returns their sum. Represents a parameterized tool with simple business logic.</p></li>
<li><p><code>trigger-long-running-operation</code>: starts a multi-step operation that takes several seconds to complete. Simulates tools that call downstream APIs or run expensive computations.</p></li>
</ul>
<p>We instrumented it with <a href="https://www.elastic.co/docs/reference/opentelemetry/edot-sdks/node/setup">EDOT Node.js</a> (<code>@elastic/opentelemetry-node</code>), which is Elastic's distribution of the OpenTelemetry SDK.
EDOT replaces the five or six individual OTel packages you would otherwise install and adds the <a href="https://www.elastic.co/docs/reference/opentelemetry/compatibility/edot-vs-upstream"><code>elasticapm</code> connector</a> that the Kibana APM UI needs to build its service maps, transaction groupings, and latency charts.
Without that connector, raw OTLP data arrives in Elasticsearch but the APM views have nothing to build from.</p>
<p>The architecture looks like this:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blte33fadb139d4917e/6a7f0d48c2cc0911f82495b0/architecture.png" alt="Architecture diagram showing Claude Desktop connected to two MCP servers: an instrumented MCP server that emits OpenTelemetry traces to Elastic APM, and the Elastic Agent Builder MCP that queries those traces back from Elasticsearch" /></p>
<p>This is the loop: Claude executes tools, generates telemetry, and then uses a second MCP to analyze that telemetry.
The observability data becomes something the AI can reason about, not just something that sits in a dashboard waiting for a human to check it.</p>
<p>Both MCP servers are active simultaneously in Claude Desktop.
The instrumented MCP generates telemetry.
The Agent Builder MCP lets us query it.</p>
<p>To connect Claude Desktop to both servers, the <code>claude_desktop_config.json</code> looks like this:</p>
<pre><code>{
  "mcpServers": {
    "everything": {
      "command": "node",
      "args": [
        "--import",
        "/path/to/node_modules/@elastic/opentelemetry-node/import.mjs",
        "/path/to/everything/dist/index.js",
        "stdio"
      ],
      "env": {
        "OTEL_SERVICE_NAME": "everything-mcp-server",
        "OTEL_EXPORTER_OTLP_ENDPOINT": "https://&lt;your-otlp-endpoint&gt;",
        "OTEL_EXPORTER_OTLP_HEADERS": "Authorization=ApiKey &lt;your-api-key&gt;",
        "OTEL_LOG_LEVEL": "none"
      }
    },
    "elastic-agent-builder": {
      "command": "npx",
      "args": [
        "mcp-remote",
        "https://&lt;your-kibana-url&gt;/api/agent_builder/mcp",
        "--header",
        "Authorization:ApiKey &lt;your-api-key&gt;"
      ]
    }
  }
}
</code></pre>
<p>The <code>--import /path/to/@elastic/opentelemetry-node/import.mjs</code> flag is all it takes for zero-code auto-instrumentation.
But auto-instrumentation only captures HTTP calls, database queries, and other Node.js instrumented libraries.
MCP tool calls are application logic, and application logic needs manual spans.</p>
<h2 id="whattracesmcptoolcallsgenerate">What traces MCP tool calls generate</h2>
<p>The OpenTelemetry specification includes <a href="https://opentelemetry.io/docs/specs/semconv/gen-ai/mcp/">official semantic conventions for MCP</a>.
Following them means your traces are consistent, searchable by name across tools and teams, and compatible with any OTel-aware backend, including Elastic APM.</p>
<p><strong>Span naming</strong> follows the pattern <code>{mcp.method.name} {target}</code>.
For a tool call, this becomes <code>tools/call echo</code> or <code>tools/call get-sum</code>.
This is what you will see as the transaction name in Kibana APM.</p>
<p><strong>Key attributes</strong> on each span:</p>
<p>| Attribute               | Value            | Purpose                   |
| ----------------------- | ---------------- | ------------------------- |
| <code>mcp.method.name</code>       | <code>tools/call</code>     | The MCP protocol method   |
| <code>gen_ai.tool.name</code>      | <code>echo</code>           | The specific tool invoked |
| <code>gen_ai.operation.name</code> | <code>execute_tool</code>   | GenAI semantic convention |
| <code>error.type</code>            | error class name | Set only on failure       |</p>
<p>The wrapper pattern that creates these spans looks like this:</p>
<pre><code>const tracer = trace.getTracer('everything-mcp-server', '1.0.0');

function withToolSpan(toolName, fn) {
  return tracer.startActiveSpan(`tools/call ${toolName}`, (span) =&gt; {
    span.setAttribute('mcp.method.name', 'tools/call');
    span.setAttribute('gen_ai.tool.name', toolName);
    span.setAttribute('gen_ai.operation.name', 'execute_tool');

    try {
      const result = fn();
      span.end();
      return result;
    } catch (err) {
      span.recordException(err);
      span.setStatus({ code: SpanStatusCode.ERROR, message: err.message });
      span.setAttribute('error.type', err.constructor.name);
      span.end();
      throw err;
    }
  });
}
</code></pre>
<p>Each tool handler wraps its logic in <code>withToolSpan</code>.
The result is a named span in Elastic APM for every tool invocation, with duration, status, and error details attached.</p>
<p><strong><em>Security note:</em></strong> <em>The OTel spec defines two optional attributes for tool calls:</em> <code>gen_ai.tool.call.arguments</code> <em>and</em> <code>gen_ai.tool.call.result</code>_. Both are flagged as potentially containing sensitive data. The_ <code>get-env</code> <em>tool in the everything server is a good example of why this matters: it returns all environment variables, which may include API keys and credentials. Capture these attributes only if you have confirmed the data is safe to store in your observability backend, and consider masking or filtering at the SDK level before export.</em></p>
<h2 id="kibanaapmexploringmcpserverperformance">Kibana APM: exploring MCP server performance</h2>
<p>After starting Claude Desktop with both MCPs configured and triggering a few tool calls, the <code>everything-mcp-server</code> service appears in Kibana under <strong>Observability &gt; Applications &gt; Services Inventory</strong>:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltdbd9c14fcc24ffb0/6a7f0d4bbd2198f4547580d3/services-inventory.png" alt="Kibana APM Services Inventory view showing the everything-mcp-server entry with latency, throughput, and error rate columns populated" /></p>
<h3 id="transactionsview">Transactions view</h3>
<p>Kibana groups traces by transaction name.
Because we follow the semantic conventions, each tool gets its own row: <code>tools/call echo</code>, <code>tools/call get-sum</code>, <code>tools/call trigger-long-running-operation</code>.
You can immediately see latency, throughput, and error rate per tool without any configuration.</p>
<p>This is where the value of consistent span naming becomes concrete.
If you have five different developers adding tools to an MCP server and everyone follows <code>tools/call {toolName}</code>, the APM UI stays organized automatically.</p>
<h3 id="tracewaterfall">Trace waterfall</h3>
<p>Clicking on a specific trace shows the waterfall view.
For a single tool call, the waterfall is straightforward: one span covering the full execution.
If your tool handler makes downstream HTTP requests or database queries that are auto-instrumented, those appear as child spans.
You can see exactly how much time was spent in business logic versus waiting for external calls.</p>
<h3 id="latencydistribution">Latency distribution</h3>
<p>The latency chart shows p50, p95, and p99 distribution across all executions of a given tool.
This makes it easy to distinguish between tools that are consistently fast and those that have occasional outliers.
The <code>trigger-long-running-operation</code> tool, for example, shows a wide distribution depending on how many steps were requested: a useful baseline for understanding expected execution time ranges before setting alerts.</p>
<h3 id="errortracking">Error tracking</h3>
<p>Failed tool calls appear in the Errors panel with their full stack trace, the span attributes attached at the time of failure, and a count of how many times the error has occurred.
If you record the exception with <code>span.recordException(err)</code>, Kibana links the error directly to the trace that produced it.</p>
<h2 id="closingtheloopwiththeagentbuildermcp">Closing the loop with the Agent Builder MCP</h2>
<p>The <a href="https://www.elastic.co/docs/explore-analyze/ai-features/agent-builder/mcp-server">Elastic Agent Builder MCP</a> lets Claude query its own trace data from Elasticsearch in the same chat session that produced the traces.
The Agent Builder MCP can query any Elasticsearch index the API key has access to, and APM traces are stored under <code>.ds-traces-apm.otel-default-*</code>.
Granting the Agent Builder API key read access to those indices is what closes the loop: the agent that executed the tool calls can now reason about how they performed.</p>
<p>Here is what this looks like in practice.
To generate traces, let's ask in Claude Desktop: <em>"Use the echo tool to say hello, then use get-sum to add 1337 and 42, then run a long-running operation with 3 steps."</em></p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltf3f00a45a235deb1/6a7f0d4eeab5be043420a709/claude-tool-execution.png" alt="Claude Desktop conversation showing three tool calls executed in sequence: echo, get-sum, and trigger-long-running-operation, each with its result rendered inline" /></p>
<p>Claude executes three tool calls on the instrumented MCP.
Three spans land in Elastic APM.</p>
<p>Now, without leaving the chat, let's try querying the traces by asking: <em>"Search the APM trace data from the last 10 minutes. What tool calls were made, and how long did each one take?"</em></p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt74ef63c895e22bbc/6a7f0d51bd219823847580d9/claude-trace-query.png" alt="Claude Desktop chat where the model uses the Elastic Agent Builder MCP to run an ES|QL query against the APM traces index and reports tool names, durations, and status in natural language" /></p>
<p>We can confirm the information against the services data:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltb362dbef6539a49f/6a7f0d5305b7b511c418b998/services-data-confirmation.png" alt="Kibana APM transactions view for everything-mcp-server showing the same three tool call transactions and their durations as returned by Claude" /></p>
<p>Claude uses the Agent Builder MCP to run a query against the traces index.
It returns the tool names, durations, and status from the actual trace data, then synthesizes an answer in natural language.</p>
<p>You can go further by asking:</p>
<ul>
<li><p>"Which of those tool calls had the highest p95 latency?"</p></li>
<li><p>"Did any tool calls fail? If so, what was the error message?"</p></li>
<li><p>"Compare the latency of the echo tool vs get-sum across all calls in the last hour."</p></li>
</ul>
<p>Each of these questions translates into an ES|QL query via the Agent Builder's <code>platform.core.execute_esql</code> tool, run against the APM trace indices.</p>
<h2 id="whyelasticformcpobservability">Why Elastic for MCP observability</h2>
<p><strong>The Agent Builder closes the loop:</strong> this is the part that is specific to the Elastic ecosystem.
Because APM data lives in Elasticsearch, and Elasticsearch is queryable via the Agent Builder MCP, you can bring your AI agent's own observability data back into the conversation.
Your AI can reflect on its own performance and spot anomalies.</p>
<p><strong>APM UI built for <a href="https://www.elastic.co/observability-labs/blog/openai-tracing-elastic-opentelemetry">distributed tracing</a>:</strong> Kibana's APM interface is designed for exactly this kind of data: named transactions, trace waterfalls, latency percentiles, error tracking with stack traces, and service maps.</p>
<p><strong>Managed <a href="https://www.elastic.co/docs/reference/opentelemetry/motlp">OTLP</a> endpoint:</strong> Elastic APM accepts OTLP directly since Elastic 8.x.
You point <code>OTEL_EXPORTER_OTLP_ENDPOINT</code> at your APM server and it works.</p>
<p><strong><a href="https://www.elastic.co/observability-labs/blog/elastic-distributions-opentelemetry-ga">EDOT</a> simplifies the setup:</strong> the <code>elasticapm</code> connector is included, which means the APM UI views work without any additional configuration.</p>
<h2 id="conclusion">Conclusion</h2>
<p>MCP servers do not need special observability tooling.
They are programmatic processes, and OpenTelemetry is the right instrument for processes.
The OTel MCP semantic conventions are stable and give you a consistent naming scheme that scales across tools and teams.</p>
<p>What makes the Elastic setup interesting is not the instrumentation itself.
It is the second MCP.
When your observability data lives in Elasticsearch, you can query it from the same AI session that generated it.
That feedback loop is new, and it opens up use cases that dashboards alone cannot cover: real-time anomaly questions, automated triage, and <a href="https://www.elastic.co/observability-labs/blog/elastic-agent-skills-observability-workflows">AI-assisted incident investigation</a> from the chat interface your team is already using.</p>
<h2 id="nextsteps">Next steps</h2>
<ul>
<li><p><a href="https://www.elastic.co/docs/reference/opentelemetry/edot-sdks/node/setup">Set up EDOT Node.js</a></p></li>
<li><p><a href="https://www.elastic.co/docs/explore-analyze/ai-features/agent-builder/mcp-server">Configure the Elastic Agent Builder MCP</a></p></li>
<li><p><a href="https://opentelemetry.io/docs/specs/semconv/gen-ai/mcp/">OTel semantic conventions for MCP</a></p></li>
<li><p><a href="https://www.elastic.co/observability-labs/blog/elastic-managed-otlp-endpoint-ga-elastic-cloud-hosted">Managed OTLP endpoint on Elastic Cloud (now GA)</a></p></li>
<li><p><a href="https://www.elastic.co/observability-labs/blog/elastic-opentelemetry-openai">Instrumenting Node.js applications with EDOT</a></p></li>
</ul>
<h2 id="frequentlyaskedquestions">Frequently asked questions</h2>
<p><strong>How do I add tracing to an MCP server?</strong>
Use the OpenTelemetry SDK and follow the official OTel MCP semantic conventions. With the Elastic Distribution of OpenTelemetry (EDOT) for Node.js, a single <code>--import</code> flag enables auto-instrumentation for HTTP and database calls. Tool-call spans need to be added manually using a wrapper that sets <code>mcp.method.name</code>, <code>gen_ai.tool.name</code>, and <code>gen_ai.operation.name</code>.</p>
<p><strong>Why are my MCP tool calls slow and how do I find the bottleneck?</strong>
Without tracing, an MCP tool call is a black box: you see the result but not where the time went. Instrument the server with OpenTelemetry, ship traces to Elastic APM, and use the trace waterfall view in Kibana to see exactly how much time was spent in business logic versus downstream HTTP or database calls.</p>
<p><strong>Can I send MCP server traces to Elastic APM without a custom collector?</strong>
Yes. Elastic APM accepts OTLP directly. Set <code>OTEL_EXPORTER_OTLP_ENDPOINT</code> to your APM endpoint and <code>OTEL_EXPORTER_OTLP_HEADERS</code> with an API key, and traces flow in. EDOT bundles the <code>elasticapm</code> connector that the Kibana APM UI needs for service maps and transaction grouping.</p>
<p><strong>What span names and attributes should I use for MCP tool calls?</strong>
Follow the OpenTelemetry MCP semantic conventions: name spans <code>{mcp.method.name} {target}</code> (for example, <code>tools/call echo</code>), and set <code>mcp.method.name</code>, <code>gen_ai.tool.name</code>, and <code>gen_ai.operation.name=execute_tool</code>. On failure, set <code>error.type</code> to the error class name. Consistent naming means the Elastic APM transactions view groups your tool calls automatically.</p>
<p><strong>How is this different from sending MCP traces to Datadog or Grafana?</strong>
Any OTel-compatible backend can receive the traces. The Elastic-specific part is the Agent Builder MCP, which lets the same AI agent that generated the traces query them back from Elasticsearch in natural language. That feedback loop, where the AI reasons about its own tool-call performance, is not available with backends that do not expose their data through an MCP server.</p>
<p><strong>Should I capture MCP tool call arguments and results in my traces?</strong>
The OTel spec defines <code>gen_ai.tool.call.arguments</code> and <code>gen_ai.tool.call.result</code> as optional and warns they may contain sensitive data. Tools like <code>get-env</code>, which returns environment variables, illustrate the risk: API keys and credentials can land in your observability backend. Capture these only when the data is safe to store, and consider masking at the SDK level before export.</p>
<p><strong>Does this work for MCP servers written in languages other than Node.js?</strong>
The OpenTelemetry MCP semantic conventions are language-agnostic. EDOT is available for Node.js, Java, Python, .NET, and other languages, and any of them can send OTLP to Elastic APM. The wrapper pattern shown in this post translates directly: open a span around the tool handler, set the standard attributes, record exceptions on failure.</p>]]></content:encoded>
    <link>https://www.elastic.co/observability-labs/blog/mcp-tracing-opentelemetry-elastic-apm</link>
    <guid isPermaLink="false">mcp-tracing-opentelemetry-elastic-apm</guid>
    <category><![CDATA[APM]]></category>
    <category><![CDATA[OpenTelemetry]]></category>
    <category><![CDATA[AI]]></category>
    <dc:creator><![CDATA[Jeffrey Rengifo]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt01a77347914d1229/6a7f0d57c2e9145f21016c0c/header_image.png" length="0" type="image/png"/>
    <pubDate>Thu, 14 May 2026 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Connecting Cursor to Production Logs via the Elastic MCP Server]]></title>
    <description><![CDATA[Learn how to connect Cursor to your Elastic APM data using the Elastic Agent Builder MCP server, so you can debug production errors and make UI decisions backed by real usage data without leaving your editor.]]></description>
    <content:encoded><![CDATA[<h2 id="prerequisites">Prerequisites</h2>
<ul>
<li><p>Elasticsearch 9.3+ (or Elastic Cloud Serverless)</p></li>
<li><p>Elasticsearch API KEY and Kibana URL</p></li>
<li><p>An application instrumented with Elastic APM: the <a href="https://www.elastic.co/guide/en/apm/agent/rum-js/current/index.html">RUM agent</a> for frontend interactions (populates <code>traces-apm-*</code>) and the <a href="https://www.elastic.co/docs/reference/apm-agents">APM agent</a> for backend errors (populates <code>logs-apm.error-*</code></p></li>
<li><p><a href="https://cursor.com/home">Cursor</a> (version 2.6+) installed</p></li>
</ul>
<h2 id="theproblemwithtwoworlds">The problem with two worlds</h2>
<p>Application logs and code are two separate worlds that don't talk to each other. If you want to apply log insights into the application you have to analyze the logs, and then come back to the editor and apply your findings.</p>
<p>The <a href="https://modelcontextprotocol.io/">Model Context Protocol (MCP)</a> changes this. MCP is an open standard that lets AI clients like Cursor connect to external tools and data sources through a standardized interface. Instead of your IDE only knowing about your local code, it can also talk to your Elasticsearch cluster, query your APM data, and reason about production behavior alongside your source files.</p>
<p>Elastic ships a <a href="https://www.elastic.co/docs/explore-analyze/ai-features/agent-builder/mcp-server">built-in MCP server</a> as part of <a href="https://www.elastic.co/docs/explore-analyze/ai-features/elastic-agent-builder">Agent Builder</a>. You define tools in Kibana, expose them via the MCP endpoint, and any MCP-compatible client can call them. Cursor supports MCP natively, which means you can set this up in minutes.</p>
<h2 id="whatwerebuilding">What we're building</h2>
<p>We're working with an eCommerce search app instrumented with Elastic APM. The RUM JS agent tracks filter click interactions from the browser, stored in <code>traces-apm-default</code>. The Node.js APM agent captures backend errors, stored in <code>logs-apm.error-default</code>.</p>
<p>Two situations come up during development:</p>
<ul>
<li><p><strong>Use case 1</strong>: The product team wants to simplify the search page. There are six filters but we don't know which ones users actually click. We need usage data to decide which to keep.</p></li>
<li><p><strong>Use case 2</strong>: Users report intermittent 500 errors on search. The errors are not constant and started two days ago. We need the error details to find the root cause.</p></li>
</ul>
<p>To bring that data into Cursor, we'll build two Agent Builder tools in Kibana and connect them via the <a href="https://www.elastic.co/docs/explore-analyze/ai-features/agent-builder/mcp-server">Elastic Agent Builder MCP Server</a>:</p>
<ul>
<li><p><code>get_filter_usage</code>: queries <code>traces-apm-default</code> for filter click events and returns a usage breakdown by filter name</p></li>
<li><p><code>get_recent_errors</code>: queries <code>logs-apm.error-default</code> for the most recent error groups for a given service, including the exception message and stack trace culprit</p></li>
</ul>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt62d3a8324299ca7a/6a7f080dc2cc09675c24935f/architecture.png" alt="Architecture diagram showing Cursor connecting to the Elastic Agent Builder MCP server, which queries Elasticsearch APM data" /></p>
<p>For a deeper look at the overall architecture, see the <a href="https://www.elastic.co/search-labs/blog/agent-builder-mcp-reference-architecture-elasticsearch">Agent Builder reference guide</a>.  </p>
<h2 id="settinguptheelasticmcpservernbspnbsp">Setting up the Elastic MCP Server  </h2>
<h3 id="step1createtheagentbuildertools">Step 1: Create the Agent Builder tools</h3>
<p>We create both tools via the <a href="https://www.elastic.co/docs/explore-analyze/ai-features/agent-builder/kibana-api">Kibana Agent Builder API</a>. Each tool is an ES|QL query with a name and description that Cursor uses to decide when to call it. The full implementation of the tools is in the following <a href="https://github.com/elastic/elasticsearch-labs/blob/main/supporting-blog-content/cursor-production-logs-elastic-mcp-server/notebook.ipynb"><code>notebook</code></a>.</p>
<h4 id="tool1get_filter_usage">Tool 1: get_filter_usage</h4>
<p>The product team needs to know which filters users actually click before deciding which ones to remove. The query reads RUM interaction events from <code>traces-apm-default</code> and groups them by filter name:</p>
<pre><code>    {
    &amp;nbsp;&amp;nbsp;"id": "get_filter_usage",
    &amp;nbsp;&amp;nbsp;"type": "esql",
    &amp;nbsp;&amp;nbsp;"description": "Returns the usage count for each search filter in the ecommerce-search-ui service, sorted by most used first.",
    &amp;nbsp;&amp;nbsp;"configuration": {
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;"query": "FROM traces-apm-default | WHERE service.name == \"ecommerce-search-ui\" | WHERE transaction.type == \"user-interaction\" | WHERE labels.filter_name IS NOT NULL | STATS count = COUNT(*) BY labels.filter_name | SORT count DESC"
    &amp;nbsp;&amp;nbsp;}
    }
</code></pre>
<h4 id="tool2get_recent_errors">Tool 2: get_recent_errors</h4>
<p>For the error debugging use case, we need to surface the most frequent recent errors for a service, along with where in the code they originate. <code>STATS ... BY</code> groups errors by their fingerprint (<code>grouping_key</code>), surfaces the exception message and the line of code that caused it (<code>culprit</code>), and ranks by frequency:</p>
<pre><code>    {
    &amp;nbsp;&amp;nbsp;"id": "get_recent_errors",
    &amp;nbsp;&amp;nbsp;"type": "esql",
    &amp;nbsp;&amp;nbsp;"description": "Returns the most frequent error groups for ecommerce-search-ui, ranked by occurrence count, with the exception message and code location.",
    &amp;nbsp;&amp;nbsp;"configuration": {
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;"query": "FROM logs-apm.error-default | WHERE service.name == \"ecommerce-search-ui\" | WHERE processor.name == \"error\" | STATS count = COUNT(*) BY error.grouping_key, error.exception.0.message, error.culprit | SORT count DESC | LIMIT 5"
    &amp;nbsp;&amp;nbsp;}
    }
</code></pre>
<p>Both tools are created with <code>POST /api/agent_builder/tools</code>. You can learn more about the Kibana API endpoints for Elastic Agent Builder <a href="https://www.elastic.co/docs/explore-analyze/ai-features/agent-builder/kibana-api">here</a>.</p>
<h3 id="step2connecttocursor">Step 2: Connect to Cursor</h3>
<p>Open <code>~/.cursor/mcp.json</code> and add the Elastic server. For detailed information, see the Cursor <a href="https://cursor.com/docs/mcp#using-mcpjson">documentation</a>. The Agent Builder MCP endpoint uses Server-Sent Events (SSE) transport, so we connect via <code>mcp-remote</code>, a lightweight bridge that Cursor invokes as a local process:</p>
<pre><code>    {
    &amp;nbsp;&amp;nbsp;"mcpServers": {
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;"elastic-agent-builder": {
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;"command": "npx",
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;"args": [
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;"-y",
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;"mcp-remote",
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;"https://YOUR_KIBANA_URL/api/agent_builder/mcp",
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;"--header",
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;"Authorization: ApiKey YOUR_API_KEY"
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;]
    &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
    &amp;nbsp;&amp;nbsp;}
    }
</code></pre>
<p>Replace <code>YOUR_KIBANA_URL</code> and <code>YOUR_API_KEY</code> with your values.</p>
<p>Restart Cursor, open the Agent panel, and confirm that <code>get_filter_usage</code> and <code>get_recent_errors</code> appear in the available tools list. </p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt26bfbf5b85c381ad/6a7f0810c2cc099f19249363/cursor-mcp-tools.png" alt="Cursor MCP panel showing the get_filter_usage and get_recent_errors tools available from the Elastic Agent Builder server" /></p>
<h2 id="usecase1datadrivenuioptimization">Use case 1: Data-driven UI optimization</h2>
<p>The eCommerce search page has six filters: category, manufacturer, price range, customer gender, day of week, and region. The product team wants to simplify the UI by removing filters that users don't use as much. Rather than guessing, we ask Cursor to check.</p>
<p>When you type a prompt in Cursor's Agent panel, the model sees the name and description of every connected MCP tool. It matches your intent to the best-fitting tool and calls it automatically. This is why the <code>description</code> field we set in Step 1 matters: it's what the model reads to decide which tool answers your question. If you are interested in learning more about Cursor’s MCP tools management, read the following <a href="https://cursor.com/docs/mcp#using-mcp-in-chat">documentation</a>.</p>
<p>Open a Cursor chat and ask: "Show me how often each search filter is used." Cursor calls the tool and returns something like:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt0cc46e50f9d94970/6a7f0813bd21980745757eda/filter-usage-chart.png" alt="Filter usage breakdown returned by the get_filter_usage tool" /></p>
<p>The category and manufacturer filters get most of the clicks. The bottom three filters (<code>customer_gender</code>, <code>day_of_week</code>, <code>region</code>) are rarely used.</p>
<p>Ask Cursor to act on this: <strong><em>"Based on this data, simplify the SearchFilters component. Keep the top 3 filters visible, collapse the others under a 'More filters' toggle."</em></strong></p>
<p>Cursor opens <code>src/components/SearchFilters.jsx</code>, reads the current implementation, and proposes the change.</p>
<p>Before: </p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt8af7e161637b0151/6a7f0816e3a219301899f2a4/search-filters-before.png" alt="SearchFilters component before the change, showing all six filters" /></p>
<p>After: </p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt61f5bd24ca30c19e/6a7f0819ead8ece767baa672/search-filters-after.png" alt="SearchFilters component after the change, showing the top three filters with the rest collapsed under a More filters toggle" /></p>
<p>The entire loop took one chat prompt. The decision was backed by production data, not a team discussion about what users probably care about.</p>
<h2 id="usecase2productionerrordebugging">Use case 2: Production error debugging</h2>
<p>A bug report comes in: intermittent 500 errors on the search endpoint. The errors started appearing two days ago but they're not constant. The developer opens Cursor and asks: "Show me what errors ecommerce-search-ui is throwing."</p>
<p>Cursor calls the tool and returns the error groups:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt67bae433977e721a/6a7f081c227b1c4eeb59841e/recent-errors.png" alt="Most recent error groups returned by the get_recent_errors tool" /></p>
<p>The error message is explicit: <code>category</code> is a text field and can't be used in terms of aggregation. The correct field is <code>category.keyword</code>. </p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt37f176d0f26a2fb9/6a7f081f3cab1cfd580e4662/error-fix-diff.png" alt="Cursor proposing the fix that changes category to category.keyword in the ES|QL query" /></p>
<p>With APM data available alongside your code, the debugging session becomes a conversation: you describe the symptom, the agent pulls the relevant logs, and you work through what's happening together. You can ask follow-up questions, check whether the error correlates with a recent deployment, or ask which endpoints are most affected, all within the same context where you'll make the fix. If you want to go further, Elastic also provides <a href="https://www.elastic.co/docs/solutions/observability/ai/agent-builder-observability">pre-built observability tools in Agent Builder</a> that you can use alongside custom tools like the ones we created here. For a complementary approach to AI-driven observability, see <a href="https://www.elastic.co/observability-labs/blog/ai-observability-web-agents-openlit">how to monitor web AI agents with OpenLIT and Elastic</a>.</p>
<h2 id="conclusion">Conclusion</h2>
<p>What we covered:</p>
<ul>
<li><p>How to create Agent Builder tools in Kibana that wrap APM data queries</p></li>
<li><p>How to connect the Elastic Agent Builder MCP Server to Cursor in three lines of JSON</p></li>
<li><p>Using production telemetry to make a UI decision backed by real usage data</p></li>
<li><p>Debugging a production error from the same window where you fix it</p></li>
</ul>
<p>These two use cases are a starting point. The same pattern works for any data you have in Elasticsearch: performance metrics, A/B test results, audit logs, feature flag usage, user session data. Define the Agent Builder tool, connect it via MCP, and it becomes part of your development context in Cursor. For other examples of what's possible, see <a href="https://www.elastic.co/observability-labs/blog/mcp-elastic-synthetics">automating synthetic monitoring with MCP</a> and <a href="https://www.elastic.co/observability-labs/blog/agentic-cicd-kubernetes-mcp-server">agentic CI/CD deployment gates</a>.</p>
<h2 id="nextsteps">Next steps</h2>
<ul>
<li><p><a href="https://www.elastic.co/docs/explore-analyze/ai-features/agent-builder/mcp-server">Elastic Agent Builder MCP server documentation</a></p></li>
<li><p><a href="https://modelcontextprotocol.io/">Model Context Protocol specification</a></p></li>
<li><p><a href="https://www.elastic.co/docs/explore-analyze/ai-features/elastic-agent-builder">Elastic Agent Builder overview</a></p></li>
</ul>]]></content:encoded>
    <link>https://www.elastic.co/observability-labs/blog/elastic-mcp-server-cursor-production-logs</link>
    <guid isPermaLink="false">elastic-mcp-server-cursor-production-logs</guid>
    <category><![CDATA[Agentic Observability]]></category>
    <category><![CDATA[Logs Analytics]]></category>
    <category><![CDATA[APM]]></category>
    <dc:creator><![CDATA[Jeffrey Rengifo]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt995e6ed7b699e8fa/6a7f08226c6eacad3ef13f31/header.png" length="0" type="image/png"/>
    <pubDate>Wed, 29 Apr 2026 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[ML and AI Ops Observability with OpenTelemetry and Elastic]]></title>
    <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 id="problemcontextwhyaisystemsarehardertodebug">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 id="solutionoverview">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 id="step1zerocodebaselineforpythonservices">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>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>export OTEL_EXPORTER_OTLP_ENDPOINT="https://&lt;motlp-endpoint&gt;" # No need when using start-local with EDOT
export OTEL_EXPORTER_OTLP_HEADERS="Authorization=ApiKey &lt;key&gt;" # No need when using start-local with EDOT
export OTEL_RESOURCE_ATTRIBUTES="deployment.environment=prod,service.version=1.0.0" # 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://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt6d8633610c0a2563/6a7f0d816693f803fe663f6f/step-1-logs.png" alt="Logs in Elastic" />
<img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltc993609820f94884/6a7f0d842f00b2803eefeb9e/step-1-log-errors.png" alt="Log errors in Elastic" />
<img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt9b8fe0bf9a75775c/6a7f0d88e3a219eee399f4ee/step-1-alerts-on-log-errors.png" alt="Alerts on log errors" />
<img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt0b5713d6d819aa11/6a7f0d8bb437702a264d6cbf/step-1-metrics.png" alt="System and process metrics" />
<img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltcc0af73d41e51f83/6a7f0d8f77b0343ab23ff4fc/step-1-auto-instrumented-traces.png" alt="Auto instrumented traces" />
<img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt462cfb3d689097c3/6a7f0d92e02facc0505d65c4/step-1-service-map.png" alt="Service map in Elastic" />
<img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blte694763a9f9e9b49/6a7f0d9505b7b54d8f18b9b4/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 id="step2addmlspecificcontextwithcustomspansandlogfields">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>logger.info("training metrics", extra={
    "ml.run_id": run_id,
    "ml.training_accuracy": train_accuracy,
    "ml.validation_accuracy": val_accuracy,
    "ml.drift_detected": 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://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltdd807b3c06794f24/6a7f0d99448e4e92995c0757/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://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt4d712336438d643b/6a7f0d9cbd219853427580f1/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://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt85b6d3abe4b55617/6a7f0d9fbdcff07e50c42e91/step-2-custom-spans.png" alt="Custom spans in code" /></p>
<pre><code>from opentelemetry import trace

tracer = trace.get_tracer("ml.pipeline")

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

with tracer.start_as_current_span("train_model") as span:
    span.set_attribute("ml.model_version", model_version)
    span.set_attribute("ml.learning_rate", 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://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltd4c919f38ce841ec/6a7f0da296b5a6f4b487b4ad/step-2-custom-spans-ui-in-elastic.png" alt="Custom spans UI in Elastic" />
<img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltc8f5c2baa1f9c4fb/6a7f0da6bdcff0091fc42e95/step-2-analysing-spans.png" alt="Analysing spans" />
<img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt33ebc4ec394dc17e/6a7f0da9bdcff070afc42e9b/step-2-latency-and-avg-latency-of-spans.png" alt="Latency and avg latency of spans" />
<img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltc3653db7a7074fa7/6a7f0dacb6b7340f57e48e2e/step-2-alerts-on-custom-log-metrics.png" alt="Alerts on custom log metrics" /></p>
<h2 id="step3traceacrosspythonandgoinproduction">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://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltdfad2556fa72468e/6a7f0db0b6b7342bdde48e36/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://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt3c5b971aef4bba7b/6a7f0db3b6b7341cdbe48e3c/step-3-multiple-services.png" alt="Multiple services request flow" />
<img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blte34455a40908ffd9/6a7f0db62f00b2ca9aefebac/step-3-spans-per-service.png" alt="Spans per service" />
<img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt7400c486e102c8f5/6a7f0dba2f00b22726efebb0/step-3-go-service-logs.png" alt="Go service logs" />
<img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltfb1810d0aed82332/6a7f0dbdde2315e666fd7c8d/step-3-go-traces-in-discover.png" alt="Go traces in discover" /></p>
<h2 id="validationchecklist">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 id="conclusionandnextsteps">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>
    <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>
    <category><![CDATA[Machine Learning]]></category>
    <category><![CDATA[OpenTelemetry]]></category>
    <category><![CDATA[APM]]></category>
    <dc:creator><![CDATA[Almudena Sanz Olivé]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltb49a2f7887e6d598/6a7f0dc0eab5bee1bb20a731/header.jpg" length="0" type="image/jpeg"/>
    <pubDate>Tue, 31 Mar 2026 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[OpenTelemetry Profiles Signal Enters Alpha: Elastic’s Continuous Commitment to Profiling]]></title>
    <description><![CDATA[OpenTelemetry Profiles has officially reached Alpha, entrenching profiling as the fourth observability signal. Elastic's core contribution of its eBPF profiling agent, continued OpenTelemetry Profiles signal work and commitment to a vendor-agnostic ecosystem are driving this industry-wide standard forward.]]></description>
    <content:encoded><![CDATA[<p>Following intensive collaboration between Elastic and the OpenTelemetry community, we are thrilled to announce that the OpenTelemetry Profiles signal has officially entered public Alpha.
This milestone is a testament to the community's dedication and marks a significant step towards establishing profiling as the fourth key observability signal in OpenTelemetry, alongside logs, metrics and traces.</p>
<p>As a core contributor, Elastic is proud to have accelerated this effort by previously donating its Universal Profiling™ eBPF-based continuous profiling agent to OpenTelemetry.
This production-grade agent enables whole-system visibility across all applications, covering a multitude of programming languages and runtimes including third-party libraries and kernel operations with minimal overhead.
It allows SREs and developers to quickly identify performance bottlenecks, maximize resource utilization, and optimize cloud spend.</p>
<p>Additionally, over the last two years, Elastic has been heavily contributing to the OpenTelemetry Collector, Semantic Conventions and Profiling Special Interest Groups (SIGs) to lay the technical foundation for the promotion of Profiles to Alpha.</p>
<p>This Alpha milestone not only boosts the standardization of continuous profiling but also accelerates the practical adoption of profiling as the fourth key signal in observability.
Customers now have a vendor-agnostic way of collecting profiling data and enabling correlation with existing signals, like logs, metrics and traces, unveiling new potential for observability insights and a more efficient troubleshooting experience.</p>
<h2 id="whatiscontinuousprofiling">What is continuous profiling?</h2>
<p>Profiling is a technique used to understand the behavior of a software application by collecting information about its execution.
This includes tracking the duration of function calls, memory usage, CPU usage, and other system resources. </p>
<p>However, traditional profiling solutions have significant drawbacks limiting adoption in production environments:</p>
<ul>
<li>Significant cost and performance overhead due to code instrumentation</li>
<li>Disruptive service restarts</li>
<li>Inability to get visibility into third-party libraries</li>
</ul>
<p>Unlike traditional profiling, which is often done only in a specific development phase or under controlled test conditions, continuous profiling runs in the background with minimal overhead, eliminating the need for service restarts or manual intervention.
This provides real-time, actionable insights without replicating issues in separate environments.
SREs, DevOps, and developers can see how code affects performance and cost, making code and infrastructure improvements easier.</p>
<h2 id="elasticscontributionpoweringthealpha">Elastic's contribution: Powering the Alpha</h2>
<p>The Elastic-donated profiler now forms the reference eBPF-based profiler implementation within OpenTelemetry: <a href="https://github.com/open-telemetry/opentelemetry-ebpf-profiler/">opentelemetry-ebpf-profiler</a>.
With the Alpha release, the eBPF profiler operates as an OpenTelemetry Collector receiver and contains numerous improvements such as automatic Go symbolization and support for new language runtimes.
Operating as an OpenTelemetry Collector receiver enables the profiler to seamlessly leverage existing OpenTelemetry processing and filtering pipelines. </p>
<p>For example, the <a href="https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/processor/k8sattributesprocessor">k8sattributesprocessor</a> can use the <code>container.id</code> resource attribute to automatically enrich every profile with its corresponding Kubernetes context.
This means you don't just see a raw stack trace; you see exactly which namespace, pod, and deployment produced it.</p>
<pre><code>receivers:
  # Profiling receiver
  profiling: {}

processors:
  k8sattributes:
    passthrough: false 
    pod_association:
      - sources:
          - from: resource_attribute
            name: container.id
    extract:
      metadata:
        - "k8s.namespace.name"
        - "k8s.deployment.name"
        - "k8s.replicaset.name"
        - "k8s.statefulset.name"
        - "k8s.daemonset.name"
        - "k8s.node.name"
        - "k8s.pod.name"
        - "k8s.pod.ip"
        - "k8s.pod.uid"
</code></pre>
<p>Besides improvements to the eBPF profiler, Elastic has made significant contributions to:</p>
<ul>
<li>Correlating profiles with the information produced by OpenTelemetry eBPF instrumentation (<a href="https://opentelemetry.io/docs/zero-code/obi/">OBI</a>), a powerful auto-instrumentation tool that can enable distributed tracing. </li>
<li><a href="https://github.com/open-telemetry/opentelemetry-specification/pull/4719">Process Context Sharing OTEP</a> which is designed to bridge the gap between application SDKs and the profiler. This mechanism will allow OpenTelemetry SDKs to "publish" their resource attributes (like <code>service.name</code>) into a small, standardized memory region. Because this data is stored in the process's own memory map, the eBPF Profiler can instantly discover and associate it with its corresponding Profile.</li>
<li>Semantic conventions and integration of OpenTelemetry Profiles with Google's pprof format (transparent conversion)</li>
<li>OpenTelemetry Collector processing pipelines, allowing it to better integrate with the profiling receiver</li>
</ul>
<h2 id="elasticsnextgenerationprofilingdevelopment">Elastic's Next-Generation Profiling Development</h2>
<p>Elastic remains deeply committed to OpenTelemetry's vision and is pushing the boundaries of what is possible with profiling data.
We are dedicating a team of profiling domain experts to co-maintain and advance profiling capabilities within OpenTelemetry, while simultaneously working on groundbreaking features built on this new open standard.</p>
<p>Exciting areas of internal profiling-specific development include:</p>
<ul>
<li>OpenTelemetry Profiles derived Metrics: We are developing innovative ways to automatically generate actionable performance metrics directly from the raw OTel Profiles data, providing a new dimension for infrastructure modeling and alerting.</li>
<li>Rapid Integration with the Elastic Stack: We are making swift progress on first-class support for OTLP Profiles within the Elastic Stack, ensuring seamless ingestion (the ebpf-profiler receiver is already integrated with the <a href="https://github.com/elastic/elastic-agent/tree/main/internal/edot#components">Elastic Distributions of OpenTelemetry (EDOT) collector</a>), storage, and visualization of this new signal alongside your existing logs, metrics and traces.</li>
<li>AI-Powered Workflows: We are leveraging the deep insights provided by continuous profiling data to power new AI-driven workflows, enabling automatic root-cause analysis, anomaly detection, and intelligent optimization suggestions for both code and infrastructure.</li>
</ul>
<p>While the Alpha release marks a significant milestone, it is just the beginning.
We encourage the community to start testing early preview versions of the OTel Profiles integration and contribute to the ongoing profiling work.
To get started with an actual, local deployment, you can use the <a href="https://github.com/open-telemetry/opentelemetry-ebpf-profiler">OpenTelemetry eBPF profiler</a> in combination with a self-hosted <a href="https://www.elastic.co/docs/solutions/observability">Elastic Observability Stack</a> or <a href="https://github.com/elastic/devfiler">devfiler</a>, a standalone desktop application that acts as an OpenTelemetry Profiles compliant backend aimed at experimentation and development.</p>]]></content:encoded>
    <link>https://www.elastic.co/observability-labs/blog/otel-profiling-alpha</link>
    <guid isPermaLink="false">otel-profiling-alpha</guid>
    <category><![CDATA[OpenTelemetry]]></category>
    <category><![CDATA[APM]]></category>
    <dc:creator><![CDATA[Christos Kalkanis,Florian Lehner,Roger Coll]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt710a2b45a0da343d/6a7f1978bd21986753758495/header.jpg" length="0" type="image/jpeg"/>
    <pubDate>Wed, 25 Mar 2026 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[A Practical Guide to end-to-end distributed tracing for Nginx with OpenTelemetry in Elastic]]></title>
    <description><![CDATA[Instrument Nginx with the OpenTelemetry tracing module and export spans to Elastic Observability's APM for full end-to-end distributed tracing.]]></description>
    <content:encoded><![CDATA[<p>Nginx sits at the very front of most modern architectures: handling SSL, routing, load balancing, authentication, and more. Yet, despite its central role, it is often absent from distributed traces.<br />
That gap creates blind spots that impact performance debugging, user experience analysis, and system reliability.</p>
<p>This article explains <strong>why Nginx tracing is important</strong> in an application context, and provides a <strong>practical guide</strong> to enable the Nginx <a href="https://nginx.org/en/docs/ngx_otel_module.html">Otel</a> tracing module exporting spans directly to <a href="https://www.elastic.co/docs/solutions/observability/apm">Elastic APM</a>.</p>
<h2 id="whynginxtracingmattersformodernobservability">Why Nginx Tracing Matters for Modern Observability</h2>
<p>Instrumenting only backend services gives you only half the picture.<br />
Nginx sees:</p>
<ul>
<li>every incoming request  </li>
<li>client trace context  </li>
<li>TLS negotiation  </li>
<li>upstream errors (502, 504)  </li>
<li>edge-layer latency  </li>
<li>routing decisions  </li>
</ul>
<p>If Nginx is not in your traces, your distributed trace is incomplete.</p>
<p>By adding OpenTelemetry tracing at this ingress layer, you unlock:</p>
<p><em>1. Full trace continuity</em> : From browser → Nginx → backend → database.
<img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltc30b2ea6cee5fdff/6a7f0eaa96b5a68e9c87b50b/document_elastic_nginx_otel_instrumentation_1.png" alt="Nginx Trace Continuity" /></p>
<p><em>2. Accurate latency attribution</em> : Edge delays vs. backend delays are clearly separated which unlock Elastic <a href="https://www.elastic.co/docs/solutions/observability/apm/machine-learning">APM Latency</a> anomaly detection for proactive detection.
<img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt234adca13afbd446/6a7f0eadbd219835c475815b/document_elastic_nginx_otel_instrumentation_2.png" alt="Nginx Latency Detection" /></p>
<p><em>3. Error root-cause clarity</em> : Nginx errors appear as spans instead of backend “mystery gaps”.</p>
<p><em>4. Complete service topology</em> : Your APM service map finally shows the real architecture.
<img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt40ad01ea2ad51be3/6a7f0eb005b7b55b5618ba0e/document_elastic_nginx_otel_instrumentation_4.png" alt="Nginx APM Service Map" /></p>
<h2 id="integratingnginxwithopentelemetryondebian">Integrating Nginx with OpenTelemetry on Debian</h2>
<p>This guide provides a comprehensive overview of why, how to install and configure the Nginx OpenTelemetry module on a Debian-based system. The configuration examples are tailored to send telemetry data directly to an Elastic APM endpoint whether it's an <a href="https://www.elastic.co/docs/reference/opentelemetry">EDOT</a> Collector or <a href="https://www.elastic.co/observability-labs/blog/elastic-managed-otlp-endpoint-for-opentelemetry">mOtel</a> in case of our serverless, enabling end-to-end distributed tracing.</p>
<h3 id="installationondebian">Installation on Debian</h3>
<p>The Nginx OTEL module is not included in the standard Nginx packages. It must be installed along with a working nginx configuration.</p>
<h4 id="prerequisites">Prerequisites</h4>
<p>First, install the necessary tools for compiling software and the Nginx development dependencies.</p>
<pre><code>sudo apt update
sudo apt install -y apt install nginx-module-otel
</code></pre>
<h4 id="loadthemoduleinnginx">Load the Module in Nginx</h4>
<p>Edit your main <code>/etc/nginx/nginx.conf</code> file to load the new module. This directive must be at the top level, before the <code>http</code> block.</p>
<pre><code># /etc/nginx/nginx.conf

load_module modules/ngx_otel_module.so;

events {
    # ...
}

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

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

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

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

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

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

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

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

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

        # --- Forward to Backend ---
        # Passes the request to the actual application (eg. localhost in this example).
        proxy_pass http://&lt;BACKEND_URL&gt;:8080;
    }
}
</code></pre>
<p>Test your configuration and restart Nginx.</p>
<pre><code>sudo nginx -t
sudo systemctl restart nginx
</code></pre>
<h2 id="conclusionturningnginxintoafirstclassobservabilitysignal">Conclusion: Turning Nginx into a First-Class Observability Signal</h2>
<p>By enabling OpenTelemetry tracing directly in Nginx and exporting spans to Elastic APM (via EDOT or Elastic’s managed OTLP endpoint), you bring your ingress layer into the same observability model as the rest of your stack. The result is:</p>
<ul>
<li>true end-to-end trace continuity from the browser to backend services  </li>
<li>clear separation between edge latency and application latency  </li>
<li>immediate visibility into gateway-level failures and retries  </li>
<li>accurate service maps that reflect real production traffic  </li>
</ul>
<p>Most importantly, this approach aligns Nginx with modern observability standards. It avoids proprietary instrumentation, fits naturally into OpenTelemetry-based architectures, and scales consistently across hybrid and cloud-native environments.</p>
<h2 id="tryitout">Try it out!</h2>
<p>Once Nginx tracing is in place, several natural extensions can further improve your observability posture:</p>
<ul>
<li>correlate Nginx traces with application <a href="https://www.elastic.co/docs/reference/apm/agents/go/log-correlation">logs and metrics using</a> Elastic’s unified observability  </li>
<li>add Real User Monitoring (<a href="https://www.elastic.co/docs/solutions/observability/apm/apm-agents/real-user-monitoring-rum">RUM</a>) to close the loop from frontend to backend  </li>
<li>introduce <a href="https://www.elastic.co/docs/solutions/observability/apm/transaction-sampling">sampling and tail-based</a> decisions at the collector level for cost control  </li>
<li>use Elastic <a href="https://www.elastic.co/docs/solutions/observability/apm/service-map">APM service maps</a> and <a href="https://www.elastic.co/docs/reference/machine-learning/ootb-ml-jobs-apm">anomaly detection</a> to proactively detect edge-related issues  </li>
</ul>
<p>Instrumenting Nginx is often the missing link in distributed tracing strategies. With OpenTelemetry and Elastic, that gap can now be closed in a clean, standards-based, and production-ready way.</p>
<p>If you want to experiment with this setup quickly, Elastic Serverless provides the fastest way to get started.
Sign up and try it out in just a few minutes using our trial environment available at https://cloud.elastic.co/ .</p>]]></content:encoded>
    <link>https://www.elastic.co/observability-labs/blog/nginx-opentelemetry-end-to-end-tracing</link>
    <guid isPermaLink="false">nginx-opentelemetry-end-to-end-tracing</guid>
    <category><![CDATA[OpenTelemetry]]></category>
    <category><![CDATA[APM]]></category>
    <category><![CDATA[Infrastructure Monitoring]]></category>
    <dc:creator><![CDATA[Frederic Maussion]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt40ad01ea2ad51be3/6a7f0eb005b7b55b5618ba0e/document_elastic_nginx_otel_instrumentation_4.png" length="0" type="image/png"/>
    <pubDate>Tue, 13 Jan 2026 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Find answers quickly, correlate OpenTelemetry traces with existing ECS logs in Elastic Observability]]></title>
    <description><![CDATA[In this blog we will discuss how EDOT enables you to collect existing ECS logs while ensuring a seamless and transparent move to OTel semantic conventions. The key benefit is that applications can continue sending logs as they do today, which minimizes the effort and impact on application developers.]]></description>
    <content:encoded><![CDATA[<p>OpenTelemetry (OTel) is the undisputed standard for vendor-neutral instrumentation. However, most established organizations don't start from a blank slate. You likely have a mature ecosystem of applications already logging in Elastic Common Schema (ECS), supported by years of refined dashboards and alerting rules.</p>
<p><strong>The challenge is clear:</strong> How do you adopt OTel’s unified observability without abandoning your proven ECS-based logging?</p>
<p>In this guide, we’ll demonstrate how to bridge this gap using the <strong>Elastic Distribution of OpenTelemetry (EDOT)</strong>. We will first show you how to leverage the EDOT Collector to ingest your logs into Elasticsearch, ensuring a seamless transition that unlocks the full power of OTel’s distributed tracing without breaking your current workflows.</p>
<p>Once the data is flowing, we will explore how Elasticsearch's underlying mapping architecture to allow that your existing filters and visualizations remain fully functional through two key features:</p>
<ul>
<li><p><strong>Field Aliases:</strong> We’ll explain how Elastic uses aliases to ensure that legacy dashboards looking for <code>log.level</code> (ECS) still work perfectly, even as your new telemetry arrives as <code>severity_text</code> (OTel).</p></li>
<li><p><strong>Passthrough Fields:</strong> We’ll show how Elastic’s native OTel mapping structures use passthrough fields to handle OTel attributes. This ensures your data remains searchable and performant without the need for complex, manual schema migrations.</p></li>
</ul>
<p>By combining EDOT for ingestion with these intelligent mapping structures, you can maintain your existing Java ECS logging while evolving toward a unified, OTel-native future.</p>
<h2 id="theecsfoundation">The ECS Foundation</h2>
<p>We begin with a Java application using <strong>Log4j2</strong> and the <strong>ecs-java-plugin</strong>. This setup generates structured JSON logs in the <a href="https://www.elastic.co/docs/reference/ecs">Elastic Common Schema (ECS)</a> that Elastic handles natively leveraging the ECS logging plugins that easily integrate with common logging libraries across various programming languages. </p>
<p>The following provides a <strong>Log4j2 Configuration Extract</strong> and this setup assumes prior configuration of Log4j2 dependencies to include the required ECS plugin libraries:</p>
<pre><code>&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;Configuration status="DEBUG"&gt;
    &lt;Appenders&gt;
        &lt;Console name="LogToConsole" target="SYSTEM_OUT"&gt;
            &lt;EcsLayout serviceName="logger-app" serviceVersion="v1.0.0"/&gt;
        &lt;/Console&gt;
    &lt;/Appenders&gt;
    &lt;Loggers&gt;
        &lt;Root level="info"&gt;
            &lt;AppenderRef ref="LogToConsole"/&gt;
        &lt;/Root&gt;
    &lt;/Loggers&gt;
&lt;/Configuration&gt;
</code></pre>
<p><strong>Note:</strong> <code>&lt;EcsLayout serviceName="logger-app" serviceVersion="v1.0.0"/&gt;</code> we will come back to this setting later in the blog article, as with Kubernets deployments these values can be automatically populated by the EDOT Collector and the setting could be simplified to <code>&lt;EcsLayout/&gt;</code></p>
<h2 id="introducingtheelasticdistributionofopentelemetryedot">Introducing the Elastic Distribution of OpenTelemetry (EDOT)</h2>
<p>The <a href="https://www.elastic.co/docs/reference/opentelemetry">Elastic Distribution of OpenTelemetry (EDOT)</a> is more than just a repackaging; it is a curated set of OTel components (Collector and SDKs) optimized for Elastic Observability. Released in v8.15, it allows you to collect traces, metrics, and logs using standard OTel receivers while benefiting from Elastic-contributed enhancements like powerful log parsing and Kubernetes metadata enrichment.</p>
<p>EDOT's Primary Benefits:</p>
<p><strong>Deliver Enhanced Features Earlier:</strong> Provides features not yet available in "vanilla" OTel components, which Elastic continuously contributes upstream.</p>
<p><strong>Enhanced OTel Support:</strong> Offers enterprise-grade support and maintenance for fixes outside of standard OTel release cycles.</p>
<p>The question then becomes: How can users transition their ingestion architecture to an OTel-native approach while maintaining the ability to collect logs in ECS format?</p>
<p>This involves replacing classic collection and instrumentation components (like Elastic Agent and the Elastic APM Java Agent). Let us show you how this can be done step by step replacing it with the full suite of components provided by EDOT. A comprehensive view of the EDOT architecture components in Kubernets is shown below.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt274ae7c75410bd05/6a7f19574c4bfb37a2ccd8d8/architecture.png" alt="EDOT reference Architecure in K8s" /></p>
<p>In a Kubernetes environment, EDOT components are typically installed via an OTel Operator and HELM chart. The main components are:</p>
<ul>
<li><strong>EDOT Collector Cluster:</strong> deployment used to collect cluster-wide metrics.</li>
<li><strong>EDOT Collector Daemon:</strong> daemonset used to collect node metrics, logs, and application telemetry data.</li>
<li><strong>EDOT Collector Gateway:</strong> performs pre-processing, aggregation, and ingestion of data into Elastic.</li>
</ul>
<p>Elastic provides a curated configuration file for all the EDOT components available as part of the the OpenTelemetry Operator using the <code>opentelemetry-kube-stack</code> Helm chart. Downloadable from <a href="https://github.com/elastic/elastic-agent/blob/main/deploy/helm/edot-collector/kube-stack/values.yml">here</a>.</p>
<h2 id="achievingcorrelationsdkloggingcontext">Achieving Correlation: SDK + Logging Context</h2>
<p>To link a log line to a specific trace, the <a href="https://www.elastic.co/docs/reference/opentelemetry/edot-sdks/java">EDOT Java SDK</a> performs a "handshake" with your logging library.
When a trace is active, the SDK extracts the <code>trace_id</code> and <code>span_id</code> and injects them into the <strong>Mapped Diagnostic Context (MDC)</strong> of Log4j2. Even though your logs are in ECS format, they now carry the OTel DNA required for correlation.
While the EDOT SDK can collect logs directly, a generally more resilient approach is to stick to file collection. This is important because if the OTel Collector is down, logs written to a file are buffered locally on the disk, preventing the data loss that can occur if the SDK's in-memory queue reaches its limit and starts discarding new logs. For an in-depth discussion on this topic we refer to the <a href="https://opentelemetry.io/docs/languages/java/instrumentation/#log-instrumentation">OpenTelemetry Documentation</a>.</p>
<h2 id="zerocodeinstrumentation">Zero-Code Instrumentation</h2>
<p>The EDOT Java SDK is a customized version of the OpenTelemetry Java Agent. In Kubernetes, zero-code Java autoinstrumentation is supported by adding an <a href="https://www.elastic.co/docs/reference/opentelemetry/edot-sdks/java/setup/k8s">annotation</a> in the pod template configuration in the deployment manifest:</p>
<pre><code>apiVersion: apps/v1
kind: Deployment
...
spec:
  ..
  template:
    metadata:
      # Auto-Instrumentation
      annotations:
        instrumentation.opentelemetry.io/inject-java: "opentelemetry-operator-system/elastic-instrumentation"
</code></pre>
<h2 id="collectingandprocessinglogswiththeedotcollector">Collecting and Processing Logs with the EDOT Collector</h2>
<p>This is the most critical step. Our logs are now JSON, they are in the console output, and they contain trace IDs. Now, we need the EDOT Collector to pick them up and map them to the <strong>OpenTelemetry Log Data Model</strong>.</p>
<h3 id="edotcollectorconfigurationdynamicworkloaddiscoveryandfilelogreceiver">EDOT Collector Configuration: Dynamic Workload Discovery and filelog receiver</h3>
<p>Applications running on containers become moving targets for monitoring systems. To handle this, we rely on <a href="https://www.elastic.co/observability-labs/blog/k8s-discovery-with-EDOT-collector">Dynamic workload discovery on Kubernetes</a>. This allows the EDOT Collector to track pod lifecycles and dynamically attach log collection configurations based on specific annotations relying on the <code>k8s_observer</code> and the <code>receiver_creator</code> component.</p>
<p>In our example, we have a Deployment with a Pod consisting of one container. We use Kubernetes annotations to:</p>
<ol>
<li><p>Enable auto-instrumentation (Java).</p></li>
<li><p>Enable log collection for this pod.</p></li>
<li><p>Instruct the collector to parse the output as JSON immediately (json-parser configuration).</p></li>
<li><p>Add custom attributes (e.g. identify the Application souce code)</p></li>
</ol>
<h4 id="deploymentmanifestexample">Deployment Manifest Example</h4>
<pre><code>apiVersion: apps/v1
kind: Deployment
metadata:
  name: logger-app-deployment
  labels:
    app: logger-app
spec:
  replicas: 1
  selector:
    matchLabels:
      app: logger-app
  template:
    metadata:
      annotations:
        # 1. Turn on Auto-Instrumentation
        instrumentation.opentelemetry.io/inject-java: "opentelemetry-operator-system/elastic-instrumentation"
        # 2. Enable Log Collection for this pod
        io.opentelemetry.discovery.logs/enabled: "true"
        # 3. Provide the parsing "hint" (Treat logs as JSON)
        io.opentelemetry.discovery.logs.ecs-log-producer/config: |
            operators:
            - type: container
              id: container-parser
            - type: json_parser
              id: json-parser
         # 4. Identify this application as Java (To allow for user interface rendering in Kibana)
        resource.opentelemetry.io/telemetry.sdk.language: "java"
      ...
</code></pre>
<p>This setup provides a bare-minimum configuration for ingesting ECS library logs.
Crucially, it decouples log collection from application logic. Developers simply need to provide a hint via annotations that their logs are in JSON format (structurally guaranteed by the ECS libraries). We then define the standardized enrichment and processing rules centrally at the <a href="https://www.elastic.co/docs/reference/edot-collector/components">processor</a> level in the (Daemon) EDOT Collector.</p>
<p>This centralization ensures consistency across the platform: if we need to update our standard formatting or enrichment strategies later, we apply the change once in the collector, and it automatically propagates to all services without developers needing to touch their manifests.</p>
<h4 id="daemonedotcollectorconfiguration">(Daemon) EDOT Collector Configuration</h4>
<p>To enable this, we configure a Receiver Creator in the Daemon Collector. This component uses the <code>k8s_observer</code> extension to monitor the Kubernetes environment and automatically discover the target pods based on the annotations above.</p>
<pre><code>daemon:
  ...
  config:
    ...
    extensions:
      extensions:
        k8s_observer:
          auth_type: serviceAccount
          node: ${env:K8S_NODE_NAME}
          observe_nodes: true
          observe_pods: true
          observe_services: true
          ...
    receivers:
        receiver_creator/logs:
          watch_observers: [k8s_observer]
          discovery:
            enabled: true
    ...
...
</code></pre>
<p>Finally, we reference the <code>receiver_creator</code> in the pipeline instead of a static filelog receiver and we make sure to include the <code>k8s_observer</code> extension:</p>
<pre><code>daemon:
  ...
  config:
    ...
    service:
      extensions:
      - k8s_observer
      pipelines:
        # Pipeline for node-level logs
        logs/node:
          receivers:
            # - filelog             # We disable direct filelog receiver
            - receiver_creator/logs # Using the configured receiver_creator instead of filelog
          processors:
            - batch
            - k8sattributes
            - resourcedetection/system
          exporters:
            - otlp/gateway # Forward to the Gateway Collector for ingestion
</code></pre>
<h3 id="thetransformationlayer">The Transformation Layer</h3>
<p>While the logs are structured, OTel sees them as generic attributes. We use the OpenTelemetry Transformation Language (OTTL) within a <code>transform</code> processor to "promote" ECS fields to top-level OTel fields.
To finalize the pipeline, we use the <a href="https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/main/processor/transformprocessor/README.md">transform processor</a>, which allows us to modify and restructure telemetry signals using the OpenTelemetry Transformation Language (OTTL).</p>
<p>We use the processor to promote specific ECS fields into the top-level OpenTelemetry fields and renaming attributes according to OpenTelemetry Semantic Conventions:</p>
<ul>
<li>Promote the <code>message</code> attribute to the top-level <code>Body</code> field.</li>
<li>Promote the <code>log.level</code> attribute to the OTel <code>SeverityText</code> field.</li>
<li>Move the <code>@timestamp</code> attribute to the OTel <code>Time</code> field.</li>
<li>Map <code>trace_id</code> and <code>span_id</code> to the right log context.</li>
</ul>
<p>The following provides a sample <code>transform</code> configuration:</p>
<pre><code> processors:
    transform/ecs_handler:
      log_statements:
      - context: log
        conditions:
          - log.attributes["ecs.version"] != nil
        statements:
          # Map ECS fields to OTel Log Model
          - set(log.body, log.attributes["message"])
          - set(log.time, Time(log.attributes["@timestamp"], "%Y-%m-%dT%H:%M:%SZ"))
          - set(log.trace_id.string, log.attributes["trace_id"])
          - set(log.span_id.string, log.attributes["span_id"])
          - set(log.severity_text, log.attributes["log.level"])
          # Cleanup original keys to save space
          - delete_key(log.attributes, "message")
          - delete_key(log.attributes, "trace_id")
          - delete_key(log.attributes, "span_id")

          # Add here additional transformations as needed...
</code></pre>
<p><strong>Note:</strong> When working with EDOT Collector and the OpenTelemetry Kube-Stack Helm Chart, resource attributes such as <code>service.name</code> and <code>service.version</code> are automatically populated based on a set of <a href="https://opentelemetry.io/docs/specs/semconv/non-normative/k8s-attributes/">well-defined</a>
rules by the <code>k8sattributes</code> processor. Thus, on Kubernetes we do not need to extract those fields from the log content itself.</p>
<p>Make sure to use the newly created processor in the logs pipeline for the Daemon Collector:</p>
<pre><code>service:
  pipelines:
    logs/node:
      receivers:
        - receiver_creator/logs
      processors:
        - batch
        - k8sattributes
        - resourcedetection/system
        - transform/ecs_handler          # Newly created transform processor
      exporters:
        - otlp/gateway
</code></pre>
<h2 id="thecompatibilitylayerbridgingecsandotel">The Compatibility Layer: Bridging ECS and OTel</h2>
<p>To bridge the gap between the Elastic Common Schema (ECS) and OpenTelemetry (OTel), Elastic provides a "compatibility layer" built directly into its Observability solution relying on existing index templates and mappings. This architecture allows you to send OTel-native data while still using your legacy ECS-based dashboards, saved searches, and other associated objects.</p>
<p>This "bridge" relies on two key features:</p>
<ul>
<li><p><strong>Bridging ECS and OTel with Passthrough:</strong> OpenTelemetry (OTel) data often uses deeply nested structures (e.g., <code>resource.attributes.*</code>). Elasticsearch uses the <strong><a href="https://www.elastic.co/docs/reference/elasticsearch/mapping-reference/passthrough">Passthrough</a></strong> object type to "promote" these nested attributes to the top level when performing a search query. Any new metadata added by the OTel collector is automatically searchable without the user needing to know the full JSON path. This creates a "virtual flattening" layer and makes sure that all fields that match in name are automatically compatible, even though thery're stored in different namespaces (attributes/resource.attributes for OTel vs top-level for ECS). To learn more about fields and attributes alignment between ECS and Otel SemanticConvention refer to this <a href="https://www.elastic.co/docs/reference/ecs/ecs-otel-alignment-details">page</a>.</p></li>
<li><p><strong>Bridging with Field Aliases</strong>: Elastic relies on OTel mapping templates that include <code>Field Aliases</code>. These aliases link OTel semantic names back to their equivalent ECS fields at query to handle fields that do not align with Otel naming convention.</p></li>
</ul>
<p><em>The Benefit:</em> If you have an existing dashboard looking for <code>message</code> (ECS), but your data is now indexed as <code>body.text</code> (OTEL), an alias allows the dashboard to aggregate and visualize data from both sources simultaneously. This ensures that your existing filters and KQL queries also work flawlessly whether the data originated from a Filebeat agent or a modern OTel SDK Agent.</p>
<p>Some more details about field aliases and pass-through objects can be found <a href="https://www.elastic.co/docs/reference/opentelemetry/compatibility/data-streams#query-compatibility-with-classic-apm-data-streams">here</a>.</p>
<p>Here is an example of the provided mapping template:</p>
<pre><code>{
  "mappings": {
    ...
    "properties": {
      "log": {
          "properties": {
            "level": {
              "type": "alias",
              "path": "severity_text"
            }
          }
        },
      "message": {
        "type": "alias",
        "path": "body.text"
      }
    ...
    }
  }
 }
</code></pre>
<p>This architectural approach provides three major advantages for teams in transition:</p>
<ul>
<li><p><strong>Zero Reindexing:</strong> You don't have to rewrite or migrate old data. Aliases resolve at query time, meaning your old indices and new indices can coexist in the same visualization.</p></li>
<li><p><strong>Future-Proofing:</strong> As OTel becomes the primary standard (following the donation of ECS to the OTel project), Elastic is shifting its native UI to look for OTel fields first. These mappings ensure that your legacy ECS-native data still appears in OTel-native views.</p></li>
<li><p><strong>Unified Observability:</strong> It enables "Correlation by Default." Because the aliases link trace_id (OTel) and trace.id (ECS), you can jump from a legacy log to a modern OTel trace without losing context or breaking the drill-down path.</p></li>
</ul>
<h2 id="sendingdatatoelasticsearch">Sending data to Elasticsearch</h2>
<p>If you are running Elastic Serverless or the latest Elastic Cloud Hosted (ECH) v9.2+, you now have access to a managed OTLP endpoint. This native functionality allows you to route telemetry directly from your Collector Gateway to Elasticsearch using the OTLP protocol.</p>
<p>Because we mapped our ECS fields to the OTel model in the collector, Elasticsearch recognizes the correlation immediately. You get the best of both worlds:
<strong><em>Legacy Compatibility:</em></strong> Your old ECS-based dashboards still work (with minor tweaks).
<strong><em>Modern Power:</em></strong> You can now click "View Trace" directly from a log entry in Kibana's Observability UI.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt2765d381ee814272/6a7f195ac2cc09588524999e/discovery.jpg" alt="Discover" /></p>
<h2 id="conclusion">Conclusion</h2>
<p>Transitioning to OpenTelemetry doesn't have to be a "big bang" migration. By using the EDOT SDK and Collector, you can:
<strong><em>Protect your investment</em></strong> in ECS-based logging libraries.
<strong><em>Centralize complexity</em></strong> by handling schema translation in the collector rather than the application.
<strong><em>Enable full correlation</em></strong> between traces and logs with zero code changes.</p>]]></content:encoded>
    <link>https://www.elastic.co/observability-labs/blog/otel-ecs-unification-elastic</link>
    <guid isPermaLink="false">otel-ecs-unification-elastic</guid>
    <category><![CDATA[OpenTelemetry]]></category>
    <category><![CDATA[APM]]></category>
    <dc:creator><![CDATA[Mirko Bez,Alessandro Brofferio]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt754e4d41f9adb854/6a7f195e227b1c7c165989e9/blog-image.jpg" length="0" type="image/jpeg"/>
    <pubDate>Thu, 04 Dec 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[OpenTelemetry for PHP: EDOT PHP joins the OpenTelemetry project]]></title>
    <description><![CDATA[Explore Elastic’s donation of its EDOT PHP to the OpenTelemetry community and discover how it makes OpenTelemetry for PHP simpler and more accessible.]]></description>
    <content:encoded><![CDATA[<p>The OpenTelemetry community has officially accepted Elastic's proposal to contribute the <strong>Elastic Distribution of OpenTelemetry for PHP (EDOT PHP)</strong> — marking an important milestone in bringing first-class observability to one of the web's most widely used languages.</p>
<p>For decades, PHP has powered everything from small business websites to large-scale SaaS platforms. Yet observability in PHP has often required manual setup, compilers, custom extensions, or changes to application code — challenges that limited adoption in production environments.
This upcoming donation aims to change that, by making OpenTelemetry for PHP <strong>as easy to deploy as any other runtime</strong>.</p>
<h2 id="whatscoming">What's coming</h2>
<p>Once the contribution process is complete, EDOT PHP will become part of the OpenTelemetry project — providing a <strong>complete, production-ready distribution</strong> that's optimized for performance, simplicity, and scalability.</p>
<p>EDOT PHP introduces a new approach to PHP observability:</p>
<ul>
<li><strong>Simple installation</strong> - installing OpenTelemetry for PHP will be as straightforward as installing a standard system package. From that point, the agent automatically detects and instruments PHP applications — no code changes, no manual setup.</li>
<li><strong>Automatic agent loading</strong> - works transparently in cloud and container environments without modifying application deployments.</li>
<li><strong>Zero configuration</strong> - ships as a single, self-contained binary; no need to install or compile any external extensions.</li>
<li><strong>Native C++ performance</strong> - a built-in serializer written in C++ reduces telemetry overhead by up to <strong>5×</strong>.</li>
<li><strong>Automatic instrumentation</strong> - instruments popular frameworks and libraries out of the box.</li>
<li><strong>Inferred spans</strong> - reveals the behavior of even uninstrumented code paths, providing full trace coverage.</li>
<li><strong>Automatic root spans</strong> - ensures complete traces, even in legacy or partially instrumented applications.</li>
<li><strong>OpAMP readiness</strong> - while the OpenTelemetry community continues to standardize configuration schemas and management workflows, the implementation in EDOT PHP is fully prepared to support these upcoming specifications — ensuring seamless adoption once the OpAMP ecosystem matures.</li>
<li><strong>Asynchronous backend communication</strong> - telemetry data is exported to the OpenTelemetry Collector or backend <strong>asynchronously</strong>, without blocking the instrumented application.
This ensures that span and metric exports do not add latency to user requests or impact response times, even under heavy load.</li>
</ul>
<p>Together, these features make EDOT PHP the first truly <strong>zero-effort observability solution for PHP</strong> — from local testing to cloud-scale production systems.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt340e08b58598fdb7/6a7f0f6ce88c651ef700b7ca/performance.png" alt="Performance comparision" /></p>
<blockquote>
  <p>The native C++ serializer and asynchronous export pipeline in EDOT PHP reduce average request time from <strong>49 ms</strong> to <strong>23 ms</strong>, more than <strong>2× faster</strong> than the pure PHP implementation.</p>
</blockquote>
<h2 id="buildingontheexistingfoundation">Building on the existing foundation</h2>
<p>EDOT PHP doesn't replace the existing OpenTelemetry PHP SDK — it <strong>extends and strengthens it</strong>.
It packages the SDK, automatic instrumentation, and native extension into a single, unified agent package that works seamlessly with existing OpenTelemetry specifications and APIs.</p>
<p>By contributing this work, Elastic helps the OpenTelemetry community accelerate PHP adoption, align implementations across languages, and make distributed tracing truly universal.</p>
<blockquote>
  <p>“This isn't a hand-off — it's a collaboration.
  We're contributing years of development to help OpenTelemetry for PHP evolve faster, run more efficiently, and reach more users in every environment.”</p>
  <ul>
  <li><em>Elastic Observability team</em></li>
  </ul>
</blockquote>
<h2 id="ongoingimprovements">Ongoing improvements</h2>
<p>Elastic continues to invest in advancing EDOT PHP ahead of its integration into OpenTelemetry.
The team is currently focused on <strong>reducing resource usage and memory footprint</strong>, particularly in <strong>multi-worker server environments</strong> such as PHP-FPM or Apache prefork.
These optimizations aim to make the agent more predictable and efficient under heavy load — ensuring that telemetry remains lightweight even in large-scale production deployments.</p>
<p>Beyond that, we're exploring further improvements that can enhance both performance and interoperability.
Areas under investigation include smarter coordination in high-concurrency scenarios, better sharing of telemetry resources across workers, and future alignment with additional OpenTelemetry signals such as metrics and logs.</p>
<p>Together, these efforts will help make EDOT PHP not only faster, but also more adaptable and seamlessly integrated into diverse runtime architectures.</p>
<h2 id="whyitmatters">Why it matters</h2>
<p>This contribution is about more than performance — it's about <strong>removing barriers</strong>.
By making OpenTelemetry for PHP installable as a simple system package and automatically loaded into running applications, the project opens observability to every PHP developer, operator, and platform provider.</p>
<p>For the OpenTelemetry ecosystem, it fills one of the last major language gaps, extending visibility to a vast portion of the internet — all under open governance and community collaboration.</p>
<h2 id="lookingahead">Looking ahead</h2>
<p>In the months ahead, Elastic and the OpenTelemetry PHP SIG will work closely on the technical integration, documentation, and community onboarding process.
Once the transition is complete, developers will gain a fully open, community-driven, and production-ready OpenTelemetry agent that “just works” — without friction, configuration, or code changes.</p>
<p>Together, we're building a future where <strong>observability just works — for every language, every framework, and every environment</strong>.</p>
<p>For more information:</p>
<p><a href="https://www.elastic.co/docs/reference/opentelemetry">EDOT documentation</a><br />
<a href="https://www.elastic.co/observability-labs/blog/elastic-managed-otlp-endpoint-for-opentelemetry">Learn about</a> OTLP Endpoint</p>]]></content:encoded>
    <link>https://www.elastic.co/observability-labs/blog/opentelemetry-accepts-elastics-donation-of-edot</link>
    <guid isPermaLink="false">opentelemetry-accepts-elastics-donation-of-edot</guid>
    <category><![CDATA[OpenTelemetry]]></category>
    <category><![CDATA[APM]]></category>
    <dc:creator><![CDATA[Pawel Filipczak]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt9499757f899f3979/6a7f18da227b1c46675989e1/otel-php.jpg" length="0" type="image/jpeg"/>
    <pubDate>Mon, 10 Nov 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Pivoting Elastic's Data Ingestion to OpenTelemetry]]></title>
    <description><![CDATA[Elastic has fully embraced OpenTelemetry as the backbone of its data ingestion strategy, aligning with the open-source community and contributing to make it the best data collection platform for a broad user base. This move benefits users by providing enhanced flexibility, efficiency, and control over telemetry data.]]></description>
    <content:encoded><![CDATA[<p>Elastic has fully embraced OpenTelemetry as the backbone of its data ingestion strategy, aligning with the open-source community and contributing to make it the best data collection platform for a broad user base. This move benefits users by providing enhanced flexibility, efficiency, and control over telemetry data.</p>
<h2 id="whyopentelemetry">Why OpenTelemetry?</h2>
<p>OpenTelemetry provides a powerful set of capabilities that make it a compelling choice for open-source-focused users. Elastic is re-architecting its data ingest tools around OpenTelemetry to offer users vendor-agnostic flexibility, performance optimization through OTel's efficient data model for correlating telemetry, and enhanced flexibility and control over data pipelines. This move brings the benefits of open-source telemetry to Elastic users.</p>
<p>Elastic engineers are active contributors to the Otel project in several areas of the project. Demonstrating its commitment to open source, Elastic continues to make significant <a href="https://opentelemetry.devstats.cncf.io/d/5/companies-table?orgId=1%5C&amp;var-period_name=Last%20year&amp;var-metric=contributions">contributions to OpenTelemetry</a>.</p>
<h2 id="opentelemetryasthecoreofelasticsdataingestion">OpenTelemetry as the Core of Elastic's Data Ingestion</h2>
<p>Elastic is transforming its data ingestion strategy by basing all ingestion mechanisms on the OpenTelemetry components. Elastic currently supports the following OTel based ingest architecture, which support OTel SDKs and Collectors from OTel or Elastic's Distribution of OpenTelemetry (EDOT). </p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt274ee07c460ad240/6a7f06003ce8e286bdcf50d4/edot-components.png" alt="EDOT components" /></p>
<p>This marks a fundamental shift, ensuring a more standardized and scalable telemetry pipeline. All the existing Elastic ingest components will become OTel based.</p>
<p>|                              |                                                                                                 |
| ---------------------------- | ----------------------------------------------------------------------------------------------- |
| <strong>Beats</strong>                    | Beats architecture will be based on OTel.                                                       |
| <strong>Elastic Agent</strong>            | Agent architecture will be based on OTel to support both beats based inputs and OTel receivers. |
| <strong>Integrations</strong>             | Integrations catalogue will additionally include OTel based modules for ease of configuration.  |
| <strong>Fleet central management</strong> | Fleet will support monitoring of Elastic OTel collectors.                                       |</p>
<p>Let's discuss how each component of Elastic's data ingestion platform will be based on an OpenTelemetry collector whilst still providing the same functionality to the user.</p>
<h3 id="beats">Beats</h3>
<p>Elastic's traditional data shippers will be re-architected as OpenTelemetry Collectors, aligning with OTel's extensibility model. Current Beat architecture is essentially made up of a few stages in its pipeline, as shown in the diagram below. It consists of an Input, Processors for enrichments, Queuing of events and Output for batching and writing the data to a specific output.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt8449b5e3334e82a0/6a7f060233fa8a7cc32023e8/filebeat.png" alt="filebeat" /></p>
<h4 id="beatreceiverconcept">Beatreceiver Concept</h4>
<p>To ensure a smooth transition without major disruptions, a "beatsreceiver" concept is being implemented. These <code>beatreceivers</code> (like <code>filebeatreceiver</code> or <code>metricbeatreceiver</code>) act as dedicated Beat inputs integrated into the OpenTelemetry Collector as native receivers. They support all existing inputs and processors, guaranteeing that the final architecture accepts the user's current configuration and delivers the same functionality as today's Beats, all without introducing any breaking changes.</p>
<p>An OTel based Beats architecture will see the Input phase embedded as an OTel receiver (eg.  <code>filebeatreceiver</code> to represent the functionality of <code>filebeat</code>). This receiver would only be available as part of Elastic's distribution of OTel in support of our current user base and not a functionality that would be available upstream.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt9b96ac33c2582cb0/6a7f06059090b0591984e72b/filebeatreceiver.png" alt="filebeat" /></p>
<p>All the remaining components of the pipeline will be based on OTel. The new Beat will accept the same filebeat configuration (as an example) and will transform it to an OTel based configuration in order to avoid any deployment disruption. It should be noted that in this architecture the Beats will continue to only support ECS formatted data. In order to keep the Beat functionality inline with what exists today, the Elasticsearch exporter (as an example) will output ECS formatted data only.</p>
<p>The following diagram illustrates the <code>beatreceiver</code> concept by showing how a basic <code>filebeat</code> configuration is automatically translated into an OpenTelemetry-based configuration. This new configuration retains the original inputs and processors but leverages the native OpenTelemetry pipeline and exporter to achieve the same overall <code>filebeat</code> functionality. Existing <code>filebeat</code> configurations will be automatically converted, eliminating the need for manual adjustments or introducing breaking changes.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltf043398fe986d14a/6a7f060873d9bd339329d8a8/elastic-agent-otel-config.png" alt="Filebeat OTel config" /></p>
<h3 id="elasticagent">Elastic Agent</h3>
<p>Elastic Agent is a unified agent for data collection, security, and observability. It can also be deployed in an OpenTelemetry only mode, enabling native OTel workflows. Elastic Agent is a supervisor that manages many other Beats as sub-processes in order to provide a more comprehensive data collection tool. It is capable of translating Agent Policy received from Fleet into configuration acceptable by the various sub-processes.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt58723135028419eb/6a7f060bbdcff0806ec42b5d/elastic-agent-architecture.png" alt="Elastic Agent Architecture" /></p>
<p>Expanding on the Beat receiver concept described above, the Elastic Agent, which currently can be deployed as an OTel collector (see <a href="https://www.elastic.co/observability-labs/blog/elastic-distributions-opentelemetry-ga">blog</a>), will be also modified to a much simpler OTel based architecture based on these receivers. As shown below, this architecture will streamline the components within the Elastic Agent and remove duplicated functionality such as queuing and output. Whilst supporting the current functionality, these changes will reduce the agent footprint and also present a reduction in number of connections opened to pipeline elements egress of the agent (such as Elasticsearch clusters, Logstash or Kafka brokers).</p>
<p>By moving to an OTel based architecture Elastic Agent is now able to operate as a truly hybrid Elastic Agent which provides not only the Beat functionality but also allows our users to create OTel native pipelines and take advantage of plethora of functionality available as part of the open source project.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltb64f338f976e9efa/6a7f060d1967ea49ed33043d/elastic-agent-otel-architecture.png" alt="Elastic Agent OTel Architecture" /></p>
<p>Elastic's commitment to OpenTelemetry will deepen through increased contributions, resulting in OpenTelemetry receivers gradually superseding Beats receiver features. This evolution will eventually reduce the need for a distinct Beats receiver within the Elastic Agent architecture. The envisioned architecture will empower the Elastic Agent to transmit data in OTLP format as well, granting users the flexibility to select any OTLP-compatible backend, thereby upholding the principle of vendor neutrality.</p>
<h3 id="fleetintegrationsmanagingopentelemetryatscale">Fleet &amp; Integrations: Managing OpenTelemetry at Scale</h3>
<p>Elastic's centralized management system will support OpenTelemetry-based configurations, making large-scale deployments easier to manage. Managing thousands of telemetry agents at scale presents a significant challenge. Elastic's <strong>Fleet &amp; Integrations</strong> simplify this process by providing robust lifecycle management for these new OpenTelemetry-based Elastic agents.</p>
<p><strong>Key Capabilities Offered:</strong></p>
<ul>
<li><p><strong>Scalability:</strong> Manage up to 100K+ agents across distributed environments.</p></li>
<li><p><strong>Automated Upgrades:</strong> Staged rollouts and automatic upgrades ensure minimal downtime.</p></li>
<li><p><strong>Monitoring &amp; Diagnostics:</strong> Real-time status updates, failure detection, and diagnostic downloads improve system reliability.</p></li>
<li><p><strong>Policy-Based Configuration Management:</strong> Enables centralized control over agent configurations, improving consistency across deployments.</p></li>
<li><p><strong>Pre-Built Integrations:</strong> Elastic offers a catalog of <strong>470+ pre-built integrations</strong>, allowing users to ingest data seamlessly from various sources. These will also include OTel based packages making configuration much more efficient across a large deployment.</p></li>
</ul>
<p>The goal is for Fleet to also provide monitoring capabilities for native OTel collectors as well in a vendor agnostic fashion.</p>
<h2 id="conclusion">Conclusion</h2>
<p>Elastic's adoption of OpenTelemetry marks a significant milestone in the evolution of open-source observability. By standardizing on OpenTelemetry, Elastic is ensuring that its data ingestion strategy remains <strong>open, scalable, and future-proof</strong>.</p>
<p>For open-source users, this shift means:</p>
<ul>
<li><p>Greater interoperability across observability tools.</p></li>
<li><p>Enhanced flexibility in choosing telemetry backends.</p></li>
<li><p>A stronger commitment to <strong>community-driven</strong> observability standards.</p></li>
<li><p>Existing Beats and Elastic Agent users can <strong>seamlessly adopt OpenTelemetry</strong> without rearchitecting their pipelines.</p></li>
<li><p>OpenTelemetry users can <strong>integrate with Elastic's observability stack</strong> without additional complexity.</p></li>
</ul>
<p>Stay tuned for more updates as Elastic continues to expand its OpenTelemetry-based data collection capabilities! In the mean time here are some other references:</p>
<ul>
<li><p><a href="https://www.elastic.co/observability-labs/blog/elastic-distributions-opentelemetry-ga">Elastic Distributions of OpenTelemetry (EDOT) Now GA</a></p></li>
<li><p><a href="https://www.elastic.co/observability-labs/blog/k8s-discovery-with-EDOT-collector">Dynamic workload discovery on Kubernetes now supported with EDOT Collector</a></p></li>
<li><p><a href="https://www.elastic.co/observability-labs/blog/introducing-the-ottl-playground-for-opentelemetry">Introducing the OTTL Playground for OpenTelemetry</a></p></li>
</ul>]]></content:encoded>
    <link>https://www.elastic.co/observability-labs/blog/elastic-agent-pivot-opentelemetry</link>
    <guid isPermaLink="false">elastic-agent-pivot-opentelemetry</guid>
    <category><![CDATA[OpenTelemetry]]></category>
    <category><![CDATA[APM]]></category>
    <dc:creator><![CDATA[Nima Rezainia]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltefce6a6a29657306/6a7f06101967ea2e1f330443/self-service-blog-image-templates.jpg" length="0" type="image/jpeg"/>
    <pubDate>Tue, 03 Jun 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Monitor your C++ Applications with Elastic APM]]></title>
    <description><![CDATA[In this article we will be using the Opentelemetry CPP client to monitor C++ application within Elastic APM]]></description>
    <content:encoded><![CDATA[<p>One of the main challenges that developers, SREs, and DevOps professionals face is the absence of an extensive tool that provides them with visibility to their application stack. Many of the APM solutions out on the market do provide methods to monitor applications that were built on languages and frameworks (i.e., .NET, Java, Python, etc.) but fall short when it comes to C++ applications.</p>
<p>Luckily, Elastic has been one of the leading solutions in observability space and a contributor to the OpenTelemetry project. Elastic’s unique position and its extensive observability capabilities allows end-users to monitor applications built with object-oriented programming languages &amp; Framework in a variety of ways.</p>
<p>In this blog we will explore using Elastic APM to investigate C++ traces with the OpenTelemetry client. We will be providing a comprehensive guide on how to implement the OpenTelemetry client for C++ applications and connecting to Elastic APM solutions. While OTel has its libraries, and this blog reviews how to use the OTel CPP library, Elastic also has its own Elastic Distributions of OpenTelemetry, which were developed to provide commercial support, and are completely upstreamed regularly.</p>
<p>Here are some resources to help get you started:</p>
<ul>
<li><p><a href="https://www.elastic.co/guide/en/observability/current/apm-open-telemetry.html">Use OpenTelemetry with APM</a></p></li>
<li><p><a href="https://github.com/open-telemetry/opentelemetry-cpp">The OpenTelemetry C++ Client</a></p></li>
<li><p><a href="https://opentelemetry.io/docs/languages/cpp/">OpenTelemetry C++ Docs</a></p></li>
</ul>
<h2 id="stepbystepguide">Step by Step Guide</h2>
<h2 id="prerequisites">Prerequisites</h2>
<ul>
<li>### Environment</li>
</ul>
<p>Choosing an environment is quite important as there is limited support for the OTEL client. We have experimented with using multiple Operating Systems and here are the suggestions:</p>
<ul>
<li><p>Ubuntu 22.04</p></li>
<li><p>Debian 11 Bullseye</p></li>
<li><p>For this guide we are focusing on Ubuntu 22.04.</p></li>
<li><p>Machine: 2 vCPU, 4GB is sufficient.</p></li>
<li><p>Image: Ubuntu 22.04 LTS (x86_64).</p></li>
<li><p>Disk: ~30 GB is enough.</p></li>
</ul>
<h2 id="implementationmethodnbsp">Implementation method </h2>
<p>We have experimented with multiple methods but we found that the most suitable approach is to use a package manager. After extensive testing, It appears that trying to run otel-cpp client could be quite challenging to the users. If practitioners desire to build with tools such as CMake and Bazel that is a viable solution. With that, as we tested both methods it became obvious that we were spending most of our time and effort fixing compatibility and dependencies’ issues for the OS Vs. Focusing on sending data to our APM. Hence we decided to move to a different method.</p>
<p>The main issues that we kept running into as we test are:</p>
<ul>
<li><p>Compatibility of packages.</p></li>
<li><p>Availability of packages.</p></li>
<li><p>Dependencies of libraries and packages.</p></li>
</ul>
<p>In this guide we will use vcpkg since it allows us to bring in all the dependencies required to run the Opentelemetry C++ client.</p>
<h2 id="installingrequiredostools">Installing required OS tools</h2>
<h3 id="updatepackagelists">Update package lists</h3>
<pre><code>    sudo apt-get update
</code></pre>
<p>Install build essentials, cmake, git, and sqlite dev library</p>
<pre><code>    sudo apt-get install -y build-essential cmake git curl zip unzip sqlite3 libsqlite3-dev
</code></pre>
<p>sqlite3 and libsqlite3-dev allow us to build/run SQLite queries in our C++ code.</p>
<h3 id="setupvcpkg">Set Up vcpkg</h3>
<p>vcpkg is the C++ package manager that we’ll use to install opentelemetry-cpp client.</p>
<pre><code>    # Clone vcpkg
    cd ~
    git clone https://github.com/microsoft/vcpkg.git
</code></pre>
<pre><code>    # Bootstrap
    cd ~/vcpkg
    ./bootstrap-vcpkg.sh
</code></pre>
<h3 id="installopentelemetrycwithotlpgrpc">Install OpenTelemetry C++ with OTLP gRPC</h3>
<p>In this guide we focus on trace export to Elastic. At time of writing, vcpkg’s opentelemetry-cpp</p>
<p>version 1.18.0 fully supports traces but has limited direct metrics exporting.</p>
<h3 id="installthepackage">Install the package</h3>
<pre><code>    cd ~/vcpkg
    ./vcpkg install opentelemetry-cpp[otlp-grpc]:x64-linux
</code></pre>
<p><strong>Note</strong></p>
<p>Sometimes when installing opentelemetry-cpp on linux it doesn't install all the required packages. As a workaround if you run into that case, try running again but pass a flag to allow-unsupported:</p>
<pre><code>    ./vcpkg install opentelemetry-cpp[*]:x64-linux --allow-unsupported
</code></pre>
<h3 id="verify">Verify</h3>
<pre><code>    ./vcpkg list | grep opentelemetry-cpp
</code></pre>
<p>The output thould be something like this: </p>
<pre><code>opentelemetry-cpp:x64-linux 1.18.0
</code></pre>
<h2 id="createthecprojectwithdatabasespans">Create the C++ Project with Database Spans</h2>
<p>We’ll build a sample in ~/otel-app that:</p>
<ul>
<li><p>Uses SQLite to do basic CREATE/INSERT/SELECT queries. This is helpful to showcase capturing transactions for apps that use databases on Elastic APM.</p></li>
<li><p>Generate random traces to showcase how they are captured on Elastic APM.</p></li>
</ul>
<p>This app is going to generate random queries where some will contain database transactions and some are just application traces. Each query is contained in a child span, so they appear in APM as separate database transactions.</p>
<pre><code># Below is the structure of our project
</code></pre>
<pre><code>    otel-app/
    ├── main.cpp
    └── CMakeLists.txt
</code></pre>
<h3 id="createappproject">Create App Project</h3>
<pre><code>    cd ~
    mkdir otel-app
    cd otel-app
</code></pre>
<p>Inside this project we will create two files</p>
<ul>
<li><p>main.cpp</p></li>
<li><p>CMakeLists.txt</p></li>
</ul>
<p>Keep in mind that main.cpp is where you are going to pass the otel exporters that are going to send data to the Elastic cluster. So for your tech stack it would be your application's source code.</p>
<h4 id="sampleapplicationcode">Sample application code</h4>
<pre><code>    main.cpp
    // Below we declare required libraries that we will be using to ship
    // traces to Elastic APM
    #include &lt;opentelemetry/exporters/otlp/otlp_grpc_exporter.h&gt;
    #include &lt;opentelemetry/sdk/trace/tracer_provider.h&gt;
    #include &lt;opentelemetry/sdk/trace/simple_processor.h&gt;
    #include &lt;opentelemetry/trace/provider.h&gt;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    set(CMAKE_CXX_STANDARD 11)
    set(CMAKE_CXX_STANDARD_REQUIRED ON)

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

    find_package(opentelemetry-cpp CONFIG REQUIRED)

    add_executable(otel_app main.cpp)

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

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

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

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

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

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

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

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

from langtrace_python_sdk import langtrace

langtrace.init(batch=False)

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

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

from opentelemetry.instrumentation.flask import FlaskInstrumentor

from langtrace_python_sdk import langtrace

langtrace.init(batch=False)

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

FlaskInstrumentor().instrument_app(app)

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

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

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

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

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

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

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

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

data:
  relay: |
    connectors:
      spanmetrics: {}
    exporters:
      debug: {}
      otlp/elastic:
        endpoint: ${env:ELASTIC_APM_ENDPOINT}
        compression: none
        headers:
          Authorization: Bearer ${ELASTIC_APM_SECRET_TOKEN}
    extensions:
    processors:
      batch: {}
      resource:
        attributes:
          - key: deployment.environment
            value: "opentelemetry-demo"
            action: upsert
      attributes:
        actions:
          - key: http.url
            action: extract
            pattern: '^(?P&lt;short_url&gt;https?://[^/]+(?:/[^/]+)*)(?:/(?P&lt;url_truncated_path&gt;[^/?]+/[^/?]+))(?:\?|/?$)'
      transform:
        trace_statements:
          - context: span
            statements:
              - set(name, attributes["url_truncated_path"])
    receivers:
      httpcheck/frontendproxy:
        targets:
        - endpoint: http://example-frontendproxy:8080
      otlp:
        protocols:
          grpc:
            endpoint: ${env:MY_POD_IP}:4317
          http:
            cors:
              allowed_origins:
              - http://*
              - https://*
            endpoint: ${env:MY_POD_IP}:4318
    service:
      extensions:
      pipelines:
        logs:
          exporters:
          - debug
          - otlp/elastic
          processors:
          - batch
          - resource
          - attributes
          - transform
          receivers:
          - otlp
        metrics:
          exporters:
          - otlp/elastic
          - debug
          processors:
          - batch
          - resource
          receivers:
          - httpcheck/frontendproxy
          - otlp
          - spanmetrics
        traces:
          exporters:
          - otlp/elastic
          - debug
          - spanmetrics
          processors:
          - batch
          - resource
          - attributes
          - transform
          receivers:
          - otlp
      telemetry:
        metrics:
          address: ${env:MY_POD_IP}:8888
</code></pre>
<p>You'll notice that we tie everything together by adding our enrichment and transformations to the traces section in pipelines at the bottom of the collector config. This is the definition of our observability pipeline, bringing together all the pieces we've discussed to create more meaningful and actionable telemetry data.</p>
<p>By implementing this configuration, you're taking a significant step towards more insightful observability. You're not just collecting data; you're refining it to provide clear, actionable insights into your application's performance, check out the final result below!</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt9b8c24e95c33f1a4/6a7f1b853cab1c804f0e4cb5/image2.png" alt="" /></p>
<h2 id="readytotakeyourobservabilitytothenextlevel">Ready to Take Your Observability to the Next Level?</h2>
<p>Implementing OpenTelemetry with Elastic Observability opens up a world of possibilities for understanding and optimizing your applications. But this is just the beginning! To further enhance your observability journey, check out these valuable resources:</p>
<ol>
<li><a href="https://www.elastic.co/observability-labs/blog/infrastructure-monitoring-with-opentelemetry-in-elastic-observability">Infrastructure Monitoring with OpenTelemetry in Elastic Observability</a></li>
<li><a href="https://www.elastic.co/observability-labs/blog/tag/opentelemetry">Explore More OpenTelemetry Content</a></li>
<li><a href="https://www.elastic.co/observability-labs/blog/using-the-otel-operator-for-injecting-java-agents">Using the OTel Operator for Injecting Java Agents</a></li>
<li><a href="https://www.elastic.co/what-is/opentelemetry">What is OpenTelemetry?</a></li>
</ol>
<p>We encourage you to dive deeper, experiment with these configurations, and see how they can transform your observability data. Remember, the key is to find the right balance between detail and manageability.</p>
<p>Have you implemented similar strategies in your observability pipeline? We'd love to hear about your experiences and insights. Share your thoughts in the comments below or reach out to us on our community forums.</p>
<p>Stay tuned for Part 2 of this series, where we will look at an advanced technique for collecting more data that can help you get even more granular by collecting Span names, baggage and data for metrics using a Java plugin all without code.</p>]]></content:encoded>
    <link>https://www.elastic.co/observability-labs/blog/tailoring-span-names-and-enriching-spans-without-changing-code-with-opentelemetry</link>
    <guid isPermaLink="false">tailoring-span-names-and-enriching-spans-without-changing-code-with-opentelemetry</guid>
    <category><![CDATA[OpenTelemetry]]></category>
    <category><![CDATA[APM]]></category>
    <category><![CDATA[Infrastructure Monitoring]]></category>
    <dc:creator><![CDATA[David Hope]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt89485fbb57db9f4e/6a7f1b8796b5a6989c87b8b1/tailor.jpg" length="0" type="image/jpeg"/>
    <pubDate>Mon, 26 Aug 2024 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Introducing Elastic Distributions of OpenTelemetry]]></title>
    <description><![CDATA[Elastic is proud to introduce Elastic Distributions of OpenTelemetry (EDOT), which contains Elastic’s versions of the OpenTelemetry Collector and several language SDKs like Python, Java, .NET, and NodeJS. These help provide enhanced features and enterprise-grade support for EDOT.]]></description>
    <content:encoded><![CDATA[<p>We are announcing the availability of Elastic Distributions of OpenTelemetry (EDOT). These Elastic distributions, currently in tech preview,  have been developed to enhance the capabilities of standard OpenTelemetry distributions and improve existing OpenTelemetry support from Elastic. </p>
<p>The Elastic Distributions of OpenTelemetry (EDOT) are composed of OpenTelemetry (OTel) project components, OTel Collector, and language SDKs,  which provide users with the necessary capabilities and out-of-the-box configurations, enabling quick and effortless infra and application monitoring.</p>
<p>While OTel components are feature-rich, enhancements through the community can take time. Additionally, support is left up to the community or individual users and organizations. Hence EDOT will bring the following to end users:</p>
<ul>
<li><p><strong>Deliver enhanced features earlier than OTel</strong>: By providing features unavailable in the “vanilla” OpenTelemetry components, we can quickly meet customers’ requirements while still providing an OpenTelemetry native and vendor-agnostic instrumentation for their applications. Elastic will continuously upstream these enhanced features.</p></li>
<li><p><strong>Enhanced OTel support</strong> - By maintaining Elastic distributions, we can better support customers with enhancements and fixes outside of the OTel release cycles. In addition, Elastic support can troubleshoot issues on the EDOT.</p></li>
</ul>
<p>EDOT currently includes the following tech preview components, which will  grow over time:</p>
<ul>
<li><p><a href="https://www.elastic.co/observability-labs/blog/elastic-distribution-opentelemetry-collector">Elastic Distribution of OpenTelemetry (EDOT) Collector</a></p></li>
<li><p><a href="https://www.elastic.co/observability-labs/blog/elastic-distribution-opentelemetry-java-agent">Elastic Distribution of OpenTelemetry (EDOT) Java</a>.</p></li>
<li><p><a href="https://www.elastic.co/observability-labs/blog/elastic-opentelemetry-distribution-python">Elastic Distribution of OpenTelemetry (EDOT) Python</a></p></li>
<li><p><a href="https://www.elastic.co/observability-labs/blog/elastic-opentelemetry-distribution-node-js">Elastic Distribution of OpenTelemetry (EDOT) NodeJS</a></p></li>
<li><p><a href="https://www.elastic.co/observability-labs/blog/elastic-opentelemetry-distribution-dotnet-applications">Elastic Distribution of OpenTelemetry (EDOT) .NET</a></p></li>
<li><p><a href="https://www.elastic.co/observability-labs/blog/apm-ios-android-native-apps">Elastic Distribution of OpenTelemetry (EDOT)  iOS and Android</a></p></li>
</ul>
<p>Details and documentation for all EDOT are available in our public <a href="https://github.com/elastic/opentelemetry">OpenTelemetry GitHub repository</a>. </p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt43eeb982d6e02dbd/6a9fb52927a5318002dcacc7/edot-components-dark.png" alt="EDOT Components" /></p>
<h2 id="elasticdistributionofopentelemetryedotcollectoraidelasticdistributionofopentelemetryedotcollectora">Elastic Distribution of OpenTelemetry (EDOT) Collector<a id="elastic-distribution-of-opentelemetry-edot-collector"></a></h2>
<p>The EDOT Collector, recently released with the 8.15 release of Elastic Observability enhances Elastic’s existing OTel capabilities. The EDOT Collector can, in addition to service monitoring, forward application logs, infrastructure logs, and metrics using standard OpenTelemetry Collector receivers like file logs and host metrics receivers.</p>
<p>Additionally, users of the Elastic Distribution of the OpenTelemetry Collector benefit from container logs automatically enriched with Kubernetes metadata by leveraging the powerful <a href="https://opentelemetry.io/blog/2024/otel-collector-container-log-parser/">container log parser</a> that Elastic recently contributed. This OpenTelemetry-based enrichment enhances the context and value of the collected logs, providing deeper insights and more effective troubleshooting capabilities.</p>
<p>This new collector distribution ensures that exported data is fully compatible with the Elastic Platform, enhancing the overall observability experience. Elastic also ensures that Elastic-curated UIs can seamlessly handle both the Elastic Common Schema (ECS) and OpenTelemetry formats.</p>
<h2 id="elasticdistributionsforlanguagesdksaidelasticdistributionsforlanguagesdksa">Elastic Distributions for Language SDKs<a id="elastic-distributions-for-language-sdks"></a></h2>
<p><a href="https://www.elastic.co/guide/en/apm/agent/index.html">Elastic's APM agents</a> have capabilities yet to be available in the OTel SDKs. EDOT brings these capabilities into the OTel language SDKs while maintaining seamless integration with Elastic Observability. Elastic will release OTel versions of all its APM agents, and continue to add additional language SDKs mirroring OTel.</p>
<h2 id="continuedsupportfornativeotelcomponentsaidcontinuedsupportfornativeotelcomponentsa">Continued support for Native OTel components<a id="continued-support-for-native-otel-components"></a></h2>
<p>EDOT does not preclude users from using native components. Users are still able to use:</p>
<ul>
<li><p><strong>OpenTelemetry Vanilla Language SDKs:</strong> use standard OpenTelemetry code instrumentation for many popular programming languages sending OTLP traces to Elastic via APM server.</p></li>
<li><p><strong>Upstream Distribution of OpenTelemetry Collector (Contrib or Custom):</strong> Send traces using the OpenTelemetry Collector with OTLP receiver and OTLP exporter to Elastic via APM server.</p></li>
</ul>
<p>Elastic is committed to contributing EDOT features or components upstream into the OpenTelemetry community, fostering a collaborative environment, and enhancing the overall OpenTelemetry ecosystem.</p>
<h2 id="extendingourcommitmenttovendoragnosticdatacollectionaidextendingourcommitmenttovendoragnosticdatacollectiona">Extending our commitment to vendor-agnostic data collection<a id="extending-our-commitment-to-vendor-agnostic-data-collection"></a></h2>
<p>Elastic remains committed to supporting OpenTelemetry by being OTel first and building a vendor-agnostic framework. As OpenTelemetry constantly grows its support of SDKs and components,  Elastic will continue to refine and mirror EDOT to OpenTelemetry and push enhancements upstream. </p>
<p>Over the past year, Elastic has been active in OTel through its <a href="https://opentelemetry.io/blog/2023/ecs-otel-semconv-convergence/">donation of Elastic Common Schema (ECS)</a>, contributions to the native <a href="https://www.elastic.co/observability-labs/blog/elastic-distribution-opentelemetry-collector">OpenTelemetry Collector</a> and language SDKs, and a recent <a href="https://www.elastic.co/observability-labs/blog/elastic-profiling-agent-acceptance-opentelemetry">donation of its Universal Profiling agent</a> to OpenTelemetry. </p>
<p>EDOT  builds on our decision to fully adopt and recommend OpenTelemetry as the preferred solution for observing applications. With EDOT, Elastic customers can future-proof their investments and adopt OpenTelemetry, giving them vendor-neutral instrumentation with Elastic enterprise-grade support.</p>
<p>Our vision is that Elastic will work with the OpenTelemetry community to donate features through the standardization processes and contribute the code to implement those in the native OpenTelemetry components. In time, as OTel capabilities advance, and many of the Elastic-exclusive features transition into OpenTelemetry, we look forward to no longer having Elastic Distributions for OpenTelemetry.. In the meantime, we can deliver those capabilities via our OpenTelemetry distributions.</p>]]></content:encoded>
    <link>https://www.elastic.co/observability-labs/blog/elastic-distributions-opentelemetry</link>
    <guid isPermaLink="false">elastic-distributions-opentelemetry</guid>
    <category><![CDATA[OpenTelemetry]]></category>
    <category><![CDATA[APM]]></category>
    <category><![CDATA[Infrastructure Monitoring]]></category>
    <dc:creator><![CDATA[Alexander Wert,Miguel Luna,Bahubali Shetti]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt1c945be5a78916b3/6a7f07a5b43770d70b4d6a91/edot-image.png" length="0" type="image/png"/>
    <pubDate>Thu, 15 Aug 2024 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Monitor your Python data pipelines with OTEL]]></title>
    <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 id="introduction">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 id="motivation">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>
<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>
<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 id="stepsforinstrumentation">Steps for Instrumentation</h2>
<p>Here are the steps to automatically instrument your Python script for exporting OTEL traces, metrics, and logs.</p>
<h3 id="step1importrequiredlibraries">Step 1: Import Required Libraries</h3>
<p>We first need to install the following libraries.</p>
<pre><code>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 id="explanationofdependencies">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 id="step2exportotelvariables">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://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt2e703cfea77bc4fb/6a7f0e189090b08f6a84ea5f/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://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltb4a4b1336cbb8a5f/6a7f0e1be3a219604299f528/otel-variables-2.png" alt="2 - Get OTEL variables step 2" /></p>
<p><strong>Find OTLP Endpoint</strong>:</p>
<ul>
<li><p>Look for the section related to OpenTelemetry or OTLP configuration.</p></li>
<li><p>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>.</p>
<p><strong>Obtain OTLP Headers</strong>:</p></li>
<li><p>In the same section, you should find instructions or a field for OTLP headers. These headers are often used for authentication purposes.</p></li>
<li><p>Copy the necessary headers provided by the interface. They might look like <code>Authorization: Bearer &lt;your-token&gt;</code>.</p></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>#!/bin/bash
echo "--- :otel: Setting OTEL variables"
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="otlp,console"
</code></pre>
<p>With these variables set, we are ready for auto-instrumentation without needing to add anything to the code.</p>
<h4 id="explanationofvariables">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 "otlp" means that logs will be exported using the OTLP protocol. Adding "console" 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 "otlp" by default, which means that metrics and traces will be exported using the OTLP protocol.</p>
<h3 id="runningpythonetls">Running Python ETLs</h3>
<p>We run Python ETLs with the following command:</p>
<pre><code>OTEL_RESOURCE_ATTRIBUTES="service.name=x-ETL,service.version=1.0,deployment.environment=production" &amp;&amp; opentelemetry-instrument python3 X_ETL.py 
</code></pre>
<h4 id="explanationofthecommand">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 id="tracing">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>from opentelemetry import trace

if __name__ == "__main__":

    tracer = trace.get_tracer("main")
    with tracer.start_as_current_span("initialization") as span:
            # Init code
            … 
    with tracer.start_as_current_span("search") as span:
            # Step 1 - Search code
            …
   with tracer.start_as_current_span("transform") as span:
           # Step 2 - Transform code
           …
   with tracer.start_as_current_span("load") 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://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt96c41c3496c22185/6a7f0e1e1967eaf8d13307db/Traces-APM-Observability-Elastic.png" alt="3 - APM Traces view" /></p>
<h3 id="metrics">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://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt18fce421bc3081e2/6a7f0e21c2cc09cf812495fe/otel-metrics-apm-view.png" alt="4 - APM Metrics view" /></p>
<h3 id="logging">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>        job.result()  # Waits for table load to complete
        job_details = client.get_job(job.job_id)  # Get job details

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

        logging.info("BigQuery load operation successful", 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://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltb6446286112e0ddf/6a7f0e24b43770fd594d6cfb/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 id="explanationofloggingmodification">Explanation of Logging Modification</h4>
<ul>
<li><p><strong>logging.info</strong>: This logs an informational message. The message "BigQuery load operation successful" 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 id="monitoringinelasticsapm">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 id="rulesandalerts">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://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt9ba2519ba5f3fa2d/6a7f0e27ea068dc2d9f09f00/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 "labels.status: error" 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://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt7f27471820068122/6a7f0e2a3cab1c6e0b0e48e6/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>logging.info(
            "Elasticsearch search operation successful",
            extra={
                "event": "Elasticsearch Search",
                "status": "success",
                "category": category,
                "index": index,
            },
        )
</code></pre>
<h3 id="morerules">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://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt238521f3b6880f17/6a7f0e2d42a1175c6895bf24/apm_custom_threshold_latency.png" alt="8 - APM Custom Threshold - Latency" /></p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt1da604ca4d0ab23c/6a7f0e30c2cc097c7e24960a/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://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blte3ac0d0af99a213b/6a7f0e3342a117206695bf28/apm_anomaly_rule_config.png" alt="10 - APM Anomaly Rule - Config" /></p>
<h3 id="anomalydetectionjob">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 <a href="https://www.elastic.co/guide/en/machine-learning/current/ml-anomaly-detection-job-types.html#multi-metric-jobs">Single metric job</a> to detect any anomalies with the incoming data source.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltaf263d1acbb97333/6a7f0e376693f817ce663fbd/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://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blta7cd8ba4e4eb7176/6a7f0e3ade2315ec2cfd7cb5/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://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt6ef0c217964808ad/6a7f0e3dbd2198cabb758135/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 id="customdashboard">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://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt5e6b6efe442d487f/6a7f0e41b4377082224d6d07/custom_dashboard.png" alt="14 - Custom Dashboard" /></p>
<h2 id="conclusion">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>
    <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>
    <category><![CDATA[OpenTelemetry]]></category>
    <category><![CDATA[Logs Analytics]]></category>
    <category><![CDATA[APM]]></category>
    <category><![CDATA[Machine Learning]]></category>
    <dc:creator><![CDATA[Tamara Dancheva,Almudena Sanz Olivé]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt62d1e1b03ea2e67c/6a7f0e43448e4e3e0c5c079f/main_image.jpg" length="0" type="image/jpeg"/>
    <pubDate>Thu, 08 Aug 2024 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Tracing LangChain apps with Elastic, OpenLLMetry, and OpenTelemetry]]></title>
    <description><![CDATA[LangChain applications are growing in use. The ability to build out RAG-based applications, simple AI Assistants, and more is becoming the norm. Observing these applications is even harder. Given the various options that are out there, this blog shows how to use OpenTelemetry instrumentation with OpenLLMetry and ingest it into Elastic Observability APM]]></description>
    <content:encoded><![CDATA[<p>LangChain has rapidly emerged as a crucial framework in the AI development landscape, particularly for building applications powered by large language models (LLMs). As its adoption has soared among developers, the need for effective debugging and performance optimization tools has become increasingly apparent. One such essential tool is the ability to obtain and analyze traces from LangChain applications. Tracing provides invaluable insights into the execution flow, helping developers understand and improve their AI-driven systems. </p>
<p>There are several options to trace for LangChain. One is Langsmith, ideal for detailed tracing and a complete breakdown of requests to large language models (LLMs). However, it is specific to Langchain. OpenTelemetry (OTel) is now broadly accepted as the industry standard for tracing. As one of the major Cloud Native Computing Foundation (CNCF) projects, with as many commits as Kubernetes, it is gaining support from major ISVs and cloud providers delivering support for the framework. </p>
<p>Hence, many LangChain-based applications will have multiple components beyond just LLM interactions. Using OpenTelemetry with LangChain is essential. OpenLLMetry is an available option for tracing Langchain apps in addition to Langsmith.</p>
<p>This blog will show how you can get LangChain tracing into Elastic using the OpenLLMetry library <code>opentelemetry-instrumentation-langchain</code>.</p>
<h2 id="prerequisitesaidprerequisitesa">Pre-requisites:<a id="pre-requisites"></a></h2>
<ul>
<li><p>An Elastic Cloud account — <a href="https://cloud.elastic.co/">sign up now</a>, and become familiar with <a href="https://www.elastic.co/guide/en/observability/current/apm-open-telemetry.html">Elastic’s OpenTelemetry configuration</a></p></li>
<li><p>Have a LangChain app to instrument</p></li>
<li><p>Be familiar with using <a href="https://opentelemetry.io/docs/languages/python/libraries/">OpenTelemetry’s Python SDK</a> </p></li>
<li><p>An account on your favorite LLM, with API keys</p></li>
</ul>
<h2 id="overview">Overview</h2>
<p>In highlighting tracing I created a simple LangChain app that does the following:</p>
<ol>
<li><p>Takes customer input on the command line. (Queries)</p></li>
<li><p>Sends these to the Azure OpenAI LLM via a LangChain.</p></li>
<li><p>Chain tools are set to use the search with Tavily </p></li>
<li><p>The LLM uses the output which returns the relevant information to the user.</p></li>
</ol>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blta85f2ad823158d9d/6a7f08acead8ec024fbaa6ad/LangChainAppCLI.png" alt="Chat Interaction" /></p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt55c861f606e4c4f0/6a7f08af42a1170c6a95bd06/LangChainAppInAPM.png" alt="LangChainChat App in Elastic APM" /></p>
<p>As you can see Elastic Observability’s APM recognizes the LangChain App, and also shows the full trace (done with manual instrumentation):</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt45bed91c1521dd6d/6a7f08b24c4bfb1a90ccd38b/LangChainAutoIntrument.png" alt="LangChainChat App in Elastic APM" /></p>
<p>As the above image shows:</p>
<ol>
<li>The user makes a query</li>
<li>Azure OpenAI is called, but it uses a tool (Tavily) to obtain some results</li>
<li>Azure OpenAI reviews and returns a summary to the end user</li>
</ol>
<p>The code was manually instrumented, but auto-instrument can also be used.</p>
<h2 id="opentelemetryconfigurationaidopentelemetryconfigurationa">OpenTelemetry Configuration<a id="opentelemetry-configuration"></a></h2>
<p>In using OpenTelemetry, we need to configure the SDK to generate traces and configure Elastic’s endpoint and authorization. Instructions can be found in <a href="https://opentelemetry.io/docs/zero-code/python/#setup">OpenTelemetry Auto-Instrumentation setup documentation</a>.</p>
<h3 id="opentelemetryenvironmentvariablesaidopentelemetryenvironmentvariablesa">OpenTelemetry Environment variables:<a id="opentelemetry-environment-variables"></a></h3>
<p>OpenTelemetry Environment variables for Elastic can be set as follows in linux (or in the code).</p>
<pre><code>OTEL_EXPORTER_OTLP_ENDPOINT=12345.apm.us-west-2.aws.cloud.es.io:443
OTEL_EXPORTER_OTLP_HEADERS="Authorization=Bearer%20ZZZZZZZ"
OTEL_RESOURCE_ATTRIBUTES="service.name=langchainChat,service.version=1.0,deployment.environment=production"
</code></pre>
<p>As you can see <code>OTEL_EXPORTER_OTLP_ENDPOINT</code> is set to Elastic, and the corresponding authorization header is also provided. These can be easily obtained from Elastic’s APM configuration screen under OpenTelemetry</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt2273479708a89677/6a7f08b5b43770a9da4d6af5/LangChainAppOTelAPMsetup.png" alt="LangChainChat App in Elastic APM" /></p>
<p><strong>Note: No agent is needed, we simply send the OTLP trace messages directly to Elastic’s APM server.</strong> </p>
<h2 id="openllmetrylibraryaidopenllmetrylibrarya">OpenLLMetry Library:<a id="openllmetry-library"></a></h2>
<p>OpenTelemetry's auto-instrumentation can be extended to trace other frameworks via instrumentation packages.</p>
<p>First, you must install the following package: </p>
<p><code>pip install opentelemetry-instrumentation-langchain</code></p>
<p>This library was developed by OpenLLMetry. </p>
<p>Then you will need to add the following to the code.</p>
<pre><code>from opentelemetry.instrumentation.langchain import LangchainInstrumentor
LangchainInstrumentor().instrument()
</code></pre>
<h2 id="instrumentationaidinstrumentationa">Instrumentation<a id="instrumentation"></a></h2>
<p>Once the libraries are added, and the environment variables are set, you can use auto-instrumentation With auto-instrumentation, the following:</p>
<pre><code>opentelemetry-instrument python tavilyAzureApp.py
</code></pre>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt45bed91c1521dd6d/6a7f08b24c4bfb1a90ccd38b/LangChainAutoIntrument.png" alt="LangChainChat App in Elastic APM" /></p>
<p>The OpenLLMetry library does pull out the flow correctly with minimal manual instrumentation except for adding the OpenLLMetry library.</p>
<ol>
<li><p>Takes customer input on the command line. (Queries)</p></li>
<li><p>Sends these to the Azure OpenAI LLM via a Lang chain.</p></li>
<li><p>Chain tools are set to use the search with Tavily </p></li>
<li><p>The LLM uses the output which returns the relevant information to the user.</p></li>
</ol>
<h3 id="manualinstrumentationaidmanualinstrumentationa">Manual-instrumentation<a id="manual-instrumentation"></a></h3>
<p>If you want to get more details out of the application, you will need to manually instrument. To get more traces follow my <a href="https://www.elastic.co/observability-labs/blog/manual-instrumentation-python-apps-opentelemetry">Python instrumentation guide</a>. This guide will walk you through setting up the necessary OpenTelemetry bits, Additionally, you can also look at the documentation in <a href="https://opentelemetry.io/docs/languages/python/instrumentation/">OTel for instrumenting in Python</a>.</p>
<p>Note that the env variables <code>OTEL_EXPORTER_OTLP_HEADERS</code> and <code>OTEL_EXPORTER_OTLP_ENDPOINT</code> are set as noted in the section above. You can also set up the <code>OTEL_RESOURCE_ATTRIBUTES</code>. </p>
<p>Once you follow the steps in either guide and initiate the tracer, you will have to essentially just add the span where you want to get more details. In the example below, only one line of code is added for span initialization. </p>
<p>Look at the placement of with <code>tracer.start_as_current_span("getting user query") as span:</code> below</p>
<pre><code># Creates a tracer from the global tracer provider
tracer = trace.get_tracer("newsQuery")

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

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

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

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


if __name__ == "__main__":
    asyncio.run(chat_interface())
</code></pre>
<p>As you can see, with manual instrumentation, we get the following trace:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt54624a5f8db253fa/6a7f08b82f00b2c4d8efe9f3/LangChainAppManualTrace.png" alt="LangChainChat App in Elastic APM" /></p>
<p>Which calls out when we enter our query function. <code>async def chat_interface()</code></p>
<h2 id="conclusionaidconclusiona">Conclusion<a id="conclusion"></a></h2>
<p>In this blog, we discussed the following:</p>
<ul>
<li><p>How to manually instrument LangChain with OpenTelemetry</p></li>
<li><p>How to properly initialize OpenTelemetry and add a custom span</p></li>
<li><p>How to easily set the OTLP ENDPOINT and OTLP HEADERS with Elastic without the need for a collector</p></li>
<li><p>See traces in Elastic Observability APM</p></li>
</ul>
<p>Hopefully, this provides an easy-to-understand walk-through of instrumenting LangChain with OpenTelemetry and how easy it is to send traces into Elastic.</p>
<p><strong>Additional resources for OpenTelemetry with Elastic:</strong></p>
<ul>
<li><p><a href="https://www.elastic.co/blog/opentelemetry-observability">Independence with OpenTelemetry on Elastic</a></p></li>
<li><p><a href="https://www.elastic.co/blog/implementing-kubernetes-observability-security-opentelemetry">Modern observability and security on Kubernetes with Elastic and OpenTelemetry</a></p></li>
<li><p><a href="https://www.elastic.co/blog/3-models-logging-opentelemetry-elastic">3 models for logging with OpenTelemetry and Elastic</a></p></li>
<li><p><a href="https://www.elastic.co/blog/adding-free-and-open-elastic-apm-as-part-of-your-elastic-observability-deployment">Adding free and open Elastic APM as part of your Elastic Observability deployment</a></p></li>
<li><p><a href="https://www.elastic.co/blog/monitor-openai-api-gpt-models-opentelemetry-elastic">Monitor OpenAI API and GPT models with OpenTelemetry and Elastic</a></p></li>
<li><p>Futureproof<a href="https://www.elastic.co/virtual-events/future-proof-your-observability-platform-with-opentelemetry-and-elastic"> your observability platform with OpenTelemetry and Elastic</a></p></li>
<li><p>Instrumentation resources:</p></li>
<li><p>Python: <a href="https://www.elastic.co/blog/auto-instrumentation-of-python-applications-opentelemetry">Auto-instrumentation</a>, <a href="https://www.elastic.co/blog/manual-instrumentation-of-python-applications-opentelemetry">Manual instrumentation</a></p></li>
<li><p>Java: <a href="https://www.elastic.co/blog/auto-instrumentation-of-java-applications-opentelemetry">Auto-instrumentation</a>, <a href="https://www.elastic.co/blog/manual-instrumentation-of-java-applications-opentelemetry">Manual instrumentation </a></p></li>
<li><p>Node.js: <a href="https://www.elastic.co/blog/auto-instrument-nodejs-applications-opentelemetry">Auto-instrumentation</a>, <a href="https://www.elastic.co/blog/manual-instrumentation-of-nodejs-applications-opentelemetry">Manual instrumentation</a></p></li>
<li><p>.NET: <a href="https://www.elastic.co/blog/auto-instrumentation-of-net-applications-opentelemetry">Auto-instrumentation</a>, <a href="https://www.elastic.co/blog/manual-instrumentation-of-net-applications-opentelemetry">Manual instrumentation</a></p></li>
</ul>
<p>Also log into <a href="https://cloud.elastic.co">cloud.elastic.co</a> to try out Elastic with a free trial.</p>]]></content:encoded>
    <link>https://www.elastic.co/observability-labs/blog/elastic-opentelemetry-langchain-tracing</link>
    <guid isPermaLink="false">elastic-opentelemetry-langchain-tracing</guid>
    <category><![CDATA[LLM Observability]]></category>
    <category><![CDATA[OpenTelemetry]]></category>
    <category><![CDATA[APM]]></category>
    <category><![CDATA[Infrastructure Monitoring]]></category>
    <dc:creator><![CDATA[Bahubali Shetti]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blted3172bb9d8e783d/6a7f08bc9090b0b4ec84e853/LangChainBlogMainImage.png" length="0" type="image/png"/>
    <pubDate>Fri, 02 Aug 2024 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Using a custom agent with the OpenTelemetry Operator for Kubernetes]]></title>
    <description><![CDATA[]]></description>
    <content:encoded><![CDATA[<p>This is the second part of a two part series. The first part is available at <a href="https://www.elastic.co/observability-labs/blog/using-the-otel-operator-for-injecting-java-agents">Zero config OpenTelemetry auto-instrumentation for Kubernetes Java applications</a>. In that first part I walk through setting up and installing the <a href="https://github.com/open-telemetry/opentelemetry-operator/">OpenTelemetry Operator for Kubernetes</a>, and configuring that for auto-instrumentation of a Java application using the <a href="https://github.com/open-telemetry/opentelemetry-java-instrumentation/">OpenTelemetry Java agent</a>. </p>
<p>In this second part, I show how to install <em>any</em> Java agent via the OpenTelemetry operator, using the Elastic Java agents as examples.</p>
<h2 id="installationandconfigurationrecap">Installation and configuration recap</h2>
<p>Part 1 of this series, <a href="https://www.elastic.co/observability-labs/blog/using-the-otel-operator-for-injecting-java-agents">Zero config OpenTelemetry auto-instrumentation for Kubernetes Java applications</a>, details the installation and configuration of the OpenTelemetry operator and an Instrumentation resource. Here is an outline of the steps as a reminder:</p>
<ol>
<li>Install cert-manager, eg <code>kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.14.4/cert-manager.yaml</code></li>
<li>Install the operator, eg <code>kubectl apply -f https://github.com/open-telemetry/opentelemetry-operator/releases/latest/download/opentelemetry-operator.yaml</code></li>
<li>Create an Instrumentation resource</li>
<li>Add an annotation to either the deployment or the namespace</li>
<li>Deploy the application as normal</li>
</ol>
<p>In that first part, steps 3, 4 &amp; 5 were implemented for the <a href="https://github.com/open-telemetry/opentelemetry-java-instrumentation/">OpenTelemetry Java agent</a>. In this blog I’ll implement them for other agents, using the Elastic APM agents as examples. I assume that steps 1 &amp; 2 outlined above have already been done, ie that the operator is now installed. I will continue using the <code>banana</code> namespace for the examples, so ensure that namespace exists (<code>kubectl create namespace banana</code>). As per part 1, if you use any of the example instrumentation definitions below, you’ll need to substitute <code>my.apm.server.url</code> and <code>my-apm-secret-token</code> with the values appropriate for your collector.</p>
<h2 id="usingtheelasticdistributionforopentelemetryjava">Using the Elastic Distribution for OpenTelemetry Java</h2>
<p>From version 0.4.0, the <a href="https://github.com/elastic/elastic-otel-java">Elastic Distribution for OpenTelemetry Java</a> includes the agent jar at the path <code>/javaagent.jar</code> in the docker image - which is essentially all that is needed for a docker image to be usable by the OpenTelemetry operator for auto-instrumentation. This means the Instrumentation resource is straightforward to define, and as it’s a distribution of the OpenTelemetry Java agent, all the OpenTelemetry environment can apply:</p>
<pre><code>apiVersion: opentelemetry.io/v1alpha1
kind: Instrumentation
metadata:
  name: elastic-otel
  namespace: banana
spec:
  exporter:
    endpoint: https://my.apm.server.url
  propagators:
    - tracecontext
    - baggage
    - b3
  sampler:
    type: parentbased_traceidratio
    argument: "1.0"
  java:
    image: docker.elastic.co/observability/elastic-otel-javaagent:1.10.0
    env:
      - name: OTEL_EXPORTER_OTLP_HEADERS
        value: "Authorization=Bearer my-apm-secret-token"
      - name: ELASTIC_OTEL_INFERRED_SPANS_ENABLED
        value: "true"
      - name: ELASTIC_OTEL_SPAN_STACK_TRACE_MIN_DURATION
        value: "50"
</code></pre>
<p>I’ve included environment for switching on several features in the agent, including</p>
<ol>
<li>ELASTIC_APM_PROFILING_INFERRED_SPANS_ENABLED to switch on the inferred spans implementation feature described in <a href="https://www.elastic.co/observability-labs/blog/tracing-data-inferred-spans-opentelemetry">this blog</a></li>
<li>Span stack traces are automatically captured if the span takes more than ELASTIC_OTEL_SPAN_STACK_TRACE_MIN_DURATION (default would be 5ms)</li>
</ol>
<p>Adding in the annotation …</p>
<pre><code>metadata:
  annotations:
    instrumentation.opentelemetry.io/inject-java: "elastic-otel"
</code></pre>
<p>… to the pod yaml gets the application traced, and displayed in the Elastic APM UI, including the inferred child spans and stack traces</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt1cf450efadd3a797/6a7f1c0296b5a66b7787b8c1/elastic-apm-ui-with-stack-trace.png" alt="Elastic APM UI showing methodB traced with stack traces and inferred spans" /></p>
<p>The additions from the features mentioned above are circled in red - inferred spans (for methodC and methodD) bottom left, and the stack trace top right. (Note that the pod included the <code>OTEL_INSTRUMENTATION_METHODS_INCLUDE</code> environment variable set to <code>"test.Testing[methodB]"</code> so that traces from methodB are shown; for pod configuration see the "Trying it" section in <a href="https://www.elastic.co/observability-labs/blog/using-the-otel-operator-for-injecting-java-agents">part 1</a>)</p>
<h2 id="usingtheelasticapmjavaagent">Using the Elastic APM Java agent</h2>
<p>From version 1.50.0, the <a href="https://github.com/elastic/apm-agent-java">Elastic APM Java agent</a> includes the agent jar at the path /javaagent.jar in the docker image - which is essentially all that is needed for a docker image to be usable by the OpenTelemetry operator for auto-instrumentation. This means the Instrumentation resource is straightforward to define:</p>
<pre><code>apiVersion: opentelemetry.io/v1alpha1
kind: Instrumentation
metadata:
  name: elastic-apm
  namespace: banana
spec:
  java:
    image: docker.elastic.co/observability/apm-agent-java:1.55.4
    env:
      - name: ELASTIC_APM_SERVER_URL
        value: "https://my.apm.server.url"
      - name: ELASTIC_APM_SECRET_TOKEN
        value: "my-apm-secret-token"
      - name: ELASTIC_APM_LOG_LEVEL
        value: "INFO"
      - name: ELASTIC_APM_PROFILING_INFERRED_SPANS_ENABLED
        value: "true"
      - name: ELASTIC_APM_LOG_SENDING
        value: "true"
</code></pre>
<p>I’ve included environment for switching on several features in the agent, including</p>
<ul>
<li>ELASTIC_APM_LOG_LEVEL set to the default value (INFO) which could easily be switched to DEBUG</li>
<li>ELASTIC_APM_PROFILING_INFERRED_SPANS_ENABLED to switch on the inferred spans implementation equivalent to the feature described in <a href="https://www.elastic.co/observability-labs/blog/tracing-data-inferred-spans-opentelemetry">this blog</a></li>
<li>ELASTIC_APM_LOG_SENDING which switches on sending logs to the APM UI, the logs are automatically correlated with transactions (for all common logging frameworks)</li>
</ul>
<p>Adding in the annotation …</p>
<pre><code>metadata:
  annotations:
     instrumentation.opentelemetry.io/inject-java: "elastic-apm"
</code></pre>
<p>… to the pod yaml gets the application traced, and displayed in the Elastic APM UI, including the inferred child spans</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt425d331c27b53881/6a7f1c0596b5a6621887b8c5/elastic-apm-ui-with-inferred-spans.png" alt="Elastic APM UI showing methodB traced with inferred spans" /></p>
<p>(Note that the pod included the <code>ELASTIC_APM_TRACE_METHODS</code> environment variable set to <code>"test.Testing#methodB"</code> so that traces from methodB are shown; for pod configuration see the "Trying it" section in <a href="https://www.elastic.co/observability-labs/blog/using-the-otel-operator-for-injecting-java-agents">part 1</a>)</p>
<h2 id="usinganextensionwiththeopentelemetryjavaagent">Using an extension with the OpenTelemetry Java agent</h2>
<p>Setting up an Instrumentation resource for the OpenTelemetry Java agent is straightforward and was done in <a href="https://www.elastic.co/observability-labs/blog/using-the-otel-operator-for-injecting-java-agents">part 1</a> of this two part series - and you can see from the above examples it’s just a matter of deciding on the docker image URL you want to use. However if you want to include an <em>extension</em> in your deployment, this is a little more complex, but also supported by the operator. Basically the extensions you want to include with the agent need to be in docker images - or you have to build an image which includes the extensions that are not already in images. Then you declare the images and the directories the extensions are in, in the Instrumentation resource. As an example, I’ll show an Instrumentation which uses version 2.5.0 of the <a href="https://github.com/open-telemetry/opentelemetry-java-instrumentation/">OpenTelemetry Java agent</a> together with the <a href="https://github.com/elastic/elastic-otel-java/tree/main/inferred-spans">inferred spans extension</a> from the <a href="https://github.com/elastic/elastic-otel-java">Elastic OpenTelemetry Java distribution</a>. The distro image includes the extension at path <code>/extensions/elastic-otel-agentextension.jar</code>. The Instrumentation resource allows either directories or file paths to be specified, here I’ll list the directory:</p>
<pre><code>apiVersion: opentelemetry.io/v1alpha1
kind: Instrumentation
metadata:
  name: otel-plus-extension-instrumentation
  namespace: banana
spec:
  exporter:
    endpoint: https://my.apm.server.url
  propagators:
    - tracecontext
    - baggage
    - b3
  sampler:
    type: parentbased_traceidratio
    argument: "1.0"
  java:
    image: ghcr.io/open-telemetry/opentelemetry-operator/autoinstrumentation-java:2.5.0
    extensions:
      - image: "docker.elastic.co/observability/elastic-otel-javaagent:1.10.0"
        dir: "/extensions"
    env:
      - name: OTEL_EXPORTER_OTLP_HEADERS
        value: "Authorization=Bearer my-apm-secret-token"
      - name: ELASTIC_OTEL_INFERRED_SPANS_ENABLED
        value: "true"
</code></pre>
<p>Note that you can have multiple <code>image … dir</code> pairs, ie include multiple extensions from different images. Note also if you are testing this specific configuration that the inferred spans extension included here will be contributed to the OpenTelemetry contrib repo at some point after this blog is published, after which the extension may no longer be present in a later version of the referred image (since it will be available from the <a href="https://github.com/open-telemetry/opentelemetry-java-contrib/">contrib repo</a> instead).</p>
<h2 id="nextsteps">Next steps</h2>
<p>Here I’ve shown how to use any agent with the <a href="https://github.com/open-telemetry/opentelemetry-operator/">OpenTelemetry Operator for Kubernetes</a>, and configure that for your system. In particular the examples have showcased how to use the Elastic Java agents to auto-instrument Java applications running in your Kubernetes clusters, along with how to enable features, using Instrumentation resources. And you can set it up for either zero config for deployments, or for just one annotation which is generally a more flexible mechanism (you can have multiple Instrumentation resource definitions, and the deployment can select the appropriate one for its application).</p>
<p><em>The release and timing of any features or functionality described in this post remain at Elastic's sole discretion. Any features or functionality not currently available may not be delivered on time or at all.</em></p>]]></content:encoded>
    <link>https://www.elastic.co/observability-labs/blog/using-the-otel-operator-for-injecting-elastic-agents</link>
    <guid isPermaLink="false">using-the-otel-operator-for-injecting-elastic-agents</guid>
    <category><![CDATA[OpenTelemetry]]></category>
    <category><![CDATA[APM]]></category>
    <category><![CDATA[Kubernetes]]></category>
    <category><![CDATA[Infrastructure Monitoring]]></category>
    <dc:creator><![CDATA[Jack Shirazi]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt55dfb115f9341105/6a7f1c08ea068d4de1f0a2f9/blog-header-720x420.jpg" length="0" type="image/jpeg"/>
    <pubDate>Tue, 16 Jul 2024 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Zero config OpenTelemetry auto-instrumentation for Kubernetes Java applications]]></title>
    <description><![CDATA[Walking through how to install and enable the OpenTelemetry Operator for Kubernetes to auto-instrument Java applications, with no configuration changes needed for deployments]]></description>
    <content:encoded><![CDATA[<p>The <a href="https://github.com/open-telemetry/opentelemetry-java-instrumentation/">OpenTelemetry Java agent</a> has a number of <a href="https://opentelemetry.io/docs/languages/java/automatic/#setup">ways to install</a> the agent into a Java application. If you are running your Java applications in Kubernetes pods, there is a separate mechanism (which under the hood uses JAVA_TOOL_OPTIONS and other environment variables) to auto-instrument Java applications. This auto-instrumentation can be achieved with zero configuration of the applications and pods!</p>
<p>The mechanism to achieve zero-config auto-instrumentation of Java applications in Kubernetes is via the <a href="https://github.com/open-telemetry/opentelemetry-operator/">OpenTelemetry Operator for Kubernetes</a>. This operator has many capabilities and the full documentation (and of course source) is available in the project itself. In this blog, I'll walk through installing, setting up and running zero-config auto-instrumentation of Java applications in Kubernetes using the OpenTelemetry Operator.</p>
<h2 id="installingtheopentelemetryoperatoraidinstallingtheopentelemetryoperatora">Installing the OpenTelemetry Operator<a id="installing-the-opentelemetry-operator"></a></h2>
<p>At the time of writing this blog, the OpenTelemetry Operator needs the certification manager to be installed, after which the operator can be installed. Installing from the web is straightforward. First install the <code>cert-manager</code> (the version to be installed will be specified in the <a href="https://github.com/open-telemetry/opentelemetry-operator/">OpenTelemetry Operator for Kubernetes</a> documentation):</p>
<pre><code>kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.14.4/cert-manager.yaml
</code></pre>
<p>Then when the cert managers are ready (<code>kubectl get pods -n cert-manager</code>)  …</p>
<pre><code>NAMESPACE&amp;nbsp; &amp;nbsp; &amp;nbsp; NAME &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; READY
cert-manager &amp;nbsp; cert-manager-67c98b89c8-rnr5s&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 1/1
cert-manager &amp;nbsp; cert-manager-cainjector-5c5695d979-q9hxz &amp;nbsp; &amp;nbsp; 1/1
cert-manager &amp;nbsp; cert-manager-webhook-7f9f8648b9-8gxgs&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 1/1
</code></pre>
<p>… you can install the OpenTelemetry Operator:</p>
<pre><code>kubectl apply -f https://github.com/open-telemetry/opentelemetry-operator/releases/latest/download/opentelemetry-operator.yaml
</code></pre>
<p>You can, of course, use a specific version of the operator instead of the <code>latest</code>. But here I’ve used the <code>latest</code> version.</p>
<h2 id="aninstrumentationresourceaidaninstrumentationresourcea">An Instrumentation resource<a id="an-instrumentation-resource"></a></h2>
<p>Now you need to add just one further Kubernetes resource to enable auto-instrumentation: an <code>Instrumentation</code> resource. I am going to use the <code>banana</code> namespace for my examples, so I have first created that namespace (<code>kubectl create namespace banana</code>). The auto-instrumentation is specified and configured by these Instrumentation resources. Here is a basic one which will allow every Java pod in the <code>banana</code> namespace to be auto-instrumented with version 2.5.0 of the <a href="https://github.com/open-telemetry/opentelemetry-java-instrumentation/">OpenTelemetry Java agent</a>:</p>
<pre><code>apiVersion: opentelemetry.io/v1alpha1
kind: Instrumentation
metadata:
  name: banana-instr
  namespace: banana
spec:
  exporter:
    endpoint: "https://my.endpoint"
  propagators:
    - tracecontext
    - baggage
    - b3
  sampler:
    type: parentbased_traceidratio
    argument: "1.0"
  java:
    image: ghcr.io/open-telemetry/opentelemetry-operator/autoinstrumentation-java:2.5.0
    env:
      - name: OTEL_EXPORTER_OTLP_HEADERS
        value: "Authorization=Bearer MyAuth"
</code></pre>
<p>Creating this resource (eg with <code>kubectl apply -f banana-instr.yaml</code>, assuming the above yaml was saved in file <code>banana-instr.yaml</code>) makes the <code>banana-instr</code> Instrumentation resource available for use. (Note you will need to change <code>my.endpoint</code> and <code>MyAuth</code> to values appropriate for your collector.) You can use this instrumentation immediately by adding an annotation to any deployment in the <code>banana</code> namespace:</p>
<pre><code>metadata:
&amp;nbsp;&amp;nbsp;annotations:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;instrumentation.opentelemetry.io/inject-java: "true"
</code></pre>
<p>The <code>banana-instr</code> Instrumentation resource is not yet set to be applied by <em>default</em> to all pods in the banana namespace. Currently it's zero-config as far as the <em>application</em> is concerned, but it requires an annotation added to a <em>pod or deployment</em>. To make it fully zero-config for <em>all pods</em> in the <code>banana</code> namespace, we need to add that annotation to the namespace itself, ie editing the namespace (<code>kubectl edit namespace banana</code>) so it would then have contents similar to</p>
<pre><code>apiVersion: v1
kind: Namespace
metadata:
&amp;nbsp;&amp;nbsp;name: banana
&amp;nbsp;&amp;nbsp;annotations:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;instrumentation.opentelemetry.io/inject-java: "banana-instr"
...
</code></pre>
<p>Now we have a namespace that is going to auto-instrument <em>every</em> Java application deployed in the <code>banana</code> namespace with the 2.5.0 <a href="https://github.com/open-telemetry/opentelemetry-java-instrumentation/">OpenTelemetry Java agent</a>!</p>
<h2 id="tryingitaidtryingita">Trying it<a id="trying-it"></a></h2>
<p>There is a simple example Java application at <a href="http://docker.elastic.co/demos/apm/k8s-webhook-test">docker.elastic.co/demos/apm/k8s-webhook-test</a> which just repeatedly calls the chain <code>main-&gt;methodA-&gt;methodB-&gt;methodC-&gt;methodD</code> with some sleeps in the calls. Running this (<code>kubectl apply -f banana-app.yaml</code>) using a very basic pod definition:</p>
<pre><code>apiVersion: v1
kind: Pod
metadata:
  name: banana-app
  namespace: banana
  labels:
    app: banana-app
spec:
  containers:
    - image: docker.elastic.co/demos/apm/k8s-webhook-test
      imagePullPolicy: Always
      name: banana-app
      env: 
      - name: OTEL_INSTRUMENTATION_METHODS_INCLUDE
        value: "test.Testing[methodB]"
</code></pre>
<p>results in the app being auto-instrumented with no configuration changes! The resulting app shows up in any APM UI, such as Elastic APM</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt5339364ee45875ef/6a7f1c0d3ce8e2a2e5cf57d0/elastic-apm-ui-transaction.png" alt="Elastic APM UI showing methodB traced" /></p>
<p>As you can see, for this example I also added this env var to the pod yaml, <code>OTEL_INSTRUMENTATION_METHODS_INCLUDE="test.Testing[methodB]"</code> so that there were traces showing from methodB.</p>
<h2 id="thetechnologybehindtheautoinstrumentationaidthetechnologybehindtheautoinstrumentationa">The technology behind the auto-instrumentation<a id="the-technology-behind-the-auto-instrumentation"></a></h2>
<p>To use the auto-instrumentation there is no specific need to understand the underlying mechanisms, but for those of you interested, here’s a quick outline. </p>
<ol>
<li>The <a href="https://github.com/open-telemetry/opentelemetry-operator/">OpenTelemetry Operator for Kubernetes</a> installs a <a href="https://kubernetes.io/docs/reference/access-authn-authz/admission-controllers/">mutating webhook</a>, a standard Kubernetes component.</li>
<li>When deploying, Kubernetes first sends all definitions to the mutating webhook.</li>
<li>If the mutating webhook sees that the conditions for auto-instrumentation should be applied (ie </li>
<li>there is an Instrumentation resource for that namespace and</li>
<li>the correct annotation for that Instrumentation is applied to the definition in some way, either from the definition itself or from the namespace),</li>
<li>then the mutating webhook “mutates” the definition to include the environment defined by the Instrumentation resource.</li>
<li>The environment includes the explicit values defined in the env, as well as some implicit OpenTelemetry values (see the <a href="https://github.com/open-telemetry/opentelemetry-operator/">OpenTelemetry Operator for Kubernetes</a> documentation for full details).</li>
<li>And most importantly, the operator</li>
<li>pulls the image defined in the Instrumentation resource,</li>
<li>extracts the file at the path <code>/javaagent.jar</code> from that image (using shell command <code>cp</code>)</li>
<li>inserts it into the pod at path <code>/otel-auto-instrumentation-java/javaagent.jar</code></li>
<li>and adds the environment variable <code>JAVA_TOOL_OPTIONS=-javaagent:/otel-auto-instrumentation-java/javaagent.jar</code>.</li>
<li>The JVM automatically picks up that JAVA_TOOL_OPTIONS environment variable on startup and applies it to the JVM command-line.</li>
</ol>
<h2 id="nextstepsaidnextstepsa">Next steps<a id="next-steps"></a></h2>
<p>This walkthrough can be repeated in any Kubernetes cluster to demonstrate and experiment with auto-instrumentation (you will need to create the banana namespace first). In part 2 of this two part series, <a href="https://www.elastic.co/observability-labs/blog/using-the-otel-operator-for-injecting-elastic-agents">Using a custom agent with the OpenTelemetry Operator for Kubernetes</a>, I show how to install any Java agent via the OpenTelemetry operator, using the Elastic Java agents as examples.</p>]]></content:encoded>
    <link>https://www.elastic.co/observability-labs/blog/using-the-otel-operator-for-injecting-java-agents</link>
    <guid isPermaLink="false">using-the-otel-operator-for-injecting-java-agents</guid>
    <category><![CDATA[OpenTelemetry]]></category>
    <category><![CDATA[APM]]></category>
    <category><![CDATA[Kubernetes]]></category>
    <category><![CDATA[Infrastructure Monitoring]]></category>
    <dc:creator><![CDATA[Jack Shirazi]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt53c963d5c03388fb/6a7f1c101967ea3597330ba4/blog-header.png" length="0" type="image/png"/>
    <pubDate>Thu, 11 Jul 2024 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Introducing Elastic Distribution for OpenTelemetry Python]]></title>
    <description><![CDATA[Announcing the first alpha release of the Elastic Distribution for OpenTelemetry Python. See how easy it is to instrument your Python applications with OpenTelemetry in this blog post.]]></description>
    <content:encoded><![CDATA[<p>We are delighted to announce the alpha release of the <a href="https://github.com/elastic/elastic-otel-python#readme">Elastic Distribution for OpenTelemetry Python</a>. This project is a customized OpenTelemetry distribution that allows us to configure better defaults for using OpenTelemetry with the Elastic cloud offering.</p>
<h2 id="background">Background</h2>
<p>Elastic is standardizing on OpenTelemetry (OTel) for observability and security data collection. As part of that effort, we are <a href="https://www.elastic.co/blog/elastic-opentelemetry-sdk-distributions">providing distributions of the OpenTelemetry Language SDKs</a>. We have recently released alpha distributions for <a href="https://github.com/elastic/elastic-otel-java#readme">Java</a>, <a href="https://github.com/elastic/elastic-otel-dotnet#readme">.NET</a> and <a href="https://github.com/elastic/elastic-otel-node#readme">Node.js</a>. Our <a href="https://github.com/elastic/apm-agent-android#readme">Android</a> and <a href="https://github.com/elastic/apm-agent-ios#readme">iOS</a> SDKs have been OpenTelemetry-based from the start. The Elastic Distribution for OpenTelemetry Python is the latest addition.</p>
<h2 id="designchoices">Design choices</h2>
<p>We have chosen to provide a lean distribution that does not install all the instrumentations by default but that instead provides tools
to do so. We leverage the <code>opentelemetry-bootstrap</code> tool provided by OpenTelemetry Python project to scan the packages installed in your
environment and recognizes libraries we are able to instrument.  This tool can just report the instrumentations available and optionally
is able to install them as well.
This allows you to avoid installing packages you are not going to need or instrument libraries you are not interested in tracing.</p>
<h2 id="gettingstarted">Getting started</h2>
<p>To get started with Elastic Distribution for OpenTelemetry Python you need to install  the package <code>elastic-opentelemetry</code> in your project
environment. We'll use <code>pip</code> in our examples but you are free to use any python package and environment manager of your choice.</p>
<pre><code>pip install elastic-opentelemetry
</code></pre>
<p>Once you have installed our distro you'll have also the <code>opentelemetry-bootstrap</code> command available. Running it:</p>
<pre><code>opentelemetry-bootstrap
</code></pre>
<p>will list all available packages for your instrumentation, e.g. you can expect something like the following:</p>
<pre><code>opentelemetry-instrumentation-asyncio==0.46b0
opentelemetry-instrumentation-dbapi==0.46b0
opentelemetry-instrumentation-logging==0.46b0
opentelemetry-instrumentation-sqlite3==0.46b0
opentelemetry-instrumentation-threading==0.46b0
opentelemetry-instrumentation-urllib==0.46b0
opentelemetry-instrumentation-wsgi==0.46b0
opentelemetry-instrumentation-grpc==0.46b0
opentelemetry-instrumentation-requests==0.46b0
opentelemetry-instrumentation-system-metrics==0.46b0
opentelemetry-instrumentation-urllib3==0.46b0
</code></pre>
<p>It also provides a command option to install the packages automatically</p>
<pre><code>opentelemetry-bootstrap --action=install
</code></pre>
<p>It is advised to run this command every time you release a new version of your application so that you can install or just revise any
instrumentation packages for your code.</p>
<p>Some environment variables are needed to provide the needed configuration for instrumenting your services. These mostly
concern the destination of your traces but also for easily identifying your service.
A <em>service name</em> is required to have your service distinguishable from the others. Then you need to provide
the <em>authorization</em> headers for authentication with Elastic Observability cloud and the Elastic cloud endpoint where the data is sent.</p>
<p>The API Key you get from your Elastic cloud serverless project must be <em>URL-encoded</em>, you can do that with the following Python snippet:</p>
<pre><code>from urllib.parse import quote
quote("ApiKey &lt;your api key&gt;)
</code></pre>
<p>Once you have all your configuration values you can export via environment variables as below:</p>
<pre><code>export OTEL_RESOURCE_ATTRIBUTES=service.name=&lt;service-name&gt;
export OTEL_EXPORTER_OTLP_HEADERS="Authorization=&lt;url encoded apikey header value&gt;"
export OTEL_EXPORTER_OTLP_ENDPOINT=&lt;your elastic cloud url&gt;
</code></pre>
<p>We are done with the configuration and the last piece of the puzzle is wrapping your service invocation with
<code>opentelemetry-instrument</code>, the wrapper that provides <em>zero-code instrumentation</em>. <em>Zero-code</em> (or Automatic) instrumentation means
that the distribution will set up the OpenTelemetry SDK and enable all the previously installed instrumentations for you.
Unfortunately <em>Zero-code</em> instrumentation does not cover all libraries and some — web frameworks in particular — will require minimal manual
configuration.</p>
<p>For a web service running with gunicorn it may look like:</p>
<pre><code>opentelemetry-instrument gunicorn main:app
</code></pre>
<p>The result is an observable application using the industry-standard <a href="https://opentelemetry.io/">OpenTelemetry</a> — offering high-quality instrumentation of many popular Python libraries, a portable API to avoid vendor lock-in and an active community.</p>
<p>Using Elastic Observability, some out-of-the-box benefits you can expect are: rich trace viewing, Service maps, integrated metrics and log analysis, and more.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltb4d06a40da2943f4/6a85cc2027c5cd6fab5f741a/traces-original.png" alt="trace sample screenshot" /></p>
<h2 id="whatsnext">What's next?</h2>
<p>Elastic is committed to helping OpenTelemetry succeed and to helping our customers use OpenTelemetry effectively in their systems. Last year, we <a href="https://opentelemetry.io/blog/2023/ecs-otel-semconv-convergence/">donated ECS</a> and continue to work on integrating it with OpenTelemetry Semantic Conventions. More recently, we are working on <a href="https://www.elastic.co/observability-labs/blog/elastic-profiling-agent-acceptance-opentelemetry">donating our eBPF-based profiler</a> to OpenTelemetry. We contribute to many of the language SDKs and other OpenTelemetry projects.</p>
<p>In the Python ecosystem we are active reviewers and contributors of both the <a href="https://github.com/open-telemetry/opentelemetry-python/">opentelemetry-python</a> and <a href="https://github.com/open-telemetry/opentelemetry-python-contrib/">opentelemetry-python-contrib</a> repositories.</p>
<p>The Elastic Distribution for OpenTelemetry Python is currently an alpha. Please <a href="https://github.com/elastic/elastic-otel-python/">try it out</a> and let us know if it might work for you. Watch for the <a href="https://github.com/elastic/elastic-otel-python/releases">latest releases here</a>. You can engage with us on <a href="https://github.com/elastic/elastic-otel-python/issues">the project issue tracker</a>.</p>
<p>We are eager to know your use cases to help you succeed in your Observability journey.</p>
<p><em>The release and timing of any features or functionality described in this post remain at Elastic's sole discretion. Any features or functionality not currently available may not be delivered on time or at all.</em></p>
<h2 id="resources">Resources</h2>
<ul>
<li>https://www.elastic.co/blog/elastic-opentelemetry-sdk-distributions</li>
<li>https://www.elastic.co/observability-labs/blog/elastic-distribution-opentelemetry-java-agent</li>
<li>https://www.elastic.co/observability-labs/blog/elastic-opentelemetry-distribution-dotnet-applications</li>
<li>https://www.elastic.co/observability-labs/blog/elastic-opentelemetry-distribution-node-js</li>
<li>https://www.elastic.co/observability-labs/blog/manual-instrumentation-python-apps-opentelemetry</li>
<li>https://www.elastic.co/observability-labs/blog/auto-instrumentation-python-applications-opentelemetry</li>
<li>https://www.elastic.co/observability-labs/blog/opentelemetry-observability</li>
</ul>]]></content:encoded>
    <link>https://www.elastic.co/observability-labs/blog/elastic-opentelemetry-distribution-python</link>
    <guid isPermaLink="false">elastic-opentelemetry-distribution-python</guid>
    <category><![CDATA[OpenTelemetry]]></category>
    <category><![CDATA[APM]]></category>
    <category><![CDATA[Infrastructure Monitoring]]></category>
    <dc:creator><![CDATA[Riccardo Magliocchetti]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt498330cde71bc9cd/6a85cc23331d7afa63c317bb/python.jpg" length="0" type="image/jpeg"/>
    <pubDate>Sun, 07 Jul 2024 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Combining Elastic Universal Profiling with Java APM Services and Traces]]></title>
    <description><![CDATA[Learn how to combine the power of Elastic universal profiling with APM data from Java services to easily pinpoint CPU bottlenecks. Compatible with both OpenTelemetry and the classic Elastic APM Agent.]]></description>
    <content:encoded><![CDATA[<p>In <a href="https://www.elastic.co/observability-labs/blog/continuous-profiling-distributed-tracing-correlation">a previous blog post</a>, we introduced the technical details of how we managed to correlate eBPF profiling data with APM traces.
This time, we'll show you how to get this feature up and running to pinpoint CPU bottlenecks in your Java services! The correlation is supported for both OpenTelemetry and the classic Elastic APM Agent. We'll show you how to enable it for both.</p>
<h2 id="demoapplication">Demo Application</h2>
<p>For this blog post, we’ll be using the <a href="https://github.com/JonasKunz/cpu-burner">cpu-burner demo application</a> to showcase the correlation capabilities of APM, tracing, and profiling in Elastic. This application was built to continuously execute several CPU-intensive tasks:</p>
<ul>
<li>It computes Fibonacci numbers using the naive, recursive algorithm.</li>
<li>It hashes random data with the SHA-2 and SHA-3 hashing algorithms.</li>
<li>It performs numerous large background allocations to stress the garbage collector.</li>
</ul>
<p>The computations of the Fibonacci numbers and the hashing will each be visible as transactions in Elastic: They have been manually instrumented using the OpenTelemetry API.</p>
<h2 id="settingupprofilingandapm">Setting up Profiling and APM</h2>
<p>First, we’ll need to set up the universal profiling host agent on the host where the demo application will run. Starting from version 8.14.0, correlation with APM data is supported and enabled out of the box for the profiler. There is no special configuration needed; we can just follow the <a href="https://www.elastic.co/guide/en/observability/current/profiling-get-started.html">standard setup guide</a>.
Note that at the time of writing, universal profiling only supports Linux.
On Windows, you'll have to use a VM to try the demo.
On macOS, you can use <a href="https://github.com/abiosoft/colima">colima</a> as docker engine and run the profiling host agent and the demo app in container images.</p>
<p>In addition, we’ll need to instrument our demo application with an APM agent. We can either use the <a href="https://github.com/elastic/apm-agent-java">classic Elastic APM agent</a> or the <a href="https://github.com/elastic/elastic-otel-java">Elastic OpenTelemetry Distribution</a>.</p>
<h3 id="usingtheclassicelasticapmagent">Using the Classic Elastic APM Agent</h3>
<p>Starting with version 1.50.0, the classic Elastic APM agent ships with the capability to correlate the traces it captures with the profiling data from universal profiling. We’ll just need to enable it explicitly via the <strong>universal_profiling_integration_enabled</strong> config option. Here is the standard command line for running the demo application with the setting enabled:</p>
<pre><code>curl -o 'elastic-apm-agent.jar' -L 'https://oss.sonatype.org/service/local/artifact/maven/redirect?r=releases&amp;g=co.elastic.apm&amp;a=elastic-apm-agent&amp;v=LATEST'
java -javaagent:elastic-apm-agent.jar \
-Delastic.apm.service_name=cpu-burner-elastic \
-Delastic.apm.secret_token=XXXXX \
-Delastic.apm.server_url=&lt;elastic-apm-server-endpoint&gt; \
-Delastic.apm.application_packages=co.elastic.demo \
-Delastic.apm.universal_profiling_integration_enabled=true \
-jar ./target/cpu-burner.jar
</code></pre>
<h3 id="usingopentelemetry">Using OpenTelemetry</h3>
<p>The feature is also available as an OpenTelemetry SDK extension.
This means you can use it as a plugin for the vanilla OpenTelemetry agent or add it to your OpenTelemetry SDK if you are not using an agent.
In addition, the feature ships by default with the Elastic OpenTelemetry Distribution for Java and can be used via any of the <a href="https://www.elastic.co/observability-labs/blog/elastic-distribution-opentelemetry-java-agent">possible usage methods</a>.
While the extension is currently Elastic-specific, we are already working with the various OpenTelemetry SIGs on standardizing the correlation mechanism, especially now after the <a href="https://www.elastic.co/observability-labs/blog/elastic-profiling-agent-acceptance-opentelemetry">eBPF profiling agent has been contributed</a>.</p>
<p>For this demo, we’ll be using the Elastic OpenTelemetry Distro Java agent to run the extension:</p>
<pre><code>curl -o 'elastic-otel-javaagent.jar' -L 'https://oss.sonatype.org/service/local/artifact/maven/redirect?r=releases&amp;g=co.elastic.otel&amp;a=elastic-otel-javaagent&amp;v=LATEST'
java -javaagent:./elastic-otel-javaagent.jar \
-Dotel.exporter.otlp.endpoint=&lt;elastic-cloud-OTLP-endpoint&gt; \
"-Dotel.exporter.otlp.headers=Authorization=Bearer XXXX" \
-Dotel.service.name=cpu-burner-otel \
-Delastic.otel.universal.profiling.integration.enabled=true \
-jar ./target/cpu-burner.jar
</code></pre>
<p>Here, we explicitly enabled the profiling integration feature via the <strong>elastic.otel.universal.profiling.integration.enabled</strong> property. Note that with an upcoming release of the universal profiling feature, this won’t be necessary anymore! The OpenTelemetry extension will then automatically detect the presence of the profiler and enable the correlation feature based on that.</p>
<p>The demo repository also comes with a Dockerfile, so you can alternatively build and run the app in docker:</p>
<pre><code>docker build -t cpu-burner .
docker run --rm -e OTEL_EXPORTER_OTLP_ENDPOINT=&lt;elastic-cloud-OTLP-endpoint&gt; -e OTEL_EXPORTER_OTLP_HEADERS="Authorization=Bearer XXXX" cpu-burner
</code></pre>
<p>And that’s it for setup; we are now ready to inspect the correlated profiling data!</p>
<h2 id="analyzingservicecpuusage">Analyzing Service CPU Usage</h2>
<p>The first thing we can do now is head to the “Flamegraph” view in Universal Profiling and inspect flamegraphs filtered on APM services. Without the APM correlation, universal profiling is limited to filtering on infrastructure concepts, such as hosts, containers, and processes.
Below is a screencast showing a flamegraph filtered on the service name of our demo application:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt7fc9f1694923ff35/6a7f1be296b5a666c387b8bd/service-profiling.gif" alt="Universal Profiling Flamegraph filtered on the service name of our demo application" /></p>
<p>With this filter applied, we get a flamegraph aggregated over all instances of our service. If that is not desired, we could narrow down the filter, e.g. based on the host or container names. Note that the same service-level flamegraph view is also available on the “Universal Profiling” tab in the APM service UI.</p>
<p>The flamegraphs show exactly how the demo application is spending its CPU time, independently of whether it is covered by instrumentation or not. From left to right, we can first see the time spent in application tasks: We can identify the background allocations not covered by APM transactions as well as the SHA-computation and Fibonacci transactions.
Interestingly, this application logic only covers roughly 60% of the total CPU time! The remaining time is spent mostly in the G1 garbage collector due to the high allocation rate of our application. The flamegraph shows all G1-related activities and the timing of the individual phases of concurrent tasks. We can easily identify those based on the native function names. This is made possible by universal profiling being capable of profiling and symbolizing the JVM’s C++ code in addition to the Java code.</p>
<h2 id="pinpointingtransactionbottlenecks">Pinpointing Transaction Bottlenecks</h2>
<p>While the service-level flamegraph already gives good insights on where our transactions consume the most CPU, this is mainly due to the simplicity of the demo application. In real-world applications, it can be much harder to pinpoint that certain stack frames come mostly from certain transactions. For this reason, the APM agent also correlates CPU profiling data from universal profiling on the transaction level.</p>
<p>We can navigate to the “Universal Profiling” tab on the transaction details page to get per-transaction flamegraphs:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blte8b420da4c4d739e/6a7f1be633fa8ae8a1202ba0/navigate-to-transaction-profiles.gif" alt="Navigation to per-transaction profiling flamegraphs" /></p>
<p>For example, let’s have a look at the flamegraph of our transaction computing SHA-2 and SHA-3 hashes of randomly generated data:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt2887385ca24655a5/6a7f1be96c6eac770cf145c5/tx-unfiltered.png" alt="Flamegraph for the hashing transaction" /></p>
<p>Interestingly, the flamegraph uncovers some unexpected results: The transactions spend more time computing the random bytes to be hashed rather than on the hashing itself! So if this were a real-world application, a possible optimization could be to use a more performant random number generator.</p>
<p>In addition, we can see that the MessageDigest.update call for computing the hash values fans out into two different code paths: One is a call into the <a href="https://www.bouncycastle.org/">BouncyCastle cryptography library</a>, the other one is a JVM stub routine, meaning that the JIT compiler has inserted special assembly code for a function.</p>
<p>The flamegraph shown in the screenshot displays the aggregated data for all “shaShenanigans” transactions in the given time filter. We can further filter this down using the transaction filter bar at the top. To make the best use of this, the demo application annotates the transactions with the hashing algorithm used via OpenTelemetry attributes:</p>
<pre><code>public static void shaShenanigans(MessageDigest digest) {
    Span span = tracer.spanBuilder("shaShenanigans")
        .setAttribute("algorithm", digest.getAlgorithm())
        .startSpan();
    ...
    span.end()
}
</code></pre>
<p>So, let’s filter our flamegraph based on the used hashing algorithm:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt2886c86a7b533c09/6a7f1bec1967ea42e1330b9e/tx-filter-bar.png" alt="Transaction Filter Bar" /></p>
<p>Note that “SHA-256” is the name of the JVM built-in SHA-2 256-bit implementation. This now gives the following flamegraph:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt1d5ae1b2fb5017f7/6a7f1bee3ce8e25d9ecf57c8/tx-sha-256.png" alt="Transaction Filter Bar" /></p>
<p>We can see that the BouncyCastle stack frames are gone and MessageDigest.update spends all its time in the JVM stub routines. Therefore, the stub routine is likely hand-crafted assembly from the JVM maintainers for the SHA2 algorithm.</p>
<p>If we instead filter on “SHA3-256”, we get the following result:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltb3d9edb17ea1ad0a/6a7f1bf1bd219839697584d5/tx-sha3.png" alt="Transaction Filter Bar" /></p>
<p>Now, as expected, MessageDigest.update spends all its time in the BouncyCastle library for the SHA3 implementation. Note that the hashing here takes up more time in relation to the random data generation, showing that the SHA2 JVM stub routine is significantly faster than the BouncyCastle Java SHA3 implementation.</p>
<p>This filtering is not limited to custom attributes like those shown in this demo. You can filter on any transaction attributes, including latency, HTTP headers, and so on. For example, for typical HTTP applications, it allows analyzing the efficiency of the used JSON serializer based on the payload size.
Note that while it is possible to filter on single transaction instances (e.g. based on trace.id), this is not recommended: To allow continuous profiling in production systems, the profiler by default runs with a low sampling rate of 20hz. This means that for typical real-world applications, this will not yield enough data when looking at a single transaction execution. Instead, we gain insights by monitoring multiple executions of a group of transactions over time and aggregating their samples, for example in a flamegraph.</p>
<h2 id="summary">Summary</h2>
<p>A common reason for applications to degrade is overly high CPU usage. In this blog post, we showed how to combine universal profiling with APM to find the actual root cause in such cases: We explained how to analyze the CPU time using profiling flamegraphs on service and transaction levels.
In addition, we further drilled down into data using custom filters.
We used a simple demo application for this purpose, so go ahead and try it yourself with your own, real-world applications to uncover the actual power of the feature!</p>]]></content:encoded>
    <link>https://www.elastic.co/observability-labs/blog/universal-profiling-with-java-apm-services-traces</link>
    <guid isPermaLink="false">universal-profiling-with-java-apm-services-traces</guid>
    <category><![CDATA[APM]]></category>
    <category><![CDATA[OpenTelemetry]]></category>
    <dc:creator><![CDATA[Jonas Kunz]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltc825e711e84ad7a0/6a7f1bf3bdcff00c0ec432d9/blog-header.jpg" length="0" type="image/jpeg"/>
    <pubDate>Thu, 20 Jun 2024 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Elastic contributes its Universal Profiling agent to OpenTelemetry]]></title>
    <description><![CDATA[Elastic is advancing the adoption of OpenTelemetry with the contribution of its universal profiling agent. Elastic is committed to ensuring a vendor-agnostic ingestion and collection of observability and security telemetry through OpenTelemetry.]]></description>
    <content:encoded><![CDATA[<p>Following great collaboration between Elastic and OpenTelemetry's profiling community, which included a thorough review process, the OpenTelemetry community has accepted Elastic's donation of our continuous profiling agent. This marks a significant milestone in helping establish profiling as the fourth telemetry signal in OpenTelemetry. Elastic’s eBPF-based continuous profiling agent observes code across different programming languages and runtimes, third-party libraries, kernel operations, and system resources with low CPU and memory overhead in production. SREs can now benefit from these capabilities: quickly identifying performance bottlenecks, maximizing resource utilization, reducing carbon footprint, and optimizing cloud spend.
Over the past year, we have been instrumental in <a href="https://opentelemetry.io/blog/2023/ecs-otel-semconv-convergence/">enhancing OpenTelemetry's Semantic Conventions</a> with the donation of Elastic Common Schema (ECS), contributing to the OpenTelemetry Collector and language SDKs, and have been working with OpenTelemetry’s Profiling Special Interest Group (SIG) to lay the foundation necessary to make profiling stable.</p>
<p>With today’s acceptance, we are officially contributing our continuous profiler technology to OpenTelemetry. We will also dedicate a team of profiling domain experts to co-maintain and advance the profiling capabilities within OTel.</p>
<p>We want to thank the OpenTelemetry community for the great and constructive cooperation on the donation proposal. We look forward to jointly establishing continuous profiling as an integral part of OpenTelemetry. </p>
<h2 id="whatiscontinuousprofiling">What is continuous profiling?</h2>
<p>Profiling is a technique used to understand the behavior of a software application by collecting information about its execution. This includes tracking the duration of function calls, memory usage, CPU usage, and other system resources. </p>
<p>However, traditional profiling solutions have significant drawbacks limiting adoption in production environments:</p>
<ul>
<li>Significant cost and performance overhead due to code instrumentation</li>
<li>Disruptive service restarts</li>
<li>Inability to get visibility into third-party libraries</li>
</ul>
<p>Unlike traditional profiling, which is often done only in a specific development phase or under controlled test conditions, continuous profiling runs in the background with minimal overhead. This provides real-time, actionable insights without replicating issues in separate environments. SREs, DevOps, and developers can see how code affects performance and cost, making code and infrastructure improvements easier.</p>
<h2 id="contributionofproductiongradefeatures">Contribution of production-grade features</h2>
<p>Elastic Universal Profiling is a whole-system, always-on, continuous profiling solution that eliminates the need for code instrumentation, recompilation, on-host debug symbols or service restarts. Leveraging eBPF, Elastic Universal Profiling profiles every line of code running on a machine, including application code, kernel, and third-party libraries. The solution measures code efficiency in three dimensions, CPU utilization, CO2, and cloud cost, to help organizations manage efficient services by minimizing computational waste.</p>
<p>The Elastic profiling agent facilitates identifying non-optimal code paths, uncovering "unknown unknowns", and provides comprehensive visibility into the runtime behavior of all applications. Elastic’s continuous profiling agent supports various runtimes and languages, such as C/C++, Rust, Zig, Go, Java, Python, Ruby, PHP, Node.js, V8, Perl, and .NET.</p>
<p>Additionally, organizations can meet sustainability objectives by minimizing computational wastage, ensuring seamless alignment with their strategic <a href="https://en.wikipedia.org/wiki/Environmental,_social,_and_corporate_governance">ESG</a> goals.</p>
<h2 id="benefitstoopentelemetry">Benefits to OpenTelemetry</h2>
<p>This contribution not only boosts the standardization of continuous profiling for observability but also accelerates the practical adoption of profiling as the fourth key signal in OTel. Customers get a vendor-agnostic way of collecting profiling data and enabling correlation with existing signals, like tracing, metrics, and logs, opening <a href="https://www.elastic.co/blog/continuous-profiling-distributed-tracing-correlation">new potential for observability insights and a more efficient troubleshooting experience</a>. </p>
<p>OTel-based continuous profiling unlocks the following possibilities for users:</p>
<ul>
<li><p>Improved customer experience: delivering consistent service quality and performance through continuous profiling ensures customers have an application that performs optimally, remains responsive, and is reliable.</p></li>
<li><p>Maximize gross margins: Businesses can optimize their cloud spend and improve profitability by reducing the computational resources needed to run applications. Whole system continuous profiling identifies the most expensive functions (down to the lines of code) across diverse environments that may span multiple cloud providers. In the cloud context, every CPU cycle saved translates to money saved. </p></li>
<li><p>Minimize environmental impact: energy consumption associated with computing is a growing concern (source: <a href="https://energy.mit.edu/news/energy-efficient-computing/">MIT Energy Initiative</a> ). More efficient code translates to lower energy consumption, reducing carbon (CO2) footprint. </p></li>
<li><p>Accelerate engineering workflows: continuous profiling provides detailed insights to help troubleshoot complex issues faster, guide development, and improve overall code quality.</p></li>
<li><p>Improved vendor neutrality and increased efficiency: an OTel eBPF-based profiling agent removes the need to use proprietary APM agents and offers a more efficient way to collect profiling telemetry.</p></li>
</ul>
<p>With these benefits, customers can now manage the overall application’s efficiency on the cloud while ensuring their engineering teams optimize it.</p>
<h2 id="whatcomesnext">What comes next?</h2>
<p>While the acceptance of Elastic’s donation of the profiling agent marks a significant milestone in the evolution of OTel’s eBPF-based continuous profiling capabilities, it represents the beginning of a broader journey. Moving forward, we will continue collaborating closely with the OTel Profiling and Collector SIGs to ensure seamless integration of the profiling agent within the broader OTel ecosystem. During this phase, users can test early preview versions of the OTel profiling integration by following the directions in the <a href="https://github.com/elastic/otel-profiling-agent/">otel-profiling-agent</a> repository.</p>
<p>Elastic remains deeply committed to OTel’s vision of enabling cross-signal correlation. We plan to further contribute to the community by sharing our innovative research and implementations, specifically those facilitating the correlation between profiling data and distributed traces, across several OTel language SDKs and the profiling agent.</p>
<p>We are excited about our <a href="https://opentelemetry.io/blog/2023/ecs-otel-semconv-convergence/">growing relationship with OTel</a> and the opportunity to donate our profiling agent in a way that benefits both the Elastic community and the broader OTel community. Learn more about <a href="https://www.elastic.co/observability/opentelemetry">Elastic’s OpenTelemetry support</a> and learn how to contribute to the ongoing profiling work in the community.</p>
<h2 id="additionalresources">Additional Resources</h2>
<p>Additional details on Elastic’s Universal Profiling can be found in the <a href="https://www.elastic.co/observability-labs/blog/elastic-profiling-agent-acceptance-opentelemetry-faq">FAQ</a>. </p>
<p>For insights into observability, visit Observability labs where OTel specific articles are also available.</p>]]></content:encoded>
    <link>https://www.elastic.co/observability-labs/blog/elastic-profiling-agent-acceptance-opentelemetry</link>
    <guid isPermaLink="false">elastic-profiling-agent-acceptance-opentelemetry</guid>
    <category><![CDATA[OpenTelemetry]]></category>
    <category><![CDATA[APM]]></category>
    <dc:creator><![CDATA[Christos Kalkanis,Alexander Wert,Abhishek Singh]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltd8cc7c3e97bcb500/6a8408894423e173149fc5f1/profiling-acceptance.png" length="0" type="image/png"/>
    <pubDate>Thu, 06 Jun 2024 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[FAQ - Elastic contributes its Universal Profiling agent to OpenTelemetry]]></title>
    <description><![CDATA[Elastic is advancing the adoption of OpenTelemetry with the contribution of its universal profiling agent. Elastic is committed to ensuring a vendor-agnostic ingestion and collection of observability and security telemetry through OpenTelemetry.]]></description>
    <content:encoded><![CDATA[<h2 id="whatisbeingannounced">What is being announced?</h2>
<p>Elastic’s <a href="https://github.com/open-telemetry/community/issues/1918">donation proposal</a> for contributing its Universal Profiling™ agent has now been accepted by the OpenTelemetry community. Elastic’s Universal Profiling agent, the industry’s most comprehensive fleet-wide Universal Profiling solution, empowers users to quickly identify performance bottlenecks, reduce cloud spend, and minimize their carbon footprint. With the contribution of the Elastic Universal Profiling Agent to OpenTelemetry, all customers will benefit from its features and capabilities.</p>
<h2 id="whatdoelasticusersneedtoknow">What do Elastic users need to know?</h2>
<p>Elastic’s contribution of the continuous profiling agent will not change the existing set of Elastic’s continuous profiling features or how we ingest and store profiling data. </p>
<p>Elastic will participate and closely collaborate with the OTel community to manage not only the addition of the continuous profiling agent to OTel but also work with and drive the OTel community’s Profiling Special Interest Group (SIG) in shaping OTel’s continuous profiling evolution. </p>
<p>Elastic has facilitated the definition of the OTel <a href="https://github.com/open-telemetry/oteps/blob/main/text/profiles/0239-profiles-data-model.md">Profiling Data Model</a>, a crucial step toward standardizing profiling data. Moreover, the recent merge of the <a href="https://github.com/open-telemetry/oteps/pull/239">OpenTelemetry Enhancement Proposal (OTEP) introducing profiling support to the OpenTelemetry Protocol (OTLP)</a> marked an additional milestone. </p>
<h2 id="whyiselasticcontributingitsprofilingagenttootel">Why is Elastic contributing its Profiling Agent to OTel?</h2>
<p>This contribution not only accelerates the standardization of continuous profiling but also makes continuous profiling the 4th key signal in observability. This empowers everyone in the observability community to continuously profile with a standardized agent. The addition of Elastic’s continuous profiling agent will:</p>
<ul>
<li><p>Align efforts around a single standard poised for broad adoption by users.</p></li>
<li><p>Drive better visibility and improvement of resource usage and cost management for operations.</p></li>
<li><p>Enable vendors and the community to focus on richer features versus dealing with data transformation tasks.</p></li>
<li><p>Enable continuous profiling to become the 4th key signal in Observability.</p></li>
<li><p>Increase continuous profiling adoption and the continued evolution and convergence of observability and security domains.</p></li>
</ul>
<h2 id="whyiscontinuousprofilingneededbyorganizations">Why is continuous profiling needed by organizations?</h2>
<p>The contribution of Elastic’s continuous profiling agent now helps customers realize the following benefits of continuous profiling:</p>
<ul>
<li><p>Maximize gross margins: By reducing the computational resources needed to run applications, businesses can optimize their cloud spend and improve profitability. Whole-system continuous profiling is one way of identifying the most expensive applications (down to the lines of code) across diverse environments that may span multiple cloud providers. This principle aligns with the familiar adage, "A penny saved is a penny earned." In the cloud context, every CPU cycle saved translates to money saved. </p></li>
<li><p>Minimize environmental impact: Energy consumption associated with computing is a growing concern (source: <a href="https://energy.mit.edu/news/energy-efficient-computing/">MIT Energy Initiative</a>). More efficient code translates to lower energy consumption, contributing to a reduction in carbon (CO2) footprint. </p></li>
<li><p>Accelerate engineering workflows: Continuous profiling provides detailed insights to help debug complex issues faster, guide development, and improve overall code quality.</p></li>
</ul>
<p>With these benefits, customers can now not only manage the overall application’s efficiency on the cloud, but also ensure the application is optimally developed.</p>
<h2 id="whatiscontinuousprofiling">What is continuous profiling?</h2>
<p>Elastic’s continuous profiling agent is a whole-system, always-on, continuous profiling solution that eliminates the need for run-time/bytecode instrumentation, recompilation, on-host debug symbols or service restarts.   </p>
<p>Profiling helps organizations run efficient services by minimizing computational wastage, thereby reducing operational costs. Leveraging <a href="https://ebpf.io/">eBPF</a>, the Elastic profiling agent provides unprecedented visibility into the runtime behavior of all applications: it builds stack traces that go from the kernel, through userspace native code, all the way into code running in higher level runtimes, enabling you to identify performance regressions, reduce wasteful computations, and debug complex issues faster. </p>
<p>To this end, it measures code efficiency in three dimensions: CPU utilization, CO2, and cloud cost. This approach resonates with the sustainability objectives of our customers –– ensuring that Elastic continuous profiling aligns seamlessly with their strategic <a href="https://en.wikipedia.org/wiki/Environmental,_social,_and_corporate_governance">ESG</a> goals</p>
<h2 id="doeselasticsupportopentelemetrytoday">Does Elastic support OpenTelemetry today?</h2>
<p><a href="https://www.elastic.co/observability/opentelemetry">Elastic supports OTel natively</a>. Elastic users can send OTel data directly from applications or through the OTel collector into Elastic APM, which processes both OTel SemConv and ECS. With this native OTel support, all <a href="https://www.elastic.co/observability/application-performance-monitoring">Elastic APM capabilities</a> are available with OTel. <a href="https://www.elastic.co/guide/en/apm/guide/current/open-telemetry.html">See Elastic documentation to learn more about OTel integration</a>.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt30a50ab674e5130c/6a7f0906c2cc09d2202493c8/blog-elastic-otel-2.png" alt="Native OpenTelemetry Support in Elastic" /></p>
<h2 id="wherecanilearnmoreaboutelasticsuniversalprofiling">Where can I learn more about Elastic’s Universal Profiling?</h2>
<p>Elastic’s resources help you understand continuous profiling and how to use it in different scenarios:</p>
<hr />
<ul>
<li><p><a href="https://www.elastic.co/observability/universal-profiling">Elastic Universal Profiling home page</a></p></li>
<li><p><a href="https://www.elastic.co/blog/elastic-universal-profiling-agent-open-source">Elastic Universal Profiling agent going open source under Apache 2</a></p></li>
<li><p><a href="https://www.elastic.co/blog/continuous-profiling-distributed-tracing-correlation">Pinpointing performance issues with profiling</a></p></li>
<li><p><a href="https://www.elastic.co/blog/continuous-profiling-is-generally-available">Elastic releases Universal Profiling</a></p></li>
<li><p><a href="https://www.elastic.co/blog/whole-system-visibility-elastic-universal-profiling">Whole system profiling with Universal Profiling</a></p></li>
<li><p><a href="https://www.elastic.co/blog/continuous-profiling-efficient-cost-effective-applications">Cost-effective applications with Universal Profiling</a></p></li>
<li><p><a href="https://www.elastic.co/guide/en/observability/current/universal-profiling.html">Elastic documentation on Universal Profiling</a></p></li>
</ul>]]></content:encoded>
    <link>https://www.elastic.co/observability-labs/blog/elastic-profiling-agent-acceptance-opentelemetry-faq</link>
    <guid isPermaLink="false">elastic-profiling-agent-acceptance-opentelemetry-faq</guid>
    <category><![CDATA[OpenTelemetry]]></category>
    <category><![CDATA[APM]]></category>
    <dc:creator><![CDATA[Elastic Observability Team]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt6fd9c621644659fd/6a7f090be3a219742d99f2fd/profiling-acceptance-faq.png" length="0" type="image/png"/>
    <pubDate>Thu, 06 Jun 2024 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Revealing unknowns in your tracing data with inferred spans in OpenTelemetry]]></title>
    <description><![CDATA[Distributed tracing is essential in understanding complex systems, but it can miss latency issue details. By combining profiling techniques with distributed tracing, Elastic provides the inferred spans feature as an extension for the OTel Java SDK.]]></description>
    <content:encoded><![CDATA[<p>In the complex world of microservices and distributed systems, achieving transparency and understanding the intricacies and inefficiencies of service interactions and request flows has become a paramount challenge. Distributed tracing is essential in understanding distributed systems. But distributed tracing, whether manually applied or auto-instrumented, is usually rather coarse-grained. Hence, distributed tracing covers only a limited fraction of the system and can easily miss parts of the system that are the most useful to trace.</p>
<p>Addressing this gap, Elastic developed the concept of inferred spans as a powerful enhancement to traditional instrumentation-based tracing as an extension for the OpenTelemetry Java SDK/Agent. We are in the process of contributing this back to OpenTelemetry, until then our <a href="https://github.com/elastic/elastic-otel-java/tree/main/inferred-spans">extension</a> can be seamlessly used with the existing OpenTelelemetry Java SDK (as described below).</p>
<p>Inferred spans are designed to augment the visibility provided by instrumentation-based traces, shedding light on latency sources within the application or libraries that were previously uninstrumented. This feature significantly expands the utility of distributed tracing, allowing for a more comprehensive understanding of system behavior and facilitating a deeper dive into performance optimization.</p>
<h2 id="whatisinferredspans">What is inferred spans?</h2>
<p>Inferred spans is an observability technique that combines distributed tracing with profiling techniques to illuminate the darker, unobserved corners of your application — areas where standard instrumentation techniques fall short. The inferred spans feature interweaves information derived from profiling stacktraces with instrumentation-based tracing data, allowing for the generation of new spans based on the insights drawn from profiling data.</p>
<p>This feature proves invaluable when dealing with custom code or third-party libraries that significantly contribute to the request latency but lack built-in or external instrumentation support. Often, identifying or crafting specific instrumentation for these segments can range from challenging to outright unfeasible. Moreover, certain scenarios exist where implementing instrumentation is impractical due to the potential for substantial performance overhead. For instance, instrumenting application locking mechanisms, despite their critical role, is not viable because of their ubiquitous nature and the significant latency overhead the instrumentation can introduce to application requests. Still, ideally, such latency issues would be visible within your distributed traces.</p>
<p>Inferred spans ensures a deeper visibility into your application’s performance dynamics including the above-mentioned scenarios.</p>
<h2 id="inferredspansinaction">Inferred spans in action</h2>
<p>To demonstrate the inferred spans feature we will use the Java implementation of the <a href="https://github.com/elastic/observability-examples/tree/main/Elastiflix/java-favorite">Elastiflix demo application</a>. Elasticflix has an endpoint called favorites that does some Redis calls and also includes an artificial delay. First, we use the plain OpenTelemetry Java Agent to instrument our application:</p>
<pre><code>java -javaagent:/path/to/otel-javaagent-&lt;version&gt;.jar \
-Dotel.service.name=my-service-name \
-Dotel.exporter.otlp.endpoint=https://&lt;our-elastic-apm-endpoint&gt; \
"-Dotel.exporter.otlp.headers=Authorization=Bearer SECRETTOKENHERE" \
-jar my-service-name.jar
</code></pre>
<p>With the OpenTelemetry Java Agent we get out-of-the-box instrumentation for HTTP entry points and calls to Redis for our Elastiflix application. The resulting traces contain spans for the POST /favorites entrypoint, as well as a few short spans for the calls to Redis.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt68a2ac377960ee63/6a7f1b9deab5be0d5220ab16/image2.png" alt="POST /favorites entrypoint" /></p>
<p>As you can see in the trace above, it’s not clear where most of the time is spent within the POST /favorites request.</p>
<p>Let’s see how inferred spans can shed light into these areas. You can use the inferred spans feature either manually with your OpenTelemetry SDK (see section below), package it as a drop-in extension for the upstream OpenTelemetry Java agent, or just use <a href="https://github.com/elastic/elastic-otel-java/tree/main">Elastic’s distribution of the OpenTelemetry Java agent</a> that comes with the inferred spans feature.</p>
<p>For convenience, we just download the <a href="https://mvnrepository.com/artifact/co.elastic.otel/elastic-otel-javaagent/0.0.1">agent jar</a> of the Elastic distribution and extend the configuration to enable the inferred spans feature:</p>
<pre><code>java -javaagent:/path/to/elastic-otel-javaagent-&lt;version&gt;.jar \
-Dotel.service.name=my-service-name \
-Dotel.exporter.otlp.endpoint=https://XX.apm.europe-west3.gcp.cloud.es.io:443 \
"-Dotel.exporter.otlp.headers=Authorization=Bearer SECRETTOKENHERE" \
-Delastic.otel.inferred.spans.enabled=true \
-jar my-service-name.jar
</code></pre>
<p>The only non-standard option here is elastic.otel.inferred.spans.enabled: The inferred spans Feature is currently opt-in and therefore needs to be enabled explicitly. Running the same application with the inferred spans feature enabled yields more comprehensive traces:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt1288446f29c1e24e/6a7f1ba096b5a6727c87b8b5/image1.png" alt="more comprehensive traces" /></p>
<p>The inferred-spans (colored blue in the above screenshot) follow the naming pattern Class#method. With that, the inferred spans feature helps us pinpoint the exact methods that contribute the most to the overall latency of the request. Note that the parent-child relationship between the HTTP entry span, the Redis spans, and the inferred spans is reconstructed correctly, resulting in a fully functional trace structure.</p>
<p>Examining the handleDelay method within the Elastiflix application reveals the use of a straightforward sleep statement. Although the sleep method is not CPU-bound, the full duration of this delay is captured as inferred spans. This stems from employing the async-profiler's wall clock time profiling, as opposed to solely relying on CPU profiling. The ability of the inferred spans feature to reflect actual latency, including for I/O operations and other non-CPU-bound tasks, represents a significant advancement. It allows for diagnosing and resolving performance issues that extend beyond CPU limitations, offering a more nuanced view of system behavior.</p>
<h2 id="usinginferredspanswithyourownopentelemetrysdk">Using inferred spans with your own OpenTelemetry SDK</h2>
<p>OpenTelemetry is a highly extensible framework: Elastic embraces this extensibility by also publishing most extensions shipped with our OpenTelemetry Java Distro as standalone-extensions to the <a href="https://github.com/open-telemetry/opentelemetry-java">OpenTelemetry Java SDK</a>.</p>
<p>As a result, if you do not want to use our distro (e.g., because you don’t need or want bytecode instrumentation in your project), you can still use our extensions, such as the extension for the inferred spans feature. All you need to do is set up the <a href="https://opentelemetry.io/docs/languages/java/instrumentation/#initialize-the-sdk">OpenTelemetry SDK in your code</a> and add the inferred spans extension as a dependency:</p>
<pre><code>&lt;dependency&gt;
    &lt;groupId&gt;co.elastic.otel&lt;/groupId&gt;
    &lt;artifactId&gt;inferred-spans&lt;/artifactId&gt;
    &lt;version&gt;{latest version}&lt;/version&gt;
&lt;/dependency&gt;
</code></pre>
<p>During your SDK setup, you’ll have to initialize and register the extension:</p>
<pre><code>InferredSpansProcessor inferredSpans = InferredSpansProcessor.builder()
  .samplingInterval(Duration.ofMillis(10)) //the builder offers all config options
  .build();
SdkTracerProvider tracerProvider = SdkTracerProvider.builder()
  .addSpanProcessor(inferredSpans)
.addSpanProcessor(BatchSpanProcessor.builder(OtlpGrpcSpanExporter.builder()
    .setEndpoint("https://&lt;your-elastic-apm-endpoint&gt;")
    .addHeader("Authorization", "Bearer &lt;secrettoken&gt;")
    .build()).build())
  .build();
inferredSpans.setTracerProvider(tracerProvider);
</code></pre>
<p>The inferred spans extension seamlessly integrates with the <a href="https://opentelemetry.io/docs/languages/java/instrumentation/#automatic-configuration">OpenTelemetry SDK Autoconfiguration mechanism</a>. By incorporating the OpenTelemetry SDK and its extensions as dependencies within your application code — rather than through an external agent — you gain the flexibility to configure them using the same environment variables or JVM properties. Once the inferred spans extension is included in your classpath, activating it for autoconfigured SDKs becomes straightforward. Simply enable it using the elastic.otel.inferred.spans.enabled property, as previously described, to leverage the full capabilities of this feature with minimal setup.</p>
<h2 id="howdoesinferredspanswork">How does inferred spans work?</h2>
<p>The inferred spans feature leverages the capabilities of collecting wall clock time profiling data of the widely-used <a href="https://github.com/async-profiler/async-profiler">async-profiler</a>, a low-overhead, popular production-time profiler in the Java ecosystem. It then transforms the profiling data into actionable spans as part of the distributed traces. But what mechanism allows for this transformation?</p>
<p>Essentially, the inferred spans extension engages with the lifecycle of span events, specifically when a span is either activated or deactivated across any thread via the <a href="https://opentelemetry.io/docs/specs/otel/context/">OpenTelemetry context</a>. Upon the activation of the initial span within a transaction, the extension commences a session of wall-clock profiling via the async-profiler, set to a predetermined duration. Concurrently, it logs the details of all span activations and deactivations, capturing their respective timestamps and the threads on which they occurred.</p>
<p>Following the completion of the profiling session, the extension processes the profiling data alongside the log of span events. By correlating the data, it reconstructs the inferred spans. It's important to note that, in certain complex scenarios, the correlation may assign an incorrect name to a span. To mitigate this and aid in accurate identification, the extension enriches the inferred spans with stacktrace segments under the code.stacktrace attribute, offering users clarity and insight into the precise methods implicated.</p>
<h2 id="inferredspansvscorrelationoftraceswithprofilingdata">Inferred spans vs. correlation of traces with profiling data</h2>
<p>In the wake of OpenTelemetry's recent <a href="https://opentelemetry.io/blog/2024/profiling/">announcement of the profiling signal</a>, coupled with <a href="https://www.elastic.co/blog/elastic-donation-proposal-to-contribute-profiling-agent-to-opentelemetry">Elastic's commitment to donating the Universal Profiling Agent</a> to OpenTelemetry, you might be wondering about how the inferred spans feature differentiates from merely correlating profiling data with distributed traces using span IDs and trace IDs. Rather than viewing these as competing functionalities, it's more accurate to consider them complementary.</p>
<p>The inferred spans feature and the correlation of tracing with profiling data both employ similar methodologies — melding tracing information with profiling data. However, they each shine in distinct areas. Inferred spans excels at identifying long-running methods that could escape notice with traditional CPU profiling, which is more adept at pinpointing CPU bottlenecks. A unique advantage of inferred spans is its ability to account for I/O time, capturing delays caused by operations like disk access that wouldn't typically be visible in CPU profiling flamegraphs.</p>
<p>However, the inferred spans feature has its limitations, notably in detecting latency issues arising from "death by a thousand cuts" — where a method, although not time-consuming per invocation, significantly impacts total latency due to being called numerous times across a request. While individual calls might not be captured as inferred spans due to their brevity, CPU-bound methods contributing to latency are unveiled through CPU profiling, as flamegraphs display the aggregate CPU time consumed by these methods.</p>
<p>An additional strength of the inferred spans feature lies in its data structure, offering a simplified tracing model that outlines typical parent-child relationships, execution order, and good latency estimates. This structure is achieved by integrating tracing data with span activation/deactivation events and profiling data, facilitating straightforward navigation and troubleshooting of latency issues within individual traces.</p>
<p>Correlating distributed tracing data with profiling data comes with a different set of advantages. Learn more about it in our related blog post, <a href="https://www.elastic.co/blog/continuous-profiling-distributed-tracing-correlation">Beyond the trace: Pinpointing performance culprits with continuous profiling and distributed tracing correlation</a>.</p>
<h2 id="whatabouttheperformanceoverhead">What about the performance overhead?</h2>
<p>As mentioned before, the inferred spans functionality is based on the widely used async-profiler, known for its minimal impact on performance. However, the efficiency of profiling operations is not without its caveats, largely influenced by the specific configurations employed. A pivotal factor in this balancing act is the sampling interval — the longer the interval between samples, the lower the incurred overhead, albeit at the expense of potentially overlooking shorter methods that could be critical to the inferred spans feature discovery process.</p>
<p>Adjusting the probability-based trace sampling presents another way for optimization, directly influencing the overhead. For instance, setting trace sampling to 50% effectively halves the profiling load, making the inferred spans feature even more resource-efficient on average per request. This nuanced approach to tuning ensures that the inferred spans feature can be leveraged in real-world, production environments with a manageable performance footprint. When properly configured, this feature offers a potent, low-overhead solution for enhancing observability and diagnostic capabilities within production applications.</p>
<h2 id="whatsnextforinferredspansandopentelemetry">What’s next for inferred spans and OpenTelemetry?</h2>
<p>This blog post outlined and introduced the inferred spans feature available as an extension for the OpenTelemetry Java SDK and built into the newly introduced Elastic OpenTelemetry Java Distro. Inferred spans allows users to troubleshoot latency issues in areas of code that are not explicitly instrumented while utilizing traditional tracing data.</p>
<p>The feature is currently merely a port of the existing feature from the proprietary Elastic APM Agent. With Elastic embracing OpenTelemetry, we plan on contributing this extension to the upstream OpenTelemetry project. For that, we also plan on migrating the extension to the latest async-profiler 3.x release. <a href="https://github.com/elastic/elastic-otel-java/tree/main/inferred-spans">Try out inferred spans for yourself</a> and see how it can help you diagnose performance problems in your applications.</p>
<p><em>The release and timing of any features or functionality described in this post remain at Elastic's sole discretion. Any features or functionality not currently available may not be delivered on time or at all.</em></p>]]></content:encoded>
    <link>https://www.elastic.co/observability-labs/blog/tracing-data-inferred-spans-opentelemetry</link>
    <guid isPermaLink="false">tracing-data-inferred-spans-opentelemetry</guid>
    <category><![CDATA[OpenTelemetry]]></category>
    <category><![CDATA[APM]]></category>
    <dc:creator><![CDATA[Jonas Kunz,Alexander Wert]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt97993d2b178280f9/6a7f1ba2bdcff03963c432c7/148360-Blog-header-image--Revealing-Unknowns-in-your-Tracing-Data-with-Inferred-Spans-in-OpenTelemetry_V1.jpg" length="0" type="image/jpeg"/>
    <pubDate>Mon, 22 Apr 2024 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Elastic Universal Profiling agent, a continuous profiling solution, is now open source]]></title>
    <description><![CDATA[At Elastic, open source isn't just philosophy, it's our DNA. Dive into the future with our open-sourced Universal Profiling agent, revolutionizing software efficiency and sustainability.]]></description>
    <content:encoded><![CDATA[<p>Elastic Universal Profiling™ agent is now open source! The industry’s most advanced fleetwide continuous profiling solution empowers users to identify performance bottlenecks, reduce cloud spend, and minimize their carbon footprint. This post explores the history of the agent, its move to open source, and its future integration with OpenTelemetry.</p>
<h2 id="elasticuniversalprofilingagentgoesopensourceunderapache2">Elastic Universal Profiling™ Agent goes open source under Apache 2</h2>
<p>At Elastic, open source is more than just a philosophy — it's our DNA. We believe the benefits of whole-system continuous profiling extend far beyond performance optimization. It's a win for businesses and the planet alike. For instance, since launching Elastic Universal Profiling in general availability (GA), we've observed a wide variety of use cases from customers.</p>
<p>These range from customers relying fully on Universal Profiling's <a href="https://www.elastic.co/guide/en/observability/current/universal-profiling.html#profiling-differential-views-intro">differential flame graphs and topN functions</a> for insights during release management to utilizing AI assistants for quickly optimizing expensive functions. This includes using profiling data to identify the optimal energy-efficient cloud region to run certain workloads. Additionally, customers are using insights that Universal Profiling provides to build evidence to challenge cloud provider bills. As it turns out, cloud providers' in-VM agents can consume a significant portion of the CPU time, which customers are billed for.</p>
<p>In a move that will empower the community to take advantage of continuous profiling's benefits, <strong>we're thrilled to announce that the Elastic Universal Profiling agent</strong> , a pioneering eBPF-based continuous profiling agent, <strong>is now open source under the Apache 2 license!</strong></p>
<p>This move democratizes <strong>hyper-scaler efficiency for everyone</strong> , opening exciting new possibilities for the future of continuous profiling, as well as its role in observability and <strong>OpenTelemetry</strong>.</p>
<h2 id="implementationoftheopentelemetryotelprofilingprotocol">Implementation of the OpenTelemetry (OTel) Profiling protocol</h2>
<p>Our commitment to open source goes beyond just the agent itself. We recently <a href="https://www.elastic.co/blog/elastic-donation-proposal-to-contribute-profiling-agent-to-opentelemetry">announced our intent to donate</a> the agent to OpenTelemetry and have further solidified this goal by implementing the experimental <a href="https://github.com/open-telemetry/oteps/blob/main/text/profiles/0239-profiles-data-model.md">OTel Profiling data model</a>. This allows the open-sourced eBPF-based continuous profiling agent to communicate seamlessly with OpenTelemetry backends.</p>
<p>But that's not all! We've also launched an innovative feature that <a href="https://www.elastic.co/blog/continuous-profiling-distributed-tracing-correlation">correlates profiling data with OpenTelemetry distributed traces</a>. This powerful capability offers a deeper level of insight into application performance, enabling the identification of bottlenecks with greater precision. Upon donating the Profiling agent to OTel, Elastic will also contribute critical components that enable distributed trace correlation within the <a href="https://github.com/elastic/elastic-otel-java">Elastic distribution of the OTel Java agent</a> to the upstream OTel Java SDK. This underscores Elastic Observability's commitment to both open source and the support of open standards like OpenTelemetry while pushing the boundaries of what is possible in observability.</p>
<h2 id="whatdoesthismeanforelasticuniversalprofilingcustomers">What does this mean for Elastic Universal Profiling customers?</h2>
<p>We'd like to express our <strong>immense gratitude to all our customers</strong> who have been part of this journey, from the early stages of private beta to GA. Your feedback has been invaluable in shaping Universal Profiling into the powerful product it is today.</p>
<p>By open-sourcing the Universal Profiling agent and contributing it to OpenTelemetry, we're fostering a win-win situation for both you and the broader community. This move opens doors for innovation and collaboration, ultimately leading to a more robust and versatile whole-system continuous profiling solution for everyone.</p>
<p>Furthermore, we're actively working on exciting novel ways to integrate Universal Profiling seamlessly within Elastic Observability. Expect further announcements soon, outlining how you can unlock even greater value from your profiling data within a unified observability experience in a way that has never been done before.</p>
<p>The open-sourced agent is using the recently released (experimental) OTel Profiling <a href="https://github.com/open-telemetry/opentelemetry-proto/pull/534">signal</a>. As a precaution, we recommend not using it in production environments.</p>
<p>Please continue using the official Elastic distribution of the Universal Profiling agent until the agent is formally accepted by OTel and the protocol reaches a stable phase. There's no need to take any action at this time, and we will ensure to have a smooth transition plan in place for you.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt5ac093f76868020d/6a7f099e9090b082d884e897/image1.png" alt="1 - Elastic Universal Profiling" /></p>
<h2 id="whatdoesthismeanfortheopentelemetrycommunity">What does this mean for the OpenTelemetry community?</h2>
<p>OpenTelemetry is adopting continuous profiling as a key signal. By open-sourcing the eBPF-based profiling agent and working towards donating it to OTel, Elastic is making it possible to accelerate the standardization of continuous profiling within OpenTelemetry. This move has a massive impact on the observability community, empowering everyone to continuously profile their systems with a standardized protocol.</p>
<p>This is particularly timely as <a href="https://www.bbc.co.uk/news/technology-32335003">Moore's Law</a> slows down and cloud computing takes hold, making computational efficiency critical for businesses.</p>
<p>Here's how whole-system continuous profiling benefits you:</p>
<ul>
<li><p><strong>Maximize gross margins:</strong> By reducing the computational resources needed to run applications, businesses can optimize their cloud spend and improve profitability. Whole-system continuous profiling is one way of identifying the most expensive applications (down to the lines of code) across diverse environments that may span multiple cloud providers. This principle aligns with the familiar adage, <em>"a penny saved is a penny earned."</em> In the cloud context, every CPU cycle saved translates to money saved. </p></li>
<li><p><strong>Minimize environmental impact:</strong> Energy consumption associated with computing is a growing concern (source: <a href="https://energy.mit.edu/news/energy-efficient-computing/">MIT Energy Initiative</a>). More efficient code translates to lower energy consumption, contributing to a reduction in carbon footprint. </p></li>
<li><p><strong>Accelerate engineering workflows:</strong> Continuous profiling provides detailed insights to help debug complex issues faster, guide development, and improve overall code quality.</p></li>
</ul>
<p>This is where Elastic Universal Profiling comes in — designed to help organizations run efficient services by minimizing computational wastage. To this end, it measures code efficiency in three dimensions: <strong>CPU utilization</strong> , <strong>CO</strong>** 2 <strong>, and</strong> cloud cost**.</p>
<p>Elastic's journey with continuous profiling began by joining forces with <a href="https://www.elastic.co/about/press/elastic-and-optimyze-join-forces-to-deliver-continuous-profiling-of-infrastructure-applications-and-services">optimyze.cloud</a> –– this became the foundation for <a href="https://www.elastic.co/observability/universal-profiling">Elastic Universal Profiling</a>. We are excited to see this product evolve into its next growth phase in the open-source world.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt87756cae4d4b2b7e/6a7f09a2e3a219861599f330/image2.png" alt="2 - car manufacturers" /></p>
<h2 id="readytogiveitaspin">Ready to give it a spin?</h2>
<p>As Elastic Universal Profiling transitions into this new open source era, the potential for transformative impact on performance optimization, cost efficiency, and environmental sustainability is immense. Elastic's approach — balancing innovation with responsibility — paves the way for a future where technology not only powers our world but does so in a way that is sustainable and accessible to all.</p>
<p>Get started with the open source Elastic Universal Profiling agent today! <a href="https://github.com/elastic/otel-profiling-agent/">Download it directly from GitHub</a> and follow the instructions in the repository.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt7da98a238725d2ae/6a7f09a53cab1cb4cb0e4714/image3.png" alt="3 - dripping graph and data" /></p>
<p><em>The release and timing of any features or functionality described in this post remain at Elastic's sole discretion. Any features or functionality not currently available may not be delivered on time or at all.</em></p>]]></content:encoded>
    <link>https://www.elastic.co/observability-labs/blog/elastic-universal-profiling-agent-open-source</link>
    <guid isPermaLink="false">elastic-universal-profiling-agent-open-source</guid>
    <category><![CDATA[APM]]></category>
    <category><![CDATA[OpenTelemetry]]></category>
    <dc:creator><![CDATA[Israel Ogbole,Christos Kalkanis]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt2f7aadb6cb2ddfa4/6a840b9d1eb9e5964b2c2b9e/tree_tunnel.jpg" length="0" type="image/jpeg"/>
    <pubDate>Mon, 15 Apr 2024 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Beyond the trace: Pinpointing performance culprits with continuous profiling and distributed tracing correlation]]></title>
    <description><![CDATA[Frustrated by slow traces but unsure where the code bottleneck lies? Elastic Universal Profiling correlates profiling stacktraces with OpenTelemetry (OTel) traces, helping you identify and pinpoint the exact lines of code causing performance issues.]]></description>
    <content:encoded><![CDATA[<p>Observability goes beyond monitoring; it's about truly understanding your system. To achieve this comprehensive view, practitioners need a unified observability solution that natively combines insights from metrics, logs, traces, and crucially, <strong>continuous profiling</strong>. While metrics, logs, and traces offer valuable insights, they can't answer the all-important "why." Continuous profiling signals act as a magnifying glass, providing granular code visibility into the system's hidden complexities. They fill the gap left by other data sources, enabling you to answer critical questions –– why is this trace slow? Where exactly in the code is the bottleneck residing?</p>
<p>Traces provide the "what" and "where" — what happened and where in your system. Continuous profiling refines this understanding by pinpointing the "why" and validating your hypotheses about the "what." Just like a full-body MRI scan, Elastic's whole-system continuous profiling (powered by eBPF) uncovers unknown-unknowns in your system. This includes not just your code, but also third-party libraries and kernel activity triggered by your application transactions. This comprehensive visibility improves your mean-time-to-detection (MTTD) and mean-time-to-recovery (MTTR) KPIs.</p>
<p><em>[Related article:</em> <a href="https://www.elastic.co/blog/observability-profiling-metrics-logs-traces"><em>Why metrics, logs, and traces aren’t enough</em></a><em>]</em></p>
<h2 id="bridgingthedisconnectbetweencontinuousprofilingandoteltraces">Bridging the disconnect between continuous profiling and OTel traces</h2>
<p>Historically, continuous profiling signals have been largely disconnected from OpenTelemetry (OTel) traces. Here's the exciting news: we're bridging this gap! We're introducing native correlation between continuous profiling signals and OTel traces, starting with Java.</p>
<p>Imagine this: You're troubleshooting a performance issue and identify a slow trace. Whole-system continuous profiling steps in, acting like an MRI scan for your entire codebase and system. It narrows down the culprit to the specific lines of code hogging CPU time within the context of your distributed trace. This empowers you to answer the "why" question with minimal effort and confidence, all within the same troubleshooting context.</p>
<p>Furthermore, by correlating continuous profiling with distributed tracing, Elastic Observability customers can measure the cloud cost and CO<sub>2</sub> impact of every code change at the service and transaction level.</p>
<p>This milestone is significant, especially considering the recent developments in the OTel community. With <a href="https://www.cncf.io/blog/2024/03/19/opentelemetry-announces-support-for-profiling/">OTel adopting profiling</a> and Elastic <a href="https://www.elastic.co/blog/elastic-donation-proposal-to-contribute-profiling-agent-to-opentelemetry">donating the industry’s most advanced eBPF-based continuous profiling agent to OTel</a>, we're set for a game-changer in observability — empowering OTel end users with a correlated system visibility that goes from a trace span in the userspace down to the kernel.</p>
<p>Furthermore, achieving this goal, especially with Java, presented significant challenges and demanded serious engineering R&amp;D. This blog post will delve into these challenges, explore the approaches we considered in our proof-of-concepts, and explain how we arrived at a solution that can be easily extended to other OTel language agents. Most importantly, this solution correlates traces with profiling signals at the agent, not in the backend — to ensure optimal query performance and minimal reliance on vendor backend storage architectures.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltbeb5f5909292eadb/6a7f0465e02fac7d7d5d61fd/trace.png" alt="Profiling flamegraph for a specific trace.id" /></p>
<h2 id="figuringouttheactiveoteltraceandspan">Figuring out the active OTel trace and span</h2>
<p>The primary technical challenge in this endeavor is essentially the following: whenever the profiler interrupts an OTel instrumented process to capture a stacktrace, we need to be able to efficiently determine the active span and trace ID (per-thread) and the service name (per-process).</p>
<p>For the purpose of this blog, we'll focus on the recently released <a href="https://github.com/elastic/elastic-otel-java">Elastic distribution of the OTel Java instrumentation</a>, but the approach that we ended up with generalizes to any language that can load and call into a native library. So, how do we get our hands on those IDs?</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt0b4ed35c8633ca78/6a7f0469e88c656dd800b2a6/service-popout.png" alt="Profiling correlated with service.name, showing  CO2 and cloud cost impact by line of code." /></p>
<p>The OTel Java agent itself keeps track of the active span by storing a stack of spans in the <a href="https://opentelemetry.io/docs/concepts/context-propagation/#context">OpenTelemetryContext</a>, which itself is stored in a <a href="https://docs.oracle.com/javase/8/docs/api/java/lang/ThreadLocal.html">ThreadLocal</a> variable. We originally considered reading these Java structures directly from BPF, but we eventually decided against that approach. There is no documented specification on how ThreadLocals are implemented, and reliably reading and following the JVM's internal data-structures would incur a high maintenance burden. Any minor update to the JVM could change details of the structure layouts. To add to this, we would also have to reverse engineer how each JVM version lays out Java class fields in memory, as well as how all the high-level Java types used in the context objects are actually implemented under the hood. This approach further wouldn't generalize to any non-JVM language and needs to be repeated for any language that we wish to support.</p>
<p>After we had convinced ourselves that reading Java ThreadLocal directly is not the answer, we decided to look for more portable alternatives instead. The option that we ultimately settled with is to load and call into a C++ library that is responsible for making the required information available via a known and defined interface whenever the span changes.</p>
<p>Other than with Java's ThreadLocals, the details on how a native shared library should expose per-process and per-thread data are well-defined in the System V ABI specification and the architecture specific ELF ABI documents.</p>
<h2 id="exposingperprocessinformation">Exposing per-process information</h2>
<p>Exposing per-process data is easy: we simply declare a global variable . . .</p>
<pre><code>void* elastic_tracecorr_process_storage_v1 = nullptr;
</code></pre>
<p>. . . and expose it via ELF symbols. When the user initializes the OTel library to set the service name, we allocate a buffer and populate it with data in a <a href="https://github.com/elastic/apm/blob/149cd3e39a77a58002344270ed2ad35357bdd02d/specs/agents/universal-profiling-integration.md#process-storage-layout">protocol that we defined for this purpose</a>. Once the buffer is fully populated, we update the global pointer to point to the buffer.</p>
<p>On the profiling agent side, we already have code in place that detects libraries and executables loaded into any process's address space. We normally use this mechanism to detect and analyze high-level language interpreters (e.g., libpython, libjvm) when they are loaded, but it also turned out to be a perfect fit to detect the OTel trace correlation library. When the library is detected in a process, we scan the exports, resolve the symbol, and read the per-process information directly from the instrumented process’ memory.</p>
<h2 id="exposingperthreadinformation">Exposing per-thread information</h2>
<p>With the easy part out of the way, let's get to the nitty-gritty portion: exposing per-thread information via thread-local storage (TLS). So, what exactly is TLS, and how does it work? At the most basic level, the idea is to have <strong>one instance of a variable for every thread</strong>. Semantically you can think of it like having a global Map\&lt;ThreadID, T&gt;, although that is not how it is implemented.</p>
<p>On Linux, there are two major options for thread locals: TSD and TLS.</p>
<h2 id="threadspecificdatatsd">Thread-specific data (TSD)</h2>
<p>TSD is the older and probably more commonly known variant. It works by explicitly allocating a key via pthread_key_create — usually during process startup — and passing it to all threads that require access to the thread-local variable. The threads can then pass that key to the pthread_getspecific and pthread_setspecific functions to read and update the variable for the currently running thread.</p>
<p>TSD is simple, but for our purposes it has a range of drawbacks:</p>
<ul>
<li><p>The pthread_key_t structure is opaque and doesn't have a defined layout. Similar to the Java ThreadLocals, the underlying data-structures aren't defined by the ABI documents and different libc implementations (glibc, musl) will handle them differently.</p></li>
<li><p>We cannot call a function like pthread_getspecific from BPF, so we'd have to reverse engineer and reimplement the logic. Logic may change between libc versions, and we’d have to detect the version and support all variants that may come up in the wild.</p></li>
<li><p>TSD performance is not predictable and varies depending on how many thread local variables have been allocated in the process previously. This may not be a huge concern for Java specifically since spans are typically not swapped super rapidly, but it’d likely be quite noticeable for user-mode scheduling languages where the context might need to be swapped at every await point/coroutine yield.</p></li>
</ul>
<p>None of this is strictly prohibitive, but a lot of this is annoying at the very least. Let’s see if we can do better!</p>
<h2 id="threadlocalstoragetls">Thread-local storage (TLS)</h2>
<p>Starting with C11 and C++11, both languages support thread local variables directly via the _Thread_local and thread_local storage specifiers, respectively. Declaring a variable as per-thread is now a matter of simply adding the keyword:</p>
<pre><code>thread_local void* elastic_tracecorr_tls_v1 = nullptr;
</code></pre>
<p>You might assume that the compiler simply inserts calls to the corresponding pthread function calls when variables declared with this are accessed, but this is not actually the case. The reality is surprisingly complicated, and it turns out that there are four different models of TLS that the compiler can choose to generate. For some of those models, there are further multiple dialects that can be used to implement them. The different models and dialects come with various portability versus performance trade-offs. If you are interested in the details, I suggest reading this <a href="https://maskray.me/blog/2021-02-14-all-about-thread-local-storage">blog article</a> that does a great job at explaining them.</p>
<p>The TLS model and dialect are usually chosen by the compiler based on a somewhat opaque and complicated set of architecture-specific rules. Fortunately for us, both gcc and clang allow users to pick a particular one using the -ftls-model and -mtls-dialect arguments. The variant that we ended up picking for our purposes is -ftls-model=global-dynamic and -mtls-dialect=gnu2 (and desc on aarch64).</p>
<p>Let's take a look at the assembly that is being generated when accessing a thread_local variable under these settings. Our function:</p>
<pre><code>void setThreadProfilingCorrelationBuffer(JNIEnv* jniEnv, jobject bytebuffer) {
  if (bytebuffer == nullptr) {
    elastic_tracecorr_tls_v1 = nullptr;
  } else {
    elastic_tracecorr_tls_v1 = jniEnv-&gt;GetDirectBufferAddress(bytebuffer);
  }
}
</code></pre>
<p>Is compiled to the following assembly code:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltbe785dc1354e597b/6a7f046b73d9bd7e5929d784/assembly.png" alt="assembly" /></p>
<p>Both possible branches assign a value to our thread-local variable. Let’s focus at the right branch corresponding to the nullptr case to get rid of the noise from the GetDirectBufferAddress function call:</p>
<pre><code>lea   rax, elastic_tracecorr_tls_v1_tlsdesc  ;; Load some pointer into rax.
call  qword ptr [rax]                        ;; Read &amp; call function pointer at rax.
mov   qword ptr fs:[rax], 0                  ;; Assign 0 to the pointer returned by
                                             ;; the function that we just called.
</code></pre>
<p>The fs: portion of the mov instruction is the actual magic bit that makes the memory read per-thread. We’ll get to that later; let’s first look at the mysterious elastic_tracecorr_tls_v1_tlsdesc variable that the compiler emitted here. It’s an instance of the tlsdesc structure that is located somewhere in the .got.plt ELF section. The structure looks like this:</p>
<pre><code>struct tlsdesc {
  // Function pointer used to retrieve the offset
  uint64_t (*resolver)(tlsdesc*);

  // TLS offset -- more on that later.
  uint64_t tp_offset;
}
</code></pre>
<p>The resolver field is initialized with nullptr and tp_offset with a per-executable offset. The first thread-local variable in an executable will usually have offset 0, the next one sizeof(first_var), and so on. At first glance this may appear to be similar to how TSD works, with the call to pthread_getspecific to resolve the actual offset, but there is a crucial difference. When the library is loaded, the resolver field is filled in with the address of __tls_get_addr by the loader (ld.so). __tls_get_addr is a relatively heavy function that allocates a TLS offset that is globally unique between all shared libraries in the process. It then proceeds by updating the tlsdesc structure itself, inserting the global offset and replacing the resolver function with a trivial one:</p>
<pre><code>void* second_stage_resolver(tlsdesc* desc) {
  return tlsdesc-&gt;tp_offset;
}
</code></pre>
<p>In essence, this means that the first access to a tlsdesc based thread-local variable is rather expensive, but all subsequent ones are cheap. We further know that by the time that our C++ library starts publishing per-thread data, it must have gone through the initial resolving process already. Consequently, all that we need to do is to read the final offset from the process's memory and memorize it. We also refresh the offset every now and then to ensure that we really have the final offset, combating the unlikely but possible race condition that we read the offset before it was initialized. We can detect this case by comparing the resolver address against the address of the __tls_get_addr function exported by ld.so.</p>
<h2 id="determiningthetlsoffsetfromanexternalprocess">Determining the TLS offset from an external process</h2>
<p>With that out of the way, the next question that arises is how to actually find the tlsdesc in memory so that we can read the offset. Intuitively one might expect that the dynamic symbol exported on the ELF file points to that descriptor, but that is not actually the case.</p>
<pre><code>$ readelf --wide --dyn-syms elastic-jvmti-linux-x64.so | grep elastic_tracecorr_tls_v1
328: 0000000000000000     8 TLS   GLOBAL DEFAULT   19 elastic_tracecorr_tls_v1
</code></pre>
<p>The dynamic symbol instead contains an offset relative to the start of the .tls ELF section and points to the initial value that libc initializes the TLS value with when it is allocated. So how does ld.so find the tlsdesc to fill in the initial resolver? In addition to the dynamic symbol, the compiler also emits a relocation record for our symbol, and that one actually points to the descriptor structure that we are looking for.</p>
<pre><code>$ readelf --relocs --wide elastic-jvmti-linux-x64.so | grep R_X86_64_TLSDESC
00000000000426e8  0000014800000024 R_X86_64_TLSDESC       0000000000000000
elastic_tracecorr_tls_v1 + 0
</code></pre>
<p>To read the final TLS offset, we thus simply have to:</p>
<ul>
<li><p>Wait for the event notifying us about a new shared library being loaded into a process</p></li>
<li><p>Do some cheap heuristics to detect our C++ library, avoiding the more expensive analysis below from being executed for every unrelated library on the system</p></li>
<li><p>Analyze the library on disk and scan ELF relocations for our per-thread variable to extract the tlsdesc address</p></li>
<li><p>Rebase that address to match where our library was loaded in that particular process</p></li>
<li><p>Read the offset from tlsdesc+8</p></li>
</ul>
<h2 id="determiningthetlsbase">Determining the TLS base</h2>
<p>Now that we have the offset, how do we use that to actually read the data that the library puts there for us? This brings us back to the magic fs: portion of the mov instruction that we discussed earlier. In X86, most memory operands can optionally be supplied with a segment register that influences the address translation.</p>
<p>Segments are an archaic construct from the early days of 16-bit X86 where they were used to extend the address space. Essentially the architecture provides a range of segment registers that can be configured with different base addresses, thus allowing more than 16-bits worth of memory to be accessed. In times of 64-bit processors, this is hardly a concern anymore. In fact, X86-64 aka AMD64 got rid of all but two of those segment registers: fs and gs.</p>
<p>So why keep two of them? It turns out that they are quite useful for the use-case of thread-local data. Since every thread can be configured to have its own base address in these segment registers, we can use it to point to a block of data for this specific thread. That is precisely what libc implementations on Linux are doing with the fs segment. The offset that we snatched from the processes memory earlier is used as an address with the fs segment register, and the CPU automatically adds it to the per-thread base address.</p>
<p>To retrieve the base address pointed to by the fs segment register in the kernel, we need to read its destination from the kernel’s task_struct for the thread that we happened to interrupt with our profiling timer event. Getting the task struct is easy because we are blessed with the bpf_get_current_task BPF helper functions. BPF helpers are pretty much syscalls for BPF programs: we can just ask the Linux kernel to hand us the pointer.</p>
<p>Armed with the task pointer, we now have to read the thread.fsbase (X86-64) or thread.uw.tp_value (aarch64) field to get our desired base address that the user-mode process accesses via fs. This is where things get complicated one last time, at least if we wish to support older kernels without <a href="https://www.kernel.org/doc/html/latest/bpf/btf.html">BTF support</a> (we do!). The <a href="https://github.com/torvalds/linux/blob/259f7d5e2baf87fcbb4fabc46526c9c47fed1914/include/linux/sched.h#L748">task_struct is huge</a> and there are hundreds of fields that can be present or not depending on how the kernel is configured. Being a core primitive of the scheduler, it is also constantly subject to changes between different kernel versions. On modern Linux distributions, the kernel is typically nice enough to tell us the offset via BTF. On older ones, the situation is more complicated. Since hardcoding the offset is clearly not an option if we hope the code to be portable, we instead have to figure out the offset by ourselves.</p>
<p>We do this by consulting /proc/kallsyms, a file with mappings between kernel functions and their addresses, and then using BPF to dump the compiled code of a kernel function that rarely changes and uses the desired offset. We dynamically disassemble and analyze the function and extract the offset directly from the assembly. For X86-64 specifically, we dump the <a href="https://elixir.bootlin.com/linux/v5.9.16/source/arch/x86/kernel/hw_breakpoint.c#L452">aout_dump_debugregs</a> function that accesses thread-&gt;ptrace_bps, which has consistently been 16 bytes away from the fsbase field that we are interested in for all kernels that we have ever looked at.</p>
<h2 id="readingtlsdatafromkernel">Reading TLS data from kernel</h2>
<p>With all the required offsets at our hands, we can now finally do what we set out to do in the first place: use them to enrich our stack traces with the OTel trace and span IDs that our C++ library prepared for us!</p>
<pre><code>void maybe_add_otel_info(Trace* trace) {
  // Did user-mode insert a TLS offset for this process? Read it.
  TraceCorrProcInfo* proc = bpf_map_lookup_elem(&amp;tracecorr_procs, &amp;trace-&gt;pid);

  // No entry -&gt; process doesn't have the C++ library loaded.
  if (!proc) return;

  // Load the fsbase offset from our global configuration map.
  u32 key = 0;
  SystemConfig* syscfg = bpf_map_lookup_elem(&amp;system_config, &amp;key);

  // Read the fsbase offset from the kernel's task struct.
  u8* fsbase;
  u8* task = (u8*)bpf_get_current_task();
  bpf_probe_read_kernel(&amp;fsbase, sizeof(fsbase), task + syscfg-&gt;fsbase_offset);

  // Use the TLS offset to read the **pointer** to our TLS buffer.
  void* corr_buf_ptr;
  bpf_probe_read_user(
    &amp;corr_buf_ptr,
    sizeof(corr_buf_ptr),
    fsbase + proc-&gt;tls_offset
  );

  // Read the information that our library prepared for us.
  TraceCorrelationBuf corr_buf;
  bpf_probe_read_user(&amp;corr_buf, sizeof(corr_buf), corr_buf_ptr);

  // If the library reports that we are currently in a trace, store it into
  // the stack trace that will be reported to our user-land process.
  if (corr_buf.trace_present &amp;&amp; corr_buf.valid) {
    trace-&gt;otel_trace_id.as_int.hi = corr_buf.trace_id.as_int.hi;
    trace-&gt;otel_trace_id.as_int.lo = corr_buf.trace_id.as_int.lo;
    trace-&gt;otel_span_id.as_int = corr_buf.span_id.as_int;
  }
}
</code></pre>
<h2 id="sendingoutthemappings">Sending out the mappings</h2>
<p>From this point on, everything further is pretty simple. The C++ library sets up a unix datagram socket during startup and communicates the socket path to the profiler via the per-process data block. The stacktraces annotated with the OTel trace and span IDs are sent from BPF to our user-mode profiler process via perf event buffers, which in turn sends the mappings between OTel span and trace and stack trace hashes to the C++ library. Our extensions to the OTel instrumentation framework then read those mappings and insert the stack trace hashes into the OTel trace.</p>
<p>This approach has a few major upsides compared to the perhaps more obvious alternative of sending out the OTel span and trace ID with the profiler’s stacktrace records. We want the stacktrace associations to be stored in the trace indices to allow filtering and aggregating stacktraces by the plethora of fields available on OTel traces. If we were to send out the trace IDs via the profiler's gRPC connection instead, we’d have to search for and update the corresponding OTel trace records in the profiling collector to insert the stack trace hashes.</p>
<p>This is not trivial: stacktraces are sent out rather frequently (every 5 seconds, as of writing) and the corresponding OTel trace might not have been sent and stored by the time the corresponding stack traces arrive in our cluster. We’d have to build a kind of delay queue and periodically retry updating the OTel trace documents, introducing avoidable database work and complexity in the collectors. With the approach of sending stacktrace mappings to the OTel instrumented process instead, the need for server-side merging vanishes entirely.</p>
<h2 id="tracecorrelationinaction">Trace correlation in action</h2>
<p>With all the hard work out of the way, let’s take a look at what trace correlation looks like in action!</p>
<div>
    
</div>
<h2 id="futureworksupportingotherlanguages">Future work: Supporting other languages</h2>
<p>We have demonstrated that trace correlation can work nicely for Java, but we have no intention of stopping there. The general approach that we discussed previously should work for any language that can efficiently load and call into our C++ library and doesn’t do user-mode scheduling with coroutines. The problem with user-mode scheduling is that the logical thread can change at any await/yield point, requiring us to update the trace IDs in TLS. Many such coroutine environments like Rust’s Tokio provide the ability to register a callback for whenever the active task is swapped, so they can be supported easily. Other languages, however, do not provide that option.</p>
<p>One prominent example in that category is Go: goroutines are built on user-mode scheduling, but to our knowledge there’s no way to instrument the scheduler. Such languages will need solutions that don’t go via the generic TLS path. For Go specifically, we have already built a prototype that uses pprof labels that are associated with a specific Goroutine, having Go’s scheduler update them for us automatically.</p>
<h2 id="gettingstarted">Getting started</h2>
<p>We hope this blog post has given you an overview of correlating profiling signals to distributed tracing, and its benefits for end-users.</p>
<p>To get started, download the <a href="https://github.com/elastic/elastic-otel-java">Elastic distribution of the OTel agent</a>, which contains the new trace correlation library. Additionally, you will need the latest version of Universal Profiling agent, bundled with <a href="https://www.elastic.co/blog/whats-new-elastic-8-13-0">Elastic Stack version 8.13</a>.</p>
<h2 id="acknowledgment">Acknowledgment</h2>
<p>We appreciate <a href="https://github.com/trask">Trask Stalnaker</a>, maintainer of the OTel Java agent, for his feedback on our approach and for reviewing the early draft of this blog post.</p>
<p><em>The release and timing of any features or functionality described in this post remain at Elastic's sole discretion. Any features or functionality not currently available may not be delivered on time or at all.</em></p>]]></content:encoded>
    <link>https://www.elastic.co/observability-labs/blog/continuous-profiling-distributed-tracing-correlation</link>
    <guid isPermaLink="false">continuous-profiling-distributed-tracing-correlation</guid>
    <category><![CDATA[APM]]></category>
    <category><![CDATA[OpenTelemetry]]></category>
    <dc:creator><![CDATA[Joel Höner,Israel Ogbole,Jonas Kunz]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blta319a1a25f2344e3/6a7f046e96b5a6604187b0b3/Under_highway_bridge.jpg" length="0" type="image/jpeg"/>
    <pubDate>Thu, 28 Mar 2024 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Analyzing OpenTelemetry apps with Elastic AI Assistant and APM]]></title>
    <description><![CDATA[Elastic Observability provides native OpenTelemetry support, but analyzing applications logs, metrics, and traces can be daunting. Elastic Observability not only provides AIOps features but also an AI Assistant (co-pilot) to help get to MTTR faster.]]></description>
    <content:encoded><![CDATA[<p>OpenTelemetry is rapidly becoming the most expansive project within the Cloud Native Computing Foundation (CNCF), boasting as many commits as Kubernetes and garnering widespread support from customers. Numerous companies are adopting OpenTelemetry and integrating it into their applications. Elastic® offers detailed <a href="https://www.elastic.co/blog/getting-started-opentelemetry-instrumentation-sample-app">guides</a> on implementing OpenTelemetry for applications. However, like many applications, pinpointing and resolving issues can be time-consuming.</p>
<p>The <a href="https://www.elastic.co/blog/context-aware-insights-elastic-ai-assistant-observability">Elastic AI Assistant</a> significantly enhances the process, not only in identifying but also in resolving issues. This is further enhanced by Elastic’s new Service Level Objective (SLO) capability, allowing you to streamline your entire site reliability engineering (SRE) process from detecting potential issues to enhancing the overall customer experience.</p>
<p>In this blog, we will demonstrate how you, as an SRE, can detect issues in a service equipped with OpenTelemetry. We will explore problem identification using Elastic APM, Elastic’s AIOps capabilities, and the Elastic AI Assistant.</p>
<p>We will illustrate this using the <a href="https://github.com/elastic/opentelemetry-demo">OpenTelemetry demo</a>, with a <a href="https://opentelemetry.io/docs/demo/feature-flags/">feature flag (cartService)</a> that is activated.</p>
<p>Our walkthrough will encompass two scenarios:</p>
<ol>
<li><p>When the SLO for cart service becomes noncompliant, we will analyze the error through Elastic APM. The Elastic AI Assistant will assist by providing a runbook and a GitHub issue to facilitate issue analysis.</p></li>
<li><p>Should the SLO for the cart service be noncompliant, we will examine the trace that indicates a high failure rate. We will employ AIOps for failure correlation and the AI Assistant to analyze logs and Kubernetes metrics directly from the Assistant.</p></li>
</ol>
<h2 id="prerequisitesandconfig">Prerequisites and config</h2>
<p>If you plan on following this blog, here are some of the components and details we used to set up the configuration:</p>
<ul>
<li><p>Ensure you have an account on <a href="http://cloud.elastic.co/">Elastic Cloud</a> and a deployed stack (<a href="https://www.elastic.co/guide/en/elastic-stack/current/installing-elastic-stack.html">see instructions here</a>).</p></li>
<li><p>We used the OpenTelemetry Demo. Directions for using Elastic with OpenTelemetry Demo are <a href="https://github.com/elastic/opentelemetry-demo">here</a>.</p></li>
<li><p>Additionally you will need to connect your AI Assistant to your favorite LLM. We used Azure OpenAI GPT-4.</p></li>
<li><p>We also ran the OpenTelemetry Demo on Kubernetes, specifically on GKE.</p></li>
</ul>
<h2 id="slononcompliance">SLO noncompliance</h2>
<p>Elastic APM recently released the SLO (Service Level Objectives) feature in <a href="https://www.elastic.co/guide/en/observability/8.12/slo.html">8.12</a>. This feature enables setting measurable performance targets for services, such as <a href="https://sre.google/sre-book/monitoring-distributed-systems/">availability, latency, traffic, errors, and saturation or define your own</a>. Key components include:</p>
<ul>
<li><p>Defining and monitoring SLIs (Service Level Indicators)</p></li>
<li><p>Monitoring error budgets indicating permissible performance shortfalls</p></li>
<li><p>Alerting on burn rates showing error budget consumption</p></li>
</ul>
<p>We set up two SLOs for cart service:</p>
<ul>
<li><p><strong>Availability SLO</strong> , which monitors its availability by ensuring that transactions succeed. We set up the feature flag in the OpenTelemetry application, which generates an error for EmptyCart transactions 10% of the time.</p></li>
<li><p><strong>Latency SLO</strong> to ensure transactions are not going below a specific latency, which will reduce customer experiences.</p></li>
</ul>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltf3eaf5cb49ab688a/6a7f0223e02fac902d5d60ff/image1.png" alt="1 - SLOs" /></p>
<p>Because of the OTel cartservice feature flag, the availability SLO is triggered, and within the SLO details, we see that over a seven-day period the availability is well below our target of 99.9, at 95.5. Additionally all the error budget that was available is also exhausted.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt26f1432b0e332202/6a7f0226b43770cf024d6825/image2.png" alt="2 - cart service otel" /></p>
<p>With SLO, you can easily identify when issues with customer experience occur, or when potential issues with services arise before they become potentially worse.</p>
<h2 id="scenario1analyzingapmtraceandlogswithaiassistant">Scenario 1: Analyzing APM trace and logs with AI Assistant</h2>
<p>Once the SLO is found as non-compliant, we can dive into cart service to investigate in Elastic APM. The following walks through the set of steps you can take in Elastic APM and how to use the AI Assistant to analyze the issue:</p>
<div>
    
</div>
<p>From the video, we can see that once in APM, we took the following steps.</p>
<ol>
<li><p>Investigated the trace EmptyCart, which was experiencing larger than normal failure rates.</p></li>
<li><p>The trace showed a significant number of failures, which also resulted in slightly larger latency.</p></li>
<li><p>We used AIOps failure correlation to identify the potential component causing the failure, which correlated to a field value of FailedPrecondition.</p></li>
<li><p>While filtering on that value and reviewing the logs, we still couldn’t understand what this meant.</p></li>
<li><p>This is where you can use Elastic’s AI Assistant to further your understanding of the issue.</p></li>
</ol>
<p>AI Assistant helped us analyze the following:</p>
<ol>
<li><p>It helped us understand what the log message meant and that it was related to the Redis connection failure issue.</p></li>
<li><p>Because we couldn’t connect to Redis, we asked the AI Assistant to give us the metrics for the Redis Kubernetes pods.</p></li>
<li><p>We learned there were two pods for Redis from the logs over the last two hours.</p></li>
<li><p>However, we also learned that the memory of one seems to be increasing.</p></li>
<li><p>It seems that Redis restarted (hence the second pod), and with this information we could dive deeper into what happened to Redis.</p></li>
</ol>
<p>You can see how quickly we could correlate a significant amount of information, logs, metrics, and traces through the AI Assistant and Elastic’s APM capabilities. We didn’t have to go through multiple screens to hunt down information.</p>
<h2 id="scenario2analyzingapmerrorwithaiassistant">Scenario 2: Analyzing APM error with AI Assistant</h2>
<p>Once the SLO is found as noncompliant, we can dive into cart service to investigate in Elastic APM. The following walks through the set of steps you can take in Elastic APM and use the AI Assistant to analyze the issue:</p>
<div>
    
</div>
<p>From the video, we can see that once in APM, we took the following steps:</p>
<ol>
<li><p>We noticed a specific error for the APM service.</p></li>
<li><p>We investigated this in the error tab, and while we see it’s an issue with connection to Redis, we still need more information.</p></li>
<li><p>The AI Assistant helps us understand the stacktrace and provides some potential causes for the error and ways to diagnose and resolve it.</p></li>
<li><p>We also asked it for a runbook, created by our SRE team, which gives us steps to work through this particular issue.</p></li>
</ol>
<p>But as you can see, AI Assistant provides us not only with information about the error message but also how to diagnose it and potentially resolve it with an internal runbook.</p>
<h2 id="achievingoperationalexcellenceoptimalperformanceandreliability">Achieving operational excellence, optimal performance, and reliability</h2>
<p>We’ve shown how an OpenTelemetry instrumented application (OTel demo) can be analyzed using Elastic’s features, especially the AI Assistant coupled with Elastic APM, AIOps, and the latest SLO features. Elastic significantly streamlines the process of identifying and resolving issues within your applications.</p>
<p>Through our detailed walkthrough of two distinct scenarios, we have seen how Elastic APM and the AI Assistant can efficiently analyze and address noncompliance with SLOs in a cart service. The ability to quickly correlate information, logs, metrics, and traces through these tools not only saves time but also enhances the overall effectiveness of the troubleshooting process.</p>
<p>The use of Elastic's AI Assistant in these scenarios underscores the value of integrating advanced AI capabilities into operational workflows. It goes beyond simple error analysis, offering insights into potential causes and providing actionable solutions, sometimes even with customized runbooks. This integration of technology fundamentally changes how SREs approach problem-solving, making the process more efficient and less reliant on manual investigation.</p>
<p>Overall, the advancements in Elastic’s APM, AIOps capabilities, and the AI Assistant, particularly in handling OpenTelemetry data, represent a significant step forward in operational excellence. These tools enable SREs to not only react swiftly to emerging issues but also proactively manage and optimize the performance and reliability of their services, thereby ensuring an enhanced customer experience.</p>
<h2 id="tryitout">Try it out</h2>
<p>Existing Elastic Cloud customers can access many of these features directly from the <a href="https://cloud.elastic.co/">Elastic Cloud console</a>. Not taking advantage of Elastic on cloud? <a href="https://www.elastic.co/cloud/cloud-trial-overview">Start a free trial</a>.</p>
<blockquote>
  <ul>
  <li><a href="https://www.elastic.co/blog/service-level-objectives-slos-logs-metrics">Build better Service Level Objectives (SLOs) from logs and metrics</a></li>
  <li><a href="https://www.elastic.co/blog/whats-new-elastic-observability-8-12-0">Elastic Observability 8.12: GA for AI Assistant, SLO, and Mobile APM support</a></li>
  <li><a href="https://www.elastic.co/blog/native-opentelemetry-support-in-elastic-observability">Native Observability support in Elastic Observability</a></li>
  <li><a href="https://www.elastic.co/blog/context-aware-insights-elastic-ai-assistant-observability">Context-aware insights using the Elastic AI Assistant for Observability</a></li>
  </ul>
</blockquote>
<p><em>The release and timing of any features or functionality described in this post remain at Elastic's sole discretion. Any features or functionality not currently available may not be delivered on time or at all.</em></p>
<p><em>In this blog post, we may have used or referred to third party generative AI tools, which are owned and operated by their respective owners. Elastic does not have any control over the third party tools and we have no responsibility or liability for their content, operation or use, nor for any loss or damage that may arise from your use of such tools. Please exercise caution when using AI tools with personal, sensitive or confidential information. Any data you submit may be used for AI training or other purposes. There is no guarantee that information you provide will be kept secure or confidential. You should familiarize yourself with the privacy practices and terms of use of any generative AI tools prior to use.</em></p>
<p><em>Elastic, Elasticsearch, ESRE, Elasticsearch Relevance Engine and associated marks are trademarks, logos or registered trademarks of Elasticsearch N.V. in the United States and other countries. All other company and product names are trademarks, logos or registered trademarks of their respective owners.</em></p>]]></content:encoded>
    <link>https://www.elastic.co/observability-labs/blog/analyzing-opentelemetry-apps-elastic-ai-assistant-apm</link>
    <guid isPermaLink="false">analyzing-opentelemetry-apps-elastic-ai-assistant-apm</guid>
    <category><![CDATA[APM]]></category>
    <category><![CDATA[OpenTelemetry]]></category>
    <category><![CDATA[AI]]></category>
    <dc:creator><![CDATA[Bahubali Shetti]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltb9decfe96627ccd0/6a7f022977b034eedd3ff0a1/ecs-otel-announcement-3.jpeg" length="0" type="image/jpeg"/>
    <pubDate>Tue, 12 Mar 2024 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Adding free and open Elastic APM as part of your Elastic Observability deployment]]></title>
    <description><![CDATA[Learn how to gather application trace data and store it alongside the logs and metrics from your applications and infrastructure with Elastic Observability and Elastic APM.]]></description>
    <content:encoded><![CDATA[<p>In a recent post, we showed you <a href="https://www.elastic.co/blog/getting-started-with-free-and-open-elastic-observability">how to get started with the free and open tier of Elastic Observability</a>. Below, we'll walk through what you need to do to expand your deployment so you can start gathering metrics from application performance monitoring (APM) or "tracing" data in your observability cluster, for free.</p>
<h2 id="whatisapm">What is APM?</h2>
<p>Application performance monitoring lets you see where your applications spend their time, what they are doing, what other applications or services they are calling, and what errors or exceptions they are encountering.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt75d1516cb178adb6/6a85c74c501a8561c6fbb28a/screenshot-serverless-distributed-trace.png" alt="" /></p>
<p>In addition, APM also lets you see history and trends for key performance indicators, such as latency and throughput, as well as transaction and dependency information:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt886c0c626ef5bb89/6a85c74f80984c6d8e668f32/ruby-overview.png" alt="" /></p>
<p>Whether you're setting up alerts for SLA breaches, trying to gauge the impact of your latest release, or deciding where to make the next improvement, APM can help with your root-cause analysis to help improve your users' experience and drive your mean time to resolution (MTTR) toward zero.</p>
<h2 id="logicalarchitecture">Logical architecture</h2>
<p>Elastic APM relies on the APM Integration inside Elastic Agent, which forwards application trace and metric data from applications instrumented with APM agents to an Elastic Observability cluster. Elastic APM supports multiple agent flavors:</p>
<ul>
<li>Native Elastic APM Agents, available for <a href="https://www.elastic.co/guide/en/apm/agent/index.html">multiple languages</a>, including Java, .NET, Go, Ruby, Python, Node.js, PHP, and client-side JavaScript</li>
<li>Code instrumented with <a href="https://www.elastic.co/guide/en/apm/get-started/current/open-telemetry-elastic.html">OpenTelemetry</a></li>
<li>Code instrumented with <a href="https://www.elastic.co/guide/en/apm/get-started/current/opentracing.html">OpenTracing</a></li>
<li>Code instrumented with <a href="https://www.elastic.co/guide/en/apm/server/current/jaeger.html">Jaeger</a></li>
</ul>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt0218d3e17c29a3b2/6a85c7515c27902af0f59a71/blog-elastic-observability-instrumented-services.png" alt="" /></p>
<p>In this blog, we'll provide a quick example of how to instrument code with the native Elastic APM Python agent, but the overall steps are similar for other languages.</p>
<p>Please note that there is a strong distinction between the <strong>Elastic APM Agent</strong> and the <strong>Elastic Agent</strong>. These are very different components, as you can see in the diagram above, so it's important not to confuse them.</p>
<h2 id="installtheelasticagent">Install the Elastic Agent</h2>
<p>The first step is to install the Elastic Agent. You either need Fleet <a href="https://www.elastic.co/guide/en/fleet/current/add-a-fleet-server.html">installed first</a>, or you can install the Elastic Agent standalone. Install the Elastic Agent somewhere by <a href="https://www.elastic.co/guide/en/fleet/master/elastic-agent-installation.html">following this guide</a>. This will give you an APM Integration endpoint you can hit. Note that this step is not necessary in Elastic Cloud, as we host the APM Integration for you. Check Elastic Agent is up by running:</p>
<pre><code>curl &lt;ELASTIC_AGENT_HOSTNAME&gt;:8200
</code></pre>
<h2 id="instrumentingsamplecodewithanelasticapmagent">Instrumenting sample code with an Elastic APM agent</h2>
<p>The instructions for the various language agents differ based on the programming language, but at a high level they have a similar flow. First, you add the dependency for the agent in the language's native spec, then you configure the agent to let it know how to find the APM Integration.</p>
<p>You can try out any flavor you'd like, but I am going to walk through the Python instructions using this Python example that <a href="https://github.com/davidgeorgehope/PythonElasticAPMExample">I created</a>.</p>
<h3 id="getthesamplecodeoruseyourown">Get the sample code (or use your own)</h3>
<p>To get started, I clone the GitHub repository then change to the directory:</p>
<pre><code>git clone https://github.com/davidgeorgehope/PythonElasticAPMExample
cd PythonElasticAPMExample
</code></pre>
<h3 id="howtoaddthedependency">How to add the dependency</h3>
<p>Adding the Elastic APM Dependency is simple — check the app.py file from <a href="https://github.com/davidgeorgehope/PythonElasticAPMExample/blob/main/app.py">the github repo</a> and you will notice the following lines of code.</p>
<pre><code>import elasticapm
from elasticapm import Client

app = Flask(__name__)
app.config["ELASTIC_APM"] = {    "SERVICE_NAME": os.environ.get("APM_SERVICE_NAME", "flask-app"),    "SECRET_TOKEN": os.environ.get("APM_SECRET_TOKEN", ""),    "SERVER_URL": os.environ.get("APM_SERVER_URL", "http://localhost:8200"),}
elasticapm.instrumentation.control.instrument()
client = Client(app.config["ELASTIC_APM"])
</code></pre>
<p>The Python library for Flask is capable of auto detecting transactions, but you can also start transactions in code as per the following, as we have done in this example:</p>
<pre><code>@app.route("/")
def hello():
    client.begin_transaction('demo-transaction')
    client.end_transaction('demo-transaction', 'success')
</code></pre>
<h3 id="configuretheagent">Configure the agent</h3>
<p>The agents need to send application trace data to the APM Integration, and to do this it has to be reachable. I configured the Elastic Agent to listen on my local host's IP, so anything in my subnet can send data to it. As you can see from the code below, we use docker-compose.yml to pass in the config via environment variables. Please edit these variables for your own Elastic installation.</p>
<pre><code># docker-compose.yml
version: "3.9"
services:
  flask_app:
    build: .
    ports:
      - "5001:5001"
    environment:
      - PORT=5001
      - APM_SERVICE_NAME=flask-app
      - APM_SECRET_TOKEN=your_secret_token
      - APM_SERVER_URL=http://host.docker.internal:8200
</code></pre>
<p>Some commentary on the above:</p>
<ul>
<li><strong>service_name:</strong> If you leave this out it will just default to the application's name, but you can override that here.</li>
<li><strong>secret_token:</strong> <a href="https://www.elastic.co/guide/en/apm/server/current/secret-token.html">Secret tokens</a> allow you to authorize requests to the APM Server, but they require that the APM Server is set up with SSL/TLS and that a secret token has been set up. We're not using HTTPS between the agents and the APM Server, so we'll comment this one out.</li>
<li><strong>server_url:</strong> This is how the agent can reach the APM Integration inside Elastic Agent. Replace this with the name or IP of your host running Elastic Agent.</li>
</ul>
<p>Now that the Elastic APM side of the configuration is done, we simply follow the steps from the <a href="https://github.com/davidgeorgehope/PythonElasticAPMExample/blob/main/README.md">README</a> to start up.</p>
<pre><code>docker-compose up --build -d
</code></pre>
<p>The build step will take several minutes.</p>
<p>You can navigate to the running sample application by visiting http://localhost:5001. There's not a lot to the sample, but it does generate some APM data. To generate a bit of a load, you can reload them a few times or run a quick little script:</p>
<pre><code>#!/bin/bash
# load_test.sh
url="http://localhost:5001"
for i in {1..1000}
do
  curl -s -o /dev/null $url
  sleep 1
done
</code></pre>
<p>This will just reload the pages every second.</p>
<p>Back in Kibana, navigate back to the APM app (hamburger icon, then select <strong>APM</strong> ) and you should see our new flask-app service (I let mine run so it shows a bit more history):</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltc0b22219b36a9336/6a85c7549d2b718e27f938c4/blog-elastic-observability-services.png" alt="" /></p>
<p>The Service Overview page provides an at-a-glance summary of the health of a service in one place. If you're a developer or an SRE, this is the page that will help you answer questions like:</p>
<ul>
<li>How did a new deployment impact performance?</li>
<li>What are the top impacted transactions?</li>
<li>How does performance correlate with underlying infrastructure?</li>
</ul>
<p>This view provides a list of all of the applications that have sent application trace data to Elastic APM in the specified period of time (in this case, the last 15 minutes). There are also sparklines showing mini graphs of latency, throughput, and error rate. Clicking on <strong>flask-app</strong> takes us to the <strong>service overview</strong> page, which shows the various transactions within the service (recall that my script is hitting the / endpoint, as seen in the <strong>Transactions</strong> section). We get bigger graphs for <strong>Latency</strong> , <strong>Throughput</strong> , <strong>Errors</strong> , and <strong>Error Rates</strong>.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt4d432bbc41e408db/6a85c75768266682891eab66/blog-elastic-observability-flask-app.png" alt="" /></p>
<p>When you're instrumenting real applications, under real load, you'll see a lot more connectivity (and errors!)</p>
<p>Clicking on a transaction in the transaction view, in this case, our sample app's demo-transaction transaction, we can see exactly what operations were called:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltb4f05eae471a702c/6a85c75a342d69fd7c21b03f/blog-elastic-observability-flask-app-demo-transaction.png" alt="" /></p>
<p>This includes detailed information about calls to external services, such as database queries:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt897018765d4cb3af/6a85c75d342d69678e21b043/blog-elastic-observability-span-details.png" alt="" /></p>
<h2 id="whatsnext">What's next?</h2>
<p>Now that you've got your Elastic Observability cluster up and running and collecting out-of-the-box application trace data, explore the public APIs for the languages that your applications are using, which allow you to take your APM data to the next level. The APIs allow you to add custom metadata, define business transactions, create custom spans, and more. You can find the public API specs for the various APM agents (such as <a href="https://www.elastic.co/guide/en/apm/agent/java/current/public-api.html">Java</a>, <a href="https://www.elastic.co/guide/en/apm/agent/ruby/current/api.html">Ruby</a>, <a href="https://www.elastic.co/guide/en/apm/agent/python/current/index.html">Python</a>, and more) on the APM agent <a href="https://www.elastic.co/guide/en/apm/agent/index.html">documentation pages</a>.</p>
<p>If you'd like to learn more about Elastic APM, check out <a href="https://www.elastic.co/webinars/introduction-to-elastic-apm-in-the-shift-to-cloud-native">our webinar on Elastic APM in the shift to cloud native</a> to see other ways that Elastic APM can help you in your ecosystem.</p>
<p>If you decide that you'd rather have us host your observability cluster, you can sign up for a free trial of the <a href="https://www.elastic.co/cloud/">Elasticsearch Service on Elastic Cloud</a> and change your agents to point to your new cluster.</p>
<p><em>Originally published May 5, 2021; updated April 6, 2023.</em></p>]]></content:encoded>
    <link>https://www.elastic.co/observability-labs/blog/free-open-elastic-apm-observability-deployment</link>
    <guid isPermaLink="false">free-open-elastic-apm-observability-deployment</guid>
    <category><![CDATA[APM]]></category>
    <category><![CDATA[OpenTelemetry]]></category>
    <category><![CDATA[Metrics]]></category>
    <dc:creator><![CDATA[David Hope]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltb8a4183daa602b2a/6a85c760bc5bb342fdf81a2d/blog-thumb-release-apm.png" length="0" type="image/png"/>
    <pubDate>Wed, 28 Feb 2024 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Elastic APM for iOS and Android Native apps]]></title>
    <description><![CDATA[This blog provides an overview of the key capabilities included in the Elastic APM solution for iOS and Android native apps, as well as a walkthrough of the configuration details and troubleshooting workflow for a few error scenarios.]]></description>
    <content:encoded><![CDATA[<blockquote>
  <p><strong>WARNING</strong>: This article shows information about the Android agent that is no longer accurate for versions <code>1.x</code>. Please refer to <a href="https://www.elastic.co/docs/reference/apm/agents/android">its documentation</a> to learn about its new APIs.</p>
</blockquote>
<p>Elastic® APM for iOS and Android native apps is generally available in the stack release v8.12. The Elastic <a href="https://github.com/elastic/apm-agent-ios">iOS</a> and <a href="https://github.com/elastic/apm-agent-android">Android</a> APM agents are open-source and have been developed on-top, i.e., as a distribution of the OpenTelemetry Swift and Android SDK/API, respectively.</p>
<h2 id="overviewofthemobileapmsolution">Overview of the Mobile APM solution</h2>
<p>The OpenTelemetry SDK/API for iOS and Android supports capabilities such as auto-instrumentation of HTTP requests, API for manual instrumentation, data model based on the OpenTelemetry semantic conventions, and buffering support. Additionally, the Elastic APM agent distributions also support an easier initialization process and novel features such as remote config and user session based sampling. The Elastic <a href="https://github.com/elastic/apm-agent-ios">iOS</a> and <a href="https://github.com/elastic/apm-agent-android">Android</a> APM agents being <em>distributions</em> are maintained per Elastic’s standard support T&amp;Cs.</p>
<p>There are curated or pre-built dashboards provided in Kibana® for monitoring, data analysis, and for troubleshooting purposes. The <strong>Service Overview</strong> view shown below provides relevant frontend KPIs such as crash rate, http requests, average app load time, and more, including the comparison view.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt91be7bfb456b1441/6a85c94aabdc296bf31224c4/1.png" alt="1 - comparison view" /></p>
<p>Further, the geographic distribution of user traffic is available on a map at a country and regional level. The service overview dashboard also shows trends of metrics such as throughput, latency, failed transaction rate, and distribution of traffic by device make-model, network connection type, and app version.</p>
<p>The <strong>Transactions</strong> view shown below highlights the performance of the different transaction groups, including the distributed trace end-to-end of individual transactions with links to associated spans, errors and crashes. Further, users can see at a glance the distribution of traffic by device make and model, app version, and OS version.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltb174c5d176ef23e6/6a85c94e11893c00d7a7ab60/2.png" alt="2- opbeans android" /></p>
<p>Tabular views such as the one highlighted below located at the bottom of <strong>Transactions</strong> tab makes it relatively easy to see how the device make and model, App version, etc., impacts latency and crash rate.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt5f83e08c0af3c9b9/6a85c950eaf24581b8a49f27/3.png" alt="3 - latency and crash rate" /></p>
<p>The <strong>Errors &amp; Crashes</strong> view shown below can be used to analyze the different error and crash groups. The unsymbolicated (iOS) or obfuscated (Android) stacktrace of the individual error or crash instance is also available in this view.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt7a48d2990a65a1ea/6a85c95318249c466b18f793/4.png" alt="4 - opbeans swift" /></p>
<p>The <strong>Service Map</strong> view shown below provides a visualization of the end-to-end service interdependencies, including any third-party APIs, proxy servers, and databases.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blta639d74a283c954d/6a85c956e2447a1d488b13d0/5.png" alt="5 - flowchart" /></p>
<p>The comprehensive pre-built dashboards for observing the mobile frontend in Kibana provide visibility into the sources of errors, crashes, and bottlenecks to ease troubleshooting of issues in the production environment. The underlying Elasticsearch® Platform also supports the ability to query raw data, build custom metrics and custom dashboards, alerting, SLOs, and anomaly detection. Altogether the platform provides a comprehensive set of tools to expedite root cause analysis and remediation, thereby facilitating a high velocity of innovation.</p>
<h2 id="walkthroughofthedebuggingworkflowforsomeerrorscenarios">Walkthrough of the debugging workflow for some error scenarios</h2>
<p>Next, we will provide a walkthrough of the configuration details and the troubleshooting workflow for a couple of error scenarios in iOS and Android native apps.</p>
<h3 id="scenario1">Scenario 1</h3>
<p>In this example, we will debug a crash in an asynchronous method using Apple’s crash report <strong>symbolication</strong> as well as <strong>breadcrumbs</strong> to deduce the cause of the crash.</p>
<p><strong>Symbolication</strong><br />
In this scenario, users notice a spike in the crash occurrences of a particular crash group in the Errors &amp; Crashes tab and decide to investigate further. A new crash comes in on the Crashes tab, and the developer follows these steps to symbolicate the crash report locally.</p>
<ol>
<li>Copy the crash via the UI and paste it into a file with the following name format \&lt;AppBinaryName&gt;_\&lt;DateTime&gt;. For example, “opbeans-swift_2024-01-18-114211.ips`.</li>
</ol>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltbec22d51521f45c7/6a85c9584710c6e8ead3cb19/6.png" alt="6 - Symbolication" /></p>
<ol>
<li>Apple provides <a href="https://developer.apple.com/documentation/xcode/adding-identifiable-symbol-names-to-a-crash-report">detailed instructions</a> on how to symbolicate this file locally either automatically through Xcode or manually using the command line.</li>
</ol>
<p><strong>Breadcrumbs</strong><br />
The second frame of the first thread shows that the crash is occuring in a Worker instance.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt12e7dbf052ee6068/6a85c95c331d7aeb58c3175d/7.png" alt="7 - Breadcrumbs" /></p>
<p>This instance is actually used in many places, and due to the asynchronous nature of this function, it’s not possible to determine immediately where this call is coming from. Nevertheless, we can utilize features of the Open Telemetry SDK to add more context to these crashes and then put the pieces together to find the site of the crash.</p>
<p>By adding “breadcrumbs” around this Worker instance, it is possible to track down which calls to the Worker are actually associated with this crash.</p>
<p><strong>Example:</strong><br />
Create a logger provider in the Worker class as a public variable for ease of access, as shown below:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt260c153e71243aed/6a85c95fe2447acd2e8b13d6/8.png" alt="8 - example code" /></p>
<p>Create breadcrumbs everywhere the Worker.doWork() function is called:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blta3fc688f6eec5690/6a85c9622d64d51b7d081d04/9.png" alt="9 - Create breadcrumbs everywhere the Worker.doWork() function" /></p>
<p>Each of these breadcrumbs will use the same event <strong>name</strong> “worker_breadcrumb” so they can be consistently queried, and the differentiation will be done using the “ <strong>source</strong> ” attribute.</p>
<p>In this example, the Worker.doWork() function is being called from a CustomerRow struct (a table row which does work ‘onTapGesture’). If you were to call this method from multiple places in a CustomerRow struct, you may also add additional differentiations to the “ <strong>source</strong> ” attribute value, such as the associated function (e.g., “CustomerRow#onTapGesture”).</p>
<p>Now that the app is reporting these breadcrumbs, we can use Discover to <strong>query</strong> for them, as shown below:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blta4792d8540ea20e4/6a85c96511893c2390a7ab64/10.png" alt="10 - Discover to query" /></p>
<p> <strong>Note:</strong>  <em>Event</em>  <strong>names</strong>  <em>sent by the agent are translated to event</em>  <strong>action</strong>  <em>in Elastic Common Schema (ECS), so ensure the query uses this field.</em></p>
<ol>
<li><p>You can add a filter: <code>event.action: “worker_breadcrumb”</code> and it shows all events generated from this new breadcrumb.</p></li>
<li><p>You can also see the various sources: ProductRow, CustomerRow, CartRow, etc.</p></li>
<li><p>If you add <strong>error.type : crash</strong> to the query, you can see crashes alongside the breadcrumbs:</p></li>
</ol>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt0d5e3d3c91d562ae/6a85c96818249cc4bd18f797/11.png" alt="11 - crashes along side the breadcrumbs" /></p>
<p>A crash and a breadcrumb next to each other in the timeline may come from completely different devices, so we need another differentiator. For each crash, we have metadata that contains the <strong>session.id</strong> associated with the crash, viewable from the Metadata tab. We can query using this <strong>session.id</strong> to ensure that the only data we are looking at in Discover is from a single user session (i.e., a single device) that resulted in the crash.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt9a61245760515fb0/6a85c96beaf2457b92a49f2b/12.png" alt="12. - session.id" /></p>
<p>In Discover, we can now see the session event flow, on a single device, concerning the crash via the breadcrumbs, as shown below:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt8f7fabb6c2755bbb/6a85c96e2d64d51251081d08/13.png" alt="13 - session event flow" /></p>
<p>It looks like the last breadcrumb before the crash was from the “CustomerRow” breadcrumb. Now this gives the app developer a good place to start their root cause analysis or investigation.</p>
<h3 id="scenario2">Scenario 2</h3>
<p> <strong>Note:</strong>  <em>This scenario requires the Elastic Android agent version “0.14.0” or higher.</em></p>
<p>An Android sample app has a form composed of two screens that are created using two fragments (<code>FirstPage</code> and <code>SecondPage</code>). In the first screen, the app makes a backend API call to get a key that identifies the form submission. This key is stored in memory in the app and must be available on the last screen where the form is sent; the key must be sent along with the form's data.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltd041ab09986f741c/6a85c970d7b2e70de0fe84b6/14.jpg" alt="14 - form submission" /></p>
<p><strong>The problem</strong><br />
We start to see a spike in crash occurrences in Kibana (null pointer exception) in the Errors &amp; Crashes tab that always seem to happen on the last screen of the form, when the users click on the "FINISH" button. Nevertheless, <strong>this is not always reproducible</strong> , so the root cause isn't clear just by looking at the crash’s stacktrace alone. Here’s what it looks like:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt5a3320046b2fa019/6a85c97493ffb9589eb9140b/15.png" alt="15 - stack trace" /></p>
<p>When we take a look at the code referenced in the stacktrace, this is what we can see:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt468d6da2973f89a0/6a85c9779bf9944f060a054b/16.png" alt="16 - When we take a look at the code referenced in the stacktrace, this is what we can see:" /></p>
<p>This is the line where the crash happens, so it seems like the variable “formId” (which is a static String located in “FirstPage”) was null by the time this code was executed, causing a null pointer exception to be raised. This variable is set within the “FirstPage” fragment after the backend request is done to retrieve the id. The only way to get to the “SecondPage” is by passing through the “FirstPage.” So, the stacktrace alone doesn’t help much as the pages have to be opened in order, and the first one will always set the “formId” variable. Therefore, it doesn’t seem likely that the formId could be null in “SecondPage.”</p>
<p><strong>Finding the root cause</strong><br />
Apart from taking a look at the crash’s stacktrace, it could also be useful to take a look at complementary data that would help put the pieces together and get a broader picture of what other things happened while our app was running when the crash happened. For this case, we know that the form ID must come from our backend service, so we could start by ruling out that there was an error with the backend call. We do this by checking the traces from the creation of our FirstPage fragment where the form ID request is executed, in the Transaction details view:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt38678706cbce8da0/6a85c97a1aa1e1ef42ff8d37/17.png" alt="17 - trace sample" /></p>
<p>The “Created” spans represent the time it took to create the first fragment. The topmost one shows the Activity creation, followed by the NavHostFragment, followed by “FirstScreen.” Not long after its creation, we see that a GET HTTP request to our backend is made to retrieve our form ID and, according to the traces, the GET request was successful. We can therefore rule out that there is an issue with the backend communication for this problem.</p>
<p>Another option could be looking at the logs sent throughout the <a href="https://opentelemetry.io/docs/specs/semconv/general/session/">session</a> in our app where the crash occurred (we could also take a look at all the logs coming from our app but they would be too many to analyze this one issue). To do so, we first copy one of the spans’ “session.id” values (any span would work since the same session ID will be available in all the data that was sent from our app during the time that the crash occurred) available in the span details flyout.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltaae166d8999ccb7b/6a85c97df9373db3c896f56e/18.png" alt="18 - red box highlighted" /></p>
<p> <strong>Note:</strong>  <em>The same session ID can also be found in the crash metadata.</em></p>
<p>Now that we have identified our session, we can open up the Logs Explorer view and take a look at all of our app’s logs within that same session, as shown below:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltcd9ae193f9bc4b72/6a85c980331d7ac6f4c31767/19.png" alt="19 - app's logs" /></p>
<p>By looking at the logs, and adding a few fields to show the app’s lifecycle status and the error types, we see the log events that are <a href="https://github.com/elastic/apm/blob/main/specs/agents/mobile/events.md">automatically collected</a> from our app. We can see the crash event at the top of the list as the latest one. We can also see our app’s lifecycle events, and if we keep scrolling through, we’ll get to some lifecycle events that are going to help find our root cause:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt166a2855c5721aa7/6a85c983f5f1a02e202ec8c9/20.png" alt="20 - root cause" /></p>
<p>We can see there are a couple of lifecycle events that tell us that the app was restarted during the session. This is an important hint because it means that the Android OS killed our app at some point, which is common when an app stays in the background for a while. With this information, we could try to reproduce the issue by forcing the OS to kill our app in the background and then see how it behaves when reopened from the recently opened apps menu.</p>
<p>After giving it a try, we could reproduce the issue and we found that the static “formId” variable was lost when the app was restarted, causing it to be null when the SecondPage fragment requested it. We can now research best practices of passing arguments to Fragments so we can change our code to prevent relying on static fields and instead store and share values between screens, thus preventing this crash from happening again.</p>
<p><strong>Bonus:</strong> For this scenario, it was enough for us to rely on the events that are sent automatically by the APM Agent; however, if those aren’t enough for other cases, we can always send custom events in the places where we want to track the state changes of our app via the OpenTelemetry event API, as shown in the the code snippet below:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt73c7313eb46de1e3/6a85c98611893cd033a7ab68/21.png" alt="21 - black code box" /></p>
<h2 id="makethemostofyourelasticapmexperience">Make the most of your Elastic APM Experience</h2>
<p>In this post, we reviewed Elastic’s new Mobile APM solution available in 8.12. The new solution uses Elastic’s new <a href="https://github.com/elastic/apm-agent-ios">iOS</a> and <a href="https://github.com/elastic/apm-agent-android">Android</a> APM agents that are open-source and have been developed on-top, i.e., as a distribution of the OpenTelemetry Swift and Android SDK/API, respectively.</p>
<p>We also reviewed configuration details and the troubleshooting workflow for two error scenarios in iOS and Android native apps.</p>
<ul>
<li><p><strong>iOS scenario:</strong> Debug a crash in an asynchronous method using Apple’s crash report <strong>symbolication</strong> as well as <strong>breadcrumbs</strong> to deduce the cause of the crash.</p></li>
<li><p><strong>Android scenario:</strong> Analyze why users get a null pointer exception on the last screen when they click on the “FINISH” button of a form. Analyzing this is not always clear by looking at the crash’s stack trace and isn’t easily reproducible.</p></li>
</ul>
<p>In both instances, we found the root cause of the crash using distributed traces from the mobile device as well as correlated logs. Hopefully this blog provided a review of how Elastic can help manage and monitor Mobile native apps.</p>
<p>Elastic invites SREs and developers to experience our Mobile APM solution firsthand and unlock new horizons in their data tasks. Try it today at <a href="https://ela.st/free-trial">https://ela.st/free-trial</a>.</p>
<p><em>The release and timing of any features or functionality described in this post remain at Elastic's sole discretion. Any features or functionality not currently available may not be delivered on time or at all.</em></p>]]></content:encoded>
    <link>https://www.elastic.co/observability-labs/blog/apm-ios-android-native-apps</link>
    <guid isPermaLink="false">apm-ios-android-native-apps</guid>
    <category><![CDATA[APM]]></category>
    <category><![CDATA[OpenTelemetry]]></category>
    <dc:creator><![CDATA[Akhilesh Pokhariyal,Cesar Munoz,Bryce Buchanan]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt3184b0ef29376383/6a85c9884710c64fbad3cb21/141949-elastic-blogheaderimage.png" length="0" type="image/png"/>
    <pubDate>Thu, 08 Feb 2024 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Universal Profiling: Detecting CO2 and energy efficiency]]></title>
    <description><![CDATA[Universal Profiling introduces the possibility to capture environmental impact. In this post, we compare Python and Go implementations and showcase the substantial CO2 savings achieved through code optimization.]]></description>
    <content:encoded><![CDATA[<p>A while ago, we posted a <a href="https://www.elastic.co/blog/importing-chess-games-elasticsearch-universal-profiling">blog</a> that detailed how we imported over 4 billion chess games with speed using Python and optimized the code leveraging our Universal Profiling<sup>TM</sup>. This was based on Elastic Stack running on version 8.9. We are now on <a href="https://www.elastic.co/blog/whats-new-elastic-8-12-0">8.12</a>, and it is time to do a second part that shows how easy it is to observe compiled languages and how Elastic®’s Universal Profiling can help you determine the benefit of a rewrite, both from a cost and environmental friendliness angle.</p>
<h2 id="whyefficiencymattersforyouandtheenvironment">Why efficiency matters — for you and the environment</h2>
<p>Data centers are estimated to consume ~3% of global electricity consumption, and their usage is expected to double by 2030.* The cost of a digital service is a close proxy to its computing efficiency, and thus, being more efficient is a win-win: less energy consumed, smaller bill.</p>
<p>In the same scenario, companies want the ability to scale to more users while spending less for each user and are effectively looking into methods of reducing their energy consumption.</p>
<p>In this spirit, <a href="https://www.elastic.co/observability/universal-profiling">Universal Profiling</a> comes equipped with data and visualizations to help determine where efficiency improvement efforts are worth the most.</p>
<p><a href="https://www.elastic.co/blog/continuous-profiling-efficient-cost-effective-applications">Energy efficiency</a> measures how much a digital service consumes to produce an output given an input. It can be measured in multiple ways, and we at Elastic Observability chose CO<sub>2</sub> emissions and annualized CO<sub>2</sub> emissions (more details on them later).</p>
<p>Let’s take the example of an e-commerce website: the energy efficiency of the “search inventory” process could be calculated as the average CPU time needed to serve a user request. Once the baseline for this value is determined, changes to the software delivering the search process may result in more or less CPU time consumed for the same feature, resulting in less or more efficient code.</p>
<h2 id="howtosetupandconfigurewattageandco2">How to set up and configure wattage and CO2</h2>
<p>You can find a “Settings” button in the top-right corner of the Universal Profiling views. From there, you can customize the coefficient used to calculate CO<sub>2</sub> emissions tied to profiling data.</p>
<p>The values set here will be used only when the profiles gathered from host agents are not already associated with publicly known data certified by cloud providers. For example, suppose you have a hybrid cloud deployment with a portion of your workload running on-premise and a portion running in GCP. In that case, the values set here will only be used to calculate the CO<sub>2</sub> emissions for the on-premise machines; we already use all the coefficients as declared by GCP to calculate the emissions of those machines.</p>
<h2 id="pythonvsgo">Python vs. Go</h2>
<p>Our first <a href="https://www.elastic.co/blog/importing-chess-games-elasticsearch-universal-profiling">blog post</a> implemented a solution to read PGN chess games, a text representation in Python. It showed how Universal Profiler can be leveraged to identify slow functions and help you rewrite your code faster and more efficiently. At the end of it, we were happy with the Python version. It is still used today to grab the monthly updates from the <a href="https://database.lichess.org/">Lichess database</a> and ingest them into Elasticsearch®. I always wanted a reason to work more with Go, and we rewrote Python to Go. We leveraged goroutines and channels to send data through message passing. You can see more about it in our <a href="https://github.com/philippkahr/blogs/tree/main/universal-profiling">GitHub repository</a>.</p>
<p>Rewriting in Go also means switching from an interpreted language to a compiled one. As with everything in IT, this has benefits as well as disadvantages. One disadvantage is that we must ship debug symbols for the compiled binary. When we build the binary, we can use the symbtool program to ship the debug symbols. Without debug symbols, we see uninterpretable information as frames will be labeled with hexadecimal addresses in the flame graph rather than source code annotations.</p>
<p>First, make sure that your executable includes debug symbols. Go per default builds with debug symbols. You can check this by using file yourbinary. The important part is that it is not stripped.</p>
<pre><code>file lichess
lichess: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, Go BuildID=gufIkqA61WnCh8haeW-2/lfn3ne3U_y8MGoFD4AvT/QJEykzbacbYEmEQpXH6U/MqVbk-402n1k3B8yPB6I, with debug_info, not stripped
</code></pre>
<p>Now we need to push the symbols using symbtool. You must create an Elasticsearch API key as the authentication method. In the Universal Profiler UI in Kibana®, an <strong>Add Data</strong> button in the top right corner will tell you exactly what to do. The command is like this. The -e is the part where you pass through the path of your executable file. In our case, this is lichess as above.</p>
<pre><code>symbtool push-symbols executable -t "ApiKey" -u "elasticsearch-url" -e "lichess"
</code></pre>
<p>Now that debug symbols are available inside the cluster, we can run both implementations with the same file simultaneously and see what Universal Profiler can tell us about it.</p>
<h2 id="identifyingco2andenergyefficiencysavings">Identifying CO2 and energy efficiency savings</h2>
<p>Python is more frequently scheduled on the CPU. Thus, it runs more often on the hardware and contributes more to the machines’ resource usage.</p>
<p>We use the differential flame graph to identify and automatically calculate the difference in the following comparison. You need to filter on process.thread.name: “python3.11” in the baseline, and for the comparison, filter for lichess.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt45bdc550b9e6dcd0/6a7f1bd533fa8a1440202b98/1-elastic-blog-uni-profiling.png" alt="1 - universal profiling" /></p>
<p>Looking at the impact of annualized CO<sub>2</sub> emissions, we see a decrease from 65.32kg of CO<sub>2</sub> from the Python solution to 16.78kg. That is a difference of 48.54kg CO<sub>2</sub> savings over a year.</p>
<p>If we take a step back, we’ll want to figure out why Python produces many more emissions. In the flamegraph view, we filter down to just showing Python, and we can click on the first frame called python3.11. A little popup tells us that it caused 32.95kg of emissions. That is nearly 50% of all emissions caused by the runtime. Our program itself caused the other ~32kg of CO<sub>2</sub>. We immediately reduced 32kg of annual emissions by cutting out the Python interpreter with Go.</p>
<p>We can lock that box using a right click and click <strong>Show more information</strong>.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt3f5519e4403d03da/6a7f1bd8b437701f404d712a/2-elastic-blog-uni-profiling.png" alt="2 - universal profiling graphs blue-orange" /></p>
<p>The <strong>Show more information</strong> link displays detailed information about the frame, like sample count, total CPU, core seconds, and dollar costs. We won’t go into more detail in this blog.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blte81b06c8b7f996c4/6a7f1bdbbdcff03595c432d3/3-elastic-blog-uni-profiling.png" alt="3 impact estimates" /></p>
<h2 id="reduceyourcarbonfootprinttodaywithuniversalprofiling">Reduce your carbon footprint today with Universal Profiling</h2>
<p>This blog post demonstrates that rewriting your code base can reduce your carbon footprint immensely. Using Universal Profiler, you could do a quick PoC to showcase how much carbon resources can be spared.</p>
<p>Learn how you can <a href="https://www.elastic.co/guide/en/observability/current/profiling-get-started.html">get started</a> with Elastic Universal Profiling today.</p>
<blockquote>
  <ul>
  <li>Cluster for storing the data where three nodes, each 64GB RAM and 32 CPU cores, are running GCP on Elastic Cloud.</li>
  <li>The machine for sending the data is a GCP e2-standard-32, thus 128GB RAM and 32 CPU cores with a 500GB balanced disk to read the games from.</li>
  <li>The file used for the games is this <a href="https://database.lichess.org/standard/lichess_db_standard_rated_2023-12.pgn.zst">Lichess database</a> containing 96,909,211 games. The extracted file size is 211GB.</li>
  </ul>
</blockquote>
<p><strong>Source:</strong></p>
<p>*<a href="https://media.ccc.de/v/camp2023-57070-energy_consumption_of_data_centers">https://media.ccc.de/v/camp2023-57070-energy_consumption_of_data_centers</a></p>
<p><em>The release and timing of any features or functionality described in this post remain at Elastic's sole discretion. Any features or functionality not currently available may not be delivered on time or at all.</em></p>]]></content:encoded>
    <link>https://www.elastic.co/observability-labs/blog/universal-profiling-detecting-co2-energy-efficiency</link>
    <guid isPermaLink="false">universal-profiling-detecting-co2-energy-efficiency</guid>
    <category><![CDATA[APM]]></category>
    <category><![CDATA[Infrastructure Monitoring]]></category>
    <dc:creator><![CDATA[Philipp Kahr,Francesco Gualazzi]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt12688c185784aad8/6a7f1bde6693f85f656643a9/141935_-_Blog_header_image-_Op1_V1.jpg" length="0" type="image/jpeg"/>
    <pubDate>Mon, 05 Feb 2024 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[How to easily add application monitoring in Kubernetes pods]]></title>
    <description><![CDATA[This blog walks through installing the Elastic APM K8s Attacher and shows how to configure your system for both common and non-standard deployments of Elastic APM agents.]]></description>
    <content:encoded><![CDATA[<p>The <a href="https://www.elastic.co/guide/en/apm/attacher/current/index.html">Elastic® APM K8s Attacher</a> allows auto-installation of Elastic APM application agents (e.g., the Elastic APM Java agent) into applications running in your Kubernetes clusters. The mechanism uses a <a href="https://kubernetes.io/docs/reference/access-authn-authz/admission-controllers/">mutating webhook</a>, which is a standard Kubernetes component, but you don’t need to know all the details to use the Attacher. Essentially, you can install the Attacher, add one annotation to any Kubernetes deployment that has an application you want monitored, and that’s it!</p>
<p>In this blog, we’ll walk through a full example from scratch using a Java application. Apart from the Java code and using a JVM for the application, everything else works the same for the other languages supported by the Attacher.</p>
<h2 id="prerequisites">Prerequisites</h2>
<p>This walkthrough assumes that the following are already installed on the system: JDK 17, Docker, Kubernetes, and Helm.</p>
<h2 id="theexampleapplication">The example application</h2>
<p>While the application (shown below) is a Java application, it would be easily implemented in any language, as it is just a simple loop that every 2 seconds calls the method chain methodA-&gt;methodB-&gt;methodC-&gt;methodD, with methodC sleeping for 10 milliseconds and methodD sleeping for 200 milliseconds. The choice of application is just to be able to clearly display in the Elastic APM UI that the application is being monitored.</p>
<p>The Java application in full is shown here:</p>
<pre><code>package test;

public class Testing implements Runnable {

  public static void main(String[] args) {
    new Thread(new Testing()).start();
  }

  public void run()
  {
    while(true) {
      try {Thread.sleep(2000);} catch (InterruptedException e) {}
      methodA();
    }
  }

  public void methodA() {methodB();}

  public void methodB() {methodC();}

  public void methodC() {
    System.out.println("methodC executed");
    try {Thread.sleep(10);} catch (InterruptedException e) {}
    methodD();
  }

  public void methodD() {
    System.out.println("methodD executed");
    try {Thread.sleep(200);} catch (InterruptedException e) {}
  }
}
</code></pre>
<p>We created a Docker image containing that simple Java application for you that can be pulled from the following Docker repository:</p>
<pre><code>docker.elastic.co/demos/apm/k8s-webhook-test
</code></pre>
<h2 id="deploythepod">Deploy the pod</h2>
<p>First we need a deployment config. We’ll call the config file webhook-test.yaml, and the contents are pretty minimal — just pull the image and run that as a pod &amp; container called webhook-test in the default namespace:</p>
<pre><code>apiVersion: v1
kind: Pod
metadata:
  name: webhook-test
  labels:
    app: webhook-test
spec:
  containers:
    - image: docker.elastic.co/demos/apm/k8s-webhook-test
      imagePullPolicy: Always
      name: webhook-test
</code></pre>
<p>This can be deployed normally using kubectl:</p>
<pre><code>kubectl apply -f webhook-test.yaml
</code></pre>
<p>The result is exactly as expected:</p>
<pre><code>$ kubectl get pods
NAME           READY   STATUS    RESTARTS   AGE
webhook-test   1/1     Running   0          10s

$ kubectl logs webhook-test
methodC executed
methodD executed
methodC executed
methodD executed
</code></pre>
<p>So far, this is just setting up a standard Kubernetes application with no APM monitoring. Now we get to the interesting bit: adding in auto-instrumentation.</p>
<h2 id="installelasticapmk8sattacher">Install Elastic APM K8s Attacher</h2>
<p>The first step is to install the <a href="https://www.elastic.co/guide/en/apm/attacher/current/index.html">Elastic APM K8s Attacher</a>. This only needs to be done once for the cluster — once installed, it is always available. Before installation, we will define where the monitored data will go. As you will see later, we can decide or change this any time. For now, we’ll specify our own Elastic APM server, which is at https://myserver.somecloud:443 — we also have a secret token for authorization to that Elastic APM server, which has value MY_SECRET_TOKEN. (If you want to set up a quick test Elastic APM server, you can do so at <a href="https://cloud.elastic.co/">https://cloud.elastic.co/</a>).</p>
<p>There are two additional environment variables set for the application that are not generally needed but will help when we see the resulting UI content toward the end of the walkthrough (when the agent is auto-installed, these two variables tell the agent what name to give this application in the UI and what method to trace). Now we just need to define the custom yaml file to hold these. On installation, the custom yaml will be merged into the yaml for the Attacher:</p>
<pre><code>apm:
  secret_token: MY_SECRET_TOKEN
  namespaces:
    - default
webhookConfig:
  agents:
    java:
      environment:
        ELASTIC_APM_SERVER_URL: "https://myserver.somecloud:443"
        ELASTIC_APM_TRACE_METHODS: "test.Testing#methodB"
        ELASTIC_APM_SERVICE_NAME: "webhook-test"
</code></pre>
<p>That custom.yaml file is all we need to install the attacher (note we’ve only specified the default namespace for agent auto-installation for now — this can be easily changed, as you’ll see later). Next we’ll add the Elastic charts to helm — this only needs to be done once, then all Elastic charts are available to helm. This is the usual helm add repo command, specifically:</p>
<pre><code>helm repo add elastic https://helm.elastic.co
</code></pre>
<p>Now the Elastic charts are available for installation (helm search repo would show you all the available charts). We’re going to use “elastic-webhook” as the name to install into, resulting in the following installation command:</p>
<pre><code>helm install elastic-webhook elastic/apm-attacher --namespace=elastic-apm --create-namespace --values custom.yaml
</code></pre>
<p>And that’s it, we now have the Elastic APM K8s Attacher installed and set to send data to the APM server defined in the custom.yaml file! (You can confirm installation with a helm list -A if you need.)</p>
<h2 id="autoinstallthejavaagent">Auto-install the Java agent</h2>
<p>The Elastic APM K8s Attacher is installed, but it doesn’t auto-install the APM application agents into every pod — that could lead to problems! Instead the Attacher is deliberately limited to auto-install agents into deployments defined a) by the namespaces listed in the custom.yaml, and b) to those deployments in those namespaces that have a specific annotation “co.elastic.apm/attach.”</p>
<p>So for now, restarting the webhook-test pod we created above won’t have any different effect on the pod, as it isn’t yet set to be monitored. What we need to do is add the annotation. Specifically, we need to add the annotation using the default agent configuration that was installed with the Attacher called “java” for the Java agent (we’ll see later how that agent configuration is altered — the default configuration installs the latest agent version and leaves everything else default for that version). So adding that annotation in to webhook-test yaml gives us the new yaml file contents (the additional config is shown labelled (1)):</p>
<pre><code>apiVersion: v1
kind: Pod
metadata:
  name: webhook-test
  annotations: #(1)
    co.elastic.apm/attach: java #(1)
  labels:
    app: webhook-test
spec:
  containers:
    - image: docker.elastic.co/demos/apm/k8s-webhook-test
      imagePullPolicy: Always
      name: webhook-test
</code></pre>
<p>Applying this change gives us the application now monitored:</p>
<pre><code>$ kubectl delete -f webhook-test.yaml
pod "webhook-test" deleted
$ kubectl apply -f webhook-test.yaml
pod/webhook-test created
$ kubectl logs webhook-test
… StartupInfo - Starting Elastic APM 1.45.0 …
</code></pre>
<p>And since the agent is now feeding data to our APM server, we can now see it in the UI:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltca8638b16b1aa112/6a85cb3b2d64d52d74081d44/webhook-test-k8s-blog.png" alt="webhook-test" /></p>
<p>Note that the agent identifies Testing.methodB method as a trace root because of the ELASTIC_APM_TRACE_METHODS environment variable set to test.Testing#methodB in the custom.yaml — this tells the agent to specifically trace that method. The time taken by that method will be available in the UI for each invocation, but we don’t see the sub-methods . . . currently. In the next section, we’ll see how easy it is to customize the Attacher, and in doing so we’ll see more detail about the method chain being executed in the application.</p>
<h2 id="customizingtheagents">Customizing the agents</h2>
<p>In your systems, you’ll likely have development, testing, and production environments. You’ll want to specify the version of the agent to use rather than just pull the latest version whatever that is, you’ll want to have debug on for some applications or instances, and you’ll want to have specific options set to specific values. This sounds like a lot of effort, but the attacher lets you enable these kinds of changes in a very simple way. In this section, we’ll add a configuration that specifies all these changes and we can see just how easy it is to configure and enable it.</p>
<p>We start at the custom.yaml file we defined above. This is the file that gets merged into the Attacher. Adding a new configuration with all the items listed in the last paragraph is easy — though first we need to decide a name for our new configuration. We’ll call it “java-interesting” here. The new custom.yaml in full is (the first part is just the same as before, the new config is simply appended):</p>
<pre><code>apm:
  secret_token: MY_SECRET_TOKEN
  namespaces:
    - default
webhookConfig:
  agents:
    java:
      environment:
        ELASTIC_APM_SERVER_URL: "https://myserver.somecloud:443"
        ELASTIC_APM_TRACE_METHODS: "test.Testing#methodB"
        ELASTIC_APM_SERVICE_NAME: "webhook-test"
    java-interesting:
      image: docker.elastic.co/observability/apm-agent-java:1.55.4
      artifact: "/usr/agent/elastic-apm-agent.jar"
      environment:
        ELASTIC_APM_SERVER_URL: "https://myserver.somecloud:443"
        ELASTIC_APM_TRACE_METHODS: "test.Testing#methodB"
        ELASTIC_APM_SERVICE_NAME: "webhook-test"
        ELASTIC_APM_ENVIRONMENT: "testing"
        ELASTIC_APM_LOG_LEVEL: "debug"
        ELASTIC_APM_PROFILING_INFERRED_SPANS_ENABLED: "true"
        JAVA_TOOL_OPTIONS: "-javaagent:/elastic/apm/agent/elastic-apm-agent.jar"
</code></pre>
<p>Breaking the additional config down, we have:</p>
<ul>
<li><p>The name of the new config java-interesting</p></li>
<li><p>The APM Java agent image docker.elastic.co/observability/apm-agent-java</p></li>
<li><p>With a specific version 1.43.0 instead of latest</p></li>
<li><p>We need to specify the agent jar location (the attacher puts it here)</p></li>
<li><p>artifact: "/usr/agent/elastic-apm-agent.jar"</p></li>
<li><p>And then the environment variables</p></li>
<li><p>ELASTIC_APM_SERVER_URL as before</p></li>
<li><p>ELASTIC_APM_ENVIRONMENT set to testing, useful when looking in the UI</p></li>
<li><p>ELASTIC_APM_LOG_LEVEL set to debug for more detailed agent output</p></li>
<li><p>ELASTIC_APM_PROFILING_INFERRED_SPANS_ENABLED turning this on (setting to true) will give us additional interesting information about the method chain being executed in the application</p></li>
<li><p>And lastly we need to set JAVA_TOOL_OPTIONS to the enable starting the agent "-javaagent:/elastic/apm/agent/elastic-apm-agent.jar" — this is fundamentally how the attacher auto-attaches the Java agent</p></li>
</ul>
<p>More configurations and details about configuration options are <a href="https://www.elastic.co/guide/en/apm/agent/java/current/configuration.html">here for the Java agent</a>, and <a href="https://www.elastic.co/guide/en/apm/agent/index.html">other language agents</a> are also available.</p>
<h2 id="theapplicationtracedwiththenewconfiguration">The application traced with the new configuration</h2>
<p>And finally we just need to upgrade the attacher with the changed custom.yaml:</p>
<pre><code>helm upgrade elastic-webhook elastic/apm-attacher --namespace=elastic-apm --create-namespace --values custom.yaml
</code></pre>
<p>This is the same command as the original install, but now using upgrade. That’s it — add config to the custom.yaml and upgrade the attacher, and it’s done! Simple.</p>
<p>Of course we still need to use the new config on an app. In this case, we’ll edit the existing webhook-test.yaml file, replacing java with java-interesting, so the annotation line is now:</p>
<pre><code>co.elastic.apm/attach: java-interesting
</code></pre>
<p>Applying the new pod config and restarting the pod, you can see the logs now hold debug output:</p>
<pre><code>$ kubectl delete -f webhook-test.yaml
pod "webhook-test" deleted
$ kubectl apply -f webhook-test.yaml
pod/webhook-test created
$ kubectl logs webhook-test
… StartupInfo - Starting Elastic APM 1.44.0 …
… DEBUG co.elastic.apm.agent. …
… DEBUG co.elastic.apm.agent. …
</code></pre>
<p>More interesting is the UI. Now that inferred spans is on, the full method chain is visible.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltf182dab36de35372/6a85cb3d6826668f5b1eac03/trace-sample-k8s-blog.png" alt="trace sample" /></p>
<p>This gives the details for methodB (it takes 211 milliseconds because it calls methodC - 10ms - which calls methodD - 200ms). The times for methodC and methodD are inferred rather than recorded, (inferred rather than traced — if you needed accurate times you would instead add the methods to trace_methods and have them traced too).</p>
<h2 id="noteontheeckoperator">Note on the ECK operator</h2>
<p>The <a href="https://www.elastic.co/guide/en/cloud-on-k8s/master/k8s-overview.html">Elastic Cloud on Kubernetes operator</a> allows you to install and manage a number of other Elastic components on Kubernetes. At the time of publication of this blog, the <a href="https://www.elastic.co/guide/en/apm/attacher/current/index.html">Elastic APM K8s Attacher</a> is a separate component, and there is no conflict between these management mechanisms — they apply to different components and are independent of each other.</p>
<h2 id="tryityourself">Try it yourself!</h2>
<p>This walkthrough is easily repeated on your system, and you can make it more useful by replacing the example application with your own and the Docker registry with the one you use.</p>
<p><a href="https://www.elastic.co/observability/kubernetes-monitoring">Learn more about real-time monitoring with Kubernetes and Elastic Observability</a>.</p>
<p><em>The release and timing of any features or functionality described in this post remain at Elastic's sole discretion. Any features or functionality not currently available may not be delivered on time or at all.</em></p>]]></content:encoded>
    <link>https://www.elastic.co/observability-labs/blog/application-monitoring-kubernetes-pods</link>
    <guid isPermaLink="false">application-monitoring-kubernetes-pods</guid>
    <category><![CDATA[Kubernetes]]></category>
    <category><![CDATA[APM]]></category>
    <dc:creator><![CDATA[Jack Shirazi,Sylvain Juge,Alexander Wert]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltbececa85f67fdcd1/6a85cb40eaf24581f5a49f65/139689_-_Blog_Header_Banner_V1.jpg" length="0" type="image/jpeg"/>
    <pubDate>Wed, 17 Jan 2024 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Continuous profiling: The key to more efficient and cost-effective applications]]></title>
    <description><![CDATA[In this post, we discuss why computational efficiency is important and how Elastic Universal Profiling enables your business to use continuous profiling in production environments to make the software that runs your business as efficient as possible.]]></description>
    <content:encoded><![CDATA[<p>Recently, Elastic Universal Profiling<sup>TM</sup> became <a href="https://www.elastic.co/blog/continuous-profiling-is-generally-available">generally available</a>. It is the part of our Observability solution that allows users to do <em>whole system, continuous profiling</em> in production environments. If you're not familiar with continuous profiling, you are probably wondering what Universal Profiling is and why you should care. That's what we will address in this post.</p>
<h2 id="efficiencyisimportantagain">Efficiency is important (again)</h2>
<p>Before we jump into continuous profiling, let's start with the "Why should I care?" question. To do that, I'd like to talk a bit about efficiency and some large-scale trends happening in our industry that are making efficiency, specifically computational efficiency, important again. I say again because in the past, when memory and storage on a computer was very limited and you had to worry about every byte of code, efficiency was an important aspect of developing software.</p>
<h3 id="theendofmooreslaw">The end of Moore’s Law</h3>
<p>First, the <a href="https://en.wikipedia.org/wiki/Moore's_law">Moore's Law</a> era is drawing to a close. This was inevitable simply due to physical limits of how small you can make a transistor and the connections between them. For a long time, software developers had the luxury of not worrying about complexity and efficiency because the next generation of hardware would mitigate any negative cost or performance impact.</p>
<p><em>If you can't rely on an endless progression of ever faster hardware, you should be interested in computational efficiency.</em></p>
<h3 id="themovetosoftwareasaservice">The move to Software-as-a-Service</h3>
<p>Another trend to consider is the shift from software vendors that sold customers software to run themselves to Software-as-a-Service businesses. A traditional software vendor didn't have to worry too much about the efficiency of their code. That issue largely fell to the customer to address; a new software version might dictate a hardware refresh to the latest and most performant. For a SaaS business, inefficient software usually degrades the customer’s experience and it certainly impacts the bottom line.</p>
<p><em>If you are a SaaS business in a competitive environment, you should be interested in computational efficiency.</em></p>
<h3 id="cloudmigration">Cloud migration</h3>
<p>Next is the ongoing <a href="https://www.elastic.co/observability/cloud-migration">cloud migration</a> to cloud computing. One of the benefits of cloud computing is the ease of scaling, both hardware and software. In the cloud, we are not constrained by the limits of our data centers or the next hardware purchase. Instead we simply spin up more cloud instances to mitigate performance problems. In addition to infrastructure scalability, microservices architectures, containerization, and the rise of Kubernetes and similar orchestration tools means that scaling services is simpler than ever. It's not uncommon to have thousands of instances of a service running in a cloud environment. This ease of scaling accounts for another trend, namely that many businesses are dealing with skyrocketing cloud computing costs.</p>
<p><em>If you are a business with ever increasing cloud costs, you should be interested in computational efficiency.</em></p>
<h3 id="ourchangingclimate">Our changing climate</h3>
<p>Lastly, if none of those reasons pique your interest, let's consider a global problem that all of us should have in mind — namely, climate change. There are many things that need to be addressed to tackle climate change, but with our dependence on software in every part of our society, computational efficiency is certainly something we should be thinking about.</p>
<p>Thomas Dullien, distinguished engineer at Elastic and one of the founders of Optymize points out that if you can save 20% on 800 servers, and assume 300W power consumption for each server, that code change is worth 160 metric tons of CO<sub>2</sub> saved per year. That may seem like a drop in the bucket but if all businesses focus more on computational efficiency, it will make an impact. Also, let's not forget the financial benefits: those 160 metric tons of CO<sub>2</sub> savings also represent a significant annual cost savings.</p>
<p><em>If you live on planet Earth, you should be interested in computational efficiency.</em></p>
<h2 id="performanceengineering">Performance engineering</h2>
<p>Who's job is it to worry about computational efficiency? Application developers usually pay at least some attention to efficiency as they develop their code. Profiling is a common approach for a developer to understand the performance of their code, and there is an entire portfolio of profiling tools available. Frequently, however, schedule pressures trump time spent on performance analysis and computational efficiency. In addition, performance problems may not become apparent until an application is running at scale in production and interacting (and competing) with everything else in that environment. Many profiling tools are not well suited to use in a production environment because they require code instrumentation and recompilation and add significant overhead.</p>
<p>When inefficient code makes it into production and begins to cause performance problems, the next line of defense is the Operations or SRE team. Their mission is to keep everything humming, and performance problems will certainly draw attention. Observability tools such as APM can shed light on these types of issues and lead the team to a specific application or service, but these tools have limits into the observability of the full system. Third-party libraries and operating system kernels functions remain hidden without a profiling solution in the production environment.</p>
<p>So, what can these teams do when there is a need to investigate a performance problem in production? That's where continuous profiling comes into the picture.</p>
<h2 id="continuousprofiling">Continuous profiling</h2>
<p>Continuous profiling is not a new idea. Google published a <a href="https://research.google/pubs/pub36575/">paper about it</a> in 2010 and began implementing continuous profiling in its environments around that time. Facebook and Netflix followed suit not long afterward.</p>
<p>Typically, continuous profiling tools have been the domain of dedicated performance engineering or operating system engineering teams, which are usually only found at extremely large scale enterprises like the ones mentioned above. The key idea is to run profiling on every server, all of the time. That way, when your observability tools point you to a specific part of an application, but you need a more detailed view into exactly where that application is consuming CPU resources, the profiling data will be there, ready to use.</p>
<p>Another benefit of continuous profiling is that it provides a view of CPU intensive software across your entire environment — whether that is a very CPU intensive function or the aggregate of a relatively small function that is run thousands of times a second in your environment.</p>
<p>While profiling tools are not new, most of them have significant gaps. Let's look at a couple of the most significant ones.</p>
<ul>
<li><strong>Limited visibility.</strong> Modern distributed applications are composed of a complex mix of building blocks, including custom software functions, third-party software libraries, networking software, operating system services, and more and more often, orchestration software such as <a href="https://kubernetes.io/">Kubernetes</a>. To fully understand what is happening in an application, you need visibility into each piece. However, even if a developer has the ability to profile their own code, everything else remains invisible. To make matters worse, most profiling tools require instrumenting the code, which adds overhead and therefore even your developers’ code is not profiled in production.</li>
<li><strong>Missing symbols in production.</strong> All of these pieces of code building blocks typically have descriptive names (some more intuitive than others) so that developers can understand and make sense of them. In a running program, these descriptive names are usually referred to as <strong>symbols</strong>. For a human being to make sense of the execution of a running application, these names are very important. Unfortunately, almost always, any software running in production has these human readable symbols stripped away for space efficiency since they are not needed by the CPU executing the software. Without all of the symbols, it makes it much more difficult to understand the full picture of what's happening in the application. To illustrate this, think of the last time you were in an SMS chat on your mobile device and you only had some of the people in the chat group in your address book while the rest simply appeared as phone numbers — this makes it very hard to tell who is saying what.</li>
</ul>
<h2 id="elasticuniversalprofilingcontinuousprofilingforall">Elastic Universal Profiling: Continuous profiling for all</h2>
<p>Our goal is to allow any business, large or small, to make computational efficiency a core consideration for all of the software that they run. Universal Profiling imposes very low overhead on your servers so it can be used in production and it provides visibility to everything running on every machine. It opens up the possibility of seeing the financial unit cost and CO<sub>2</sub> impact of every line of code running on every system in your business. How do we do that?</p>
<h3 id="wholesystemvisibilitysimple">Whole-system visibility — SIMPLE</h3>
<p>Universal Profiling is based on <a href="https://www.elastic.co/blog/ebpf-observability-security-workload-profiling">eBPF</a>, which means that it imposes very low overhead (our goal is less than 1% CPU and less than 250MB of RAM) on your servers because it doesn't require code instrumentation. That low overhead means it can be run continuously, on every server, even in production.</p>
<p>eBPF also lets us deploy a single profiler agent on a host and peek inside the operating system to see every line of code executing on the CPU. That means we have visibility into all of those application building blocks described above — the operating system itself as well as <a href="https://en.wikipedia.org/wiki/Containerization_(computing)">containerization and orchestration frameworks</a> without complex configuration.</p>
<h3 id="allthesymbols">All the symbols</h3>
<p>A key part of Universal Profiling is our hosted symbolization service. This means that symbols are not required on your servers, which not only eliminates a need for recompiling software with symbols, but it also helps to reduce overhead by allowing the Universal Profiling agent to send very sparse data back to the Elasticsearch platform where it is enriched with all of the missing symbols. Since we maintain a repository of most popular third-party software libraries and Linux operating system symbols, the Universal Profiling UI can show you all the symbols.</p>
<h3 id="yourfavoritelanguageandthensome">Your favorite language, and then some</h3>
<p>Universal Profiling is multilanguage. We support all of today’s popular programming languages, including Python, Go, Java (and any other JVM-based languages), Ruby, NodeJS, PHP, Perl, and of course, C and C++, which is critical since these languages still underly so many third-party libraries used by the other languages. In addition, we support profiling <a href="https://en.wikipedia.org/wiki/Machine_code">native code</a> a.k.a. machine language.</p>
<p>Speaking of native code, all profiling tools are tied to a specific type of CPU. Most tools today only support the Intel x86 CPU architecture. Universal Profiling supports both x86 and ARM-based processors. With the expanding use of ARM-based servers, especially in cloud environments, Universal Profiling future-proofs your continuous profiling.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt5fa0007bcb27d7b6/6a83ffb863e95931e374778e/elastic-blog-1-universal-profiling.png" alt="A flamegraph showing traces across Python, Native, Kernel, and Java code" /></p>
<p>Many businesses today employ polyglot programming — that is, they use multiple languages to build an application — and Universal Profiling is the only profiler available that can build a holistic view across all of these languages. This will help you look for hotspots in the environment, leading you to "unknown unknowns" that warrant deeper performance analysis. That might be a simple interest rate calculation that should be efficient and lightweight but, surprisingly, isn't. Or perhaps it is a service that is reused much more frequently than originally expected, resulting in thousands of instances running across your environment every second, making it a prime target for efficiency improvement.</p>
<h3 id="visualizeyourimpact">Visualize your impact</h3>
<p>Elastic Universal Profiling has an intuitive UI that immediately shows you the impact of any given function, including the time it spends executing on the CPU and how much that costs both in dollars and in carbon emissions.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt4073680455313cb0/6a83ffbcfc63ab419c655f39/elastic-blog-2-universal-profiling-flamegraph.png" alt="Annualized dollar cost and CO2 emissions for any function" /></p>
<p>Finally, with the level of software complexity in most production environments, there's a good chance that making a code change will have unanticipated effects across the environment. That code change may be due to a new feature being rolled out or a change to improve efficiency. In either case, a differential view, before and after the change, will help you understand the impact.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltbf09ac4457d6c078/6a83ffbf4c4bfb4aeacd6900/elastic-blog-3.png" alt="Performance, CO2, and cost improvements of a more efficient hashing function" /></p>
<h2 id="letsrecap">Let's recap</h2>
<p>Computational efficiency is an important topic, both from the perspective of the ultra-competitive business climate we all work in and from living through the challenges of our planet's changing climate. Improving efficiency can be a challenging endeavor, but we can't even begin to attempt to make improvements without knowing where to focus our efforts. Elastic Universal Profiling is here to provide every business with visibility into computational efficiency.</p>
<p>How will you use Elastic Universal Profiling in your business?</p>
<ul>
<li>If you are an application developer or part of the site reliability team, Universal Profiling will provide you with unprecedented visibility into your applications that will not only help you troubleshoot performance problems in production, but also understand the impact of new features and deliver an optimal user experience.</li>
<li>If you are involved in cloud and infrastructure financial management and capacity planning, Universal Profiling will provide you with unprecedented visibility into the unit cost of every line of code that your business runs.</li>
<li>If you are involved in your business’s <a href="https://www.elastic.co/blog/sustainability-elastic-6-months-reflection">ESG</a> initiative, Universal Profiling will provide you with unprecedented visibility into your CO<sub>2</sub> emissions and open up new avenues for reducing your carbon footprint.</li>
</ul>
<p>These are just a few examples. For more ideas, read how <a href="https://www.elastic.co/customers/appomni">AppOmni benefits from Elastic Universal Profiling</a>.</p>
<p>You can <a href="https://www.elastic.co/guide/en/observability/current/profiling-get-started.html">get started</a> with Elastic Universal Profiling right now!</p>
<p><em>The release and timing of any features or functionality described in this post remain at Elastic's sole discretion. Any features or functionality not currently available may not be delivered on time or at all.</em></p>]]></content:encoded>
    <link>https://www.elastic.co/observability-labs/blog/continuous-profiling-efficient-cost-effective-applications</link>
    <guid isPermaLink="false">continuous-profiling-efficient-cost-effective-applications</guid>
    <category><![CDATA[APM]]></category>
    <category><![CDATA[Infrastructure Monitoring]]></category>
    <category><![CDATA[Data Management]]></category>
    <dc:creator><![CDATA[John Knoepfle]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt8efe6e25a7389fad/6a83ffc2227b1ce0985a173b/the-end-of-databases-A_(1).jpg" length="0" type="image/jpeg"/>
    <pubDate>Fri, 27 Oct 2023 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[How to deploy a Hello World web app with Elastic Observability on Azure Container Apps]]></title>
    <description><![CDATA[Follow the step-by-step process of instrumenting Elastic Observability for a Hello World web app running on Azure Container Apps.]]></description>
    <content:encoded><![CDATA[<p>Elastic Observability is the optimal tool to provide visibility into your running web apps. Microsoft Azure Container Apps is a fully managed environment that enables you to run containerized applications on a serverless platform so that your applications scale up and down. This allows you to accomplish the dual objective of serving every customer’s need for availability while meeting your needs to do so as efficiently as possible.</p>
<p>Using Elastic Observability and Azure Container Apps is a perfect combination for developers to deploy <a href="https://www.elastic.co/blog/observability-powerful-flexible-efficient">web apps that are auto-scaled with fully observable operations</a>.</p>
<p>This blog post will show you how to deploy a simple Hello World web app to Azure Container Apps and then walk you through the steps to instrument the Hello World web app to enable observation of the application’s operations with Elastic Cloud.</p>
<h2 id="elasticobservabilitysetup">Elastic Observability setup</h2>
<p>We’ll start with setting up an Elastic Cloud deployment, which is where observability will take place for the web app we’ll be deploying.</p>
<p>From the <a href="https://cloud.elastic.co">Elastic Cloud console</a>, select <strong>Create deployment</strong>.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt04a8dfdb151a3d22/6a85cb6933f244f73949f516/elastic-blog-1-create-deployment.png" alt="create deployment" /></p>
<p>Enter a deployment name and click <strong>Create deployment</strong>. It takes a few minutes for your deployment to be created. While waiting, you are prompted to save the admin credentials for your deployment, which provides you with superuser access to your Elastic® deployment. Keep these credentials safe as they are shown only once.</p>
<p>Elastic Observability requires an APM Server URL and an APM Secret token for an app to send observability data to Elastic Cloud. Once the deployment is created, we’ll copy the Elastic Observability server URL and secret token and store them somewhere safely for adding to our web app code in a later step.</p>
<p>To copy the APM Server URL and the APM Secret Token, go to <a href="https://cloud.elastic.co/home">Elastic Cloud</a> . Then go to the <a href="https://cloud.elastic.co/deployments">Deployments</a> page, which lists all of the deployments you have created. Select the deployment you want to use, which will open the deployment details page. In the <strong>Kibana</strong> row of links, click on <strong>Open</strong> to open Kibana® for your deployment.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt47482afb48a1d23e/6a85cb6c682666f0011eac15/elastic-blog-2-my-deployment.png" alt="my deployment" /></p>
<p>Select <strong>Integrations</strong> from the top-level menu. Then click the <strong>APM</strong> tile.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt45a4aa16f5df8769/6a85cb6fd7b2e73943fe84de/elastic-blog-3-apm.png" alt="apm" /></p>
<p>On the APM Agents page, copy the secretToken and the serverUrl values and save them for use in a later step.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blte655ab8445d7c133/6a85cb72f9373d073096f5a8/elastic-blog-4-apm-agents.png" alt="apm agents" /></p>
<p>Now that we’ve completed the Elastic Cloud setup, the next step is to set up our account in Azure for deploying apps to the Container Apps service.</p>
<h2 id="azurecontainerappssetup">Azure Container Apps setup</h2>
<p>First we’ll need an Azure account, so let’s create one by going to the <a href="https://azure.microsoft.com">Microsoft Azure portal</a> and creating a new project. Click the <strong>Start free</strong> button and follow the steps to sign in or create a new account.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt91f340f08b739bd9/6a85cb75f61d6e74669c2b2f/elastic-blog-5-azure-start-free.png" alt="azure start free" /></p>
<h2 id="deployahelloworldwebapptocontainerapps">Deploy a Hello World web app to Container Apps</h2>
<p>We’ll perform the process of deploying a C# Hello World web app to Container Apps using the handy Azure tool called <a href="https://azure.microsoft.com/en-us/get-started/azure-portal/cloud-shell">Cloud Shell</a>. To deploy the Hello World app, we’ll perform the following 12 steps:</p>
<ol>
<li>From the <a href="https://portal.azure.com/">Azure portal</a>, click the Cloud Shell icon at the top of the portal to open Cloud Shell…</li>
</ol>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltc019a1fdbed88bc1/6a85cb789d2b71346af93990/elastic-blog-6-cloud-shell.png" alt="cloud shell" /></p>
<p>… and when the Cloud Shell first opens, select <strong>Bash</strong> as the shell type to use.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt9dabc7b5e45e53d2/6a85cb7b80984c73bd668fe8/elastic-blog-7-bash.png" alt="bash" /></p>
<ol>
<li>If you’re prompted that “You have no storage mounted,” then click the <strong>Create storage</strong> button to create a file store to be used for saving and editing files from Cloud Shell.</li>
</ol>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt81412588a4041647/6a85cb7e07829085b4321778/elastic-blog-8-create-storage.png" alt="create storage" /></p>
<ol>
<li>In Cloud Shell, clone a <a href="https://github.com/elastic/observability-examples/tree/main/azure/container-apps/helloworld">C# Hello World sample app</a> repo from GitHub by entering the following command.</li>
</ol>
<pre><code>git clone https://github.com/elastic/observability-examples
</code></pre>
<ol>
<li>Change directory to the location of the Hello World web app code.</li>
</ol>
<pre><code>cd observability-examples/azure/container-apps/helloworld
</code></pre>
<ol>
<li>Define the environment variables that we’ll be using in the commands throughout this blog post.</li>
</ol>
<pre><code>RESOURCE_GROUP="helloworld-containerapps"
LOCATION="centralus"
ENVIRONMENT="env-helloworld-containerapps"
APP_NAME="elastic-helloworld"
</code></pre>
<ol>
<li>Define a registry container name that is unique by running the following command.</li>
</ol>
<pre><code>ACR_NAME="helloworld"$RANDOM
</code></pre>
<ol>
<li>Create an Azure resource group by running the following command.</li>
</ol>
<pre><code>az group create --name $RESOURCE_GROUP --location "$LOCATION"
</code></pre>
<ol>
<li>Run the following command to create a registry container in Azure Container Registry.</li>
</ol>
<pre><code>az acr create --resource-group $RESOURCE_GROUP \
--name $ACR_NAME --sku Basic --admin-enable true
</code></pre>
<ol>
<li>Build the app image and push it to Azure Container Registry by running the following command.</li>
</ol>
<pre><code>az acr build --registry $ACR_NAME --image $APP_NAME .
</code></pre>
<ol>
<li>Register the Microsoft.OperationalInsights namespace as a provider by running the following command.</li>
</ol>
<pre><code>az provider register -n Microsoft.OperationalInsights --wait
</code></pre>
<ol>
<li>Run the following command to create a Container App environment for deploying your app into.</li>
</ol>
<pre><code>az containerapp env create --name $ENVIRONMENT \
--resource-group $RESOURCE_GROUP --location "$LOCATION"
</code></pre>
<ol>
<li>Create a new Container App by deploying the Hello World app’s image to Container Apps, using the following command.</li>
</ol>
<pre><code>az containerapp create \
  --name $APP_NAME \
  --resource-group $RESOURCE_GROUP \
  --environment $ENVIRONMENT \
  --image $ACR_NAME.azurecr.io/$APP_NAME \
  --target-port 3500 \
  --ingress 'external' \
  --registry-server $ACR_NAME.azurecr.io \
  --query properties.configuration.ingress.fqdn
</code></pre>
<p>This command will output the deployed Hello World app's fully qualified domain name (FQDN). Copy and paste the FQDN into a browser to see your running Hello World app.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt3b75b6f92e5b965a/6a85cb812d64d55903081d4e/elastic-blog-9-hello-world.png" alt="hello world" /></p>
<h2 id="instrumentthehelloworldwebappwithelasticobservability">Instrument the Hello World web app with Elastic Observability</h2>
<p>With a web app successfully running in Container Apps, we’re now ready to add the minimal code necessary to enable observability for the Hello World app in Elastic Cloud. We’ll perform the following eight steps:</p>
<ol>
<li>In Azure Cloud Shell, create a new file named Telemetry.cs by typing the following command.</li>
</ol>
<pre><code>touch Telemetry.cs
</code></pre>
<ol>
<li>Open the Azure Cloud Shell file editor by typing the following command in Cloud Shell.</li>
</ol>
<pre><code>code .
</code></pre>
<ol>
<li>In the Azure Cloud Shell editor, open the Telemetry.cs file and paste in the following code. Save the edited file in Cloud Shell by pressing the [Ctrl] + [s] keys on your keyboard (or if you’re on a macOS computer, use the [⌘] + [s] keys). This class file is used to create a tracer ActivitySource, which can generate trace Activity spans for observability.</li>
</ol>
<pre><code>using System.Diagnostics;

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

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

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

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

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

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

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

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

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

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

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

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

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

WORKDIR /app

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

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

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

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

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

app = Flask(__name__)

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

@app.after_request
def after_request(response):
    with tracer.start_as_current_span("bye"):
        logging.info("goodbye")
        return response
</code></pre>
<p>Note: You can close the nano text editor and save the file by typing “Ctrl + x”. Press the “y” key and then the “Enter” key to save the changes.</p>
<ol>
<li>Rebuild the updated Hello World sample app using Docker from within the application’s directory. Run the following command in CloudShell.</li>
</ol>
<pre><code>sudo docker build -t elastic-helloworld/web .
</code></pre>
<ol>
<li>Tag the application image using Docker. Replace \&lt;account_id&gt; with your AWS Account ID in the Docker command below and then run it in CloudShell.</li>
</ol>
<pre><code>sudo docker tag elastic-helloworld/web:latest &lt;account_id&gt;.dkr.ecr.us-east-2.amazonaws.com/elastic-helloworld/web:latest
</code></pre>
<ol>
<li>Push the updated application image to ECR. Replace \&lt;account_id&gt; with your AWS Account ID in the Docker command below and then run it in CloudShell.</li>
</ol>
<pre><code>sudo docker push &lt;account_id&gt;.dkr.ecr.us-east-2.amazonaws.com/elastic-helloworld/web:latest
</code></pre>
<p>Pushing the image to ECR will automatically deploy the new version of the Hello World app.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt0a8c9b9db2a545ab/6a85c8a711893c1df1a7ab32/elastic-blog-15-green-banner-successfully.png" alt="15 green banner successful deployment" /></p>
<p>Open the <a href="http://console.aws.amazon.com/apprunner">App Runner</a> console. After a few minutes, the Hello World app will be deployed to App Runner. Click the <strong>Default domain</strong> URL to view the updated Hello World app running in App Runner.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt65db284a7eb00d66/6a85c8aaf61d6eb6bb9c2ac3/elastic-blog-16-elastic-logo-text-top.png" alt="16 elastic" /></p>
<h2 id="observethehelloworldwebapp">Observe the Hello World web app</h2>
<p>Now that we’ve instrumented the web app to send observability data to Elastic Observability, we can now use Elastic Cloud to monitor the web app’s operations.</p>
<ol>
<li><p>In Elastic Cloud, select the Observability <strong>Services</strong> menu item.</p></li>
<li><p>Click the <strong>helloworld</strong> service.</p></li>
<li><p>Click the <strong>Transactions</strong> tab.</p></li>
<li><p>Scroll down and click the <strong>“/”</strong> transaction.</p></li>
<li><p>Scroll down to the Trace Sample section to see the <strong>“/,” “hi,”</strong> and <strong>“bye”</strong> trace samples.</p></li>
</ol>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt6aea5ad7fcd9fd2c/6a85c8ad33f244bad049f4c0/elastic-blog-17-trace-sample.png" alt="17 trace sample" /></p>
<h2 id="observabilitymadetoscale">Observability made to scale</h2>
<p>You’ve seen the complete process of deploying a web app to AWS App Runner that is instrumented with Elastic Observability. The end result is a web app that will scale up and down with usage, combined with the observability tools to monitor the web app as it serves one user or millions of users.</p>
<p>Now that you’ve seen how to deploy a serverless web app instrumented with observability, visit <a href="https://www.elastic.co/observability">Elastic Observability</a> to learn more about how to implement a complete observability solution for your apps. Or visit <a href="https://www.elastic.co/getting-started/aws">Getting started with Elastic on AWS</a> for more examples of how you can drive the data insights you need by combining AWS’s cloud computing services with Elastic’s search-powered platform.</p>
<p><em>The release and timing of any features or functionality described in this post remain at Elastic's sole discretion. Any features or functionality not currently available may not be delivered on time or at all.</em></p>]]></content:encoded>
    <link>https://www.elastic.co/observability-labs/blog/deploy-app-observability-aws-app-runner</link>
    <guid isPermaLink="false">deploy-app-observability-aws-app-runner</guid>
    <category><![CDATA[APM]]></category>
    <category><![CDATA[Infrastructure Monitoring]]></category>
    <dc:creator><![CDATA[Jonathan Simon]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt5b613993e5cae1c3/6a85c8b0f61d6ecd539c2ad1/library-branding-elastic-observability-white-1680x980.png" length="0" type="image/png"/>
    <pubDate>Mon, 02 Oct 2023 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Unlocking whole-system visibility with Elastic Universal Profiling™]]></title>
    <description><![CDATA[Visual profiling data can be overwhelming. This blog post aims to demystify continuous profiling and guide you through its unique visualizations. We will equip you with the knowledge to derive quick, actionable insights from Universal Profiling™.]]></description>
    <content:encoded><![CDATA[<h2 id="identifyoptimizemeasurerepeat">Identify, optimize, measure, repeat!</h2>
<p>SREs and developers who want to maintain robust, efficient systems and achieve optimal code performance need effective tools to measure and improve code performance. Profilers are invaluable for these tasks, as they can help you boost your app's throughput, ensure consistent system reliability, and gain a deeper understanding of your code's behavior at runtime. However, traditional profilers can be cumbersome to use, as they often require code recompilation and are limited to specific languages. Additionally, they can also have a high overhead that negatively affects performance and makes them less suitable for quick, real-time debugging in production environments.</p>
<p>To address the limitations of traditional profilers, Elastic<sup>®</sup> recently <a href="https://www.elastic.co/blog/continuous-profiling-is-generally-available">announced the general availability of Elastic Universal Profiling</a>, a <a href="https://www.elastic.co/observability/universal-profiling">continuous profiling</a> product that is refreshingly straightforward to use, eliminating the need for instrumentation, recompilations, or restarts. Moreover, Elastic Universal Profiling does not require on-host debug symbols and is language-agnostic, allowing you to profile any process running on your machines — from your application's code to third-party libraries and even kernel functions.</p>
<p>However, even the most advanced tools require a certain level of expertise to interpret the data effectively. The wealth of visual profiling data — flamegraphs, stacktraces, or functions — can initially seem overwhelming. This blog post aims to demystify <a href="https://www.elastic.co/observability/universal-profiling">continuous profiling</a> and guide you through its unique visualizations. We will equip you with the knowledge to derive quick, actionable insights from Universal Profiling.</p>
<p>Let’s begin.</p>
<h2 id="stacktracesthecornerstoneforprofiling">Stacktraces: The cornerstone for profiling</h2>
<h3 id="itallbeginswithastacktraceasnapshotcapturingthecascadeoffunctioncalls">It all begins with a stacktrace — a snapshot capturing the cascade of function calls.</h3>
<p>A stacktrace is a snapshot of the call stack of an application at a specific point in time. It captures the sequence of function calls that the program has made up to that point. In this way, a stacktrace serves as a historical record of the call stack, allowing you to trace back the steps that led to a particular state in your application.</p>
<p>Further, stacktraces are the foundational data structure that profilers rely on to determine what an application is executing at any given moment. This is particularly useful when, for instance, your infrastructure monitoring indicates that your application servers are consuming 95% of CPU resources. While utilities such as 'top -H' can show the top processes that are consuming CPU, they lack the granularity needed to identify the specific lines of code (in the top process) responsible for the high usage.</p>
<p>In the case of Elastic Universal Profiling, <a href="https://www.elastic.co/blog/ebpf-observability-security-workload-profiling">eBPF is used</a> to perform sampling of every process that is keeping a CPU core busy. Unlike most instrumentation profilers that focus solely on your application code, Elastic Universal Profiling provides whole-system visibility — it profiles not just your code, but also code you don't own, including third-party libraries and even kernel operations.</p>
<p>The diagram below shows how the Universal Profiling agent works at a very high level. Step 5 indicates the ingestion of the stacktraces into the profiling collector, a new part of the Elastic Stack.</p>
<p> <strong>Just</strong>  <a href="https://www.elastic.co/guide/en/observability/current/profiling-get-started.html"> <strong>deploy the profiling host agent</strong> </a> <strong>and receive profiling data (in Kibana</strong><em><sup><em>®</em></sup></em><strong>) a few minutes later.</strong> <a href="https://www.elastic.co/guide/en/observability/current/profiling-get-started.html"> <strong>Get started now</strong> </a> <strong>.</strong> </p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt24a35acab2bfac24/6a7f1c8eeab5be2f8520ab54/elastic-blog-1-flowchart-linux.png" alt="High-level depiction of how the profiling agent works" /></p>
<ol>
<li><p>Unwinder eBPF programs (bytecode) are sent to the kernel.</p></li>
<li><p>The kernel verifies that the BPF program is safe. If accepted, the program is attached to the probes and executed when the event occurs.</p></li>
<li><p>The eBPF programs pass the collected data to userspace via maps.</p></li>
<li><p>The agent reads the collected data from maps. The data transferred from the agent to the maps are process-specific and interpreter-specific meta-information that help the eBPF unwinder programs perform unwinding.</p></li>
<li><p>Stacktraces, metrics, and metadata are pushed to the Elastic Stack.</p></li>
<li><p>Visualize data as flamegraphs, stacktraces, and functions via Kibana.</p></li>
</ol>
<p>While stacktraces are the key ingredient for most profiling tools, interpreting them can be tricky. Let's take a look at a simple example to make things a bit easier. The table below shows a group of stacktraces from a Java application and assigns each a percentage to indicate its share of CPU time consumption.</p>
<p><strong>Table 1: Grouped Stacktraces with CPU Time Percentage</strong></p>
<p>| Percentage | Function Calls                                                                                                                                 |
| ---------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
| 60%        | startApp -&gt; authenticateUser -&gt; processTransaction                                                                                           |
| 20%        | startApp -&gt; loadAccountDetails -&gt; fetchRecentTransactions                                                                                    |
| 10%        | startApp -&gt; authenticateUser -&gt; processTransaction -&gt; verifyFunds                                                                           |
| 2%         | startApp -&gt; authenticateUser -&gt; processTransaction -&gt;libjvm.so                                                                              |
| 1%         | startApp -&gt; authenticateUser -&gt; processTransaction -&gt;libjvm.so -&gt;vmlinux: asm_common_interrupt -&gt;vmlinux: asm_sysvec_apic_timer_interrupt |</p>
<p>The percentages above represent the relative frequency of each specific stacktrace compared to the total number of stacktraces collected over the observation period, not actual CPU usage percentages. Also, the libjvm.so and kernel frames (vmlinux:*) in the example are commonly observed with whole-system profilers like Elastic Universal Profiling.</p>
<p>Also, we can see that <strong>60%</strong> of the time is spent in the sequence startApp; authenticateUser; processTransaction. An additional <strong>10%</strong> of the processing time is allocated to verifyFunds, a function invoked by processTransaction. Given these observations, it becomes evident that optimization initiatives would yield the most impact if centered on the processTransaction function, as it is one of the most expensive functions. However, real-world stacktraces can be far more intricate than this example. So how do we make sense of them quickly? The answer to this problem resulted in the creation of flamegraphs.</p>
<h2 id="flamegraphsavisualizationofstacktraces">Flamegraphs: A visualization of stacktraces</h2>
<p>While the above example may appear straightforward, it scarcely reflects the complexities encountered when aggregating multiple stacktraces across a fleet of machines on a continuous basis. The depth of the stack traces and the numerous branching paths can make it increasingly difficult to pinpoint where code is consuming resources. This is where flamegraphs, a concept popularized by <a href="https://www.brendangregg.com/flamegraphs.html">Brendan Gregg</a>, come into play.</p>
<p>A flamegraph is a visual interpretation of stacktraces, designed to quickly and accurately identify the functions that are consuming the most resources. Each function is represented by a rectangle, where the width of the rectangle represents the amount of time spent in the function, and the number of stacked rectangles represents the stack depth. The stack depth is the number of functions that were called to reach the current function.</p>
<p>Elastic Universal Profiling uses icicle graphs, which is an inverted variant of the standard flamegraph. In an icicle graph, the root function is at the top, and its child functions are shown below their parents –– making it easier to see the hierarchy of functions and how they are related to each other.</p>
<p>In most flamegraphs, the y-axis represents stack depth, but there is no standardization for the x-axis. Some profiling tools use the x-axis to indicate the passage of time; in these instances, the graph is more accurately termed a flame chart. Others sort the x-axis alphabetically. Universal Profiling sorts functions on the x-axis based on relative CPU percentage utilization, starting with the function that consumes the most CPU time on the left, as shown in the example icicle graph below.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blta62f1f38e20b961a/6a7f1c9277b034046c3ff951/elastic-blog-2-cpu-time.png" alt="Example icicle graph: The percentage represents relative CPU time, not the real CPU usage time. " /></p>
<h2 id="debuggingandoptimizingperformanceissuesstacktracestopnfunctionsflamegraphs">Debugging and optimizing performance issues: Stacktraces, TopN functions, flamegraphs</h2>
<p>SREs and SWEs can use Universal Profiling for troubleshooting, debugging, and performance optimization. It builds stacktraces that go from the kernel, through userspace native code, all the way into code running in higher level runtimes, enabling you to <strong>identify performance regressions</strong> , <strong>reduce wasteful computations</strong> , and <strong>debug complex issues faster</strong>.</p>
<p>To this end, Universal Profiling offers three main visualizations: Stacktraces, TopN Functions, and flamegraphs.</p>
<h3 id="stacktraceview">Stacktrace view</h3>
<p>The stacktraces view shows grouped stacktrace graphs by threads, hosts, Kubernetes deployments, and containers. It can be used to detect unexpected CPU spikes across threads and drill down into a smaller time range to investigate further with a flamegraph. Refer to the <a href="https://www.elastic.co/guide/en/observability/current/universal-profiling.html#profiling-stacktraces-intro">documentation</a> for details.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt08b2152aae420c59/6a7f1c9542a117f46295c357/elastic-blog-3-wave-patterns.png" alt="Notice the wave pattern in the stacktrace view, enabling you to drill down into a CPU spike " /></p>
<h3 id="topnfunctionsview">TopN functions view</h3>
<p>Universal Profiling's topN functions view shows the most frequently sampled functions, broken down by CPU time, annualized CO<sub>2</sub>, and annualized cost estimates. You can use this view to identify the most expensive functions across your entire fleet, and then apply filters to focus on specific components for a more detailed analysis. Clicking on a function name will redirect you to the flamegraph, enabling you to examine the call hierarchy.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltbf146dc55ba6a784/6a7f1c98e3a219591799f8f8/elastic-blog-4-topN-functions-page.png" alt="TopN functions page" /></p>
<h3 id="flamegraphsview">Flamegraphs view</h3>
<p>The flamegraph page is where you will most likely spend the most time, especially when debugging and optimizing. We recommend that you use the guide below to identify performance bottlenecks and optimization opportunities with flamegraphs. The three key elements-conditions to look for are <strong>width</strong> , <strong>hierarchy</strong> , and <strong>height</strong>.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltfb9411cc4265f87a/6a7f1c9b6693f8465b6643c5/elastic-blog-5-icivle-flamegraph.png" alt="Icicle flamegraph: We use the colors to determine different types of code (e.g., native, interpreted, kernel)." /></p>
<p><strong>Width matters:</strong> In icicle graphs, wider rectangles signify functions taking up more CPU time. Always read the graph from left to right and note the widest rectangles, as these are the prime hot spots.</p>
<p><strong>Hierarchy matters:</strong> Navigate the graph's stack to understand function relationships. This vertical examination will help you identify whether one or multiple functions are responsible for performance bottlenecks. This could also uncover opportunities for code improvements, such as swapping an inefficient library or avoiding unnecessary I/O operations.</p>
<p><strong>Height matters:</strong> Elevated or tall stacks in the graph usually point to deep call hierarchies. These can be an indicator of complex and less efficient code structures that may require attention.</p>
<p>Also, when navigating a flamegraph, you may want to look for specific function names to validate your assumptions on their presence: in the Universal Profiling flamegraphs view, there is a “Search” bar at the bottom left corner of the view. You can input a regex, and the match will be highlighted in the flamegraph; by clicking on the left and right arrows next to the Search bar, you can move across the occurrences on the flamegraph and spot callers and callee of the matched function.</p>
<p>In summary,</p>
<ul>
<li><strong>Scan</strong> horizontally from left to right, focusing on width for CPU-intensive functions.</li>
<li><strong>Examine</strong> vertically to examine the stack and spot bottlenecks.</li>
<li><strong>Look</strong> for <strong>towering stacks</strong> to identify potential complexities in the code.</li>
</ul>
<p>To recap, use topN functions to generate optimization hypotheses and validate them with stacktraces and/or flamegraphs. Use stacktraces to monitor CPU utilization trends and to delve into the finer details. Use flamegraphs to quickly debug and optimize your code, using width, hierarchy, and height as guides.</p>
<p> <strong>Identify. Optimize. Measure. Repeat!</strong> </p>
<h2 id="measuretheimpactofyourchange">Measure the impact of your change</h2>
<h3 id="fortheveryfirsttimeinhistorydeveloperscannowmeasuretheperformancegainedorlostcloudcostandcarbonfootprintimpactofeverydeployedchange">For the very first time in history, developers can now measure the performance (gained or lost), cloud cost, and carbon footprint impact of every deployed change.</h3>
<p>Once you have identified a performance issue and applied fixes or optimizations to your code, it is essential to measure the impact of your changes. The differential topN functions and differential flamegraph pages are invaluable for this, as they can help you identify regressions and measure your change impact not only in terms of performance but also in terms of carbon emissions and cost savings.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt090bc5faa9451173/6a7f1c9f5967e558185dd71d/elastic-blog-6-uni-profiling.png" alt="A differential function view, showing the performance, CO2, and cost impact of a change" /></p>
<p>The Diff column indicates a change in the function’s rank.</p>
<p>You may need to use tags or other metadata, such as container and deployment name, in combination with time ranges to differentiate between the optimized and non-optimized changes.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blteef982ebf02792db/6a7f1ca33cab1c1c860e4cdb/elastic-blog-7-differential-flamegraph.png" alt="A differential flamegraph showing regression in A/B testing" /></p>
<h2 id="universalprofilingthekeytooptimizingapplicationresources">Universal Profiling: The key to optimizing application resources</h2>
<p>Computational efficiency is no longer just a nice-to-have, but a must-have from both a financial and environmental sustainability perspective. Elastic Universal Profiling provides unprecedented visibility into the runtime behavior of all your applications, so you can identify and optimize the most resource-intensive areas of your code. The result is not merely better-performing software but also reduced resource consumption, lower cloud costs, and a reduction in carbon footprint. Optimizing your code with Universal Profiling is not only the right thing to do for your business, it’s the right thing to do for our world.</p>
<p><a href="https://www.elastic.co/guide/en/observability/current/profiling-get-started.html">Get started</a> with Elastic Universal Profiling today.</p>
<p><em>The release and timing of any features or functionality described in this post remain at Elastic's sole discretion. Any features or functionality not currently available may not be delivered on time or at all.</em></p>]]></content:encoded>
    <link>https://www.elastic.co/observability-labs/blog/whole-system-visibility-elastic-universal-profiling</link>
    <guid isPermaLink="false">whole-system-visibility-elastic-universal-profiling</guid>
    <category><![CDATA[APM]]></category>
    <dc:creator><![CDATA[Israel Ogbole,Francesco Gualazzi]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltfac2dbc805d03c16/6a7f1ca6b6b7348344e49212/universal-profiling-blog-720x420.jpg" length="0" type="image/jpeg"/>
    <pubDate>Mon, 25 Sep 2023 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Native OpenTelemetry support in Elastic Observability]]></title>
    <description><![CDATA[Elastic offers native support for OpenTelemetry by allowing for direct ingest of OpenTelemetry traces, metrics, and logs without conversion, and applying any Elastic feature against OTel data without degradation in capabilities.]]></description>
    <content:encoded><![CDATA[<p>NOTE: Since writing this blog, new OTel data ingest configurations are now available in Elastic. See recent <a href="https://www.elastic.co/observability-labs/blog/elastic-opentelemetry-otel-operator">blog</a></p>
<p>OpenTelemetry is more than just becoming the open ingestion standard for observability. As one of the major Cloud Native Computing Foundation (CNCF) projects, with as many commits as Kubernetes, it is gaining support from major ISVs and cloud providers delivering support for the framework. Many global companies from finance, insurance, tech, and other industries are starting to standardize on OpenTelemetry. With OpenTelemetry, DevOps teams have a consistent approach to collecting and ingesting telemetry data providing a de-facto standard for observability.</p>
<p>Elastic<sup>®</sup> is strategically standardizing on OpenTelemetry for the main data collection architecture for observability and security. Additionally, Elastic is making a commitment to help OpenTelemetry become the best de facto data collection infrastructure for the observability ecosystem. Elastic is deepening its relationship with OpenTelemetry beyond the recent contribution of Elastic Common Schema (ECS) to OpenTelemetry (OTel).</p>
<p>Today, Elastic supports OpenTelemetry natively, since Elastic 7.14, by being able to directly ingest OpenTelemetry protocol (OTLP) based traces, metrics, and logs.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt68f6108956523f81/6a7f0e5ffc63ab7fae64cd0f/elastic-blog-1-otel-config-options.png" alt="otel configuration options" /></p>
<p>In this blog, we’ll review the current OpenTelemetry support provided by Elastic, which includes the following:</p>
<ul>
<li><a href="https://www.elastic.co/observability-labs/blog/native-opentelemetry-support-in-elastic-observability#ingesting-opentelemetry-into-elastic"><strong>Easy ingest of distributed tracing and metrics</strong></a> for applications configured with OpenTelemetry agents for Python, NodeJS, Java, Go, and .NET</li>
<li><a href="https://www.elastic.co/observability-labs/blog/native-opentelemetry-support-in-elastic-observability#opentelemetry-logs-in-elastic"><strong>OpenTelemetry logs instrumentation and ingest</strong></a> using various configurations</li>
<li><a href="https://www.elastic.co/observability-labs/blog/native-opentelemetry-support-in-elastic-observability#opentelemetry-is-elastics-preferred-schema"><strong>Open semantic conventions</strong></a> for logs and more through ECS, which is not part of OpenTelemetry</li>
<li><a href="https://www.elastic.co/observability-labs/blog/native-opentelemetry-support-in-elastic-observability#elastic-observability-apm-and-machine-learning-capabilities"><strong>Machine learning based AIOps capabilities</strong></a>, such as latency correlations, failure correlations, anomaly detection, log spike analysis, predictive pattern analysis, Elastic AI Assistant support, and more, all apply to native OTLP telemetry.</li>
<li><a href="https://www.elastic.co/observability-labs/blog/native-opentelemetry-support-in-elastic-observability#elastic-allows-you-to-migrate-to-otel-on-your-schedule"><strong>Migrate applications to OpenTelemetry at your own speed</strong></a>. Elastic’s APM capabilities all work seamlessly even with a mix of services using OpenTelemetry and/or Elastic APM agents. You can even combine OpenTelemetry instrumentation with Elastic Agent.</li>
<li><a href="https://www.elastic.co/observability-labs/blog/native-opentelemetry-support-in-elastic-observability#integrated-kubernetes-and-opentelemetry-views-in-elastic"><strong>Integrated views and analysis with Kubernetes clusters</strong></a>, which most OpenTelemetry applications are running on. Elastic can highlight specific pods and containers related to each service when analyzing issues for applications based on OpenTelemetry.</li>
</ul>
<h2 id="ingestingopentelemetryintoelastic">Ingesting OpenTelemetry into Elastic</h2>
<p>If you’re interested in seeing how simple it is to ingest OpenTelemetry traces and metrics into Elastic, follow the steps outlined in this blog.</p>
<p>Let’s outline what Elastic provides for ingesting OpenTelemetry data. Here are all your options:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blta805e805b620c7c6/6a7f0e61c2cc0960a8249626/elastic-blog-2-flowchart.png" alt="flowchart" /></p>
<h3 id="usingtheopentelemetrycollector">Using the OpenTelemetry Collector</h3>
<p>When using the OpenTelemetry Collector, which is the most common configuration option, you simply have to add two key variables.</p>
<p>The instructions utilize a specific opentelemetry-collector configuration for Elastic. Essentially, the Elastic <a href="https://github.com/elastic/opentelemetry-demo/blob/main/kubernetes/elastic-helm/values.yaml">values.yaml</a> file specified in the elastic/opentelemetry-demo configure the opentelemetry-collector to point to the Elastic APM Server using two main values:</p>
<p>OTEL_EXPORTER_OTLP_ENDPOINT is Elastic’s APM Server<br />
OTEL_EXPORTER_OTLP_HEADERS Elastic Authorization</p>
<p>These two values can be found in the OpenTelemetry setup instructions under the APM integration instructions (Integrations-&gt;APM) in your Elastic Cloud.</p>
<h3 id="nativeopentelemetryagentsembeddedincode">Native OpenTelemetry agents embedded in code</h3>
<p>If you are thinking of using OpenTelemetry libraries in your code, you can simply point the service to Elastic’s APM server, because it supports native OLTP protocol. No special Elastic conversion is needed.</p>
<p>To demonstrate this effectively and provide some education on how to use OpenTelemetry, we have two applications you can use to learn from:</p>
<ul>
<li><a href="https://github.com/elastic/opentelemetry-demo">Elastic’s version of OpenTelemetry demo</a>: As with all the other observability vendors, we have our own forked version of the OpenTelemetry demo.</li>
<li><a href="https://github.com/elastic/workshops-instruqt/tree/main/Elastiflix">Elastiflix:</a> This demo application is an example to help you learn how to instrument on various languages and telemetry signals.</li>
</ul>
<p>Check out our blogs on using the Elastiflix application and instrumenting with OpenTelemetry:</p>
<ul>
<li><a href="https://www.elastic.co/blog/getting-started-opentelemetry-instrumentation-sample-app">Elastiflix application</a>, a guide to instrument different languages with OpenTelemetry</li>
<li>Python: <a href="https://www.elastic.co/blog/auto-instrumentation-of-python-applications-opentelemetry">Auto-instrumentation</a>, <a href="https://www.elastic.co/blog/manual-instrumentation-of-python-applications-opentelemetry">Manual-instrumentation</a></li>
<li>Java: <a href="https://www.elastic.co/blog/auto-instrumentation-of-java-applications-opentelemetry">Auto-instrumentation</a>, <a href="https://www.elastic.co/blog/manual-instrumentation-of-java-applications-opentelemetry">Manual-instrumentation</a></li>
<li>Node.js: <a href="https://www.elastic.co/blog/auto-instrument-nodejs-applications-opentelemetry">Auto-instrumentation</a>, <a href="https://www.elastic.co/blog/manual-instrumentation-of-nodejs-applications-opentelemetry">Manual-instrumentation</a></li>
<li>.NET: <a href="https://www.elastic.co/blog/auto-instrumentation-of-net-applications-opentelemetry">Auto-instrumentation</a>, <a href="https://www.elastic.co/blog/manual-instrumentation-of-net-applications-opentelemetry">Manual-instrumentation</a></li>
</ul>
<p>We have created YouTube videos on these topics as well:</p>
<ul>
<li><a href="https://youtu.be/wMXMRsjFg-8?feature=shared">How to Manually Instrument Java with OpenTelemetry (Part 1)</a></li>
<li><a href="https://youtu.be/PX7s6RRLGaU?feature=shared">How to Manually Instrument Java with OpenTelemetry (Part 2)</a></li>
<li><a href="https://youtu.be/hXTlV_RnELc?feature=shared">Custom Java Instrumentation with OpenTelemetry</a></li>
<li><a href="https://youtu.be/E8g9u_uOFO4?feature=shared">Elastic APM - Automatic .NET Instrumentation with OpenTelemetry</a></li>
<li><a href="https://youtu.be/7J9M2JsHwRE?feature=shared">How to Manually Instrument .NET Applications with OpenTelemetry</a></li>
</ul>
<p>Given Elastic and OpenTelemetry’s vast user base, these provide a rich source of education for anyone trying to learn the intricacies of instrumenting with OpenTelemetry.</p>
<h3 id="elasticagentssupportingopentelemetry">Elastic Agents supporting OpenTelemetry</h3>
<p>If you’ve already implemented OpenTelemetry, you can still use them with OpenTelemetry. <a href="https://www.elastic.co/blog/opentelemetry-instrumentation-elastic-apm-agent-features">Elastic APM agents today are able to ship OpenTelemetry</a> spans as part of a trace. This means that if you have any component in your application that emits an OpenTelemetry span, it’ll be part of the trace the Elastic APM agent captures.</p>
<h2 id="opentelemetrylogsinelastic">OpenTelemetry logs in Elastic</h2>
<p>If you look at OpenTelemetry documentation, you will see that a lot of language libraries are still in experimental or not implemented yet state. Java is in stable state, per the documentation. Depending on your service’s language, and your appetite for adventure, there exist several options for exporting logs from your services and applications and marrying them together in your observability backend.</p>
<p>In a previous blog, we discussed <a href="https://www.elastic.co/blog/3-models-logging-opentelemetry-elastic">3 different configurations to properly get logging data into Elastic for Java</a>. The blog explores the current state of the art of OpenTelemetry logging and provides guidance on the available approaches with the following tenants in mind:</p>
<ul>
<li>Correlation of service logs with OTel-generated tracing where applicable</li>
<li>Proper capture of exceptions</li>
<li>Common context across tracing, metrics, and logging</li>
<li>Support for slf4j key-value pairs (“structured logging”)</li>
<li>Automatic attachment of metadata carried between services via OTel baggage</li>
<li>Use of an Elastic Observability backend</li>
<li>Consistent data fidelity in Elastic regardless of the approach taken</li>
</ul>
<p>Three models, which are covered in the blog, currently exist for getting your application or service logs to Elastic with correlation to OTel tracing and baggage:</p>
<ul>
<li>Output logs from your service (alongside traces and metrics) using an embedded OpenTelemetry Instrumentation library to Elastic via the OTLP protocol</li>
<li>Write logs from your service to a file scrapped by the OpenTelemetry Collector, which then forwards to Elastic via the OTLP protocol</li>
<li>Write logs from your service to a file scrapped by Elastic Agent (or Filebeat), which then forwards to Elastic via an Elastic-defined protocol</li>
</ul>
<p>Note that (1), in contrast to (2) and (3), does not involve writing service logs to a file prior to ingestion into Elastic.</p>
<h2 id="opentelemetryiselasticspreferredschema">OpenTelemetry is Elastic’s preferred schema</h2>
<p>Elastic recently contributed the <a href="https://opentelemetry.io/blog/2023/ecs-otel-semconv-convergence/">Elastic Common Schema (ECS) to the OpenTelemetry (OTel)</a> project, enabling a unified data specification for security and observability data within the OTel Semantic Conventions framework.</p>
<p>ECS, an open source specification, was developed with support from the Elastic user community to define a common set of fields to be used when storing event data in Elasticsearch<sup>®</sup>. ECS helps reduce management and storage costs stemming from data duplication, improving operational efficiency.</p>
<p>Similarly, OTel’s Semantic Conventions (SemConv) also specify common names for various kinds of operations and data. The benefit of using OTel SemConv is in following a common naming scheme that can be standardized across a codebase, libraries, and platforms for OTel users.</p>
<p>The merging of ECS and OTel SemConv will help advance OTel’s adoption and the continued evolution and convergence of observability and security domains.</p>
<h2 id="elasticobservabilityapmandmachinelearningcapabilities">Elastic Observability APM and machine learning capabilities</h2>
<p>All of Elastic Observability’s APM capabilities are available with OTel data (read more on this in our blog, <a href="https://www.elastic.co/blog/opentelemetry-observability">Independence with OpenTelemetry</a>):</p>
<ul>
<li>Service maps</li>
<li>Service details (latency, throughput, failed transactions)</li>
<li>Dependencies between services</li>
<li>Transactions (traces)</li>
<li>ML correlations (specifically for latency)</li>
<li>Service logs</li>
</ul>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt64b1360c6f4f835b/6a7f0e652f00b28c7befebf4/elastic-blog-3-services.png" alt="services" /></p>
<p>In addition to Elastic’s APM and unified view of the telemetry data, you will now be able to use Elastic’s powerful machine learning capabilities to reduce the analysis, and alerting to help reduce MTTR. Here are some of the ML based AIOps capabilities we have:</p>
<ul>
<li><a href="https://www.elastic.co/blog/reduce-mttd-ml-machine-learning-observability"><strong>Anomaly detection:</strong></a> Elastic Observability, when turned on (<a href="https://www.elastic.co/guide/en/kibana/current/xpack-ml-anomalies.html">see documentation</a>), automatically detects anomalies by continuously modeling the normal behavior of your OpenTelemetry data — learning trends, periodicity, and more.</li>
<li><a href="https://www.elastic.co/blog/reduce-mttd-ml-machine-learning-observability"><strong>Log categorization:</strong></a> Elastic also identifies patterns in your OpenTelemetry log events quickly, so that you can take action quicker.</li>
<li><strong>High-latency or erroneous transactions:</strong> Elastic Observability’s APM capability helps you discover which attributes are contributing to increased transaction latency and identifies which attributes are most influential in distinguishing between transaction failures and successes.</li>
<li><a href="https://www.elastic.co/blog/observability-logs-machine-learning-aiops"><strong>Log spike detector</strong></a> helps identify reasons for increases in OpenTelemetry log rates. It makes it easy to find and investigate causes of unusual spikes by using the analysis workflow view.</li>
<li><a href="https://www.elastic.co/blog/observability-logs-machine-learning-aiops"><strong>Log pattern analysis</strong></a> helps you find patterns in unstructured log messages and makes it easier to examine your data.</li>
</ul>
<h2 id="elasticallowsyoutomigratetootelonyourschedule">Elastic allows you to migrate to OTel on your schedule</h2>
<p>Although OpenTelemetry supports many programming languages, the <a href="https://opentelemetry.io/docs/instrumentation/">status of its major functional components</a> — metrics, traces, and logs — are still at various stages. Thus migrating applications written in Java, Python, and JavaScript are good choices to start with as their metrics, traces, and logs (for Java) are stable.</p>
<p>For the other languages that are not yet supported, you can easily instrument those using Elastic Agents, therefore running your <a href="https://www.elastic.co/observability">full stack observability platform</a> in mixed mode (Elastic agents with OpenTelemetry agents).</p>
<p>Here is a simple example:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltbff34e303f9e3330/6a7f0e67ea068d2474f09f1c/elastic-blog-4-services2.png" alt="services 2" /></p>
<p>The above shows a simple variation of our standard Elastic Agent application with one service flipped to OTel — the newsletter-otel service. But we can easily and as needed convert each of these services to OTel as development resources allow.</p>
<p>Hence you can migrate what you need to OpenTelemetry with Elastic as specific languages reach a stable state, and you can then continue your migration to OpenTelemetry agents.</p>
<h2 id="integratedkubernetesandopentelemetryviewsinelastic">Integrated Kubernetes and OpenTelemetry views in Elastic</h2>
<p>Elastic manages your Kubernetes cluster using the Elastic Agent, and you can use it on your Kubernetes cluster where your OpenTelemetry application is running. Hence you can not only use OpenTelemetry for your application, but Elastic can also monitor the corresponding Kubernetes cluster.</p>
<p>There are two configurations for Kubernetes:</p>
<p><strong>1. Simply deploying the Elastic Agent daemon set on the kubernetes cluster.</strong> We outline this out in the article entitled <a href="https://www.elastic.co/blog/kubernetes-cluster-metrics-logs-monitoring">Managing your Kubernetes cluster with Elastic Observability</a>. This would also push just the Kubernetes metrics and logs to Elastic.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt0f91de133133262e/6a7f0e6a3ce8e2abc1cf540f/elastic-blog-5-cloud-nodes.png" alt="elastic cloud nodes" /></p>
<p><strong>2. Deploying the Elastic Agent with not only the Kubernetes Daemon set, but also Elastic’s APM integration, the Defend (Security) integration, and Network Packet capture integration</strong> to provide more comprehensive Kubernetes cluster observability. We outline this configuration in the following article <a href="https://www.elastic.co/blog/implementing-kubernetes-observability-security-opentelemetry">Modern observability and security on Kubernetes with Elastic and OpenTelemetry</a>.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltd34ef1f6e71a7446/6a7f0e6dea068d609ff09f20/elastic-blog-6-flowhcart.png" alt="flowchart" /></p>
<p>Both <a href="https://www.elastic.co/observability/opentelemetry">OpenTelemetry visualization</a> examples use the OpenTelemetry demo, and in Elastic, we tie the Kubernetes information with the application to provide you an ability to see Kubernetes information from your traces in APM. This provides a more integrated approach when troubleshooting.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt0b4b8776d336437e/6a7f0e706c6eac80c7f141a9/elastic-blog-7-pod-deets.png" alt="pod details" /></p>
<h2 id="summary">Summary</h2>
<p>In essence, Elastic's commitment goes beyond mere support for OpenTelemetry. We are dedicated to ensuring our customers not only adopt OpenTelemetry but thrive with it. Through our solutions, expertise, and resources, we aim to elevate the observability journey for every business, turning data into actionable insights that drive growth and innovation.</p>
<blockquote>
  <p>Developer resources:</p>
  <ul>
  <li><a href="https://www.elastic.co/blog/getting-started-opentelemetry-instrumentation-sample-app">Elastiflix application</a>, a guide to instrument different languages with OpenTelemetry</li>
  <li>Python: <a href="https://www.elastic.co/blog/auto-instrumentation-of-python-applications-opentelemetry">Auto-instrumentation</a>, <a href="https://www.elastic.co/blog/manual-instrumentation-of-python-applications-opentelemetry">Manual-instrumentation</a></li>
  <li>Java: <a href="https://www.elastic.co/blog/auto-instrumentation-of-java-applications-opentelemetry">Auto-instrumentation</a>, <a href="https://www.elastic.co/blog/manual-instrumentation-of-java-applications-opentelemetry">Manual-instrumentation</a></li>
  <li>Node.js: <a href="https://www.elastic.co/blog/auto-instrument-nodejs-applications-opentelemetry">Auto-instrumentation</a>, <a href="https://www.elastic.co/blog/manual-instrumentation-of-nodejs-applications-opentelemetry">Manual-instrumentation</a></li>
  <li>.NET: <a href="https://www.elastic.co/blog/auto-instrumentation-of-net-applications-opentelemetry">Auto-instrumentation</a>, <a href="https://www.elastic.co/blog/manual-instrumentation-of-net-applications-opentelemetry">Manual-instrumentation</a></li>
  <li>Go: <a href="https://elastic.co/blog/manual-instrumentation-of-go-applications-opentelemetry">Manual-instrumentation</a></li>
  <li><a href="https://www.elastic.co/blog/best-practices-instrumenting-opentelemetry">Best practices for OpenTelemetry</a></li>
  </ul>
  <p>General configuration and use case resources:</p>
  <ul>
  <li><a href="https://www.elastic.co/blog/opentelemetry-observability">Independence with OpenTelemetry on Elastic</a></li>
  <li><a href="https://www.elastic.co/blog/implementing-kubernetes-observability-security-opentelemetry">Modern observability and security on Kubernetes with Elastic and OpenTelemetry</a></li>
  <li><a href="https://www.elastic.co/blog/3-models-logging-opentelemetry-elastic">3 models for logging with OpenTelemetry and Elastic</a></li>
  <li><a href="https://www.elastic.co/blog/adding-free-and-open-elastic-apm-as-part-of-your-elastic-observability-deployment">Adding free and open Elastic APM as part of your Elastic Observability deployment</a></li>
  <li><a href="https://www.elastic.co/blog/custom-metrics-app-code-java-agent-plugin">Capturing custom metrics through OpenTelemetry API in code with Elastic</a></li>
  <li><a href="https://www.elastic.co/virtual-events/future-proof-your-observability-platform-with-opentelemetry-and-elastic">Future-proof your observability platform with OpenTelemetry and Elastic</a></li>
  <li><a href="https://www.elastic.co/blog/kubernetes-k8s-observability-elasticsearch-cncf">Elastic Observability: Built for open technologies like Kubernetes, OpenTelemetry, Prometheus, Istio, and more</a></li>
  </ul>
</blockquote>
<p>Don’t have an Elastic Cloud account yet? <a href="https://cloud.elastic.co/registration">Sign up for Elastic Cloud</a> and try out the instrumentation capabilities that I discussed above. I would be interested in getting your feedback about your experience in gaining visibility into your application stack with Elastic.</p>
<p><em>The release and timing of any features or functionality described in this post remain at Elastic's sole discretion. Any features or functionality not currently available may not be delivered on time or at all.</em></p>]]></content:encoded>
    <link>https://www.elastic.co/observability-labs/blog/native-opentelemetry-support-in-elastic-observability</link>
    <guid isPermaLink="false">native-opentelemetry-support-in-elastic-observability</guid>
    <category><![CDATA[OpenTelemetry]]></category>
    <category><![CDATA[Metrics]]></category>
    <category><![CDATA[APM]]></category>
    <category><![CDATA[Kubernetes]]></category>
    <dc:creator><![CDATA[Bahubali Shetti]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt2700a8e353c3fb55/6a7f0e7342a117e08695bf4c/ecs-otel-announcement-2.jpg" length="0" type="image/jpeg"/>
    <pubDate>Wed, 13 Sep 2023 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Manual instrumentation of Go applications with OpenTelemetry]]></title>
    <description><![CDATA[In this blog post, we will show you how to manually instrument Go applications using OpenTelemetry. We will explore how to use the proper OpenTelemetry Go packages and, in particular, work on instrumenting tracing in a Go application.]]></description>
    <content:encoded><![CDATA[<p>DevOps and SRE teams are transforming the process of software development. While DevOps engineers focus on efficient software applications and service delivery, SRE teams are key to ensuring reliability, scalability, and performance. These teams must rely on a full-stack observability solution that allows them to manage and monitor systems and ensure issues are resolved before they impact the business.</p>
<p>Observability across the entire stack of modern distributed applications requires data collection, processing, and correlation often in the form of dashboards. Ingesting all system data requires installing agents across stacks, frameworks, and providers — a process that can be challenging and time-consuming for teams who have to deal with version changes, compatibility issues, and proprietary code that doesn't scale as systems change.</p>
<p>Thanks to <a href="http://opentelemetry.io">OpenTelemetry</a> (OTel), DevOps and SRE teams now have a standard way to collect and send data that doesn't rely on proprietary code and have a large support community reducing vendor lock-in.</p>
<p>In this blog post, we will show you how to manually instrument Go applications using OpenTelemetry. This approach is slightly more complex than using auto-instrumentation</p>
<p>In a <a href="https://www.elastic.co/blog/opentelemetry-observability">previous blog</a>, we also reviewed how to use the OpenTelemetry demo and connect it to Elastic<sup>®</sup>, as well as some of Elastic’s capabilities with OpenTelemetry. In this blog, we will use <a href="https://github.com/elastic/observability-examples">an alternative demo application</a>, which helps highlight manual instrumentation in a simple way.</p>
<p>Finally, we will discuss how Elastic supports mixed-mode applications, which run with Elastic and OpenTelemetry agents. The beauty of this is that there is <strong>no need for the otel-collector</strong>! This setup enables you to slowly and easily migrate an application to OTel with Elastic according to a timeline that best fits your business.</p>
<h2 id="applicationprerequisitesandconfig">Application, prerequisites, and config</h2>
<p>The application that we use for this blog is called <a href="https://github.com/elastic/observability-examples">Elastiflix</a>, a movie streaming application. It consists of several micro-services written in .NET, NodeJS, Go, and Python.</p>
<p>Before we instrument our sample application, we will first need to understand how Elastic can receive the telemetry data.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt13a3ccd8116bdc07/6a85ccb1f5f1a0cd052ec93b/GO-flowhcart.png" alt="Elastic configuration options for OpenTelemetry" /></p>
<p>All of Elastic Observability’s APM capabilities are available with OTel data. Some of these include:</p>
<ul>
<li>Service maps</li>
<li>Service details (latency, throughput, failed transactions)</li>
<li>Dependencies between services, distributed tracing</li>
<li>Transactions (traces)</li>
<li>Machine learning (ML) correlations</li>
<li>Log correlation</li>
</ul>
<p>In addition to Elastic’s APM and a unified view of the telemetry data, you will also be able to use Elastic’s powerful machine learning capabilities to reduce the analysis, and alerting to help reduce MTTR.</p>
<h2 id="prerequisites">Prerequisites</h2>
<ul>
<li>An Elastic Cloud account — <a href="https://cloud.elastic.co/">sign up now</a></li>
<li>A clone of the <a href="https://github.com/elastic/observability-examples">Elastiflix demo application</a>, or your own Go application</li>
<li>Basic understanding of Docker — potentially install <a href="https://www.docker.com/products/docker-desktop/">Docker Desktop</a></li>
<li>Basic understanding of Go</li>
</ul>
<h2 id="viewtheexamplesourcecode">View the example source code</h2>
<p>The full source code including the Dockerfile used in this blog can be found on <a href="https://github.com/elastic/observability-examples/tree/main/Elastiflix/go-favorite-otel-manual">GitHub</a>. The repository also contains the <a href="https://github.com/elastic/observability-examples/tree/main/Elastiflix/go-favorite">same application without instrumentation</a>. This allows you to compare each file and see the differences.</p>
<p>Before we begin, let’s look at the non-instrumented code first.</p>
<p>This is our simple go application that can receive a GET request. Note that the code shown here is a slightly abbreviated version.</p>
<pre><code>package main

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

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

    "github.com/sirupsen/logrus"

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

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

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

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

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

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

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

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

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

        userID := c.Query("user_id")

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

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

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

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

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

    "go.opentelemetry.io/otel/propagation"

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

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

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

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


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

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

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

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

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

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

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


    "github.com/sirupsen/logrus"

    "github.com/gin-gonic/gin"

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

    "go.opentelemetry.io/otel/propagation"

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

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

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

    "go.opentelemetry.io/otel/trace"

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

)

var tracer trace.Tracer

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

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

    secureOption := otlptracegrpc.WithInsecure()

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

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

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

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

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

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


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

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

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

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

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

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


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


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

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

        userID := c.Query("user_id")

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

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

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

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

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

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

public static class Telemetry
{
    //...

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

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

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

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

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

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

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

app = Flask(__name__)

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

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

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

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

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

    favorites = r.smembers(user_id)

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

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

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

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

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

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

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

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

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

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

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

        favorites = r.smembers(user_id)

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


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

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

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

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

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

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

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

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


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

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

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

resource = Resource.create(resourceAttributes)


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

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

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


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

app = Flask(__name__)


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

#app.wsgi_app = OpenTelemetryMiddleware(app.wsgi_app)

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

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

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

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

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

        favorites = r.smembers(user_id)

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

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

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

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

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

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

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

const axios = require("axios");

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

var favorites = {};

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

const axios = require("axios");

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

const tracer = opentelemetry.trace.getTracer();

var favorites = {};

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

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

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

# or alternatively issue a request every second

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

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

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

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

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

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

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

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

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

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

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

///.....


 span.setStatus(StatusCode.OK);

        span.end();

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

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

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

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

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

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

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

# or alternatively issue a request every second

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

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

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

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

RUN opentelemetry-bootstrap -a install

# Add the application
COPY . .

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

# or alternatively issue a request every second

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

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

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

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

# Copy the local code to the container
COPY . .

# Build the application
RUN mvn clean install

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

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

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

# or alternatively issue a request every second

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

WORKDIR /app

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

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


EXPOSE 3001

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

# or alternatively issue a request every second

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

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

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

const port = parseInt(process.env.PORT) || 8080;
app.listen(port, () =&gt; {
  console.log(`helloworld: listening on port ${port}`);
});
</code></pre>
<ol>
<li>Rebuild the Hello World app image and push the image to the Google Container Registry by running the command below in the terminal. Be sure to replace your-project-id in the command below with your actual Google Cloud project ID.</li>
</ol>
<pre><code>gcloud builds submit --tag gcr.io/your-project-id/elastic-helloworld
</code></pre>
<ol>
<li>Redeploy the Hello World app to Google Cloud Run by running the command below. Be sure to replace your-project-id in the command below with your actual Google Cloud project ID.</li>
</ol>
<pre><code>gcloud run deploy elastic-helloworld --image gcr.io/your-project-id/elastic-helloworld
</code></pre>
<ol>
<li>When the deployment process is complete, a Service URL will be displayed within the terminal. Copy and paste the Service URL in a browser to view the updated Hello World app running in Cloud Run.</li>
</ol>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt3a655bc1371c8bc8/6a85cb5fd7b2e7b297fe84da/elastic-blog-9-elastic-logo.png" alt="elastic logo" /></p>
<h2 id="observethehelloworldwebapp">Observe the Hello World web app</h2>
<p>Now that we’ve instrumented the web app to send observability data to Elastic Observability, we can now use Elastic Cloud to monitor the web app’s operations.</p>
<ol>
<li><p>In Elastic Cloud, select the Observability <strong>Services</strong> menu item.</p></li>
<li><p>Click the <strong>helloworld</strong> service.</p></li>
<li><p>Click the <strong>Transactions</strong> tab.</p></li>
<li><p>Scroll down and click the <strong>GET /</strong> transaction.</p></li>
<li><p>Scroll down to the <strong>Trace Sample</strong> section to see the <strong>GET /</strong> , <strong>hi</strong> and <strong>bye</strong> trace samples.</p></li>
</ol>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltee3cd0244463c5a9/6a85cb622d64d5f7f3081d48/elastic-blog-10-trace-sample.png" alt="trace sample" /></p>
<h2 id="observabilitymadetoscale">Observability made to scale</h2>
<p>You’ve seen the entire process of deploying a web app to Google Cloud Run that is instrumented with Elastic Observability. The end result is a web app that will scale up and down with demand combined with the observability tools to monitor the web app as it serves a single user or millions of users.</p>
<p>Now that you’ve seen how to deploy a serverless web app instrumented with observability, visit <a href="https://www.elastic.co/observability">Elastic Observability</a> to learn more about how to implement a complete observability solution for your apps. Or visit <a href="https://www.elastic.co/getting-started/google-cloud">Getting started with Elastic on Google Cloud</a> for more examples of how you can drive the data insights you need by combining <a href="https://www.elastic.co/observability/google-cloud-monitoring">Google Cloud monitoring</a> and cloud computing services with Elastic’s search-powered platform.</p>
<p><em>The release and timing of any features or functionality described in this post remain at Elastic's sole discretion. Any features or functionality not currently available may not be delivered on time or at all.</em></p>]]></content:encoded>
    <link>https://www.elastic.co/observability-labs/blog/deploy-observability-google-cloud-run</link>
    <guid isPermaLink="false">deploy-observability-google-cloud-run</guid>
    <category><![CDATA[Infrastructure Monitoring]]></category>
    <category><![CDATA[APM]]></category>
    <dc:creator><![CDATA[Jonathan Simon]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltab62612301e9c429/6a85cb65f5f1a02e662ec909/illustration-dev-sec-ops-cloud-automations-1680x980.png" length="0" type="image/png"/>
    <pubDate>Mon, 28 Aug 2023 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Understanding APM: How to add extensions to the OpenTelemetry Java Agent]]></title>
    <description><![CDATA[This blog post provides a comprehensive guide for Site Reliability Engineers (SREs) and IT Operations to gain visibility and traceability into applications, especially those written with non-standard frameworks or without access to the source code.]]></description>
    <content:encoded><![CDATA[<h2 id="withoutcodeaccesssresanditoperationscannotalwaysgetthevisibilitytheyneed">Without code access, SREs and IT Operations cannot always get the visibility they need</h2>
<p>As an SRE, have you ever had a situation where you were working on an application that was written with non-standard frameworks, or you wanted to get some interesting business data from an application (number of orders processed for example) but you didn’t have access to the source code?</p>
<p>We all know this can be a challenging scenario resulting in visibility gaps, inability to fully trace code end to end, and missing critical business monitoring data that is useful for understanding the true impact of issues.</p>
<p>How can we solve this? One way we discussed in the following three blogs:</p>
<ul>
<li><a href="https://www.elastic.co/blog/create-your-own-instrumentation-with-the-java-agent-plugin">Create your own instrumentation with the Java Agent Plugin</a></li>
<li><a href="https://www.elastic.co/blog/custom-metrics-app-code-java-agent-plugin">How to capture custom metrics without app code changes using the Java Agent Plugin</a></li>
<li><a href="https://www.elastic.co/blog/regression-testing-your-java-agent-plugin">Regression testing your Java Agent Plugin</a></li>
</ul>
<p>This is where we develop a plugin for the Elastic<sup>®</sup> APM Agent to help get access to critical business data for monitoring and add tracing where none exists.</p>
<p>What we will discuss in this blog is how you can do the same with the <a href="https://opentelemetry.io/docs/instrumentation/java/automatic/">OpenTelemetry Java Agent</a> using the Extensions framework.</p>
<h2 id="basicconceptshowapmworks">Basic concepts: How APM works</h2>
<p>Before we continue, let's first understand a few basic concepts and terms.</p>
<ul>
<li><strong>Java Agent:</strong> This is a tool that can be used to instrument (or modify) the bytecode of class files in the Java Virtual Machine (JVM). Java agents are used for many purposes like performance monitoring, logging, security, and more.</li>
<li><strong>Bytecode:</strong> This is the intermediary code generated by the Java compiler from your Java source code. This code is interpreted or compiled on the fly by the JVM to produce machine code that can be executed.</li>
<li><strong>Byte Buddy:</strong> Byte Buddy is a code generation and manipulation library for Java. It is used to create, modify, or adapt Java classes at runtime. In the context of a Java Agent, Byte Buddy provides a powerful and flexible way to modify bytecode. <strong>Both the Elastic APM Agent and the OpenTelemetry Agent use Byte Buddy under the covers.</strong></li>
</ul>
<p><strong>Now, let's talk about how automatic instrumentation works with Byte Buddy:</strong></p>
<p>Automatic instrumentation is the process by which an agent modifies the bytecode of your application's classes, often to insert monitoring code. The agent doesn't modify the source code directly, but rather the bytecode that is loaded into the JVM. This is done while the JVM is loading the classes, so the modifications are in effect during runtime.</p>
<p>Here's a simplified explanation of the process:</p>
<ol>
<li><p><strong>Start the JVM with the agent:</strong> When starting your Java application, you specify the Java agent with the -javaagent command line option. This instructs the JVM to load your agent before the main method of your application is invoked. At this point, the agent has the opportunity to set up class transformers.</p></li>
<li><p><strong>Register a class file transformer with Byte Buddy:</strong> Your agent will register a class file transformer with Byte Buddy. A transformer is a piece of code that is invoked every time a class is loaded into the JVM. This transformer receives the bytecode of the class and it can modify this bytecode before the class is actually used.</p></li>
<li><p><strong>Transform the bytecode:</strong> When your transformer is invoked, it will use Byte Buddy's API to modify the bytecode. Byte Buddy allows you to specify your transformations in a high-level, expressive way rather than manually writing complex bytecode. For example, you could specify a certain class and method within that class that you want to instrument and provide an "interceptor" that will add new behavior to that method.</p></li>
<li><p><strong>Use the transformed classes:</strong> Once the agent has set up its transformers, the JVM continues to load classes as usual. Each time a class is loaded, your transformers are invoked, allowing them to modify the bytecode. Your application then uses these transformed classes as if they were the original ones, but they now have the extra behavior that you've injected through your interceptor.</p></li>
</ol>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt374de29a3dcd79d9/6a85cb1af61d6e245b9c2b1b/elastic-blog-1-flowchart-process.png" alt="flowchart process" /></p>
<p>In essence, automatic instrumentation with Byte Buddy is about modifying the behavior of your Java classes at runtime, without needing to alter the source code directly. This is especially useful for cross-cutting concerns like logging, monitoring, or security, as it allows you to centralize this code in your Java Agent, rather than scattering it throughout your application.</p>
<h2 id="applicationprerequisitesandconfig">Application, prerequisites, and config</h2>
<p>There is a really simple application in <a href="https://github.com/davidgeorgehope/custom-instrumentation-examples">this GitHub repository</a> that is used throughout this blog. What it does is it simply asks you to input some text and then it counts the number of words.</p>
<p>It’s also listed below:</p>
<pre><code>package org.davidgeorgehope;
import java.util.Scanner;
import java.util.logging.Logger;

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

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

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

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

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

public class WordCountInstrumentation implements TypeInstrumentation {

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

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

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

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

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

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

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

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

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

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

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

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

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

WORKDIR /

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

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

ENTRYPOINT ["/start.sh"]
</code></pre>
<p>This dockerfile works in two parts: during the docker build process, we build the simple-java application from source followed by the custom instrumentation. After this, we download the latest OpenTelemetry Java Agent. During runtime, we simple execute the start.sh file described below:</p>
<pre><code>#!/bin/sh
java \
-javaagent:/opentelemetry-javaagent.jar \
-Dotel.exporter.otlp.endpoint=${SERVER_URL} \
-Dotel.exporter.otlp.headers="Authorization=Bearer ${SECRET_KEY}" \
-Dotel.metrics.exporter=otlp \
-Dotel.logs.exporter=otlp \
-Dotel.resource.attributes=service.name=simple-java,service.version=1.0,deployment.environment=production \
-Dotel.service.name=your-service-name \
-Dotel.javaagent.extensions=/usr/local/lib/opentelemetry-custom-instrumentation-1.0-SNAPSHOT.jar \
-Dotel.javaagent.debug=true \
-jar /usr/local/lib/simple-java-1.0-SNAPSHOT.jar
</code></pre>
<p>There are two important things to note with this script: the first is that we start the javaagent parameter set to the opentelemetry-javaagent.jar — this will start the OpenTelemetry javaagent running, which starts before any code is executed.</p>
<p>Inside this jar there has to be a class with a premain method which the JVM will look for. This bootstraps the java agent. As described above, any bytecode that is compiled is essentially filtered through the javaagent code so it can modify the class before being executed.</p>
<p>The second important thing here is the configuration of the javaagent.extensions, which loads our extension that we built to add instrumentation for our simple-java application.</p>
<p>Now run the following commands:</p>
<pre><code>docker build -t djhope99/custom-otel-instrumentation:1 .
docker run -it -e 'SERVER_URL=XXX' -e 'SECRET_KEY=XX djhope99/custom-otel-instrumentation:1
</code></pre>
<p>If you use the SERVER_URL and SECRET_KEY you got earlier in here, you should see this connect to Elastic.</p>
<p>When it starts up, it will ask you to enter a sentence, enter a few sentences, and press enter. Do this a few times — there is a sleep in here to force a long running transaction:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt155731d7462ebeb8/6a85cb20eaf245a1ada49f61/elastic-blog-3-codeblack.png" alt="code" /></p>
<p>Eventually you will see the service show up in the service map:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt209a74d642d0278a/6a85cb239bf994bab90a056f/elastic-blog-4-services.png" alt="services" /></p>
<p>Traces will appear:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt912f14b090cdde8c/6a85cb26f61d6e7e8d9c2b21/elastic-blog-5-your-service-name.png" alt="service name" /></p>
<p>And in the span you will see the wordcount attribute we collected:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt7c3de41db6310bb9/6a85cb299d2b716bd4f93984/elastic-blog-6-transaction-details.png" alt="transaction details" /></p>
<p>This can be used for further dashboarding and AI/ML, including anomaly detection if you need, which is easy to do, as you can see below.</p>
<p>First click on the burger on the left side and select <strong>Dashboard</strong> to create a new dashboard:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blteeec7457390a809c/6a85cb2b2d64d53d02081d40/elastic-blog-7-manage-deployment-analytics.png" alt="analytics" /></p>
<p>From here, click <strong>Create Visualization</strong>.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltbabf439d0dac824b/6a85cb2e9bf994451b0a0573/elastic-blog-8-visualization.png" alt="visualization" /></p>
<p>Search for the wordcount label in the APM index as shown below:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltb7703450e4ce97d8/6a85cb3111893c4072a7ab96/elastic-blog-9-dashboard-word.png" alt="dashboard" /></p>
<p>As you can see, because we created this attribute in the Span code as below with wordCount as a type “Integer,” we were able to automatically assign it as a numeric field in Elastic:</p>
<pre><code>span.setAttribute("wordCount", wordCount);
</code></pre>
<p>From here we can drag and drop it into the visualization for display on our Dashboard! Super easy.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt7da2d84e050356a3/6a85cb34501a85781efbb327/elastic-blog-10-drag-drop.png" alt="dra and drop" /></p>
<h2 id="inconclusion">In conclusion</h2>
<p>This blog elucidates the invaluable role of OpenTelemetry Java Agent in filling the visibility gaps and obtaining crucial business monitoring data, especially when access to the source code is not feasible.</p>
<p>The blog unraveled the basic understanding of Java Agent, Bytecode, and Byte Buddy, followed by a comprehensive examination of the automatic instrumentation process with Byte Buddy.</p>
<p>The implementation of the OpenTelemetry Java Agent, using the Extensions framework, was demonstrated with the aid of a simple Java application, which underscored the agent's ability to inject trace code into the application to facilitate monitoring.</p>
<p>It detailed how to configure the agent and integrate OpenTelemetry Extension, and it outlined the operation of a sample application to help users comprehend the practical application of the information discussed. This instructive blog post is an excellent resource for SREs and IT Operations seeking to optimize their work with applications using OpenTelemetry's automatic instrumentation feature.</p>
<blockquote>
  <ul>
  <li><a href="https://www.elastic.co/blog/opentelemetry-observability">Independence with OpenTelemetry on Elastic</a></li>
  <li><a href="https://www.elastic.co/blog/implementing-kubernetes-observability-security-opentelemetry">Modern observability and security on Kubernetes with Elastic and OpenTelemetry</a></li>
  <li><a href="https://www.elastic.co/blog/3-models-logging-opentelemetry-elastic">3 models for logging with OpenTelemetry and Elastic</a></li>
  <li><a href="https://www.elastic.co/blog/adding-free-and-open-elastic-apm-as-part-of-your-elastic-observability-deployment">Adding free and open Elastic APM as part of your Elastic Observability deployment</a></li>
  <li><a href="https://www.elastic.co/blog/monitor-openai-api-gpt-models-opentelemetry-elastic">Monitor OpenAI API and GPT models with OpenTelemetry and Elastic</a></li>
  <li><a href="https://www.elastic.co/virtual-events/future-proof-your-observability-platform-with-opentelemetry-and-elastic">Future proof your observability platform with OpenTelemetry and Elastic</a></li>
  </ul>
</blockquote>
<p>Don’t have an Elastic Cloud account yet? Sign up <a href="https://cloud.elastic.co/registration">for Elastic Cloud</a>.</p>
<p><em>The release and timing of any features or functionality described in this post remain at Elastic's sole discretion. Any features or functionality not currently available may not be delivered on time or at all.</em></p>]]></content:encoded>
    <link>https://www.elastic.co/observability-labs/blog/extensions-opentelemetry-java-agent</link>
    <guid isPermaLink="false">extensions-opentelemetry-java-agent</guid>
    <category><![CDATA[OpenTelemetry]]></category>
    <category><![CDATA[APM]]></category>
    <dc:creator><![CDATA[David Hope]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt1c9c06c4bc5e5cbd/6a85cb37bc5bb3bb24f81b01/flexible-implementation-1680X980.png" length="0" type="image/png"/>
    <pubDate>Mon, 24 Jul 2023 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[How to combine OpenTelemetry instrumentation with Elastic APM Agent features]]></title>
    <description><![CDATA[This post shows you how you can combine the OpenTelemetry tracing APIs with Elastic APM Agents. You'll learn how OpenTelemetry spans became part of a trace that Elastic APM Agents report.]]></description>
    <content:encoded><![CDATA[<p>Elastic APM supports OpenTelemetry on multiple levels. One easy-to understand scenario, which <a href="https://www.elastic.co/blog/opentelemetry-observability">we previously blogged about</a>, is the direct OpenTelemetry Protocol (OTLP) support in APM Server. This means that you can connect any OpenTelemetry agent to an Elastic APM Server and the APM Server will happily take that data, ingest it into Elasticsearch<sup>®</sup>, and you can view that OpenTelemetry data in the APM app in Kibana<sup>®</sup>.</p>
<p>This blog post will showcase a different use-case: within Elastic APM, we have <a href="https://www.elastic.co/guide/en/apm/agent/index.html">our own APM Agents</a>. Some of these have download numbers in the tens of millions, and some of them predate OpenTelemetry. Of course we realize OpenTelemetry is very important and it’s here to stay, so we wanted to make these agents OpenTelemetry compatible and illustrate them using <a href="https://www.elastic.co/observability/opentelemetry">OpenTelemetry visualizations</a> in this blog.</p>
<p>Most of our Elastic APM Agents today are able to ship OpenTelemetry spans as part of a trace. This means that if you have any component in your application that emits an OpenTelemetry span, it’ll be part of the trace the Elastic APM Agent captures. This can be a library you use that is already instrumented by the OpenTelemetry API, or it can be any other OpenTelemetry span that an application developer added into the application’s code for manual instrumentation.</p>
<p>This feature of the Elastic APM Agents not only reports those spans but also properly maintains parent-child relationships between all spans, making OpenTelemetry a first-class citizen for these agents. If, for example, an Elastic APM Agent starts a span for a specific action by auto-instrumentation and then within that span the OpenTelemetry API starts another span, then the OpenTelemetry span will be the child of the outer span created by the agent. This is reflected in the parent.id field of the spans. It’s the same the other way around as well: if a span is created by the OpenTelemetry API and within that span an Elastic APM agent captures another span, then the span created by the Elastic APM Agent will be the child of the other span created by the OpenTelemetry API.</p>
<p>This feature is present in the following agents:</p>
<ul>
<li><a href="https://www.elastic.co/guide/en/apm/agent/java/current/opentelemetry-bridge.html">Java</a></li>
<li><a href="https://www.elastic.co/guide/en/apm/agent/dotnet/master/opentelemetry-bridge.html">.NET</a></li>
<li><a href="https://www.elastic.co/guide/en/apm/agent/python/current/opentelemetry-bridge.html">Python</a></li>
<li><a href="https://www.elastic.co/guide/en/apm/agent/nodejs/current/opentelemetry-bridge.html">Node.js</a></li>
<li><a href="https://www.elastic.co/guide/en/apm/agent/go/current/opentelemetry.html">Go</a></li>
</ul>
<h2 id="capturingopentelemetryspansintheelasticnetapmagent">Capturing OpenTelemetry spans in the Elastic .NET APM Agent</h2>
<p>As a first example, let’s take an ASP.NET Core application. We’ll put the .NET Elastic APM Agent into this application, and we’ll turn on the feature, which automatically bridges OpenTelemetry spans, so the Elastic APM Agent will make those spans part of the trace it reports.</p>
<p>The following code snippet shows a controller:</p>
<pre><code>namespace SampleAspNetCoreApp.Controllers
{
    public class HomeController : Controller
    {
        private readonly SampleDataContext _sampleDataContext;
        private ActivitySource _activitySource = new ActivitySource("HomeController");
        public HomeController(SampleDataContext sampleDataContext) =&gt; _sampleDataContext = sampleDataContext;
        public async Task&lt;IActionResult&gt; Index()
        {
            await ReadGitHubStars();
            return View();
        }
        public async Task ReadGitHubStars()
        {
            using var activity = _activitySource.StartActivity();
            var httpClient = new HttpClient();
            httpClient.DefaultRequestHeaders.Add("User-Agent", "APM-Sample-App");
            var responseMsg = await httpClient.GetAsync("https://api.github.com/repos/elastic/apm-agent-dotnet");
            var responseStr = await responseMsg.Content.ReadAsStringAsync();
            // …use responseStr
        }
    }
}
</code></pre>
<p>The Index method calls the ReadGitHubStars method and after that we simply return the corresponding view from the method.</p>
<p>The incoming HTTP call and the outgoing HTTP call by the HttpClient are automatically captured by the Elastic APM Agent — this is part of the auto instrumentation we had for a very long time.</p>
<p>The ReadGitHubStars is the one where we use the OpenTelemetry API. OpenTelemetry in .NET uses the ActivitySource and Activity APIs. The _activitySource.StartActivity() call simply creates an OpenTelemetry span that automatically takes the name of the method by using the <a href="https://learn.microsoft.com/en-us/dotnet/api/system.runtime.compilerservices.callermembernameattribute?view=net-7.0">CallerMemberNameAttribute</a> C# language feature, and this span will end when the method runs to completion.</p>
<p>Additionally, within this span we call the GitHub API with the HttpClient type. For this type, the .NET Elastic APM Agent again offers auto instrumentation, so the HTTP call will be also captured as a span by the agent automatically.</p>
<p>And here is how the water-flow chart for this transaction looks in Kibana:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt74ab52d5f4ac902d/6a85cdc999083f5dd640fa25/elastic-blog-1-trace-sample.png" alt="trace sample kibana" /></p>
<p>As you can see, the agent was able to capture the OpenTelemetry span as part of the trace.</p>
<h2 id="bridgingopentelemetryspansinpythonbyusingthepythonelasticapmagent">Bridging OpenTelemetry spans in Python by using the Python Elastic APM Agent</h2>
<p>Let’s see how this works in the case of Python. The idea is the same, so all the concepts introduced previously apply to this example as well.</p>
<p>We take a very simple Django example:</p>
<pre><code>from django.http import HttpResponse
from elasticapm.contrib.opentelemetry import Tracer
import requests


def index(request):
   tracer = Tracer(__name__)
   with tracer.start_as_current_span("ReadGitHubStars"):
       url = "https://api.github.com/repos/elastic/apm-agent-python"
       response = requests.get(url)
       return HttpResponse(response)
</code></pre>
<p>The first step to turn on capturing OpenTelemetry spans in Python is to import the Tracer implementation from elasticapm.contrib.opentelemetry.</p>
<p>And then on this Tracer you can start a new span — in this case, we manually name the span ReadGitHubStars.</p>
<p>Similarly to the previous example, the call to http://127.0.0.1:8000/otelsample/ is captured by the Elastic APM Python Agent, and then the next span is created by the OpenTelemetry API, which, as you can see, is captured by the agent automatically, and then finally the HTTP call to the GitHub API is captured again by the auto instrumentation of the agent.</p>
<p>Here is how it looks in the water-flow chart:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt8ca9d5ade61555bd/6a85cdcc501a854155fbb389/elastic-blog-2-trace-sample-2.png" alt="water-flow chart" /></p>
<p>As already mentioned, the agent maintains the parent-child relationship for all the OTel spans. Let’s take a look at the parent.id of the GET api.github.com call:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt88dfea9f113f54c9/6a85cdce93ffb9618fb9147b/elastic-blog-3-span-details.png" alt="OTel span details" /></p>
<p>As you can see, the id of this span is c98401c94d40b87a.</p>
<p>If we look at the span.id of the ReadGitHubStars OpenTelemetry span, then we can see that the id of this span is exactly c98401c94d40b87a — so the APM Agent internally maintains parent-child relationships across OpenTelemetry and non-OpenTelemetry spans, which makes OpenTelemetry spans first-class citizens in Elastic APM Agents.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt70765de3fe7cab11/6a85cdd199083f015d40fa29/elastic-blog-4-span-details-2.png" alt="OpenTelemetry spans first-class citizens in Elastic APM Agents" /></p>
<h2 id="otherlanguages">Other languages</h2>
<p>At this point, I'll stop to just replicate the exact same sample code in further languages — I think you already got the point here: in each language listed above, our Elastic APM Agents are able to bridge OpenTelemetry traces and show them in Kibana as native spans. We also <a href="https://www.elastic.co/blog/create-your-own-instrumentation-with-the-java-agent-plugin">blogged about using the same API in Java</a>, and you can see examples for the rest of the languages in the corresponding agent documentation (linked above).</p>
<h2 id="whentousethisfeatureandwhentousepureopentelemetrysdks">When to use this feature and when to use pure OpenTelemetry SDKs</h2>
<p>This is really up to you. If you want to only have pure OpenTelemetry usage in your applications and you really want to avoid any vendor-related software, then feel free to use OpenTelemetry SDKs directly — that is a use case we clearly support. If you go that route, this feature is not so relevant to you.</p>
<p>However, our Elastic APM Agents already have a very big user base and they offer features that are not present in OpenTelemetry. Some of these features are <a href="https://www.elastic.co/guide/en/apm/guide/current/span-compression.html">span compression</a>, <a href="https://www.elastic.co/guide/en/kibana/current/agent-configuration.html">central configuration</a>, <a href="https://www.elastic.co/guide/en/apm/agent/java/current/method-sampling-based.html">inferred spans</a>, distributed <a href="https://www.elastic.co/guide/en/apm/guide/current/configure-tail-based-sampling.html">tail based sampling</a> with multiple APM Servers, and many more.</p>
<p>If you are one of the many existing Elastic APM Agent users, or you plan to use an Elastic APM Agent because of the features mentioned above, then bridging OpenTelemetry spans enables you to still use the OpenTelemetry API and not rely on any vendor related API usage. That way your developer teams can instrument your application with OpenTelemetry, and you can also use any third-party library already instrumented by OpenTelemetry, and Elastic APM Agents will happily report those spans as part of the traces they report. With this, you can combine the vendor independent nature of OpenTelemetry and still use the feature rich Elastic APM Agents.</p>
<p>The OpenTelemetry bridge feature is also a good tool to use if you wish to change your telemetry library from an Elastic APM Agent to OpenTelemetry (and vice-versa), as it allows you to use both libraries together and switch them using atomic changes.</p>
<h2 id="nextsteps">Next steps</h2>
<p>In this blog post, we discussed how you can bridge OpenTelemetry spans with Elastic APM Agents. Of course OpenTelemetry is more than just traces. We know that, and we plan to cover further areas: currently we are working on bridging OpenTelemetry metrics in our Elastic APM Agents in a very similar fashion. You can watch the progress <a href="https://github.com/elastic/apm/issues/691">here</a>.</p>
<p><a href="https://www.elastic.co/blog/adding-free-and-open-elastic-apm-as-part-of-your-elastic-observability-deployment">Learn more about adding Elastic APM as part of your Elastic Observability deployment</a>.</p>]]></content:encoded>
    <link>https://www.elastic.co/observability-labs/blog/opentelemetry-instrumentation-apm-agent-features</link>
    <guid isPermaLink="false">opentelemetry-instrumentation-apm-agent-features</guid>
    <category><![CDATA[APM]]></category>
    <category><![CDATA[OpenTelemetry]]></category>
    <dc:creator><![CDATA[Greg Kalapos]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt39db6e3f1e9f8164/6a85cdd48c294404feb890a7/opentelemetry_apm-blog-720x420.jpeg" length="0" type="image/jpeg"/>
    <pubDate>Thu, 13 Jul 2023 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Improving the Elastic APM UI performance with continuous rollups and service metrics]]></title>
    <description><![CDATA[We made significant improvements to the UI performance in Elastic APM to make it scale with even the most demanding workloads, by pre-aggregating metrics at the service level, and storing the metrics at different levels of granularity.]]></description>
    <content:encoded><![CDATA[<p>In today's fast-paced digital landscape, the ability to monitor and optimize application performance is crucial for organizations striving to deliver exceptional user experiences. At Elastic, we recognize the significance of providing our user base with a reliable <a href="https://www.elastic.co/observability">observability platform</a> that scales with you as you’re onboarding thousands of services that produce terabytes of data each day. We have been diligently working behind the scenes to enhance our solution to meet the demands of even the largest deployments.</p>
<p>In this blog post, we are excited to share the significant strides we have made in improving the UI performance of Elastic APM. Maintaining a snappy user interface can be a challenge when interactively summarizing the massive amounts of data needed to provide an overview of the performance for an entire enterprise-scale service inventory. We want to assure our customers that we have listened, taken action, and made notable architectural changes to elevate the scalability and maturity of our solution.</p>
<h2 id="architecturalenhancements">Architectural enhancements</h2>
<p>Our journey began back in the 7.x series where we noticed that doing ad-hoc aggregations on raw <a href="https://www.elastic.co/guide/en/apm/guide/current/data-model-transactions.html">transaction</a> data put Elasticsearch<sup>®</sup> under a lot of pressure in large-scale environments. Since then, we’ve begun to pre-aggregate the transactions into transaction metrics during ingestion. This has helped to keep the performance of the UI relatively stable. Regardless of how busy the monitored application is and how many transaction events it is creating, we’re just querying pre-aggregated metrics that are stored at a constant rate. We’ve enabled the metrics-powered UI by default in <a href="https://github.com/elastic/kibana/issues/92024">7.15</a>.</p>
<p>However, when showing an inventory of a large number of services over large time ranges, the number of metric data points that need to be aggregated can still be large enough to cause performance issues. We also create a time series for each distinct set of dimensions. The dimensions include metadata, such as the transaction name and the host name. Our <a href="https://www.elastic.co/guide/en/apm/guide/current/data-model-metrics.html#_transaction_metrics">documentation</a> includes a full list of all available dimensions. If there’s a very high number of unique transaction names, which could be a result of improper instrumentation (see <a href="https://www.elastic.co/guide/en/kibana/current/troubleshooting.html#troubleshooting-too-many-transactions">docs</a> for more details), this will create a lot of individual time series that will need to be aggregated when requesting a summary of the service’s overall performance. Global labels that are added to the APM Agent configuration are also added as dimensions to these metrics, and therefore they can also impact the number of time series. Refer to the FAQs section below for more details.</p>
<p>Within the 8.7 and 8.8 releases, we’ve addressed these challenges with the following architectural enhancements that aim to reduce the number of documents Elasticsearch needs to search and aggregate on-the-fly, resulting in faster response times:</p>
<ul>
<li><strong>Pre-aggregation of transaction metrics into service metrics.</strong> Instead of aggregating all distinct time series that are created for each individual transaction name on-the-fly for every user request, we’re already pre-aggregating a summary time series for each service during data ingestion. Depending on how many unique transaction names the services have, this reduces the number of documents Elasticsearch needs to look up and aggregate by a factor of typically 10–100. This is particularly useful for the <a href="https://www.elastic.co/guide/en/kibana/master/services.html">service inventory</a> and the <a href="https://www.elastic.co/guide/en/kibana/master/service-overview.html">service overview</a> pages.</li>
<li><strong>Pre-aggregation of all metrics into different levels of granularity.</strong> The APM UI chooses the most appropriate level of granularity, depending on the selected time range. In addition to the metrics that are stored at a 1-minute granularity, we’re also summarizing and storing metrics at a 10-minute and 60-minute granularity level. For example, when looking at a 7-day period, the 60-minute data stream is queried instead of the 1-minute one, resulting in 60x fewer documents for Elasticsearch to examine. This makes sure that all graphs are rendered quickly, even when looking at larger time ranges.</li>
<li><strong>Safeguards on the number of unique transactions per service for which we are aggregating metrics.</strong> Our agents are designed to keep the cardinality of the transaction name low. But in the wild, we’ve seen some services that have a huge amount of unique transaction names. This used to cause performance problems in the UI because APM Server would create many time series that the UI needed to aggregate at query time. In order to protect APM Server from running out of memory when aggregating a large number of time series for each unique transaction name, metrics were published without aggregating when limits for the number of time series were reached. This resulted in a lot of individual metric documents that needed to be aggregated at query time. To address the problem, we've introduced a system where we aggregate metrics in a dedicated overflow bucket for each service when limits are reached. Refer to our <a href="https://www.elastic.co/guide/en/kibana/8.8/troubleshooting.html#troubleshooting-too-many-transactions">documentation</a> for more details.</li>
</ul>
<p>The exact factor of the document count reduction depends on various conditions. But to get a feeling for a typical scenario, if your services, on average, have 10 instances, no instance-specific global labels, 100 unique transaction names each, and you’re looking at time ranges that can leverage the 60m granularity, you’d see a reduction of documents that Elasticsearch needs to aggregate by a factor of 180,000 (10 instances x 100 transaction names x 60m x 3 because we’re also collapsing the event.outcome dimension). While the response times of Elasticsearch aggregations isn’t exactly scaling linearly with the number of documents, there is a strong correlation.</p>
<h2 id="faqs">FAQs</h2>
<h3 id="whenupgradingtothelatestversionwillmyolddataalsoloadfaster">When upgrading to the latest version, will my old data also load faster?</h3>
<p>Updating to 8.8 doesn’t immediately make the UI faster. Because the improvements are powered by pre-aggregations that APM Server is doing during ingestion, only new data will benefit from it. For that reason, you should also make sure to update APM Server as well. The UI can still display data that was ingested using an older version of the stack.</p>
<h3 id="iftheuiisbasedonmetricscanistillsliceanddiceusingcustomlabels">If the UI is based on metrics, can I still slice and dice using custom labels?</h3>
<p>High cardinality analysis is a big strength of Elastic Observability, and this focus on pre-aggregated metrics does not compromise that in any way.</p>
<p>The UI implements a sophisticated fallback mechanism that uses service metrics, transaction metrics, or raw transaction events, depending on which filters are applied. We’re not creating metrics for each user.id, for example. But you can still filter the data by user.id and the UI will then use raw transaction events. Chances are that you’re looking at a narrow slice of data when filtering by a dimension that is not available on the pre-aggregated metrics, therefore aggregations on the raw data are typically very fast.</p>
<p>Note that all global labels that are added to the APM agent configuration are part of the dimension of the pre-aggregated metrics, with the exception of RUM (see more details in <a href="https://github.com/elastic/apm-server/issues/11037">this issue</a>).</p>
<h3 id="caniusethepreaggregatedmetricsincustomdashboards">Can I use the pre-aggregated metrics in custom dashboards?</h3>
<p>Yes! If you use <a href="https://www.elastic.co/guide/en/kibana/current/lens.html">Lens</a> and select the "APM" data view, you can filter on either metricset.name:service_transaction or metricset.name:transaction, depending on the level of detail you need. Transaction latency is captured in transaction.duration.histogram, and successful outcomes and failed outcomes are stored in event.success_count. If you don't need a distribution of values, you can also select the transaction.duration.summary field for your metric aggregations, which should be faster. If you want to calculate the failure rate, here's a <a href="https://www.elastic.co/guide/en/kibana/current/lens.html#lens-formulas">Lens formula</a>: 1 - (sum(event.success_count) / count(event.success_count)). Note that the only granularity supported here is 1m.</p>
<h3 id="dotheadditionalmetricshaveanimpactonthestorage">Do the additional metrics have an impact on the storage?</h3>
<p>While we’re storing more metrics than before, and we’re storing all metrics in different levels of granularity, we were able to offset that by enabling <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/mapping-source-field.html#synthetic-source">synthetic source</a> for all metric data streams. We’ve even increased the default retention for the metrics in the coarse-grained granularity levels, so that the 60m rollup data streams are now stored for 390 days. Please consult our <a href="https://www.elastic.co/guide/en/apm/guide/current/apm-data-streams.html">documentation</a> for more information about the different metric data streams.</p>
<h3 id="aretherelimitsontheamountoftimeseriesthatapmservercanaggregate">Are there limits on the amount of time series that APM Server can aggregate?</h3>
<p>APM Server performs pre-aggregations in memory, which is fast, but consumes a considerable amount of memory. There are limits in place to protect APM Server from running out of memory, and from 8.7, most of them scale with available memory by default, meaning that allocating more memory to APM Server will allow it to handle more unique pre-aggregation groups like services and transactions. These limits are described in <a href="https://www.elastic.co/guide/en/apm/guide/current/data-model-metrics.html#_aggregated_metrics_limits_and_overflows">APM Server Data Model docs</a>.</p>
<p>On the APM Server roadmap, we have plans to move to a LSM-based approach where pre-aggregations are performed with the help of disks in order to reduce memory usage. This will enable APM Server to scale better with the input size and cardinality.</p>
<p>A common pitfall when working with pre-aggregations is to add instance-specific global labels to APM agents. This may exhaust the aggregation limits and cause metrics to be aggregated under the overflow bucket instead of the corresponding service. Therefore, make sure to follow the best practice of only adding a limited set of global labels to a particular service.</p>
<h2 id="validation">Validation</h2>
<p>To validate the effectiveness of the new architecture, and to ensure that the accuracy of the data is not negatively affected, we prepared a test environment where we generated 35K+ transactions per minute in a timespan of 14 days resulting in approximately 850 million documents.</p>
<p>We’ve tested the queries that power our service inventory, the service overview, and the transaction details using different time ranges (1d, 7d, 14d). Across the board, we’ve seen orders of magnitude improvements. Particularly, queries across larger time ranges that benefit from using the coarse-grained metrics in addition to the pre-aggregated service metrics saw incredible reductions of the response time.</p>
<p>We’ve also validated that there’s no loss in accuracy when using the more coarse-grained metrics for larger time ranges.</p>
<p>Every environment will behave a bit differently, but we’re confident that the impressive improvements in response time will translate well to setups of even bigger scale.</p>
<h2 id="plannedimprovements">Planned improvements</h2>
<p>As mentioned in the FAQs section, the number of time series for transaction metrics can grow quickly, as it is the product of multiple dimensions. For example, given a service that runs on 100 hosts and has 100 transaction names that each have 4 transaction results, APM Server needs to track 40,000 (100 x 100 x 4) different time series for that service. This would even exceed the maximum per-service limit of 32,000 for APM Servers with 64GB of main memory.</p>
<p>As a result, the UI will show an entry for “Remaining Transactions” in the Service overview page. This tracks the transaction metrics for a service once it hits the limit. As a result, you may not see all transaction names of your service. It may also be that all distinct transaction names are listed, but that the transaction metrics for some of the instances of that service are combined in the “Remaining Transactions” category.</p>
<p>We’re currently considering restructuring the dimensions for the metrics to avoid that the combination of the dimensions for transaction name and service instance-specific dimensions (such as the host name) lead to an explosion of time series. Stay tuned for more details.</p>
<h2 id="conclusion">Conclusion</h2>
<p>The architectural improvements we’ve delivered in the past releases provide a step-function in terms of the scalability and responsiveness of our UI. Instead of having to aggregate massive amounts of data on-the-fly as users are navigating through the user interface, we pre-aggregate the results for the most common queries as data is coming in. This ensures we have the answers ready before users have even asked their most frequently asked questions, while still being able to answer ad-hoc questions.</p>
<p>We are excited to continue supporting our community members as they push boundaries on their growth journey, providing them with a powerful and mature platform that can effortlessly handle the demands of the largest workloads. Elastic is committed to its mission to enable everyone to find the answers that matter. From all data. In real time. At scale.</p>
<p><em>The release and timing of any features or functionality described in this post remain at Elastic's sole discretion. Any features or functionality not currently available may not be delivered on time or at all.</em></p>]]></content:encoded>
    <link>https://www.elastic.co/observability-labs/blog/apm-ui-performance-continuous-rollups-service-metrics</link>
    <guid isPermaLink="false">apm-ui-performance-continuous-rollups-service-metrics</guid>
    <category><![CDATA[APM]]></category>
    <category><![CDATA[Metrics]]></category>
    <category><![CDATA[Data Management]]></category>
    <dc:creator><![CDATA[Felix Barnsteiner,Yngrid Coello,Dario Gieselaar,Carson Ip]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt569decf8bd6851af/6a85cbf2342d6985a621b0fd/elastic-blog-header-ui.png" length="0" type="image/png"/>
    <pubDate>Thu, 29 Jun 2023 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Gaining new perspectives beyond logging: An introduction to application performance monitoring]]></title>
    <description><![CDATA[Change is on the horizon for the world of logging. In this post, we’ll outline a recommended journey for moving from just logging to a fully integrated solution with logs, traces, and APM.]]></description>
    <content:encoded><![CDATA[<h2 id="prioritizecustomerexperiencewithapmandtracing">Prioritize customer experience with APM and tracing</h2>
<p>Enterprise software development and operations has become an interesting space. We have some incredibly powerful tools at our disposal, yet as an industry, we have failed to adopt many of these tools that can make our lives easier. One such tool that is currently underutilized is <a href="https://www.elastic.co/blog/adding-free-and-open-elastic-apm-as-part-of-your-elastic-observability-deployment">application performance monitoring</a> (APM) and tracing, despite the fact that OpenTelemetry has made it possible to adopt at low friction.</p>
<p>Logging, however, is ubiquitous. Every software application has logs of some kind, and the default workflow for troubleshooting (even today) is to go from exceptions experienced by customers and systems to the logs and start from there to find a solution.</p>
<p>There are various challenges with this, one of the main ones being that logs often do not give enough information to solve the problem. Many services today return ambiguous 500 errors with little or nothing to go on. What if there isn’t an error or log file at all or the problem is that the system is very slow? Logging alone cannot help solve these problems. This leaves users with half broken systems and poor user experiences. We’ve all been on the wrong side of this, and it can be incredibly frustrating.</p>
<p>The question I find myself asking is why does the customer experience often come second to errors? If the customer experience is a top priority, then a strategy should be in place to adopt tracing and APM and make this as important as logging. Users should stop going to logs by default and thinking primarily in logs, as many are doing today. This will also come with some required changes to mental models.</p>
<p>What’s the path to get there? That’s exactly what we will explore in this blog post. We will start by talking about supporting organizational changes, and then we’ll outline a recommended journey for moving from just logging to a fully integrated solution with logs, traces, and APM.</p>
<h2 id="cultivatinganewmonitoringmindsethowtodriveapmandtracingadoption">Cultivating a new monitoring mindset: How to drive APM and tracing adoption</h2>
<p>To get teams to shift their troubleshooting mindset, what organizational changes need to be made?</p>
<p>Initially, businesses should consider strategic priorities and goals that need to be shared broadly among the teams. One thing that can help drive this in a very large organization is to consider an entire product team devoted to Observability or a CoE (Center of Excellence) with its own roadmap and priorities.</p>
<p>This team (either virtual or permanent) should start with the customer in mind and work backward, starting with key questions like: What do I need to collect? What do I need to observe? How do I act? Once team members understand the answers to these questions, they can start to think about the technology decisions needed to drive those outcomes.</p>
<p>From a tracing and APM perspective, the areas of greatest concern are the customer experience, service level objectives, and service level outcomes. From here, organizations can start to implement programs of work to continuously improve and share knowledge across teams. This will help to align teams around a common framework with shared goals.</p>
<p>In the next few sections, we will go through a four step journey to help you maximize your success with APM and tracing. This journey will take you through the following key steps on your journey to successful APM adoption:</p>
<ol>
<li><strong>Ingest:</strong> What choices do you have to make to get tracing activated and start ingesting trace data into your observability tools?</li>
<li><strong>Integrate:</strong> How does tracing integrate with logs to enable full end-to-end observability, and what else beyond simple tracing can you utilize to get even better resolution on your data?</li>
<li><strong>Analytics and AIOPs:</strong> Improve the customer experience and reduce the noise through machine learning.</li>
<li><strong>Scale and total cost of ownership:</strong> Roll out enterprise-wide tracing and adopt strategies to deal with data volume.</li>
</ol>
<h2 id="1ingest">1. Ingest</h2>
<p>Ingesting data for APM purposes generally involves “instrumenting” the application. In this section, we will explore methods for instrumenting applications, talk a little bit about sampling, and finally wrap up with a note on using common schemas for data representation.</p>
<h3 id="gettingstartedwithinstrumentation">Getting started with instrumentation</h3>
<p>What options do we have for ingesting APM and trace data? There are many, many options we will discuss to help guide you, but first let's take a step back. APM has a deep history — in very first implementations of APM, people were concerned mainly with timing methods, like this below:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt1b8ff9b268b8a134/6a85cc2799083f8a0c40f9f1/blog-elastic-timing-methods.png" alt="timing methods" /></p>
<p>Usually you had a configuration file to specify which methods you wanted to time, and the APM implementation would instrument the specified code with method timings.</p>
<p>From here things started to evolve, and one of the first additions to APM was to add in tracing.</p>
<p>For Java, it’s fairly trivial to implement a system to do this by using what's known as a Java agent. You just specify -javagent command line argument, and the agent code gets access to the dynamic compilation routines within Java so it can modify the code before it is compiled into machine code, allowing you to “wrap” specific methods with timing or tracing routines. So, auto instrumenting Java was one of the first things that the original APM vendors did.</p>
<p><a href="https://opentelemetry.io/docs/instrumentation/java/automatic/">OpenTelemetry has agents like this</a>, and most observability vendors that offer APM solutions have their own proprietary ways of doing this, often with more advanced and differing features from the open source tooling.</p>
<p>Things have moved on since then, and Node.JS and Python are now popular.</p>
<p>As a result, ways of auto instrumenting these language runtimes have appeared, which mostly work by injecting the libraries into the code before starting them up. OpenTelemetry has a way of doing this on Kubernetes with an Operator and sidecar <a href="https://github.com/open-telemetry/opentelemetry-operator/blob/main/README.md">here</a>, which supports Python, Node.JS, Java, and DotNet.</p>
<p>The other alternative is to start adding APM and tracing API calls into your own code, which is not dissimilar to adding logging functionality. You may even wish to create an abstraction in your code to deal with this cross-cutting concern, although this is less of a problem now that there are open standards with which you can implement this.</p>
<p>You can see an example of how to add OpenTelemetry spans and attributes to your code for manual instrumentation below and <a href="https://github.com/davidgeorgehope/ChatGPTMonitoringWithOtel/blob/main/monitor.py">here</a>.</p>
<pre><code>from flask import Flask
import monitor  # Import the module
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter
import urllib
import os

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


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

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

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

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

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

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

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

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

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

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

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

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

        # Set OpenTelemetry attributes
        span = trace.get_current_span()
        if span:
            span.set_attribute("completion_count", counters['completion_count'])
            span.set_attribute("token_count", token_count)
            span.set_attribute("prompt_tokens", prompt_tokens)
            span.set_attribute("completion_tokens", completion_tokens)
            span.set_attribute("model", response.model)
            span.set_attribute("cost", cost)
            span.set_attribute("response", strResponse)
        return response
    return wrapper
</code></pre>
<h3 id="usingbusinessdataforfunandprofit">Using business data for fun and profit</h3>
<p>Once you have the business data in your traces, you can start to have some fun with it. Take a look at the example below for a financial services fraud team. Here we are tracking transactions — average transaction value for our larger business customers. Crucially, we can see if there are any unusual transactions.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt968f966ddbe0150e/6a85cc332d64d515ef081d5c/blog-elastic-customer-count.png" alt="customer count" /></p>
<p>A lot of this is powered by machine learning, which can classify transactions or do <a href="https://www.elastic.co/blog/reduce-mttd-ml-machine-learning-observability">anomaly detection</a>. Once you start capturing the data, it is possible to do a lot of useful things like this, and with a flexible platform, integrating machine learning models into this process becomes a breeze.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltc046307a571de036/6a85cc3733f244b66149f524/blog-elastic-fraud-12h.png" alt="fraud 12-h" /></p>
<h3 id="slisandslos">SLIs and SLOs</h3>
<p>Service level indicators (SLIs) and service level objectives (SLOs) serve as critical components for maintaining and enhancing application performance. SLIs, which represent key performance metrics such as latency, error rate, and throughput, help quantify an application's performance, while SLOs establish target performance levels to meet user expectations.</p>
<p>By selecting relevant SLIs and setting achievable SLOs, organizations can better monitor their application's performance using APM tools. Continually evaluating and adjusting SLIs and SLOs in response to changes in application requirements, user expectations, or the competitive landscape ensures that the application remains competitive and delivers an exceptional user experience.</p>
<p>In order to define and track SLIs and SLOs, APM becomes a critical perspective that is needed for understanding the user experience. Once APM is implemented, we recommend that organizations perform the following steps.</p>
<ul>
<li>Define SLOs and SLIs required to track them.</li>
<li>Define SLO budgets and how they are calculated. Reflect business’ perspective and set realistic targets.</li>
<li>Define SLIs to be measured from a user experience perspective.</li>
<li>Define different alerting and paging rules, page only on customer facing SLO degradations, record symptomatic alerts, notify on critical symptomatic alerts.</li>
</ul>
<p>Synthetic monitoring and end user monitoring (EUM) can also help with getting even more data required to understand latency, throughput, and error rate from the user’s perspective, where it is critical to get good business focused metrics and data from.</p>
<h2 id="4scaleandtotalcostofownership">4. Scale and total cost of ownership</h2>
<p>With increased perspectives, customers often run into scalability and total cost of ownership issues. All this new data can be overwhelming. Luckily there are various techniques you can use to deal with this. Tracing itself can actually help with volume challenges because you can decompose unstructured logs and combine them with traces, which leads to additional efficiency. You can also use different sampling methods to deal with scale challenges (i.e., both techniques we previously mentioned).</p>
<p>In addition to this, for large enterprise scale, we can use streaming pipelines like Kafka or Pulsar to manage the data volumes. This has an additional benefit that you get for free: if you take down the systems consuming the data or they face outages, it is less likely you will lose data.</p>
<p>With this configuration in place, your “Observability pipeline” architecture would look like this:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltd4444d4d2f26f46f/6a85cc3a4710c62b50d3cb73/blog-elastic-opentelemetry-collector.png" alt="opentelemetry collector" /></p>
<p>This completely decouples your sources of data from your chosen observability solution, which will future proof your observability stack going forward, enable you to reach massive scale, and make you less reliant on specific vendor code for collection of data.</p>
<p>Another thing we recommend doing is being intelligent about instrumentation. This will serve two benefits: you will get some CPU cycles back in the instrumented application, and your backend data collection systems will have less data to process. If you know, for example, that you have no interest in tracking calls to a specific endpoint, you can exclude those classes and methods from instrumentation.</p>
<p>And finally, data tiering is a transformative approach for managing data storage that can significantly reduce the total cost of ownership (TCO) for businesses. Primarily, it allows organizations to store data across different types of storage mediums based on their accessibility needs and the value of the data. For instance, frequently accessed, high-value data can be stored in expensive, high-speed storage, while less frequently accessed, lower-value data can be stored in cheaper, slower storage.</p>
<p>This approach, often incorporated in cloud storage solutions, enables cost optimization by ensuring that businesses only pay for the storage they need at any given time. Furthermore, it provides the flexibility to scale up or down based on demand, eliminating the need for large capital expenditures on storage infrastructure. This scalability also reduces the need for costly over-provisioning to handle potential future demand.</p>
<h2 id="conclusion">Conclusion</h2>
<p>In today's highly competitive and fast-paced software development landscape, simply relying on logging is no longer sufficient to ensure top-notch customer experiences. By adopting APM and distributed tracing, organizations can gain deeper insights into their systems, proactively detect and resolve issues, and maintain a robust user experience.</p>
<p>In this blog, we have explored the journey of moving from a logging-only approach to a comprehensive observability strategy that integrates logs, traces, and APM. We discussed the importance of cultivating a new monitoring mindset that prioritizes customer experience, and the necessary organizational changes required to drive APM and tracing adoption. We also delved into the various stages of the journey, including data ingestion, integration, analytics, and scaling.</p>
<p>By understanding and implementing these concepts, organizations can optimize their monitoring efforts, reduce MTTR, and keep their customers satisfied. Ultimately, prioritizing customer experience through APM and tracing can lead to a more successful and resilient enterprise in today's challenging environment.</p>
<p><a href="https://www.elastic.co/observability/application-performance-monitoring">Learn more about APM at Elastic</a>.</p>]]></content:encoded>
    <link>https://www.elastic.co/observability-labs/blog/introduction-apm-tracing-logging</link>
    <guid isPermaLink="false">introduction-apm-tracing-logging</guid>
    <category><![CDATA[APM]]></category>
    <category><![CDATA[OpenTelemetry]]></category>
    <category><![CDATA[Logs Analytics]]></category>
    <dc:creator><![CDATA[David Hope]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt7bda210049148e3b/6a85cc3dd7b2e756d4fe850a/log-management-720x420_(2).jpeg" length="0" type="image/jpeg"/>
    <pubDate>Tue, 30 May 2023 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Trace your Azure Function application with Elastic Observability]]></title>
    <description><![CDATA[Serverless applications deployed on Azure Functions are growing in usage. This blog shows how to deploy a serverless application on Azure functions with Elastic Agent and use Elastic's APM capability to manage and troubleshoot issues.]]></description>
    <content:encoded><![CDATA[<p>Adoption of Azure Functions in cloud-native applications on Microsoft Azure has been increasing exponentially over the last few years. Serverless functions, such as the Azure Functions, provide a high level of abstraction from the underlying infrastructure and orchestration, given these tasks are managed by the cloud provider. Software development teams can then focus on the implementation of business and application logic. Some additional benefits include billing for serverless functions based on the actual compute and memory resources consumed, along with automatic on-demand scaling.</p>
<p>While the benefits of using serverless functions are manifold, it is also necessary to make them observable in the wider end-to-end microservices architecture context.</p>
<h2 id="elasticobservabilityapmforazurefunctionsthearchitecture">Elastic Observability (APM) for Azure Functions: The architecture</h2>
<p><a href="https://www.elastic.co/blog/whats-new-elastic-observability-8-7-0">Elastic Observability 8.7</a> introduced distributed tracing for Microsoft Azure Functions — available for the Elastic APM Agents for .NET, Node.js, and Python. Auto-instrumentation of HTTP requests is supported out-of-the-box, enabling the detection of performance bottlenecks and sources of errors.</p>
<p>The key components of the solution for observing Azure Functions are:</p>
<ol>
<li>The Elastic APM Agent for the relevant language</li>
<li>Elastic Observability</li>
</ol>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltb31ffdc829e957a8/6a85ce28eaf2456480a49fb7/blog-elastic-azure-function.png" alt="azure function" /></p>
<p>The APM server validates and processes incoming events from individual APM Agents and transforms them into Elasticsearch documents. The APM Agent provides auto-instrumentation capabilities for the application being observed. The Node.js APM Agent can trace function invocations in an Azure Functions app.</p>
<h2 id="settingupelasticapmforazurefunctions">Setting up Elastic APM for Azure Functions</h2>
<p>To demonstrate the setup and usage of Elastic APM, we will use a <a href="https://github.com/elastic/azure-functions-apm-nodejs-sample-app">sample Node.js application</a>.</p>
<h3 id="applicationoverview">Application overview</h3>
<p>The Node.js application has two <a href="https://learn.microsoft.com/en-us/azure/azure-functions/functions-bindings-http-webhook">HTTP-triggered</a> functions named "<a href="https://github.com/elastic/azure-functions-apm-nodejs-sample-app/blob/main/Hello/index.js">Hello</a>" and "<a href="https://github.com/elastic/azure-functions-apm-nodejs-sample-app/blob/main/Goodbye/index.js">Goodbye</a>." Once deployed, they can be called as follows, and tracing data will be sent to the configured Elastic Observability deployment.</p>
<pre><code>curl -i https://&lt;APP_NAME&gt;.azurewebsites.net/api/hello
curl -i https://&lt;APP_NAME&gt;.azurewebsites.net/api/goodbye
</code></pre>
<h3 id="setup">Setup</h3>
<p><strong>Step 0. Prerequisites</strong></p>
<p>To run the sample application, you will need:</p>
<ul>
<li>An installation of <a href="https://nodejs.org/">Node.js</a> (v14 or later)</li>
<li>Access to an Azure subscription with an appropriate role to create resources</li>
<li>The <a href="https://learn.microsoft.com/en-us/cli/azure/install-azure-cli">Azure CLI (az)</a> logged into an Azure subscription</li>
</ul>
<ol>
<li>Use az login to login</li>
<li>See the output of az account show</li>
</ol>
<ul>
<li>The <a href="https://learn.microsoft.com/en-us/azure/azure-functions/functions-run-local?tabs=v4%2Cwindows%2Ccsharp%2Cportal%2Cbash#install-the-azure-functions-core-tools">Azure Functions Core Tools (func)</a> (func --version should show a 4.x version)</li>
<li>An Elastic Observability deployment to which monitoring data will be sent</li>
</ul>
<ol>
<li>The simplest way to get started with Elastic APM Microsoft Azure is through Elastic Cloud. <a href="https://www.elastic.co/guide/en/elastic-stack-deploy/current/azure-marketplace-getting-started.html">Get started with Elastic Cloud on Azure Marketplace</a> or <a href="https://www.elastic.co/cloud/elasticsearch-service/signup">sign up for a trial on Elastic Cloud</a>.</li>
</ol>
<ul>
<li>The APM server URL (serverUrl) and secret token (secretToken) from your Elastic stack deployment for configuration below</li>
</ul>
<ol>
<li><a href="https://www.elastic.co/guide/en/apm/guide/8.7/install-and-run.html">How to get the serverUrl and secretToken documentation</a></li>
</ol>
<p><strong>Step 1. Clone the sample application repo and install dependencies</strong></p>
<pre><code>git clone https://github.com/elastic/azure-functions-apm-nodejs-sample-app.git
cd azure-functions-apm-nodejs-sample-app
npm install
</code></pre>
<p><strong>Step 2. Deploy the Azure Function App</strong><br />
Caution icon! Deploying a function app to Azure can incur <a href="https://azure.microsoft.com/en-us/pricing/details/functions/">costs</a>. The following setup uses the free tier of Azure Functions. Step 5 covers the clean-up of resources.</p>
<p><strong>Step 2.1</strong><br />
To avoid name collisions with others that have independently run this demo, we need a short unique identifier for some resource names that need to be globally unique. We'll call it the DEMO_ID. You can run the following to generate one and save it to DEMO_ID and the "demo-id" file.</p>
<pre><code>if [[ ! -f demo-id ]]; then node -e 'console.log(crypto.randomBytes(3).toString("hex"))' &gt;demo-id; fi
export DEMO_ID=$(cat demo-id)
echo $DEMO_ID
</code></pre>
<p><strong>Step 2.2</strong><br />
Before you can deploy to Azure, you will need to create some Azure resources: a Resource Group, Storage Account, and the Function App. For this demo, you can use the following commands. (See <a href="https://learn.microsoft.com/en-us/azure/azure-functions/create-first-function-cli-node#create-supporting-azure-resources-for-your-function">this Azure docs section</a> for more details.)</p>
<pre><code>REGION=westus2   # Or use another region listed in 'az account list-locations'.
az group create --name "AzureFnElasticApmNodeSample-rg" --location "$REGION"
az storage account create --name "eapmdemostor${DEMO_ID}" --location "$REGION" \
    --resource-group "AzureFnElasticApmNodeSample-rg" --sku Standard_LRS
az functionapp create --name "azure-functions-apm-nodejs-sample-app-${DEMO_ID}" \
    --resource-group "AzureFnElasticApmNodeSample-rg" \
    --consumption-plan-location "$REGION" --runtime node --runtime-version 18 \
    --functions-version 4 --storage-account "eapmdemostor${DEMO_ID}"
</code></pre>
<p><strong>Step 2.3</strong><br />
Next, configure your Function App with the APM server URL and secret token for your Elastic deployment. This can be done in the <a href="https://portal.azure.com/">Azure Portal</a> or with the az CLI.</p>
<p>In the Azure portal, browse to your Function App, then its Application Settings (<a href="https://learn.microsoft.com/en-us/azure/azure-functions/functions-how-to-use-azure-function-app-settings?tabs=portal#settings">Azure user guide</a>). You'll need to add two settings:</p>
<p>First set your APM URL and token.</p>
<pre><code>export ELASTIC_APM_SERVER_URL="&lt;your serverUrl&gt;"
export ELASTIC_APM_SECRET_TOKEN="&lt;your secretToken&gt;"
</code></pre>
<p>Or you can use the az functionapp config appsettings set … CLI command as follows:</p>
<pre><code>az functionapp config appsettings set \
  -g "AzureFnElasticApmNodeSample-rg" -n "azure-functions-apm-nodejs-sample-app-${DEMO_ID}" \
  --settings "ELASTIC_APM_SERVER_URL=${ELASTIC_APM_SERVER_URL}"
az functionapp config appsettings set \
  -g "AzureFnElasticApmNodeSample-rg" -n "azure-functions-apm-nodejs-sample-app-${DEMO_ID}" \
  --settings "ELASTIC_APM_SECRET_TOKEN=${ELASTIC_APM_SECRET_TOKEN}"
</code></pre>
<p>The ELASTIC_APM_SERVER_URL and ELASTIC_APM_SECRET_TOKEN are set in Azure function’s settings for the app and used by the Elastic APM Agent. This is initiated by the initapm.js file, which starts the Elastic APM agent with:</p>
<pre><code>require("elastic-apm-node").start();
</code></pre>
<p>When you log in to Azure and look at the function’s configuration, you will see them set:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt17f40523d90b8ed0/6a85ce2a1aa1e1b268ff8dc3/blog-elastic-azure-functions-application-settings.png" alt="azure functions application settings" /></p>
<p><strong>Step 2.4</strong><br />
Now you can publish your app. (Re-run this command every time you make a code change.)</p>
<pre><code>func azure functionapp publish "azure-functions-apm-nodejs-sample-app-${DEMO_ID}"
</code></pre>
<p>You should log in to Azure to see the function running.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt461d0d8fdb235dc9/6a85ce2d2d64d52d7c081d9a/blog-elastic-azure-function-app.png" alt="azure function app" /></p>
<p><strong>Step 3. Try it out</strong></p>
<pre><code>% curl https://azure-functions-apm-nodejs-sample-app-${DEMO_ID}.azurewebsites.net/api/Hello
{"message":"Hello."}
% curl https://azure-functions-apm-nodejs-sample-app-${DEMO_ID}.azurewebsites.net/api/Goodbye
{"message":"Goodbye."}
</code></pre>
<p>In a few moments, the APM app in your Elastic deployment will show tracing data for your Azure Function app.</p>
<p><strong>Step 4. Apply some load to your app</strong><br />
To get some more interesting data, you can run the following to generate some load on your deployed function app:</p>
<pre><code>npm run loadgen
</code></pre>
<p>This uses the <a href="https://github.com/mcollina/autocannon">autocannon</a> node package to generate some light load (2 concurrent users, each calling at 5 requests/s for 60s) on the "Goodbye" function.</p>
<p><strong>Step 5. Clean up resources</strong><br />
If you deployed to Azure, you should make sure to delete any resources so you don't incur any costs.</p>
<pre><code>az group delete --name "AzureFnElasticApmNodeSample-rg"
</code></pre>
<h2 id="analyzingazurefunctionapmdatainelastic">Analyzing Azure Function APM data in Elastic</h2>
<p>Once you have successfully set up the sample application and started generating load, you should see APM data appearing in the Elastic Observability APM Services capability.</p>
<h2 id="servicemap">Service map</h2>
<p>With the default setup, you will see two services in the APM Service map.</p>
<p>The main function: azure-functions-apm-nodejs-sample-app</p>
<p>And the end point where your function is accessible: azure-functions-apm-nodejs-sample-app-ec7d4c.azurewebsites.net</p>
<p>You will see that there is a connection between the two as your application is taking requests and answering through the endpoint.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt54f1085b82117c5a/6a85ce3018249c0a4218f833/blog-elastic-observability-services.png" alt="observability services" /></p>
<p>From the <a href="https://www.elastic.co/observability/application-performance-monitoring">APM Service</a> map you can further investigate the function, analyze traces, look at logs, and more.</p>
<h3 id="servicedetails">Service details</h3>
<p>When we dive into the details, we can see several items.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt7320943d10a3b565/6a85ce33f9373d5edb96f5ec/blog-elastic-observability-azure-functions-apm.png" alt="observability azure functions apm" /></p>
<ul>
<li>Latency for the recent load we ran against the application</li>
<li>Transactions (Goodbye and Hello)</li>
<li>Average throughput</li>
<li>And more</li>
</ul>
<h3 id="transactiondetails">Transaction details</h3>
<p>We can see transaction details.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt57205ffed9ed8840/6a85ce366826666d441eac65/blog-elastic-observability-get-api-goodbye.png" alt="observability get api goodbye" /></p>
<p>An individual trace shows us that the "Goodbye" function <a href="https://github.com/elastic/azure-functions-apm-nodejs-sample-app/blob/main/Goodbye/index.js#L6-L10">calls the "Hello" function</a> in the same function app before returning:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blta3f4a958241d2755/6a85ce39342d69cc5021b15d/blog-elastic-latency-distribution-trace-sample.png" alt="latency distribution trace sample" /></p>
<h3 id="machinelearningbasedlatencycorrelation">Machine learning based latency correlation</h3>
<p>As we’ve mentioned in other blogs, we can also correlate issues such as higher than normal latency. Since we see a spike at 1s, we run the embedded latency correlation, which uses machine learning to help analyze the potential impacting component by analyzing logs, metrics, and traces.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt4cc5483b0e4f032c/6a85ce3bbc5bb384a7f81b53/blog-elastic-latency-distribution-correlations.png" alt="latency distribution correlations" /></p>
<p>The correlation indicated there is a potential cause (25%) due to the host sending the load (my machine).</p>
<h3 id="coldstartdetection">Cold start detection</h3>
<p>Also, we can see the impact a <a href="https://azure.microsoft.com/en-ca/blog/understanding-serverless-cold-start/">cold start</a> can have on the latency of a request:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt2bc48a2793c2d457/6a85ce3e27c5cd6b9f5f7454/blog-elastic-trace-sample.png" alt="trace sample" /></p>
<h2 id="summary">Summary</h2>
<p>Elastic Observability provides real-time monitoring of Azure Functions in your production environment for a broad range of use cases. Curated dashboards assist DevOps teams in performing root cause analysis for performance bottlenecks and errors. SRE teams can quickly view upstream and downstream dependencies, as well as perform analyses in the context of distributed microservices architecture.</p>
<h2 id="learnmore">Learn more</h2>
<p>To learn how to add the Elastic APM Agent to an existing Node.js Azure Function app, read <a href="https://www.elastic.co/guide/en/apm/agent/nodejs/master/azure-functions.html">Monitoring Node.js Azure Functions</a>. Additional resources include:</p>
<ul>
<li><a href="https://www.elastic.co/blog/getting-started-with-the-azure-integration-enhancement">How to deploy and manage Elastic Observability on Microsoft Azure</a></li>
<li><a href="https://www.elastic.co/guide/en/apm/guide/current/apm-quick-start.html">Elastic APM Quickstart</a></li>
</ul>]]></content:encoded>
    <link>https://www.elastic.co/observability-labs/blog/trace-azure-function-application-observability</link>
    <guid isPermaLink="false">trace-azure-function-application-observability</guid>
    <category><![CDATA[APM]]></category>
    <category><![CDATA[Infrastructure Monitoring]]></category>
    <dc:creator><![CDATA[Trent Mick,Bahubali Shetti,Hemant Malik]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltb7529874ffa275f9/6a85ce419d2b719823f93a0e/09-road.jpeg" length="0" type="image/jpeg"/>
    <pubDate>Tue, 16 May 2023 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Monitor OpenAI API and GPT models with OpenTelemetry and Elastic]]></title>
    <description><![CDATA[Get ready to be blown away by this game-changing approach to monitoring cutting-edge ChatGPT applications! As the ChatGPT phenomenon takes the world by storm, it's time to supercharge your monitoring game with OpenTelemetry and Elastic Observability.]]></description>
    <content:encoded><![CDATA[<p>ChatGPT is so hot right now, it broke the internet. As an avid user of ChatGPT and a developer of ChatGPT applications, I am incredibly excited by the possibilities of this technology. What I see happening is that there will be exponential growth of ChatGPT-based solutions, and people are going to need to monitor those solutions.</p>
<p>Since this is a pretty new technology, we wouldn’t want to burden our shiny new code with proprietary technology, would we? No, we would not, and that is why we are going to use OpenTelemetry to monitor our ChatGPT code in this blog. This is particularly relevant for me as I recently created a service to generate meeting notes from Zoom calls. If I am to release this into the wild, how much is it going to cost me and how do I make sure it is available?</p>
<h2 id="openaiapistotherescue">OpenAI APIs to the rescue</h2>
<p>The OpenAI API is pretty awesome, there is no doubt. It also gives us the information shown below in each response to each API call, which can help us with understanding what we are being charged. By using the token counts, the model, and the pricing that OpenAI has put up on its website, we can calculate the cost. The question is, how do we get this information into our monitoring tools?</p>
<pre><code>{
  "choices": [
    {
      "finish_reason": "length",
      "index": 0,
      "logprobs": null,
      "text": "\n\nElastic is an amazing observability tool because it provides a comprehensive set of features for monitoring"
    }
  ],
  "created": 1680281710,
  "id": "cmpl-70CJq07gibupTcSM8xOWekOTV5FRF",
  "model": "text-davinci-003",
  "object": "text_completion",
  "usage": {
    "completion_tokens": 20,
    "prompt_tokens": 9,
    "total_tokens": 29
  }
}
</code></pre>
<h2 id="opentelemetrytotherescue">OpenTelemetry to the rescue</h2>
<p><a href="https://www.elastic.co/blog/opentelemetry-observability">OpenTelemetry</a> is truly a fantastic piece of work. It has had so much adoption and work committed to it over the years, and it seems to really be getting to the point where we can call it the Linux of Observability. We can use it to record logs, metrics, and traces and get those in a vendor neutral way into our favorite observability tool — in this case, Elastic Observability.</p>
<p>With the latest and greatest otel libraries in Python, we can auto-instrument external calls, and this will help us understand how OpenAI calls are performing. Let's take a sneak peek at our sample Python application, which implements Flask and the ChatGPT API and also has OpenTelemetry. If you want to try this yourself, take a look at the GitHub link at the end of this blog and follow these steps.</p>
<h3 id="setupelasticcloudaccountifyoualreadydonthaveone">Set up Elastic Cloud account (if you already don’t have one)</h3>
<ol>
<li>Sign up for a two-week free trial at <a href="https://www.elastic.co/cloud/elasticsearch-service/signup">https://www.elastic.co/cloud/elasticsearch-service/signup</a>.</li>
<li>Create a deployment.</li>
</ol>
<p>Once you are logged in, click <strong>Add integrations</strong>.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt2a530a6a1d8ae18c/6a85cd3eeaf2458371a49f8f/blog-elastic-cloud-deployment-add-integrations.png" alt="elastic cloud deployment add integrations" /></p>
<p>Click on <strong>APM Integration</strong>.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt12f670bb3d7aad2c/6a85cd411aa1e1660eff8da3/blog-elastic-apm-integration.png" alt="elastic apm integration" /></p>
<p>Then scroll down to get the details you need for this blog:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltfa14098df2f3aab7/6a85cd44d6cf2912dcbb0925/blog-elastic-opentelemetry-download.png" alt="elastic opentelemetry download" /></p>
<p>Be sure to set the following Environment variables, replacing the variables with data you get from Elastic as above and OpenAI from <a href="https://platform.openai.com/account/api-keys">here</a>, and then run these export commands on the command line.</p>
<pre><code>export OPEN_AI_KEY=sk-abcdefgh5ijk2l173mnop3qrstuvwxyzab2cde47fP2g9jij
export OTEL_EXPORTER_OTLP_AUTH_HEADER=abc9ldeofghij3klmn
export OTEL_EXPORTER_OTLP_ENDPOINT=https://123456abcdef.apm.us-west2.gcp.elastic-cloud.com:443
</code></pre>
<p>And install the following Python libraries:</p>
<pre><code>pip3 install opentelemetry-api
pip3 install opentelemetry-sdk
pip3 install opentelemetry-exporter-otlp
pip3 install opentelemetry-instrumentation
pip3 install opentelemetry-instrumentation-requests
pip3 install openai
pip3 install flask
</code></pre>
<p>Here is a look at the code we are using for the example application. In the real world, this would be your own code. All this does is call OpenAI APIs with the following message: “Why is Elastic an amazing observability tool?”</p>
<pre><code>import openai
from flask import Flask
import monitor  # Import the module
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter
import urllib
import os
from opentelemetry import trace
from opentelemetry.sdk.resources import SERVICE_NAME, Resource
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor
from opentelemetry.instrumentation.requests import RequestsInstrumentor

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



# Initialize Flask app and instrument it

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


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

if __name__ == "__main__":
    app.run()
</code></pre>
<p>This code should be fairly familiar to anyone who has implemented OpenTelemetry with Python here — there is no specific magic. The magic happens inside the “monitor” code that you can use freely to instrument your own OpenAI applications.</p>
<h2 id="monkeyingaround">Monkeying around</h2>
<p>Inside the monitor.py code, you will see we do something called “Monkey Patching.” Monkey patching is a technique in Python where you dynamically modify the behavior of a class or module at runtime by modifying its attributes or methods. Monkey patching allows you to change the functionality of a class or module without having to modify its source code. It can be useful in situations where you need to modify the behavior of an existing class or module that you don't have control over or cannot modify directly.</p>
<p>What we want to do here is modify the behavior of the “Completion” call so we can steal the response metrics and add them to our OpenTelemetry spans. You can see how we do that below:</p>
<pre><code>def count_completion_requests_and_tokens(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        counters['completion_count'] += 1
        response = func(*args, **kwargs)
        token_count = response.usage.total_tokens
        prompt_tokens = response.usage.prompt_tokens
        completion_tokens = response.usage.completion_tokens
        cost = calculate_cost(response)
        strResponse = json.dumps(response)
        # Set OpenTelemetry attributes
        span = trace.get_current_span()
        if span:
            span.set_attribute("completion_count", counters['completion_count'])
            span.set_attribute("token_count", token_count)
            span.set_attribute("prompt_tokens", prompt_tokens)
            span.set_attribute("completion_tokens", completion_tokens)
            span.set_attribute("model", response.model)
            span.set_attribute("cost", cost)
            span.set_attribute("response", strResponse)
        return response
    return wrapper
# Monkey-patch the openai.Completion.create function
openai.Completion.create = count_completion_requests_and_tokens(openai.Completion.create)
</code></pre>
<p>By adding all this data to our Span, we can actually send it to our OpenTelemetry OTLP endpoint (in this case it will be Elastic). The benefit of doing this is that you can easily use the data for search or to build dashboards and visualizations. In the final step, we also want to calculate the cost. We do this by implementing the following function, which will calculate the cost of a single request to the OpenAI APIs.</p>
<pre><code>def calculate_cost(response):
    if response.model in ['gpt-4', 'gpt-4-0314']:
        cost = (response.usage.prompt_tokens * 0.03 + response.usage.completion_tokens * 0.06) / 1000
    elif response.model in ['gpt-4-32k', 'gpt-4-32k-0314']:
        cost = (response.usage.prompt_tokens * 0.06 + response.usage.completion_tokens * 0.12) / 1000
    elif 'gpt-3.5-turbo' in response.model:
        cost = response.usage.total_tokens * 0.002 / 1000
    elif 'davinci' in response.model:
        cost = response.usage.total_tokens * 0.02 / 1000
    elif 'curie' in response.model:
        cost = response.usage.total_tokens * 0.002 / 1000
    elif 'babbage' in response.model:
        cost = response.usage.total_tokens * 0.0005 / 1000
    elif 'ada' in response.model:
        cost = response.usage.total_tokens * 0.0004 / 1000
    else:
        cost = 0
    return cost
</code></pre>
<h2 id="elastictotherescue">Elastic to the rescue</h2>
<p>Once we are capturing all this data, it’s time to have some fun with it in Elastic. In Discover, we can see all the data points we sent over using the OpenTelemetry library:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltfce6ddd6aa2ec67b/6a85cd460782905a9f3217aa/blog-elastic-discover-apm.png" alt="elastic discover apm" /></p>
<p>With these labels in place, it is very easy to build a dashboard. Take a look at this one I built earlier (<a href="https://github.com/davidgeorgehope/ChatGPTMonitoringWithOtel/blob/main/chatGPTDashboard.ndjson">which is also checked into my GitHub Repository</a>):</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt771ed8e0409e9e81/6a85cd4907829032893217ae/blog-elastic-labels-dashboard.png" alt="elastic labels dashboard" /></p>
<p>We can also see Transactions, Latency of the OpenAI service, and all the spans related to our ChatGPT service calls.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt090652b31aa8510a/6a85cd4c4710c62948d3cba0/blog-elastic-observability-service-name.png" alt="observability service name" /></p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blta8534a667cecc5f1/6a85cd4f18249c222918f803/blog-elastic-your-service-name.png" alt="elastic your service name" /></p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltfc32738277650edf/6a85cd529bf994220f0a05b5/blog-elastic-api-openai.png" alt="elastic api openai" /></p>
<p>In the transaction view, we can also see how long specific OpenAI calls have taken:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt0101e66241fca1b4/6a85cd54f9373dad1d96f5de/blog-elastic-latency-distribution.png" alt="elastic latency distribution" /></p>
<p>Some requests to OpenAI here have taken over 3 seconds. ChatGPT can be very slow, so it’s important for us to understand how slow this is and if users are becoming frustrated.</p>
<h2 id="summary">Summary</h2>
<p>We looked at monitoring ChatGPT with OpenTelemetry with Elastic. ChatGPT is a worldwide phenomenon and it’s going to no doubt grow and grow, and pretty soon everyone will be using it. Because it can be slow to get responses out, it is critical that people are able to understand the performance of any code that is using this service.</p>
<p>There is also the issue of cost, since it’s incredibly important to understand if this service is eating into your margins and if what you are asking for is profitable for your business. With the current economic environment, we have to keep an eye on profitability.</p>
<p>Take a look at the code for this solution <a href="https://github.com/davidgeorgehope/ChatGPTMonitoringWithOtel">here</a>. And please feel free to use the “monitor” library to instrument your own OpenAI code.</p>
<p>Interested in learning more about Elastic Observability? Check out the following resources:</p>
<ul>
<li><a href="https://www.elastic.co/virtual-events/intro-to-elastic-observability">An Introduction to Elastic Observability</a></li>
<li><a href="https://www.elastic.co/training/observability-fundamentals">Observability Fundamentals Training</a></li>
<li><a href="https://www.elastic.co/observability/demo">Watch an Elastic Observability demo</a></li>
<li><a href="https://www.elastic.co/blog/observability-predictions-trends-2023">Observability Predictions and Trends for 2023</a></li>
</ul>
<p>And sign up for our <a href="https://www.elastic.co/virtual-events/emerging-trends-in-observability">Elastic Observability Trends Webinar</a> featuring AWS and Forrester, not to be missed!</p>
<p><em>In this blog post, we may have used third party generative AI tools, which are owned and operated by their respective owners. Elastic does not have any control over the third party tools and we have no responsibility or liability for their content, operation or use, nor for any loss or damage that may arise from your use of such tools. Please exercise caution when using AI tools with personal, sensitive or confidential information. Any data you submit may be used for AI training or other purposes. There is no guarantee that information you provide will be kept secure or confidential. You should familiarize yourself with the privacy practices and terms of use of any generative AI tools prior to use.</em></p>
<p><em>Elastic, Elasticsearch and associated marks are trademarks, logos or registered trademarks of Elasticsearch N.V. in the United States and other countries. All other company and product names are trademarks, logos or registered trademarks of their respective owners.</em></p>]]></content:encoded>
    <link>https://www.elastic.co/observability-labs/blog/monitor-openai-api-gpt-models-opentelemetry</link>
    <guid isPermaLink="false">monitor-openai-api-gpt-models-opentelemetry</guid>
    <category><![CDATA[LLM Observability]]></category>
    <category><![CDATA[OpenTelemetry]]></category>
    <category><![CDATA[APM]]></category>
    <dc:creator><![CDATA[David Hope]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltc8ce30804f9f5a2b/6a85cd5743c0b79e872f0666/opentelemetry-graphic-ad-2-1920x1080.png" length="0" type="image/png"/>
    <pubDate>Tue, 04 Apr 2023 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[How to monitor Kafka and Confluent Cloud with Elastic Observability]]></title>
    <description><![CDATA[This blog post will take you through best practices to observe Kafka-based solutions implemented on Confluent Cloud with Elastic Observability.]]></description>
    <content:encoded><![CDATA[<p>The blog will take you through best practices to observe Kafka-based solutions implemented on Confluent Cloud with Elastic Observability. (To monitor Kafka brokers that are not in Confluent Cloud, I recommend checking out <a href="https://www.elastic.co/blog/how-to-monitor-containerized-kafka-with-elastic-observability">this blog</a>.) We will instrument Kafka applications with <a href="https://www.elastic.co/observability/application-performance-monitoring">Elastic APM</a>, use the Confluent Cloud metrics endpoint to get data about brokers, and pull it all together with a unified Kafka and Confluent Cloud monitoring dashboard in <a href="https://www.elastic.co/observability">Elastic Observability</a>.</p>
<h2 id="usingfullstackelasticobservabilitytounderstandkafkaandconfluentperformance">Using full-stack Elastic Observability to understand Kafka and Confluent performance</h2>
<p>In the <a href="https://dice.viewer.foleon.com/ebooks/dice-tech-salary-report-explore/">2023 Dice Tech Salary Report</a>, Elasticsearch and Kakfa are ranked #3 and #5 out of the top 12 <a href="https://dice.viewer.foleon.com/ebooks/dice-tech-salary-report-explore/salary-trends#Skills">most in demand skills</a> at the moment, so it’s no surprise that we are seeing a large number of customers who are implementing data in motion with Kafka.</p>
<p><a href="https://www.elastic.co/integrations/data-integrations?search=kafka">Kafka</a> comes with some additional complexities that go beyond traditional architectures and which make observability an even more important topic. Understanding where the bottlenecks are in messaging and stream-based architectures can be tough. This is why you need a comprehensive observability solution with <a href="https://www.elastic.co/blog/aiops-use-cases-observability-operations">machine learning</a> to help you.</p>
<p>In this blog, we will explore how to get Kafka applications instrumented with <a href="https://www.elastic.co/blog/apm-correlations-elastic-observability-root-cause-transactions">Elastic APM</a>, how to collect performance data with JMX, and how you can use the Elasticsearch Platform to pull in data from Confluent Cloud — which is by far the easiest and most cost-effective way to implement Kafka architectures.</p>
<p>For this blog post, we will be following the code at this <a href="https://github.com/davidgeorgehope/multi-cloud">git repository</a>. There are three services here that are designed to run on two clouds and push data from one cloud to the other and finally into Google BigQuery. We want to monitor all of this using Elastic Observability to give you a complete picture of Confluent and Kafka Services performance as a teaser — this is the goal below:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt2b83acb7398304ef/6a85cb8e80984cb656668fec/blog-elastic-observability-producer_metrics.png" alt="kafka producer metrics" /></p>
<h2 id="alookatthearchitecture">A look at the architecture</h2>
<p>As mentioned, we have three <a href="https://www.elastic.co/observability/cloud-monitoring">multi-cloud services</a> implemented in our example application.</p>
<p>The first service is a Spring WebFlux service that runs inside AWS EKS. This service will take a message from a REST Endpoint and simply put it straight on to a Kafka topic.</p>
<p>The second service, which is also a Spring WebFlux service hosted inside Google Cloud Platform (GCP) with its <a href="https://www.elastic.co/observability/google-cloud-monitoring">Google Cloud monitoring</a>, will then pick this up and forward it to another service that will put the message into BigQuery.</p>
<p>These services are all instrumented using Elastic APM. For this blog, we have decided to use Spring config to inject and configure the APM agent. You could of course use the “-javaagent” argument to inject the agent instead if preferred.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt26abffd850b918eb/6a85cb90078290b03c32177c/blog-elastic-obsevability-aws-kafka-google-cloud.png" alt="aws kafka google cloud" /></p>
<h2 id="gettingstartedwithelasticobservabilityandconfluentcloud">Getting started with Elastic Observability and Confluent Cloud</h2>
<p>Before we dive into the application and its configuration, you will want to get an Elastic Cloud and Confluent Cloud account. You can sign up here for <a href="https://www.elastic.co/cloud/">Elastic</a> and here for <a href="https://www.confluent.io/confluent-cloud/">Confluent Cloud</a>. There are some initial configuration steps we need to do inside Confluent Cloud, as you will need to create three topics: gcpTopic, myTopic, and topic_2.</p>
<p>When you sign up for Confluent Cloud, you will be given an option of what type of cluster to create. For this walk-through, a Basic cluster is fine (as shown) — if you are careful about usage, it will not cost you a penny.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltc4c5c643934dbd2f/6a85cb9411893c4c32a7aba0/blog-elastic-observability-confluent-create-cluster.png" alt="confluent create cluster" /></p>
<p>Once you have a cluster, go ahead and create the three topics.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltb7f03631f81bcf62/6a85cb96331d7aaed8c317a9/blog-elastic-observability-confluent-topics.png" alt="confluent topics" /></p>
<p>For this walk-through, you will only need to create single partition topics as shown below:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt480e1df0acaafce5/6a85cb999bf99456280a0581/blog-elastic-observability-new-topic.png" alt="new topic" /></p>
<p>Now we are ready to set up the Elastic Cloud cluster.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt3c52f84578c22908/6a85cb9c18249c40f018f7cb/blog-elastic-observability-create-a-deployment.png" alt="create a deployment" /></p>
<p>One thing to note here is that when setting up an Elastic cluster, the defaults are mostly OK. With one minor tweak to add in the Machine Learning under “Advanced Settings,” add capacity for machine learning here.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt6d43d7fb6ea1311d/6a85cb9f99083f43c340f9e5/blog-elastic-observability-machine-learning-instances.png" alt="machine learning instances" /></p>
<h2 id="gettingapmupandrunning">Getting APM up and running</h2>
<p>The first thing we want to do here is get our Spring Boot Webflux-based services up and running. For this blog, I have decided to implement this using the Spring Configuration, as you can see below. For brevity, I have not listed all the JMX configuration information, but you can see those details in <a href="https://github.com/davidgeorgehope/multi-cloud/blob/main/aws-multi-cloud/src/main/java/com/elastic/multicloud/ElasticApmConfig.java">GitHub</a>.</p>
<pre><code>package com.elastic.multicloud;
import co.elastic.apm.attach.ElasticApmAttacher;
import jakarta.annotation.PostConstruct;
import lombok.Setter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;

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

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

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

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

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

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

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

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

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

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


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

kubectl create secret generic my-app-config-gcp-bigdata --from-file=application.properties
</code></pre>
<h3 id="createakubernetesdeployment">Create a Kubernetes deployment</h3>
<p>Create a Kubernetes deployment YAML file and add your Docker image to it. You can use the deployment.yaml file provided in the project as a template. Make sure to update the image name in the file to match the name of the Docker image you just created.</p>
<pre><code>kubectl apply -f deployment.yaml
</code></pre>
<h3 id="createakubernetesservice">Create a Kubernetes service</h3>
<p>Create a Kubernetes service YAML file and add your deployment to it. You can use the service.yaml file provided in the project as a template.</p>
<pre><code>kubectl apply -f service.yaml
</code></pre>
<h3 id="accessyourapplication">Access your application</h3>
<p>Your application is now running in a Kubernetes cluster. To access it, you can use the service's cluster IP and port. You can get the service's IP and port using the following command.</p>
<pre><code>kubectl get services
</code></pre>
<p>Now once you know where the service is, you need to execute it!</p>
<p>You can regularly poke the service endpoint using the following command.</p>
<pre><code>curl -X POST -H "Content-Type: application/json" -d '{"name": "linuxize", "email": "linuxize@example.com"}' http://localhost:8080/api/my-objects/publish
</code></pre>
<p>With this up and running, you should see the following service map build out in the Elastic APM product.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt6bcd176aacf5c2bf/6a85cbaa68266613df1eac25/blog-elastic-observability-aws-elastic-apm-spring-boot.png" alt="aws elastic apm spring boot" /></p>
<p>And traces will contain a waterfall graph showing all the spans that have executed across this distributed application, allowing you to pinpoint where any issues are within each transaction.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt94b9bc91dc092bb9/6a85cbad9bf994e9330a0585/blog-elastic-observability-services.png" alt="observability services" /></p>
<h2 id="jmxforkafkaproducerconsumermetrics">JMX for Kafka Producer/Consumer metrics</h2>
<p>In the previous part of this blog, we briefly touched on the JMX metric configuration you can see below.</p>
<pre><code>"capture_jmx_metrics","object_name[kafka.producer:type=producer-metrics,client-id=*] attribute[batch-size-avg:metric_name=kafka.producer.batch-size-avg]"
</code></pre>
<p>We can use this “capture_jmx_metrics” configuration to configure JMX for any Kafka Producer/Consumer metrics we want to monitor.</p>
<p>Check out the documentation <a href="https://www.elastic.co/guide/en/apm/agent/java/current/config-jmx.html">here</a> to understand how to configure this and <a href="https://docs.confluent.io/platform/current/kafka/monitoring.html">here</a> to see the available JMX metrics you can monitor. In the <a href="https://github.com/davidgeorgehope/multi-cloud/blob/main/gcp-bigdata-consumer-multi-cloud/src/main/java/com/elastic/multicloud/ElasticApmConfig.java">example code in GitHub</a>, we actually pull all the available metrics in, so you can check in there how to configure this.</p>
<p>One thing that’s worth pointing out here is that it’s important to use the “metric_name” property shown above or it gets quite difficult to find the metrics in Elastic Discover without being specific here.</p>
<h2 id="monitoringconfluentcloudwithelasticobservability">Monitoring Confluent Cloud with Elastic Observability</h2>
<p>So we now have some good monitoring set up for Kafka Producers and Consumers and we can trace transactions between services down to the lines of code that are executing. The core part of our Kafka infrastructure is hosted in Confluent Cloud. How, then, do we get data from there into our <a href="https://www.elastic.co/observability">full stack observability solution</a>?</p>
<p>Luckily, Confluent has done a fantastic job of making this easy. It provides important Confluent Cloud metrics via an open Prometheus-based metrics URL. So let's get down to business and configure this to bring data into our <a href="https://www.elastic.co/observability">observability tool</a>.</p>
<p>The first step is to configure Confluent Cloud with the MetricsViewer. The MetricsViewer role provides service account access to the Metrics API for all clusters in an organization. This role also enables service accounts to import metrics into third-party metrics platforms.</p>
<p>To assign the MetricsViewer role to a new service account:</p>
<ol>
<li>In the top-right administration menu (☰) in the upper-right corner of the Confluent Cloud user interface, click <strong>ADMINISTRATION &gt; Cloud API keys</strong>.</li>
<li>Click <strong>Add key</strong>.</li>
<li>Click the <strong>Granular access tile</strong> to set the scope for the API key. Click <strong>Next</strong>.</li>
<li>Click <strong>Create a new one</strong> and specify the service account name. Optionally, add a description. Click <strong>Next</strong>.</li>
<li>The API key and secret are generated for the service account. You will need this API key and secret to connect to the cluster, so be sure to safely store this information. Click <strong>Save</strong>. The new service account with the API key and associated ACLs is created. When you return to the API access tab, you can view the newly-created API key to confirm.</li>
<li>Return to Accounts &amp; access in the administration menu, and in the Accounts tab, click <strong>Service accounts</strong> to view your service accounts.</li>
<li>Select the service account that you want to assign the MetricsViewer role to.</li>
<li>In the service account’s details page, click <strong>Access</strong>.</li>
<li>In the tree view, open the resource where you want the service account to have the MetricsViewer role.</li>
<li>Click <strong>Add role assignment</strong> and select the MetricsViewer tile. Click <strong>Save</strong>.</li>
</ol>
<p>Next we can head to <a href="https://www.elastic.co/observability">Elastic Observability</a> and configure the Prometheus integration to pull in the metrics data.</p>
<p>Go to the integrations page in Kibana.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt18bf177b652a48e6/6a85cbb04710c62eb0d3cb55/blog-elastic-observability-integrations.png" alt="observability integrations" /></p>
<p>Find the Prometheus integration. We are using the Prometheus integration because the Confluent Cloud metrics server can provide data in prometheus format. Trust us, this works really well — good work Confluent!</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt4d48901dde740fee/6a85cbb243c0b72c932f0622/blog-elastic-observability-integrations-prometheus.png" alt="integrations prometheus" /></p>
<p>Add Prometheus in the next page.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt84626c46ada410b0/6a85cbb507829026aa321782/blog-elastic-observability-add-prometheus.png" alt="add prometheus" /></p>
<p>Configure the Prometheus plugin in the following way: In the hosts box, add the following URL, replacing the resource kafka id with the cluster id you want to monitor.</p>
<pre><code>https://api.telemetry.confluent.cloud:443/v2/metrics/cloud/export?resource.kafka.id=lkc-3rw3gw
</code></pre>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltb9a0004dfff705f8/6a85cbb793ffb91265b91441/blog-elastic-observability-collect-prometheus-metrics.png" alt="collect prometheus metrics" /></p>
<p>Add the username and password under the advanced options you got from the API keys step you executed against Confluent Cloud above.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt3cf8ea6231c6d4a4/6a85cbba9d2b716e39f9399c/blog-elastic-observability-http-config-options.png" alt="http config options" /></p>
<p>Once the Integration is created, <a href="https://www.elastic.co/guide/en/fleet/current/agent-policy.html#apply-a-policy">the policy needs to be applied</a> to an instance of a running Elastic Agent.</p>
<p>That’s it! It’s that easy to get all the data you need for a full stack observability monitoring solution.</p>
<p>Finally, let’s pull all this together in a dashboard.</p>
<h2 id="pullingitalltogether">Pulling it all together</h2>
<p>Using Kibana to generate dashboards is super easy. If you configured everything the way we recommended above, you should find the metrics (producer/consumer/brokers) you need to create your own dashboard as per the following screenshot.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt3ad8b73135f3f525/6a85cbbd27c5cdc4635f7400/blog-elastic-observability-dashboard-metrics.png" alt="dashboard metrics" /></p>
<p>Luckily, I made a dashboard for you and stored it in <a href="https://github.com/davidgeorgehope/multi-cloud/blob/main/export.ndjson">GitHub</a>. Take a look below and use this to import it into your own environments.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt2b83acb7398304ef/6a85cb8e80984cb656668fec/blog-elastic-observability-producer_metrics.png" alt="producer metrics" /></p>
<h2 id="addingtheicingonthecakemachinelearninganomalydetection">Adding the icing on the cake: machine learning anomaly detection</h2>
<p>Now that we have all the critical bits in place, we are going to add the icing on the cake: machine learning (ML)!</p>
<p>Within Kibana, let's head over to the Machine Learning tab in “Analytics.”</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt216b48d488ae22bf/6a85cbc0d7b2e7b72bfe84f0/blog-elastic-observability-kibana-analytics.png" alt="kibana analytics" /></p>
<p>Go to the jobs page, where we’ll get started creating our first anomaly detection job.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltda3a4d09361a209d/6a85cbc3eaf245fde1a49f6b/blog-elastic-observability-create-your-first-anomaly-detection-job.png" alt="create your first anomaly detection job" /></p>
<p>The metrics data view contains what we need to create this new anomaly detection job.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt790afdb1a6ce3000/6a85cbc580984c60f4668ff0/blog-elastic-observability-metrics.png" alt="observability metrics" /></p>
<p>Use the wizard and select a “Single Metric.”</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt035ea305957b7ced/6a85cbc84710c67cdcd3cb59/blog-elastic-observability-use-a-wizard.png" alt="use a wizard" /></p>
<p>Use the full data.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt18b90b2f2b100e42/6a85cbca93ffb9f68ab91445/blog-elastic-observability-use-full-data.png" alt="use full data" /></p>
<p>In this example, we are going to look for anomalies in the connection count. We really do not want a major deviation here, as this could indicate something very bad occurring if we suddenly have too many or too few things connecting to our Kafka cluster.</p>
<p>Once you have selected the connection count metric, you can proceed through the wizard and eventually your ML job will be created and you should be able to view the data as per the example below.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltbb7c65944c99c89b/6a85cbcdf61d6ebd009c2b35/blog-elastic-observability-single-metric-viewer.png" alt="single metric viewer" /></p>
<p>Congratulations, you have now created a machine learning job to alert you if there are any problems with your Kafka cluster, adding <a href="https://www.elastic.co/observability/aiops">a full AIOps solution</a> to your Kafka and Confluent observability!</p>
<h2 id="summary">Summary</h2>
<p>We looked at monitoring Kafka-based solutions implemented on Confluent Cloud using Elastic Observability.</p>
<p>We covered the architecture of a multi-cloud solution involving AWS EKS, Confluent Cloud, and GCP GKE. We looked at how to instrument Kafka applications with Elastic APM, use JMX for Kafka Producer/Consumer metrics, integrate Prometheus, and set up machine learning anomaly detection.</p>
<p>We went through a detailed walk-through with code snippets, configuration steps, and deployment instructions included to help you get started.</p>
<p>Interested in learning more about Elastic Observability? Check out the following resources:</p>
<ul>
<li><a href="https://www.elastic.co/virtual-events/intro-to-elastic-observability">An Introduction to Elastic Observability</a></li>
<li><a href="https://www.elastic.co/training/observability-fundamentals">Observability Fundamentals Training</a></li>
<li><a href="https://www.elastic.co/observability/demo">Watch an Elastic Observability demo</a></li>
<li><a href="https://www.elastic.co/blog/observability-predictions-trends-2023">Observability Predictions and Trends for 2023</a></li>
</ul>
<p>And sign up for our <a href="https://www.elastic.co/virtual-events/emerging-trends-in-observability">Elastic Observability Trends Webinar</a> featuring AWS and Forrester, not to be missed!</p>]]></content:encoded>
    <link>https://www.elastic.co/observability-labs/blog/monitor-kafka-confluent-cloud-elastic-observability</link>
    <guid isPermaLink="false">monitor-kafka-confluent-cloud-elastic-observability</guid>
    <category><![CDATA[Infrastructure Monitoring]]></category>
    <category><![CDATA[APM]]></category>
    <category><![CDATA[Kubernetes]]></category>
    <dc:creator><![CDATA[David Hope]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltdff999229029e1b1/6a85cbd0bc5bb32326f81b11/patterns-white-background-no-logo-observability_(1).png" length="0" type="image/png"/>
    <pubDate>Mon, 03 Apr 2023 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Monitoring Android applications with Elastic APM]]></title>
    <description><![CDATA[Elastic has launched its APM agent for Android applications, allowing developers to track key aspects of applications to help troubleshoot issues and performance flaws with mobile applications, corresponding backend services, and their interactions.]]></description>
    <content:encoded><![CDATA[<blockquote>
  <p><strong>WARNING</strong>: This article shows information about the Android agent that is no longer accurate for versions <code>1.x</code>. Please refer to <a href="https://www.elastic.co/docs/reference/apm/agents/android">its documentation</a> to learn about its new APIs.</p>
</blockquote>
<p>People are handling more and more matters on their smartphones through mobile apps both privately and professionally. With thousands or even millions of users, ensuring great <a href="https://www.elastic.co/observability/application-performance-monitoring">monitor application performance</a> and reliability is a key challenge for providers and operators of mobile apps and related backend services. Understanding the behavior of mobile apps, the occurrences and types of crashes, the <a href="https://www.elastic.co/blog/apm-correlations-elastic-observability-root-cause-transactions">root causes of slow response times</a>, and the real user impact of backend issues is key to managing the performance of mobile apps and associated backend services.</p>
<p>Elastic has launched its application performance monitoring (<a href="https://www.elastic.co/observability/application-performance-monitoring">APM</a>) agent for Android applications, allowing developers to keep track of key aspects of their applications, from crashes and HTTP requests to screen rendering times and end-to-end distributed tracing. All of this helps troubleshoot issues and performance flaws with mobile applications, corresponding backend services, and their interaction. The Elastic APM Android Agent automatically instruments your application and its dependencies so that you can simply “plug-and-play” the agent into your application without having to worry about changing your codebase much.</p>
<p>The Elastic APM Android Agent has been developed from scratch on top of OpenTelemetry, an open standard and framework for observability. Developers will be able to take full advantage of its capabilities, as well as the support provided by a huge and active community. If you’re familiar with OpenTelemetry and your application is already instrumented with OpenTelemetry, then you can simply reuse it all when switching to the Elastic APM Android Agent. But no worries if that’s not the case — the agent is configured to handle common traceable scenarios automatically without having to deep dive into the specifics of the OpenTelemetry API.</p>
<p>[Related article: <a href="https://www.elastic.co/blog/adding-free-and-open-elastic-apm-as-part-of-your-elastic-observability-deployment">Adding free and open Elastic APM as part of your Elastic Observability deployment</a>]</p>
<h2 id="howitworks">How it works</h2>
<p>The Elastic APM Android Agent is a combination of an SDK plus a Gradle plugin. The SDK contains utilities that will let you initialize and configure the agent’s behavior, as well as prepare and initialize the OpenTelemetry SDK. You can use the SDK for programmatic configuration and initialization of the agent, in particular for advanced and special use cases.</p>
<p>In most cases, a programmatic configuration and initialization won’t be necessary. Instead, you can use the provided Gradle plugin to configure the agent and automatically instrument your app. The Gradle plugin uses Byte Buddy and the official Android Gradle plugin API under the hood to automatically inject instrumentation code into your app through compile-time transformation of your application’s and its dependencies’ classes.</p>
<p>Compiling your app with the Elastic Android APM Agent Gradle Plugin configured and enabled will make your Android app report tracing data, metrics, and different events and logs at runtime.</p>
<h2 id="usingtheelasticapmagentinanandroidapp">Using the Elastic APM Agent in an Android app</h2>
<p>By means of a <a href="https://github.com/elastic/sample-app-android-apm">simple demo application</a>, we’re going through the steps mentioned in the “<a href="https://www.elastic.co/guide/en/apm/agent/android/current/setup.html">Set up the Agent</a>” guide to set up the Elastic Android APM Agent.</p>
<h3 id="prerequisites">Prerequisites</h3>
<p>For this example, you will need the following:</p>
<ul>
<li>An Elastic Stack with APM enabled (We recommend using Elastic’s Cloud offering. <a href="https://www.elastic.co/cloud/elasticsearch-service/signup?baymax=docs-body&amp;elektra=docs">Try it for free</a>.)</li>
<li>Java 11+</li>
<li><a href="https://developer.android.com/studio?gclid=Cj0KCQiAic6eBhCoARIsANlox87QsDnyjpKObQSivZz6DHMLTiL76CmqZGXTEqf4L7h3jQO7ljm8B14aAo4xEALw_wcB&amp;gclsrc=aw.ds">Android Studio</a></li>
<li><a href="https://developer.android.com/studio/run/emulator">Android Emulator, AVD device</a></li>
</ul>
<p>You’ll also need a way to push the app’s <a href="https://opentelemetry.io/docs/concepts/signals/">signals</a> into Elastic. Therefore, you will need Elastic APM’s <a href="https://www.elastic.co/guide/en/apm/guide/current/secret-token.html#create-secret-token">secret token</a> that you’ll configure into our sample app later.</p>
<h3 id="testprojectforourexample">Test project for our example</h3>
<p>To showcase an end-to-end scenario including distributed tracing, in this example, we’ll instrument a <a href="https://github.com/elastic/sample-app-android-apm">simple weather application</a> that comprises two Android UI fragments and a simple local backend service based on Spring Boot.</p>
<p>The first fragment will have a dropdown list with some city names and also a button that takes you to the second one, where you’ll see the selected city’s current temperature. If you pick a non-European city on the first screen, you’ll get an error from the (local) backend when you head to the second screen. This is to demonstrate how network and backend errors are captured and correlated in Elastic APM.</p>
<h3 id="applyingtheelasticapmagentplugin">Applying the Elastic APM Agent plugin</h3>
<p>In the following, we will explain <a href="https://www.elastic.co/guide/en/apm/agent/android/current/setup.html">all the steps required to set up the Elastic APM Android Agent</a> from scratch for an Android application. In case you want to skip these instructions and see the agent in action right away, use the main branch of that repo and apply only Step (3.b) before continuing with the next Section (“Setting up the local backend service”).</p>
<ol>
<li>Clone the <a href="https://github.com/elastic/sample-app-android-apm">sample app</a> repo and open it in Android Studio.</li>
<li>Switch to the uninstrumented repo branch to start from a blank, uninstrumented Android application. You can run this command to switch to the uninstrumented branch:</li>
</ol>
<pre><code>git checkout uninstrumented
</code></pre>
<ol>
<li>Follow the Elastic APM Android Agent’s <a href="https://www.elastic.co/guide/en/apm/agent/android/current/setup.html">setup guide</a>:</li>
</ol>
<p>Add the co.elastic.apm.android plugin to the app/build.gradle file (please make sure to use the latest version available of the plugin, which you can find <a href="https://plugins.gradle.org/plugin/co.elastic.apm.android">here</a>).</p>
<p>Configure the agent’s connection to the Elastic APM backend by providing the ‘serverUrl’ and ‘secretToken’ in the ‘elasticAPM’ section of the app/build.gradle file.</p>
<pre><code>// Android app's build.gradle file
plugins {
    //...
    id "co.elastic.apm.android" version "[latest_version]"
}

//...

elasticApm {
    // Minimal configuration
    serverUrl = "https://your.elastic.apm.endpoint"

    // Optional
    serviceName = "weather-sample-app"
    serviceVersion = "0.0.1"
    secretToken = "your Elastic APM secret token"
}
</code></pre>
<ol>
<li>The only actual code change required is a one-liner to initialize the Elastic APM Android Agent in the Application.onCreate method. The application class for this sample app is located at app/src/main/java/co/elastic/apm/android/sample/MyApp.kt.</li>
</ol>
<pre><code>package co.elastic.apm.android.sample

import android.app.Application
import co.elastic.apm.android.sdk.ElasticApmAgent

class MyApp : Application() {

    override fun onCreate() {
        super.onCreate()
        ElasticApmAgent.initialize(this)
    }
}
</code></pre>
<p>Bear in mind that for this example, we’re not changing the agent’s default configuration — if you want more information about how to do so, take a look at the agent’s <a href="https://www.elastic.co/guide/en/apm/agent/android/current/configuration.html#_runtime_configuration">runtime configuration guide</a>.</p>
<p>Before launching our Android Weather App, we need to configure and start the local weather-backend service as described in the next section.</p>
<h3 id="settingupthelocalbackendservice">Setting up the local backend service</h3>
<p>One of the key features the agent provides is distributed tracing, which allows you to see the full end-to-end story of an HTTP transaction, starting from our mobile app and traversing instrumented backend services used by the app. Elastic APM will show you the full picture as one distributed trace, which comes in very handy for troubleshooting issues, especially the ones related to high latency and backend errors.</p>
<p>As part of our sample app, we’re going to launch a simple local backend service that will handle our app’s HTTP requests. The backend service is instrumented with the <a href="https://www.elastic.co/guide/en/apm/agent/java/current/index.html">Elastic APM Java agent</a> to collect and send its own APM data over to Elastic APM, allowing it to correlate the mobile interactions with the processing of the backend requests.</p>
<p>In order to configure the local server, we need to set our Elastic APM endpoint and secret token (the same used for our Android app in the previous step) into the backend/src/main/resources/elasticapm.properties file:</p>
<pre><code>service_name=weather-backend
application_packages=co.elastic.apm.android.sample
server_url=YOUR_ELASTIC_APM_URL
secret_token=YOUR_ELASTIC_APM_SECRET_TOKEN
</code></pre>
<h3 id="launchingthedemo">Launching the demo</h3>
<p>Our sample app will get automatic instrumentation for the agent’s currently <a href="https://www.elastic.co/guide/en/apm/agent/android/current/supported-technologies.html">supported frameworks</a>, which means that we’ll get to see screen rendering spans as well as OkHttp requests out of the box. For frameworks not currently supported, you could apply manual instrumentation to enrich your APM data (see “Manual Instrumentation” below).</p>
<p>We are ready to launch the demo. (The demo is meant to be executed on a local environment using an emulator for Android.) Therefore, we need to:</p>
<ol>
<li>Launch the backend service using this command in a terminal located in the root directory of our sample project: ./gradlew bootRun (or gradlew.bat bootRun if you’re on Windows). Alternatively, you can start the backend service from Android Studio.</li>
<li>Launch the weather sample app in an Android emulator (from Android Studio).</li>
</ol>
<p>Once everything is running, we need to navigate around in the app to generate some load that we would like to observe in Elastic APM. So, select a city, click <strong>Next</strong> and repeat it multiple times. Please, also make sure to select <strong>New York</strong> at least once. You will see that the weather forecast won’t work for New York as the city. Below, we will use Elastic APM to find out what’s going wrong when selecting New York.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt26502250a19f20aa/6a85cd9f1aa1e148b3ff8daf/blog-elastic-android-apm-city-selection.png" alt="apm android city selection" /></p>
<h2 id="firstglanceattheapmresults">First glance at the APM results</h2>
<p>Let’s open Kibana and navigate to the Observability solution.</p>
<p>Under the Services navigation item, you should see a list of two services: our Android app <strong>weather-sample-app</strong> and the corresponding backend service <strong>weather-backend</strong>. Click on the <strong>Service map</strong> tab to see a visualization of the dependencies between those services and any external services.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltb626bf83786928dc/6a85cda143c0b74d4e2f067a/blog-elastic-apm-android-services.png" alt="apm android services" /></p>
<p>Click on the <strong>weather-sample-app</strong> to dive into the dashboard for the Android app. The service view for mobile applications is in technical preview at the publishing of this blog post, but you can already see insightful information about the app on that screen. You see information like the amount of active sessions in the selected time frame, number of HTTP requests emitted by the weather-sample-app, geographical distribution of the requests as well as breakdowns on device models, OS versions, network connection types, and app versions. (Information on crashes and app load times are under development.)</p>
<p>For the purpose of demonstration, we kept this demo simple, so the data is less diversified and also rather limited. However, this kind of data is particularly useful when you are monitoring a mobile app with higher usage numbers and higher diversification on device models, OS versions, etc. Troubleshooting problems and performance issues becomes way easier when you can use these properties to filter and group your APM data. You can use the quick filters at the top to do so and see how the metrics adopt depending on your selection.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt33aa6afedf3d3908/6a85cda49829262416583940/blog-elastic-apm-android-weather-sample-app.png" alt="apm android weather sample app" /></p>
<p>Now, let’s see how individual user interactions are processed, including downstream calls into the backend service. Under the Transactions tab (at the top), we see the different end-to-end transaction groups, including the two transactions for the FirstFragment and the SecondFragment.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltf5598c2c3a0088c5/6a85cda793ffb939a5b91475/blog-elastic-apm-android-latency-distribution.png" alt="apm android latency distribution" /></p>
<p>Let’s deep dive into the SecondFragment - View appearing transaction, to see the metrics (e.g., latency, throughput) for this transaction group and also the invocation waterfall view for the individual user interactions. As we can see in the following screenshot, after view creation, the fragment performs an HTTP GET request to 10.0.2.2, which takes ~130 milliseconds. In the same waterfall, we see that the HTTP call is processed by the weather-backend service, which itself conducts an HTTP call to api.open-meteo.com.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt972b5b98aa165784/6a85cdaa43c0b75a5a2f067e/blog-elastic-apm-android-trace-samples.png" alt="apm android trace samples" /></p>
<p>Now, when looking at the waterfall view for a request where New York was selected as the city, we see an error happening on the backend service that explains why the forecast didn’t work for New York. By clicking on the red <strong>View related error</strong> badge, you will get details on the error and the actual root cause of the problem.</p>
<p>The exception message on the weather-backend states that “This service can only retrieve geo locations for European cities!” That’s the problem with selecting New York as the city.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltcf29b683d0b281a6/6a85cdad9d2b714ce1f93a02/blog-elastic-apm-android-weather-backend.png" alt="apm android weather backend" /></p>
<h2 id="manualinstrumentation">Manual instrumentation</h2>
<p>As previously mentioned, the Elastic APM Android Agent does a bunch of automatic instrumentation on your behalf for the <a href="https://www.elastic.co/guide/en/apm/agent/android/current/supported-technologies.html">supported frameworks</a>; however, in some cases, you might want to get extra instrumentation depending on your app’s use cases. For those cases, you’ve gotten covered by the OpenTelemetry API, which is what the Elastic APM Android Agent is based on. The OpenTelemetry Java SDK contains tools to create custom spans, metrics, and logs, and since it’s the base of the Elastic APM Android Agent, it’s available for you to use without having to add any extra dependencies into your project and without having to configure anything to connect your custom signals to your own Elastic environment either, as the agent does that for you.</p>
<p>The way to start would be by getting OpenTelemetry’s instance like so:</p>
<pre><code>OpenTelemetry openTelemetry = GlobalOpenTelemetry.get();
</code></pre>
<p>And then you can follow the instructions from the <a href="https://opentelemetry.io/docs/instrumentation/java/manual/#acquiring-a-tracer">OpenTelemetry Java documentation</a> in order to create your custom signals. See the following example for the creation of a custom span:</p>
<pre><code>OpenTelemetry openTelemetry = GlobalOpenTelemetry.get();
Tracer tracer = openTelemetry.getTracer("instrumentation-library-name", "1.0.0");
Span span = tracer.spanBuilder("my span").startSpan();

// Make the span the current span
try (Scope ss = span.makeCurrent()) {
  // In this scope, the span is the current/active span
} finally {
    span.end();
}
</code></pre>
<h2 id="conclusion">Conclusion</h2>
<p>In this blog post, we demonstrated how you can use the Elastic APM Android Agent to achieve end-to-end observability into your Android-based mobile applications. Setting up the agent is a matter of a few minutes and the provided insights allow you to analyze your app’s performance and its dependencies on backend services. With the Elastic APM Android Agent in place, you can leverage Elastic’s rich APM feature as well as the various possibilities to customize your analysis workflows through custom instrumentation and custom dashboards.</p>
<p>Are you curious? Then try it yourself. Sign up for a <a href="https://www.elastic.co/cloud/elasticsearch-service/signup">free trial on the Elastic Cloud</a>, enrich your Android app with the Elastic APM Android agent as described in this blog, and explore the data in <a href="https://www.elastic.co/observability">Elastic’s Observability solution</a>.</p>]]></content:encoded>
    <link>https://www.elastic.co/observability-labs/blog/monitoring-android-applications-apm</link>
    <guid isPermaLink="false">monitoring-android-applications-apm</guid>
    <category><![CDATA[APM]]></category>
    <category><![CDATA[OpenTelemetry]]></category>
    <dc:creator><![CDATA[Alexander Wert,Cesar Munoz]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt4c265817c1268d81/6a85cdb0e2447a8c018b1446/illustration-indusrty-technology-social-1680x980.png" length="0" type="image/png"/>
    <pubDate>Tue, 21 Mar 2023 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Trace-based testing with Elastic APM and Tracetest]]></title>
    <description><![CDATA[Want to run trace-based tests with Elastic APM? We're happy to announce that Tracetest now integrates with Elastic Observability APM. Check out this hands-on example of how Tracetest works with Elastic Observability APM and OpenTelemetry.]]></description>
    <content:encoded><![CDATA[<p><em>This post was originally published on the</em> <a href="https://tracetest.io/blog/tracetest-integration-elastic-trace-based-testing-application-performance-monitoring"><em>Tracetest blog</em></a><em>.</em></p>
<p>Want to run trace-based tests with Elastic APM? Today is your lucky day. We're happy to announce that Tracetest now integrates with Elastic Observability APM.</p>
<p>Check out this <a href="https://github.com/kubeshop/tracetest/tree/main/examples/tracetest-elasticapm-with-elastic-agent">hands-on example</a> of how Tracetest works with Elastic Observability APM and OpenTelemetry!</p>
<p><a href="https://tracetest.io/">Tracetest</a> is a <a href="https://www.cncf.io/">CNCF</a> project aiming to provide a solution for deep integration and system testing by leveraging the rich data in distributed system traces. In this blog, we intend to provide an introduction to Tracetest and its capabilities, including how it can be integrated with <a href="https://www.elastic.co/observability/application-performance-monitoring">Elastic Application Performance Monitoring</a> and <a href="https://opentelemetry.io/">OpenTelemetry</a> to enhance the testing process.</p>
<h2 id="yourgoodfrienddistributedtracing">Your good friend distributed tracing</h2>
<p>Distributed tracing is a way to understand how a distributed system works by tracking the flow of requests through the system. It can be used for a variety of purposes, such as identifying and fixing performance issues, figuring out what went wrong when an error occurs, and making sure that the system is running smoothly. Here are a few examples of how distributed tracing can be used:</p>
<ul>
<li><strong>Monitoring performance:</strong> Distributed tracing can help you keep an eye on how your distributed system is performing by showing you what's happening in real time. This can help you spot and fix problems like bottlenecks or slow response times that can make the system less reliable.</li>
<li><strong>Finding the source of problems:</strong> When something goes wrong, distributed tracing can help you figure out what happened by showing you the sequence of events that led up to the problem. This can help you pinpoint the specific service or component that's causing the issue and fix it.</li>
<li><strong>Debugging:</strong> Distributed tracing can help you find and fix bugs by giving you detailed information about what's happening in the system. This can help you understand why certain requests are behaving in unexpected ways and how to fix them.</li>
<li><strong>Security:</strong> Distributed tracing can help you keep an eye on security by showing you who is making requests to the system, where they are coming from, and what services are being accessed.</li>
<li><strong>Optimization:</strong> Distributed tracing can help you optimize the performance of the system by providing insight into how requests are flowing through it, which can help you identify areas that can be made more efficient and reduce the number of requests that need to be handled.</li>
</ul>
<h2 id="distributedtracingnowalsofortesting">Distributed tracing — Now also for testing</h2>
<p>Observability, previously only used in operations, is now being applied in other areas of development, such as testing. This shift has led to the emergence of <a href="https://www.infoq.com/articles/observability-driven-development/">"Observability-driven development"</a> and "trace-based testing" as new methods for using distributed tracing to test distributed applications.</p>
<p>Instead of just checking that certain parts of the code are working, trace-driven testing follows the path that a request takes as it goes through the system. This way, you can make sure that the entire system is working properly and that the right output is produced for a given input. By using distributed tracing, developers can record what happens during the test and then use that information to check that everything is working as it should.</p>
<p>This method of testing can help to find problems that may be hard to detect with other types of testing and can better validate that the new code is working as expected. Additionally, distributed tracing provides information about what is happening during the test, such as how long it takes for a request to be processed and which services are being used, which can help developers understand how the code behaves in a real-world scenario.</p>
<h2 id="enterstracetest">Enters Tracetest</h2>
<p><a href="https://tracetest.io/">Tracetest</a> is a CNCF project that can run tests by verifying new traces against previously created assertions against other traces captured from the real systems. Here's how you can use Tracetest:</p>
<ul>
<li>Capture the baseline good known trace. This will be the golden standard that you will use to write your tests and assertions. Trace-driven development is a better way to test how different parts of the system work together because it allows developers to test the entire process from start to finish, making sure that everything is working as it should and giving a more complete view of how the system is functioning instead of trying to create disjointed assertions validating the request execution.</li>
<li>Now you can start validating your code changes against good known behavior captured previously.</li>
<li>Tracetest can validate the resulting traces from the test and see if the system is working as it should. This can help you find problems that traditional testing methods might not catch.</li>
<li>Create reports: Tracetest can also create reports that summarize the results of the test so that you can share the information with your team.</li>
<li>Help you validate in production that the new requests follow the known path and run the predefined assertions against them.</li>
</ul>
<p>The APM tool in Kibana, which is a familiar UI for many developers, can provide extra information when used with Tracetest. The APM tool can show you how the system is performing during the test and help you find issues using the familiar user interface. For example, the APM tool can show you how requests are moving through the system, how long requests take to be processed, and which parts of the system are being used. This information can help you identify and fix problems during testing.</p>
<p>Furthermore, the APM tool can be set to show you all the data in real-time, which allows you to monitor the system's behavior during the test or even in production and helps you make sense of what Tracetest is showing.</p>
<h2 id="howtracetestworkswithelasticapmtotesttheapplication">How Tracetest works with Elastic APM to test the application</h2>
<p>The components work together to provide a complete solution for testing distributed systems. The telemetry captured by the OpenTelemetry agent is sent to the Elastic APM Server, which processes and formats the data for indexing in Elasticsearch. The data can then be queried and analyzed using Kibana APM UI, and Tracetest can be used to conduct deep integration and system tests by utilizing the rich data contained in the distributed system trace.</p>
<p>For more details on Elastic's support for OpenTelelemetry, check out <a href="https://www.elastic.co/blog/opentelemetry-observability">Independence with OpenTelemetry on Elastic</a>.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt37b66ebc149d52e9/6a85ce461aa1e1d2d0ff8dc9/blog-elastic-distributed-system-trace.png" alt="" /></p>
<ol>
<li>Tracetest initiates the test by sending a request to the application under test.</li>
<li>The application processes the request, and the built-in OpenTelemetry agent captures the telemetry data of the request. This data includes information such as request and response payloads, request and response headers, and any errors that occurred during the request processing. The agent then sends the captured telemetry data to the Elastic APM Server.</li>
<li>Elastic APM server consumes OpenTelemetry or Elastic APM spans and sends the data to be stored and indexed in Elasticsearch.</li>
<li>Tracetest polls Elasticsearch to retrieve the captured trace data. It makes use of Elasticsearch query to fetch the trace data. Tracetest compares the received trace data with the expected trace data and runs the assertions. This step is used to check whether the data received from the application matches the expected data and to check for any errors or issues that may have occurred during the request processing. Based on the results of the comparison, Tracetest will report any errors or issues found and will provide detailed information about the root cause of the problem. If the test passes, Tracetest will report that the test passed, and the test execution process will be completed.</li>
<li>The trace data is visible and can be analyzed in Kibana APM UI as well.</li>
</ol>
<h2 id="runningyourfirsttracetestenvironmentwithelasticapmanddockercompose">Running your first Tracetest environment with Elastic APM and Docker compose</h2>
<p>In your existing observability setup, you have the <a href="https://opentelemetry.io/docs/instrumentation/js/getting-started/nodejs/">OpenTelemetry Nodejs agent</a> configured in your code and <a href="https://www.elastic.co/blog/opentelemetry-observability">sending OpenTelemetry traces to the Elastic APM server that then stores</a> them in Elasticsearch. Adding Tracetest to the infrastructure lets you write detailed trace-based tests based on the existing tracing infrastructure. Tracetest runs tests against endpoints and uses trace data to run assertions.</p>
<p>The example that we are going to run is from the Tracetest GitHub repository. It contains a docker-compose setup, which is a convenient way to run multiple services together in a defined environment. The example includes a sample application that has been instrumented with an OpenTelemetry agent. The example also includes the Tracetest server with its Postgres database, which is responsible for invoking the test, polling Elasticsearch to retrieve the captured trace data, comparing the received trace data with the expected trace data, and running the assertions. Finally, the example includes Elasticsearch, Kibana, and the Elastic APM server from the Elastic Stack.</p>
<p>To quickly access the example, you can run the following:</p>
<pre><code>git clone https://github.com/kubeshop/tracetest.git
cd tracetest/examples/tracetest-elasticapm-with-otel
docker-compose up -d
</code></pre>
<p>Once you have Tracetest set up, open http://localhost:11633 in your browser to check out the Web UI.</p>
<p>Navigate to the Settings menu and ensure the connection to Elasticsearch is working by pressing Test Connection:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt6d75ca76b91d6d52/6a85ce4999083f3b2e40fa39/blog-elastic-tracetest-configure-data-store.png" alt="" /></p>
<p>To create a test, click the Create dropdown and choose Create New Test. Select the HTTP Request and give it a name and description.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltbe8653b1de8ab688/6a85ce4b331d7a5c6dc3181d/blog-elastic-create-new-test.png" alt="" /></p>
<p>For this simple example, GET the Node.js app, which runs at http://app:8080.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt296b5c966e386a1f/6a85ce4e33f244370749f56b/blog-elastic-trace-request-details.png" alt="" /></p>
<p>With the test created, you can click the Trace tab to see the distributed trace. It’s simple, but you can start to see how it delivers immediate visibility into every transaction your HTTP request generates.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt06be2003f1b7f3ce/6a85ce514710c69bb2d3cbc6/blog-elastic-tracetest-trigger.png" alt="" /></p>
<p>From here, you can continue by adding assertions.</p>
<p>To make an assertion based on the GET / span of our trace, select that span in the graph view and click <strong>Current span</strong> in the Test Spec modal. Or, copy this span selector directly, using the <a href="https://docs.tracetest.io/concepts/selectors/">Tracetest Selector Language</a>:</p>
<pre><code>span[tracetest.span.type="http" name="GET /" http.target="/" http.method="GET"]
</code></pre>
<p>Below, add the attr:http.status_code attribute and the expected value, which is 200. You can add more complex assertions as well, like testing whether the span executes in less than 500ms. Add a new assertion for attr:http.status_code, choose \&lt;, and add 500ms as the expected value.</p>
<p>You can check against other properties, return statuses, timing, and much more, but we’ll keep it simple for now.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltb212b9a5cdf7de6d/6a85ce54d7b2e7ca4afe8550/blog-elastic-tracetest-edit-test-spec.png" alt="" /></p>
<p>Then click <strong>Save Test Spec</strong> , followed by <strong>Publish</strong> , and you’ve created your first assertion.If you open the APM app in Kibana at https://localhost:5601 (find the username and password from the examples/tracetest-elasticapm- <strong>with</strong> -otel/.env file), you will be able to navigate to the transaction generated by the test representing the overall application call with three underlying spans:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt5e43322f5697d9ee/6a85ce5793ffb91887b91493/blog-elastic-latency-distribution.png" alt="" /></p>
<h2 id="summary">Summary</h2>
<p>Elastic APM and Tracetest are tools that can help make testing distributed applications easier by providing a more comprehensive view of the system's behavior and allowing developers to identify and diagnose performance issues more efficiently. Tracetest allows you to test the entire process from start to finish, making sure that everything is working as it should, by following the path that a request takes.</p>
<p>Elastic APM provides detailed information about the performance of a system, including how requests are flowing through the system, how long requests take to be processed, and which services are being called. Together, these tools can help developers to identify and fix issues more quickly, improve collaboration and communication among the team, and ultimately improve the overall quality of the system.</p>
<blockquote>
  <ul>
  <li>Elastic APM documentation: <a href="https://www.elastic.co/guide/en/apm/guide/current/index.html">https://www.elastic.co/guide/en/apm/guide/current/index.html</a></li>
  <li>Tracetest documentation: <a href="https://tracetest.io/docs/">https://tracetest.io/docs/</a> </li>
  <li>Tracetest Github page: <a href="https://github.com/kubeshop/tracetest">https://github.com/kubeshop/tracetest</a> </li>
  <li>Elastic blog: <a href="https://www.elastic.co/blog/category/technical-topics">https://www.elastic.co/blog/category/technical-topics</a> </li>
  <li>Elastic APM community forum: <a href="https://discuss.elastic.co/c/apm">https://discuss.elastic.co/c/apm</a> </li>
  <li>Tracetest support: <a href="https://discord.com/channels/884464549347074049/963470167327772703">Discord channel</a></li>
  </ul>
</blockquote>]]></content:encoded>
    <link>https://www.elastic.co/observability-labs/blog/trace-based-testing-apm-tracetest</link>
    <guid isPermaLink="false">trace-based-testing-apm-tracetest</guid>
    <category><![CDATA[APM]]></category>
    <dc:creator><![CDATA[Michael Hyatt]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt403b8c5c1f266c5f/6a85ce5a68266621121eac6b/telescope-search-1680x980.png" length="0" type="image/png"/>
    <pubDate>Wed, 15 Feb 2023 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Independence with OpenTelemetry on Elastic]]></title>
    <description><![CDATA[OpenTelemetry has become a key component for observability given its open standards and developer-friendly tools. See how easily Elastic Observability integrates with OTel to provide a platform that minimizes vendor lock-in and maximizes flexibility.]]></description>
    <content:encoded><![CDATA[<p>The drive for faster, more scalable services is on the rise. Our day-to-day lives depend on apps, from a food delivery app to have your favorite meal delivered, to your banking app to manage your accounts, to even apps to schedule doctor’s appointments. These apps need to be able to grow from not only a features standpoint but also in terms of user capacity. The scale and need for global reach drives increasing complexity for these high-demand cloud applications.</p>
<p>In order to keep pace with demand, most of these online apps and services (for example, mobile applications, web pages, SaaS) are moving to a distributed microservice-based architecture and Kubernetes. Once you’ve migrated your app to the cloud, how do you manage and monitor production, scale, and availability of the service? <a href="https://opentelemetry.io/">OpenTelemetry</a> is quickly becoming the de facto standard for instrumentation and collecting application telemetry data for Kubernetes applications.</p>
<p><a href="https://www.elastic.co/what-is/opentelemetry">OpenTelemetry (OTel)</a> is an open source project providing a collection of tools, APIs, and SDKs that can be used to generate, collect, and export telemetry data (metrics, logs, and traces) to understand software performance and behavior. OpenTelemetry recently became a CNCF incubating project and has a significant amount of growing community and vendor support.</p>
<p>While OTel provides a standard way to instrument applications with a standard telemetry format, it doesn’t provide any backend or analytics components. Hence using OTel libraries in applications, infrastructure, and user experience monitoring provides flexibility in choosing the appropriate <a href="https://www.elastic.co/observability">observability tool</a> of choice. There is no longer any vendor lock-in for application performance monitoring (APM).</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt5ac0a045fdf76a37/6a7f193205b7b51e0118bd21/blog-elastic-otel-1.png" alt="" /></p>
<p>Elastic Observability natively supports OpenTelemetry and its OpenTelemetry protocol (OTLP) to ingest traces, metrics, and logs. All of Elastic Observability’s APM capabilities are available with OTel data. Hence the following capabilities (and more) are available for OTel data:</p>
<ul>
<li>Service maps</li>
<li>Service details (latency, throughput, failed transactions)</li>
<li>Dependencies between services</li>
<li>Transactions (traces)</li>
<li>ML correlations (specifically for latency)</li>
<li>Service logs</li>
</ul>
<p>In addition to Elastic’s APM and unified view of the telemetry data, you will now be able to use Elastic’s powerful machine learning capabilities to reduce the analysis, and alerting to help reduce MTTR.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blta01b4eb3f8dbfb6f/6a7f1935e02fac237a5d698d/blog-elastic-otel-2.png" alt="" /></p>
<p>Given its open source heritage, Elastic also supports other CNCF based projects, such as Prometheus, Fluentd, Fluent Bit, Istio, Kubernetes (K8S), and many more.</p>
<p>This blog will show:</p>
<ul>
<li>How to get a popular OTel instrumented demo app (Hipster Shop) configured to ingest into <a href="http://cloud.elastic.co">Elastic Cloud</a> through a few easy steps</li>
<li>Highlight some of the Elastic APM capabilities and features around OTel data and what you can do with this data once it’s in Elastic</li>
</ul>
<p>In follow-up blogs, we will detail how to use Elastic’s machine learning with OTel telemetry data, how to instrument OTel application metrics for specific languages, how we can support Prometheus ingest through the OTel collector, and more. Stay tuned!</p>
<h2 id="prerequisitesandconfig">Prerequisites and config</h2>
<p>If you plan on following this blog, here are some of the components and details we used to set up the configuration:</p>
<ul>
<li>Ensure you have an account on <a href="http://cloud.elastic.co">Elastic Cloud</a> and a deployed stack (<a href="https://www.elastic.co/guide/en/elastic-stack/current/installing-elastic-stack.html">see instructions here</a>).</li>
<li>We used the OpenTelemetry Demo. Directions for using Elastic with OpenTelemetry Demo are <a href="https://github.com/elastic/opentelemetry-demo">here</a>.</li>
<li>Make sure you have <a href="https://kubernetes.io/docs/reference/kubectl/">kubectl</a> and <a href="https://helm.sh/">helm</a> also installed locally.</li>
<li>Additionally, we are using an OTel manually instrumented version of the application. No OTel automatic instrumentation was used in this blog configuration.</li>
<li>Location of our clusters. While we used Google Kubernetes Engine (GKE), you can use any Kubernetes platform of your choice.</li>
<li>While Elastic can ingest telemetry directly from OTel instrumented services, we will focus on the more traditional deployment, which uses the OpenTelemetry Collector.</li>
<li>Prometheus and FluentD/Fluent Bit — traditionally used to pull all Kubernetes data — is not being used here versus Kubernetes Agents. Follow-up blogs will showcase this.</li>
</ul>
<p>Here is the configuration we will get set up in this blog:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt757221af75648dce/6a7f193896b5a66c5387b867/blog-elastic-otel-3.png" alt="Configuration to ingest OpenTelemetry data used in this blog" /></p>
<h2 id="settingitallup">Setting it all up</h2>
<p>Over the next few steps, I’ll walk through an <a href="https://www.elastic.co/observability/opentelemetry">Opentelemetry visualization</a>:</p>
<ul>
<li>Getting an account on Elastic Cloud</li>
<li>Bringing up a GKE cluster</li>
<li>Bringing up the application</li>
<li>Configuring Kubernetes OTel Collector configmap to point to Elastic Cloud</li>
<li>Using Elastic Observability APM with OTel data for improved visibility</li>
</ul>
<h3 id="step0createanaccountonelasticcloud">Step 0: Create an account on Elastic Cloud</h3>
<p>Follow the instructions to <a href="https://cloud.elastic.co/registration?fromURI=/home">get started on Elastic Cloud</a>.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt588fbb3e515933fa/6a7f193aea068d34baf0a2b1/blog-elastic-otel-4.png" alt="" /></p>
<h3 id="step1bringupak8scluster">Step 1: Bring up a K8S cluster</h3>
<p>We used Google Kubernetes Engine (GKE), but you can use any Kubernetes platform of your choice.</p>
<p>There are no special requirements for Elastic to collect OpenTelemetry data from a Kubernetes cluster. Any normal Kubernetes cluster on GKE, EKS, AKS, or Kubernetes compliant cluster (self-deployed and managed) works.</p>
<h3 id="step2loadtheopentelemetrydemoapplicationonthecluster">Step 2: Load the OpenTelemetry demo application on the cluster</h3>
<p>Get your application on a Kubernetes cluster in your cloud service of choice or local Kubernetes platform. The application I am using is available <a href="https://github.com/bshetti/opentelemetry-microservices-demo/tree/main/deploy-with-collector-k8s">here</a>.</p>
<p>First clone the directory locally:</p>
<pre><code>git clone https://github.com/elastic/opentelemetry-demo.git
</code></pre>
<p>(Make sure you have <a href="https://kubernetes.io/docs/reference/kubectl/">kubectl</a> and <a href="https://helm.sh/">helm</a> also installed locally.)</p>
<p>The instructions utilize a specific opentelemetry-collector configuration for Elastic. Essentially, the Elastic <a href="https://github.com/elastic/opentelemetry-demo/blob/main/kubernetes/elastic-helm/values.yaml">values.yaml</a> file specified in the elastic/opentelemetry-demo configure the opentelemetry-collector to point to the Elastic APM Server using two main values:</p>
<p>OTEL_EXPORTER_OTLP_ENDPOINT is Elastic’s APM Server<br />
OTEL_EXPORTER_OTLP_HEADERS Elastic Authorization</p>
<p>These two values can be found in the OpenTelemetry setup instructions under the APM integration instructions (Integrations-&gt;APM) in your Elastic cloud.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blte776fb3258454d4d/6a7f193d42a117a8cd95c2df/blog-elastic-apm-agents.png" alt="elastic apm agents" /></p>
<p>Once you obtain this, the first step is to create a secret key on the cluster with your Elastic APM server endpoint, and your APM Secret Token with the following instruction:</p>
<pre><code>kubectl create secret generic elastic-secret \
  --from-literal=elastic_apm_endpoint='YOUR_APM_ENDPOINT_WITHOUT_HTTPS_PREFIX' \
  --from-literal=elastic_apm_secret_token='YOUR_APM_SECRET_TOKEN'
</code></pre>
<p>Don't forget to replace:</p>
<ul>
<li>YOUR_APM_ENDPOINT_WITHOUT_HTTPS_PREFIX: your Elastic APM endpoint ( <strong>without https:// prefix</strong> ) with OTEL_EXPORTER_OTLP_ENDPOINT</li>
<li>YOUR_APM_SECRET_TOKEN: your Elastic APM secret token OTEL_EXPORTER_OTLP_HEADERS</li>
</ul>
<p>Now execute the following commands:</p>
<pre><code># switch to the kubernetes/elastic-helm directory
cd kubernetes/elastic-helm

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

# deploy the demo through helm install
helm install -f values.yaml my-otel-demo open-telemetry/opentelemetry-demo
</code></pre>
<p>Once your application is up on Kubernetes, you will have the following pods (or some variant) running on the <strong>default</strong> namespace.</p>
<pre><code>kubectl get pods -n default
</code></pre>
<p>Output should be similar to the following:</p>
<pre><code>NAME                                                  READY   STATUS    RESTARTS      AGE
my-otel-demo-accountingservice-5c77754b4f-vwph6       1/1     Running   0             5d4h
my-otel-demo-adservice-6b8b7c7dc5-mb7j5               1/1     Running   0             5d4h
my-otel-demo-cartservice-76d94b7dcd-2g4lf             1/1     Running   0             5d4h
my-otel-demo-checkoutservice-988bbdb88-hmkrp          1/1     Running   0             5d4h
my-otel-demo-currencyservice-6cf4b5f9f6-vz9t2         1/1     Running   0             5d4h
my-otel-demo-emailservice-868c98fd4b-lpr7n            1/1     Running   6 (18h ago)   5d4h
my-otel-demo-featureflagservice-8446ff9c94-lzd4w      1/1     Running   0             5d4h
my-otel-demo-ffspostgres-867945d9cf-zzwd7             1/1     Running   0             5d4h
my-otel-demo-frauddetectionservice-5c97c589b9-z8fhz   1/1     Running   0             5d4h
my-otel-demo-frontend-d85ccf677-zg9fp                 1/1     Running   0             5d4h
my-otel-demo-frontendproxy-6c5c4fccf6-qmldp           1/1     Running   0             5d4h
my-otel-demo-kafka-68bcc66794-dsbr6                   1/1     Running   0             5d4h
my-otel-demo-loadgenerator-64c545b974-xfccq           1/1     Running   1 (36h ago)   5d4h
my-otel-demo-otelcol-fdfd9c7cf-6lr2w                  1/1     Running   0             5d4h
my-otel-demo-paymentservice-7955c68859-ff7zg          1/1     Running   0             5d4h
my-otel-demo-productcatalogservice-67c879657b-wn2wj   1/1     Running   0             5d4h
my-otel-demo-quoteservice-748d754ffc-qcwm4            1/1     Running   0             5d4h
my-otel-demo-recommendationservice-df78894c7-lwm5v    1/1     Running   0             5d4h
my-otel-demo-redis-7d48567546-h4p4t                   1/1     Running   0             5d4h
my-otel-demo-shippingservice-f6fc76ddd-2v7qv          1/1     Running   0             5d4h
</code></pre>
<h3 id="step3openkibanaandusetheapmservicemaptoviewyourotelinstrumentedservices">Step 3: Open Kibana and use the APM Service Map to view your OTel instrumented Services</h3>
<p>In the Elastic Observability UI under APM, select servicemap to see your services.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt5ec18e0b8fe27ba9/6a7f194033fa8a5adb202b64/blog-elastic-observability-APM.png" alt="elastic observability APM" /></p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt1d2c8e5136dc6f53/6a7f19426693f8101666435d/blog-elastic-observability-OTEL-service-map.png" alt="elastic observability OTEL service map" /></p>
<p>If you are seeing this, then the OpenTelemetry Collector is sending data into Elastic:</p>
<p><em>Congratulations,</em> <em>you've instrumented the OpenTelemetry demo application using and successfully ingested the telemetry data into the Elastic!</em></p>
<h3 id="step4whatcanelasticshowme">Step 4: What can Elastic show me?</h3>
<p>Now that the OpenTelemetry data is ingested into Elastic, what can you do?</p>
<p>First, you can view the APM service map (as shown in the previous step) — this will give you a full view of all the services and the transaction flows between services.</p>
<p>Next, you can now check out individual services and the transactions being collected.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltd116b5c740f25566/6a7f19456693f83ea1664361/blog-elastic-observability-frontend-overview.png" alt="elastic observability frontend overview" /></p>
<p>As you can see, the frontend details are listed. Everything from:</p>
<ul>
<li>Average service latency</li>
<li>Throughput</li>
<li>Main transactions</li>
<li>Failed traction rate</li>
<li>Errors</li>
<li>Dependencies</li>
</ul>
<p>Let’s get to the trace. In the Transactions tab, you can review all the types of transactions related to the frontend service:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltf1669d81336b9a3e/6a7f194873d9bdc7e029df3b/blog-elastic-observability-frontend-transactions.png" alt="elastic observability frontend transactions" /></p>
<p>Selecting the HTTP POST transaction, we can see the full trace with all the spans:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt7b4a0689cfa8ad76/6a7f194b33fa8a0360202b68/blog-elastic-observability-frontend-HTTP-POST.png" alt="Average latency for this transaction, throughput, any failures, and of course the trace!" /></p>
<p>Not only can you review the trace but you can also analyze what is related to higher than normal latency for HTTP POST .</p>
<p>Elastic uses machine learning to help identify any potential latency issues across the services from the trace. It’s as simple as selecting the Latency Correlations tab and running the correlation.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blta53b6a97f8d8cacf/6a7f194ee88c653c1c00bae0/blog-elastic-latency-correlations.png" alt="elastic observability latency correlations" /></p>
<p>This shows that the high latency transactions are occurring in checkout service with a medium correlation.</p>
<p>You can then drill down into logs directly from the trace view and review the logs associated with the trace to help identify and pinpoint potential issues.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt8ad97b9fd8b1ea36/6a7f19505967e50ea75dd69d/blog-elastic-latency-distribution.png" alt="elastic observability latency distribution" /></p>
<h3 id="analyzeyourdatawithelasticmachinelearningml">Analyze your data with Elastic machine learning (ML)</h3>
<p>Once OpenTelemetry metrics are in Elastic, start analyzing your data through Elastic’s ML capabilities.</p>
<p>A great review of these features can be found here: <a href="https://www.elastic.co/blog/apm-correlations-elastic-observability-root-cause-transactions">Correlating APM telemetry to determine root causes in transactions</a>. And there are many more videos and blogs on <a href="https://www.elastic.co/blog/">Elastic’s Blog</a>. We’ll follow up with additional blogs on leveraging Elastic’s machine learning capabilities for OpenTelemetry data.</p>
<h2 id="conclusion">Conclusion</h2>
<p>I hope you’ve gotten an appreciation for how Elastic Observability can help you ingest and analyze OpenTelemetry data with Elastic’s APM capabilities.</p>
<p>A quick recap of lessons and more specifically learned:</p>
<ul>
<li>How to get a popular OTel instrumented demo app (Hipster Shop) configured to ingest into <a href="http://cloud.elastic.co">Elastic Cloud</a>, through a few easy steps</li>
<li>Highlight some of the Elastic APM capabilities and features around OTel data and what you can do with this once it’s in Elastic</li>
</ul>
<p>Ready to get started? Sign up <a href="https://cloud.elastic.co/registration">for Elastic Cloud</a> and try out the features and capabilities I’ve outlined above to get the most value and visibility out of your OpenTelemetry data.</p>]]></content:encoded>
    <link>https://www.elastic.co/observability-labs/blog/opentelemetry-observability</link>
    <guid isPermaLink="false">opentelemetry-observability</guid>
    <category><![CDATA[OpenTelemetry]]></category>
    <category><![CDATA[APM]]></category>
    <category><![CDATA[Kubernetes]]></category>
    <category><![CDATA[Infrastructure Monitoring]]></category>
    <dc:creator><![CDATA[Bahubali Shetti]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt785e1bd8fa6dd28d/6a7f19532f00b2a466efef13/illustration-scalability-gear-1680x980_(1).jpg" length="0" type="image/jpeg"/>
    <pubDate>Tue, 15 Nov 2022 00:00:00 GMT</pubDate>
  </item>
  </channel>
</rss>