<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: She11 QA</title>
    <description>The latest articles on DEV Community by She11 QA (@she11_qa).</description>
    <link>https://dev.to/she11_qa</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4079421%2Fd58c59f2-4f10-4f9e-b125-7dc79f2166db.png</url>
      <title>DEV Community: She11 QA</title>
      <link>https://dev.to/she11_qa</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/she11_qa"/>
    <language>en</language>
    <item>
      <title>GenAI in Test Automation: Accelerating Testing with GitHub Copilot &amp; Agentic Solutions</title>
      <dc:creator>She11 QA</dc:creator>
      <pubDate>Tue, 25 Aug 2026 08:48:23 +0000</pubDate>
      <link>https://dev.to/she11_qa/genai-in-test-automation-accelerating-testing-with-github-copilot-agentic-solutions-1pj</link>
      <guid>https://dev.to/she11_qa/genai-in-test-automation-accelerating-testing-with-github-copilot-agentic-solutions-1pj</guid>
      <description>&lt;h1&gt;
  
  
  GenAI in Test Automation: Accelerating Testing with GitHub Copilot &amp;amp; Agentic Solutions
&lt;/h1&gt;

&lt;p&gt;Generative AI is shifting how quality assurance teams design, generate, and maintain test automation suites. By using GitHub Copilot (GHCP) alongside agentic workflows, software teams can significantly cut setup time while maintaining production-grade standards.&lt;/p&gt;

&lt;p&gt;Here is a practical framework for leveraging GenAI and agentic solutions to accelerate test automation.&lt;/p&gt;




&lt;h2&gt;
  
  
  🛠️ Tooling &amp;amp; Core Capabilities
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Primary Tool:&lt;/strong&gt; GitHub Copilot (GHCP)&lt;/p&gt;

&lt;h3&gt;
  
  
  Framework &amp;amp; Language Support
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Frameworks:&lt;/strong&gt; Selenium, Playwright, Cypress&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Languages:&lt;/strong&gt; Java, JavaScript, Python&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scope:&lt;/strong&gt; Web &amp;amp; API application testing&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Architectural Patterns:&lt;/strong&gt; Generates modular scripts using Page Object Model (POM), BDD, and Data-Driven Testing paradigms.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  ⚡ Modes of Operation
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Interactive Mode:&lt;/strong&gt; Human-in-the-loop workflow tailored for prompt refinement and continuous validation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Agentic Mode:&lt;/strong&gt; Autonomous end-to-end test generation, execution, and reporting pipeline.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Edit Mode:&lt;/strong&gt; Quick prompt-driven corrections and inline code modifications.&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Crucial Rule:&lt;/strong&gt; Human-in-the-loop oversight is mandatory to validate, monitor, and correct generated outputs against real-world domain requirements.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  📋 Implementation Workflow
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Step 1: Pre-requisites &amp;amp; Setup
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Configure &lt;strong&gt;GitHub Copilot, GHCP&lt;/strong&gt;, and relevant automation-oriented VSIX extensions in your IDE.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Format test cases in Markdown (Test ID, Objective, Steps, Expected Results, Test Data). Keep individual files under 20 MB.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Collect element locators (JSON, Excel, or Markdown format) and application configurations.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Step 2: Prompt Engineering &amp;amp; Execution
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Use prompt templates populated with your framework details, target language, and app context.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Set explicit constraints, rules, and coding style guides within the prompt.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Process prompts through GHCP agentic workflows to generate test cases and code scripts.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Execute generated test suites, evaluate performance metrics, and iteratively refine prompts.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  💡 Key Use Cases
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Scenario &amp;amp; Case Generation:&lt;/strong&gt; Automatically derive BDD or non-BDD user stories, scenarios, and test cases.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Manual to Automated Scripting:&lt;/strong&gt; Rapidly translate manual test documentation into executable code.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Suite Maintenance:&lt;/strong&gt; Automate locator fixes and script updates whenever application flows change.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Intelligent Prioritization:&lt;/strong&gt; Target tests based on risk, business criticality, and execution frequency.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Data &amp;amp; Reporting:&lt;/strong&gt; Automate test data creation and post-execution reporting.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  📌 Best Practices Checklist
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Human Oversight:&lt;/strong&gt; Always inspect AI-generated code for edge cases, logical errors, and adherence to company coding standards.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Track Impact Metrics:&lt;/strong&gt; Measure success by tracking output accuracy, test coverage improvements, bug reduction rates, and overall engineering time saved.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Document Structured Inputs:&lt;/strong&gt; Provide clean input context (BRD documents, locator files, code samples) to minimize hallucination risks during script generation.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>testing</category>
      <category>ai</category>
      <category>githubcopilot</category>
      <category>automation</category>
    </item>
    <item>
      <title>Essential Git Commands for Feature Branch Workflow</title>
      <dc:creator>She11 QA</dc:creator>
      <pubDate>Tue, 25 Aug 2026 08:40:14 +0000</pubDate>
      <link>https://dev.to/she11_qa/essential-git-commands-for-feature-branch-workflow-2e13</link>
      <guid>https://dev.to/she11_qa/essential-git-commands-for-feature-branch-workflow-2e13</guid>
      <description>&lt;h1&gt;
  
  
  How to Switch, Sync, and Merge Git Branches in VS Code
&lt;/h1&gt;

&lt;p&gt;Keeping your feature or QA branch up to date with the main codebase is essential for preventing merge conflicts later down the line. Here is a quick reference guide on how to switch branches, fetch new remotes, and pull the latest changes into your branch using VS Code and Git terminal.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. How to Switch Branches in VS Code
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Using the Command Palette:
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Press Ctrl + Shift + P (Windows/Linux) or Cmd + Shift + P (Mac).&lt;/li&gt;
&lt;li&gt;Type and select &lt;strong&gt;Git: Checkout to...&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Select your target branch (e.g., your-feature-branch).&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Using the Terminal:
&lt;/h3&gt;

&lt;p&gt;If the branch hasn't been fetched locally yet:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Fetch the specific branch from remote&lt;/span&gt;
git fetch origin your-feature-branch

&lt;span class="c"&gt;# Switch to the branch&lt;/span&gt;
git checkout your-feature-branch
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  2. Syncing Your Branch with the Latest Main Code
&lt;/h2&gt;

&lt;p&gt;Follow these 5 steps to update your local working branch with the latest upstream code from main:&lt;/p&gt;

&lt;p&gt;Step 1: Switch to the Main Branch&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git checkout main
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Step 2: Pull Remote Changes&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git pull origin main
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Step 3: Return to Your Feature Branch&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git checkout your-feature-branch
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Step 4: Merge Main into Your Branch&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git merge main
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Handling Conflicts: If VS Code highlights merge conflicts, edit the files to resolve them, then stage and commit:&lt;br&gt;
git add .&lt;br&gt;
git commit -m "Merge main into your-feature-branch"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Step 5: Push Updates to Remote&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git push origin your-feature-branch
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Quick Summary Checklist&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;git checkout main → Jump to main&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;git pull origin main → Get latest changes&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;git checkout  → Return to your working branch&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;git merge main → Bring latest main changes into your branch&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;git push origin  → Push updated work to remote&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>git</category>
      <category>vscode</category>
      <category>developer</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Setting Up a Playwright JavaScript BDD Automation Framework with Cucumber</title>
      <dc:creator>She11 QA</dc:creator>
      <pubDate>Tue, 25 Aug 2026 08:31:19 +0000</pubDate>
      <link>https://dev.to/she11_qa/setting-up-a-playwright-javascript-bdd-automation-framework-with-cucumber-1779</link>
      <guid>https://dev.to/she11_qa/setting-up-a-playwright-javascript-bdd-automation-framework-with-cucumber-1779</guid>
      <description>&lt;p&gt;Setting up a scalable test automation framework using Playwright JavaScript with BDD (Behavior-Driven Development) via Cucumber helps streamline testing and maintain consistency across complex test suites. &lt;/p&gt;

&lt;p&gt;Here is a step-by-step guide to onboarding and initializing this test environment.&lt;/p&gt;




&lt;h3&gt;
  
  
  Prerequisites &amp;amp; Tools
&lt;/h3&gt;

&lt;p&gt;Make sure you have the following software installed:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://nodejs.org/" rel="noopener noreferrer"&gt;Node.js&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://code.visualstudio.com/" rel="noopener noreferrer"&gt;Visual Studio Code&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  Recommended VS Code Extensions
&lt;/h3&gt;

&lt;p&gt;Open the Extensions tab in VS Code (Ctrl + Shift + X / Cmd + Shift + X) and install:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Cucumber (Gherkin) Support&lt;/strong&gt; — Syntax highlighting and step definition navigation for .feature files.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;JavaScript / TypeScript Support&lt;/strong&gt; — Auto-completion and language tooling.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ESLint&lt;/strong&gt; (Optional) — Code linting to maintain style rules and clean syntax.&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  Setup &amp;amp; Initialization Commands
&lt;/h3&gt;

&lt;p&gt;Clone the repository, navigate to your root workspace, and execute the following commands in your integrated terminal:&lt;/p&gt;

&lt;h4&gt;
  
  
  1. Install required project dependencies
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  2. Download and configure required Playwright browser binaries
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx playwright &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--with-deps&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  3. Install Excel utility packages for handling test data
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install &lt;/span&gt;exceljs &lt;span class="nt"&gt;--save&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  4. Optional security/vulnerability audit
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm audit
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  5. Launch tests in Playwright's UI Mode for debugging
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm run &lt;span class="nb"&gt;test&lt;/span&gt;/ui
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Best Practices for BDD Automation Suites&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Align Feature Files with Test Specifications:&lt;/strong&gt; Always map .feature Gherkin steps directly against your functional requirements sheet to prevent missing coverage.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Centralize Documentation:&lt;/strong&gt; Keep detailed architecture notes and setup guides inside the README.md file and a dedicated /doc folder for faster onboarding.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Data Handling:&lt;/strong&gt; Use structured files (such as Excel via exceljs or JSON) to separate test scripts from dynamic test data.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>javascript</category>
      <category>playwright</category>
      <category>testing</category>
      <category>bdd</category>
    </item>
    <item>
      <title>Fixing PowerShell Script Execution Policy Issue When Activating Python venv</title>
      <dc:creator>She11 QA</dc:creator>
      <pubDate>Tue, 25 Aug 2026 08:26:09 +0000</pubDate>
      <link>https://dev.to/she11_qa/fixing-powershell-script-execution-policy-issue-when-activating-python-venv-582j</link>
      <guid>https://dev.to/she11_qa/fixing-powershell-script-execution-policy-issue-when-activating-python-venv-582j</guid>
      <description>&lt;p&gt;When setting up a Python virtual environment on Windows, running .\venv\Scripts\activate in PowerShell often throws a script execution policy error (such as PSSecurityException or cannot be loaded because running scripts is disabled on this system).&lt;/p&gt;

&lt;p&gt;Here is a quick walkthrough to resolve this issue safely without disabling your system's global security policies.&lt;/p&gt;




&lt;h3&gt;
  
  
  The Cause
&lt;/h3&gt;

&lt;p&gt;Windows PowerShell restricts script execution by default under the Restricted policy to prevent malicious scripts from executing. Because virtual environment activation scripts (Activate.ps1) are local scripts, PowerShell blocks them.&lt;/p&gt;




&lt;h3&gt;
  
  
  Step-by-Step Fix
&lt;/h3&gt;

&lt;h4&gt;
  
  
  1. Open PowerShell
&lt;/h4&gt;

&lt;p&gt;Launch PowerShell (or your integrated terminal in VS Code).&lt;/p&gt;

&lt;h4&gt;
  
  
  2. Update Execution Policy for Current User
&lt;/h4&gt;

&lt;p&gt;Run the following command to allow locally generated scripts to execute for your Windows profile only:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;Set-ExecutionPolicy&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-ExecutionPolicy&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;RemoteSigned&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Scope&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;CurrentUser&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why -Scope CurrentUser?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Scope limiting ensures you only grant permission to your current user session, eliminating the need to alter global system policies or risk security system-wide.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Verification Workflow&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Once the policy is set, you can create and activate your environment seamlessly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="c"&gt;# 1. Create your virtual environment&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;python&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-m&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;venv&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;venv&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="c"&gt;# 2. Activate the virtual environment&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;\venv\Scripts\activate&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="c"&gt;# Output: Your prompt will now show the environment prefix:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="c"&gt;# (.venv) PS C:\your-project-path&amp;gt;&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Summary&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Error&lt;/strong&gt;: PowerShell blocks Activate.ps1.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Fix&lt;/strong&gt;: Set -ExecutionPolicy RemoteSigned scoped to -Scope CurrentUser.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Security&lt;/strong&gt;: Keeps remote unsigned scripts blocked while allowing local development tools to function smoothly.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>python</category>
      <category>powershell</category>
      <category>windows</category>
      <category>developers</category>
    </item>
    <item>
      <title>Setting Up Playwright with JavaScript for Test Automation: Prerequisites &amp; VS Code Extensions</title>
      <dc:creator>She11 QA</dc:creator>
      <pubDate>Tue, 25 Aug 2026 08:20:04 +0000</pubDate>
      <link>https://dev.to/she11_qa/setting-up-playwright-with-javascript-for-test-automation-prerequisites-vs-code-extensions-181c</link>
      <guid>https://dev.to/she11_qa/setting-up-playwright-with-javascript-for-test-automation-prerequisites-vs-code-extensions-181c</guid>
      <description>&lt;p&gt;Setting up a new test automation framework can be tricky if you miss essential tools and extensions. Below is a structured checklist to get your environment fully prepared for automation testing using &lt;strong&gt;Playwright with JavaScript&lt;/strong&gt;.&lt;/p&gt;




&lt;h3&gt;
  
  
  1. Framework &amp;amp; Core Requirements
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Framework Type:&lt;/strong&gt; Playwright (JavaScript)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Configuration:&lt;/strong&gt; settings.xml (shared via your internal communications channel)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Repository:&lt;/strong&gt; xlc-portfolio-tech-shared-services/xlc-app-kc-test-automation&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  2. Development Environment Setup
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;IDE:&lt;/strong&gt; Install &lt;a href="https://code.visualstudio.com/" rel="noopener noreferrer"&gt;Visual Studio Code&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Configuration Files:&lt;/strong&gt; Ensure settings.xml and Framework.zip are downloaded and properly configured in your root workspace.&lt;/li&gt;
&lt;/ol&gt;




&lt;h3&gt;
  
  
  3. Recommended VS Code Extensions
&lt;/h3&gt;

&lt;p&gt;To streamline writing BDD-style tests, running Playwright suites, and debugging, install the following extensions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Playwright &amp;amp; Testing:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Playwright Test Runner&lt;/li&gt;
&lt;li&gt;Playwright Test for VSCode&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;BDD &amp;amp; Gherkin / Cucumber Support:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Cucumber&lt;/li&gt;
&lt;li&gt;Cucumber (Gherkin) Full Support&lt;/li&gt;
&lt;li&gt;Snippets and Syntax Highlight for Gherkin (Cucumber)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AI &amp;amp; GitHub Integration:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;GitHub Copilot&lt;/li&gt;
&lt;li&gt;GitHub Copilot Chat&lt;/li&gt;
&lt;li&gt;GitHub Actions&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Code Quality &amp;amp; Utilities:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;ESLint&lt;/li&gt;
&lt;li&gt;XML Tools&lt;/li&gt;
&lt;li&gt;Rainbow CSV&lt;/li&gt;
&lt;li&gt;Excel Viewer&lt;/li&gt;
&lt;li&gt;Microsoft Edge Tools for VS Code&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  💡 Pro Tip
&lt;/h3&gt;

&lt;p&gt;Make sure all team members maintain identical VS Code extension sets to prevent formatting inconsistencies and ensure smooth local test runs!&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>playwright</category>
      <category>testing</category>
      <category>automation</category>
    </item>
    <item>
      <title>Building an AI Test Automation Factory: How We Reduced Automation Effort by 78% with Multi-Agent Systems &amp; MCP</title>
      <dc:creator>She11 QA</dc:creator>
      <pubDate>Sat, 22 Aug 2026 09:25:45 +0000</pubDate>
      <link>https://dev.to/she11_qa/building-an-ai-test-automation-factory-how-we-reduced-automation-effort-by-78-with-multi-agent-45h3</link>
      <guid>https://dev.to/she11_qa/building-an-ai-test-automation-factory-how-we-reduced-automation-effort-by-78-with-multi-agent-45h3</guid>
      <description>&lt;p&gt;Traditional test automation frameworks often carry heavy maintenance costs, slow release cycles, and high knowledge dependency. By transitioning from standard script creation to a governed &lt;strong&gt;AI Test Automation Factory&lt;/strong&gt;, engineering teams can shift their focus from writing boilerplate code to high-value validation and architectural optimization.&lt;/p&gt;

&lt;p&gt;Here is an architectural breakdown of how multi-agent AI systems, governed telemetry, and Model Context Protocol (MCP) transform enterprise quality engineering.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Problem: The 45-Hour Manual Bottleneck&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Building a end-to-end BDD automation suite manually requires significant time per user story—often taking up to 45 hours across five distinct steps:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Context Generation &amp;amp; Requirements Review&lt;/strong&gt; (~8 hrs)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Manual Test Case Design&lt;/strong&gt; (~9 hrs)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Cucumber Feature File Creation&lt;/strong&gt; (~8 hrs)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Page Object Model Generation&lt;/strong&gt; (~8 hrs)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Step Definition Implementation&lt;/strong&gt; (~10 hrs)&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This traditional workflow creates coverage gaps, inconsistent code quality, and defect leakage.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Solution: Multi-Agent AI Automation Pipeline&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Instead of relying on single prompts, an AI Test Automation Factory routes requirement artifacts (BRDs / User Stories) through specialized agents:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
[BRD / User Story] 
       │
       ▼
[Context Agent] ──► [Test Case Agent] ──► [Feature File Agent]
                                                  │
[Automation Suite] ◄── [Step Definition Agent] ◄── [Page Object Agent]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Context Agent:&lt;/strong&gt; Parses acceptance criteria and enterprise domain knowledge.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Test Case Agent:&lt;/strong&gt; Auto-generates exhaustive test scenario matrices.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Feature File Agent:&lt;/strong&gt; Drafts standardized BDD Cucumber feature files.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Page Object &amp;amp; Step Def Agents:&lt;/strong&gt; Constructs clean design patterns (POM) and matching step implementations.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Measurable ROI: Before vs. After AI&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;By replacing manual generation with agentic workflows, the effort to automate a scenario drops from &lt;strong&gt;45 hours to 9.5 hours:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Phase&lt;/th&gt;
&lt;th&gt;Manual Effort&lt;/th&gt;
&lt;th&gt;AI-Driven Effort&lt;/th&gt;
&lt;th&gt;Time Saved&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Context Generation&lt;/td&gt;
&lt;td&gt;8 hrs&lt;/td&gt;
&lt;td&gt;2 hrs&lt;/td&gt;
&lt;td&gt;75%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Test Design&lt;/td&gt;
&lt;td&gt;9 hrs&lt;/td&gt;
&lt;td&gt;2 hrs&lt;/td&gt;
&lt;td&gt;78%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Feature File Creation&lt;/td&gt;
&lt;td&gt;8 hrs&lt;/td&gt;
&lt;td&gt;0.5 hrs&lt;/td&gt;
&lt;td&gt;94%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Page Object Creation&lt;/td&gt;
&lt;td&gt;8 hrs&lt;/td&gt;
&lt;td&gt;2 hrs&lt;/td&gt;
&lt;td&gt;75%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Step Definitions&lt;/td&gt;
&lt;td&gt;10 hrs&lt;/td&gt;
&lt;td&gt;3 hrs&lt;/td&gt;
&lt;td&gt;70%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Total Effort&lt;/td&gt;
&lt;td&gt;45 hrs&lt;/td&gt;
&lt;td&gt;9.5 hrs&lt;/td&gt;
&lt;td&gt;78% Reduction&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Key Business Metrics:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Productivity Multiplier:&lt;/strong&gt; 4X Faster Delivery&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Test Coverage:&lt;/strong&gt; Increased from 65% to 90%&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Defect Leakage:&lt;/strong&gt; Reduced from 12% to 5%&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Overall Cost Footprint:&lt;/strong&gt; Scaled down to 22% of original baseline&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;AI Governance &amp;amp; Observability&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Enterprise deployment requires strict guardrails around LLM usage. A telemetry layer sits between the agents and executive reporting dashboards to monitor performance in real time:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Token &amp;amp; Usage Tracking:&lt;/strong&gt; Daily audit trails for prompt/completion token consumption.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Cost &amp;amp; Adoption Monitoring:&lt;/strong&gt; Sprint-by-sprint metrics tracking user engagement vs. API spend.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Executive Visibility:&lt;/strong&gt; Real-time Power BI reporting reflecting total hours saved and generated code assets.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;The Future: Autonomous Testing via MCP&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The future of QA lies in moving from &lt;strong&gt;AI-Assisted&lt;/strong&gt; generation to &lt;strong&gt;Autonomous Self-Healing Execution&lt;/strong&gt;. Leveraging the Model Context Protocol (MCP) enables seamless enterprise knowledge integration, allowing agents to directly query system context, adjust broken locators automatically, and deliver a fully autonomous QA pipeline.&lt;/p&gt;

</description>
      <category>testing</category>
      <category>ai</category>
      <category>automation</category>
      <category>devops</category>
    </item>
    <item>
      <title>How to Configure Full Parallel Execution in TestNG via Maven (with Dynamic Thread Control)</title>
      <dc:creator>She11 QA</dc:creator>
      <pubDate>Sat, 22 Aug 2026 09:16:16 +0000</pubDate>
      <link>https://dev.to/she11_qa/how-to-configure-full-parallel-execution-in-testng-via-maven-with-dynamic-thread-control-o86</link>
      <guid>https://dev.to/she11_qa/how-to-configure-full-parallel-execution-in-testng-via-maven-with-dynamic-thread-control-o86</guid>
      <description>&lt;p&gt;Running automation test suites sequentially can quickly become a bottleneck in CI/CD pipelines. To optimize execution efficiency, you can enable full parallel execution using TestNG and Maven, allowing you to control thread counts dynamically right from the command line without modifying XML files every time.&lt;/p&gt;

&lt;p&gt;Here is a step-by-step guide on how to configure your framework for flexible parallel execution, along with a comparison of native TestNG execution versus custom Allocator/Run Manager approaches.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step-by-Step Configuration&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Update testng_regression.xml&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Modify the  tag to set your default parallel mode and thread count. You can set parallel to methods, classes, tests, or instances based on your architecture:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;
&lt;span class="nt"&gt;&amp;lt;suite&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"Regression"&lt;/span&gt; &lt;span class="na"&gt;parallel=&lt;/span&gt;&lt;span class="s"&gt;"methods"&lt;/span&gt; &lt;span class="na"&gt;thread-count=&lt;/span&gt;&lt;span class="s"&gt;"10"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2. Configure pom.xml for Dynamic Control&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;To override these settings at runtime without touching the codebase, map properties inside the  block of the maven-surefire-plugin:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;
&lt;span class="nt"&gt;&amp;lt;configuration&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;parallel&amp;gt;&lt;/span&gt;${parallel}&lt;span class="nt"&gt;&amp;lt;/parallel&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;threadCount&amp;gt;&lt;/span&gt;${threadCount}&lt;span class="nt"&gt;&amp;lt;/threadCount&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/configuration&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Command-Line Usage Examples&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Once configured, you can pass parameters dynamically via Maven:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Default Run (10 threads):&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   mvn clean &lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="nt"&gt;-P&lt;/span&gt; runTestNGTests
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Dynamic Thread Count (15 threads):&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;
   mvn clean &lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="nt"&gt;-P&lt;/span&gt; runTestNGTests &lt;span class="nt"&gt;-DthreadCount&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;15
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Change Parallel Mode at Runtime:&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;
   mvn clean &lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="nt"&gt;-P&lt;/span&gt; runTestNGTests &lt;span class="nt"&gt;-Dparallel&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;classes &lt;span class="nt"&gt;-DthreadCount&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;15
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;High-Throughput Run (Match CPU cores, e.g., 24 threads):&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;
   mvn clean &lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="nt"&gt;-P&lt;/span&gt; runTestNGTests &lt;span class="nt"&gt;-DthreadCount&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;24
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Comparison: Native TestNG vs. Custom Excel Allocator&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If your framework currently uses a custom Excel-based Run Manager alongside native TestNG, here is how the two approaches compare:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature / Aspect&lt;/th&gt;
&lt;th&gt;Custom Allocator (Run Manager)&lt;/th&gt;
&lt;th&gt;Native TestNG&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Entry Point&lt;/td&gt;
&lt;td&gt;allocator.Allocator.main() via Maven exec plugin&lt;/td&gt;
&lt;td&gt;maven-surefire-plugin running testng.xml&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Test Selection&lt;/td&gt;
&lt;td&gt;Reads Excel sheets via custom properties&lt;/td&gt;
&lt;td&gt;Reads testng_regression.xml classes/methods&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Thread Management&lt;/td&gt;
&lt;td&gt;Managed via custom ExecutorService&lt;/td&gt;
&lt;td&gt;Native TestNG thread pool (parallel + thread-count)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Execution Command&lt;/td&gt;
&lt;td&gt;mvn clean test -P runAllocator&lt;/td&gt;
&lt;td&gt;mvn clean test -P runTestNGTests&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pros&lt;/td&gt;
&lt;td&gt;Data-driven via Excel; multi-sheet aggregation support&lt;/td&gt;
&lt;td&gt;Lightweight, faster startup, zero Excel dependencies&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cons&lt;/td&gt;
&lt;td&gt;Requires global property tuning; code changes for structural updates&lt;/td&gt;
&lt;td&gt;Restricted to TestNG parallel modes (methods/classes)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Which Approach Should You Choose?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Choose Custom Allocator&lt;/strong&gt; if your suite relies heavily on fine-grained Excel-driven iteration control or multi-sheet test scheduling.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Choose Native TestNG&lt;/strong&gt; if you want a cleaner footprint, faster execution loops without file-parsing overhead, and straightforward CLI thread scaling.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>testing</category>
      <category>automation</category>
      <category>java</category>
      <category>devops</category>
    </item>
    <item>
      <title>Complete End-to-End OpenTelemetry Setup Guide for AI Agents &amp; Power BI Observability</title>
      <dc:creator>She11 QA</dc:creator>
      <pubDate>Fri, 21 Aug 2026 09:14:14 +0000</pubDate>
      <link>https://dev.to/she11_qa/complete-end-to-end-opentelemetry-setup-guide-for-ai-agents-power-bi-observability-4p66</link>
      <guid>https://dev.to/she11_qa/complete-end-to-end-opentelemetry-setup-guide-for-ai-agents-power-bi-observability-4p66</guid>
      <description>&lt;p&gt;&lt;strong&gt;OpenTelemetry E2E Setup Guide for AI Agents&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This guide shows how to set up end-to-end OpenTelemetry observability for AI agents, from local tracing to production export. It covers the OpenTelemetry Collector, Python and Node.js instrumentation, agent-specific spans, LLM and tool-call tracing, validation, and production recommendations.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. What You Are Instrumenting&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For an AI agent, the most useful trace structure is a top-level agent run with child spans for every meaningful operation.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
agent.run
|-- agent.plan
|-- llm.chat
|-- tool.call: knowledge_search
|-- retrieval.query
|-- memory.read
|-- llm.chat
|-- memory.write
`-- agent.finalize

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At minimum, trace these operations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;agent.run : one full user request or autonomous task&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;llm.chat : each model call&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;tool.call : each tool invocation&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;retrieval.query : vector search, database lookup, or document retrieval&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;memory.read : agent memory lookup&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;memory.write : agent memory update&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;agent.handoff : transfer to another agent or human&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;agent.finalize : final response construction&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;2. Recommended Architecture&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
Agent Application
|
v
OTLP traces, metrics, logs
|
v
OpenTelemetry Collector
|
v Exporter
v
Observability Backend

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Common observability backends include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Jaeger&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Grafana Tempo&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Honeycomb&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Datadog&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;New Relic&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Azure Monitor&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;AWS X-Ray&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Google Cloud Trace&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Elastic Observability&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For local development, you can start with the OpenTelemetry Collector plus a logging exporter. For production, send data from the Collector to your preferred backend.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Local OpenTelemetry Collector Setup&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Create a file named otel-collector-config.yaml:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;
&lt;span class="na"&gt;receivers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;otlp&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;protocols&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;http&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;endpoint&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;0.0.0.0:4318&lt;/span&gt;
      &lt;span class="na"&gt;grpc&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;endpoint&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;0.0.0.0:4317&lt;/span&gt;

&lt;span class="na"&gt;processors&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;batch&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;

&lt;span class="na"&gt;exporters&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;logging&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;verbosity&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;detailed&lt;/span&gt;

&lt;span class="na"&gt;service&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;pipelines&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;traces&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;receivers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;otlp&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
      &lt;span class="na"&gt;processors&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;batch&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
      &lt;span class="na"&gt;exporters&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;logging&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run the Collector with Docker on Windows PowerShell:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;docker&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;run&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;--rm&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-p&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;4317:4317&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-p&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;4318:4318&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;\&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nt"&gt;-v&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;${PWD}&lt;/span&gt;&lt;span class="s2"&gt;/otel-collector-config.yaml:/etc/otelcol/config.yaml"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;\&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nx"&gt;otel/opentelemetry-collector:latest&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run the Collector on macOS or Linux:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;--rm&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; 4317:4317 &lt;span class="nt"&gt;-p&lt;/span&gt; 4318:4318 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$PWD&lt;/span&gt;&lt;span class="s2"&gt;/otel-collector-config.yaml:/etc/otelcol/config.yaml"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  otel/opentelemetry-collector:latest

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The local OTLP endpoints are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;HTTP: &lt;a href="http://localhost:4318/v1/traces" rel="noopener noreferrer"&gt;http://localhost:4318/v1/traces&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;gRPC: localhost:4317&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;4. Python Agent Setup&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Install dependencies:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;opentelemetry-api opentelemetry-sdk opentelemetry-exporter-otlp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create telemetry.py:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;opentelemetry&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;trace&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;opentelemetry.exporter.otlp.proto.http.trace_exporter&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;OTLPSpanExporter&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;opentelemetry.sdk.resources&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Resource&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;opentelemetry.sdk.trace&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;TracerProvider&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;opentelemetry.sdk.trace.export&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;BatchSpanProcessor&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;configure_telemetry&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;resource&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Resource&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;service.name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;agent-service&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;service.version&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;1.0.0&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;deployment.environment&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;local&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;

    &lt;span class="n"&gt;provider&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;TracerProvider&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;resource&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;exporter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;OTLPSpanExporter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;endpoint&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http://localhost:4318/v1/traces&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;provider&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_span_processor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;BatchSpanProcessor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;exporter&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="n"&gt;trace&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set_tracer_provider&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;provider&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Instrument the agent:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;opentelemetry&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;trace&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;opentelemetry.trace&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;StatusCode&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;telemetry&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;configure_telemetry&lt;/span&gt;

&lt;span class="nf"&gt;configure_telemetry&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;tracer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;trace&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_tracer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;agent-service&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;run_agent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_input&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;tracer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start_as_current_span&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;agent.run&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;span&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;span&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set_attribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;agent.name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;support-agent&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;span&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set_attribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;agent.version&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;1.0.0&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;span&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set_attribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;agent.input.length&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_input&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

        &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;tracer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start_as_current_span&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;agent.plan&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;plan_span&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;plan&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;create_plan&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_input&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="n"&gt;plan_span&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set_attribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;agent.plan.steps&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;plan&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

            &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;tracer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start_as_current_span&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;llm.chat&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;llm_span&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;llm_span&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set_attribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gen_ai.system&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;openai&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="n"&gt;llm_span&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set_attribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gen_ai.operation.name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;chat&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="n"&gt;llm_span&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set_attribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gen_ai.request.model&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gpt-4.1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

                &lt;span class="n"&gt;llm_response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;call_llm&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_input&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

                &lt;span class="n"&gt;llm_span&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set_attribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gen_ai.response.model&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gpt-4.1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="n"&gt;llm_span&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set_attribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gen_ai.usage.input_tokens&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;llm_response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;input_tokens&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="n"&gt;llm_span&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set_attribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gen_ai.usage.output_tokens&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;llm_response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;output_tokens&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

            &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;tracer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start_as_current_span&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;tool.call&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;tool_span&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;tool_span&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set_attribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;tool.name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;knowledge_search&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="n"&gt;tool_span&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set_attribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;tool.call_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;llm_response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tool_call_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

                &lt;span class="n"&gt;documents&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;search_documents&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;llm_response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tool_query&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

                &lt;span class="n"&gt;tool_span&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set_attribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;tool.success&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="n"&gt;tool_span&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set_attribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;retrieval.document_count&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;documents&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

            &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;tracer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start_as_current_span&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;agent.finalize&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;final_span&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;final_answer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;produce_answer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;llm_response&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;documents&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="n"&gt;final_span&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set_attribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;agent.output.length&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;final_answer&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;final_answer&lt;/span&gt;

        &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;Exception&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;exc&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;span&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;record_exception&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;exc&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;span&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set_status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;StatusCode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ERROR&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;exc&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
            &lt;span class="k"&gt;raise&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;5. Node.js or TypeScript Agent Setup&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Install dependencies:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;
npm &lt;span class="nb"&gt;install&lt;/span&gt; @opentelemetry/api @opentelemetry/sdk-node @opentelemetry/exporter-trace-otlp-http
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create telemetry.ts:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;NodeSDK&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@opentelemetry/sdk-node&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;OTLPTraceExporter&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@opentelemetry/exporter-trace-otlp-http&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;telemetrySdk&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;NodeSDK&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;serviceName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;agent-service&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;traceExporter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;OTLPTraceExporter&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;http://localhost:4318/v1/traces&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;}),&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;startTelemetry&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;telemetrySdk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;stopTelemetry&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;telemetrySdk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;shutdown&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Instrument the agent:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;SpanStatusCode&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;trace&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@opentelemetry/api&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;startTelemetry&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;stopTelemetry&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./telemetry&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tracer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;trace&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getTracer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;agent-service&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;runAgent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userInput&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;tracer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startActiveSpan&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;agent.run&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nx"&gt;span&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;span&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;agent.name&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;support-agent&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;span&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;agent.version&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;1.0.0&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;span&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;agent.input.length&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;userInput&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;plan&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;tracer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startActiveSpan&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;agent.plan&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nx"&gt;planSpan&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;createPlan&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userInput&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nx"&gt;planSpan&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;agent.plan.steps&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nx"&gt;planSpan&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;end&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;

      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;llmResponse&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;tracer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startActiveSpan&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;llm.chat&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nx"&gt;llmSpan&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;llmSpan&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;gen_ai.system&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;openai&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nx"&gt;llmSpan&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;gen_ai.operation.name&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;chat&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nx"&gt;llmSpan&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;gen_ai.request.model&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;gpt-4.1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;callLlm&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userInput&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;plan&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="nx"&gt;llmSpan&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;gen_ai.response.model&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;gpt-4.1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nx"&gt;llmSpan&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;gen_ai.usage.input_tokens&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;inputTokens&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nx"&gt;llmSpan&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;gen_ai.usage.output_tokens&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;outputTokens&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nx"&gt;llmSpan&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;end&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;

      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;documents&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;tracer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startActiveSpan&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;tool.call&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nx"&gt;toolSpan&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;toolSpan&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;tool.name&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;knowledge_search&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nx"&gt;toolSpan&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;tool.call_id&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;llmResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;toolCallId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;searchDocuments&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;llmResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;toolQuery&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="nx"&gt;toolSpan&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;tool.success&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nx"&gt;toolSpan&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;retrieval.document_count&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nx"&gt;toolSpan&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;end&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;

      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;finalAnswer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;tracer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startActiveSpan&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;agent.finalize&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nx"&gt;finalSpan&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;answer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;produceAnswer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;llmResponse&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;documents&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nx"&gt;finalSpan&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;agent.output.length&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;answer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nx"&gt;finalSpan&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;end&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;answer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;

      &lt;span class="nx"&gt;span&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;end&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;finalAnswer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;span&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;recordException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nb"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nx"&gt;span&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setStatus&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;code&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;SpanStatusCode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ERROR&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;
      &lt;span class="nx"&gt;span&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;end&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;startTelemetry&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;runAgent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;How do I reset my password?&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;finally&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;stopTelemetry&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;6. Recommended Span Attributes&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Use OpenTelemetry semantic conventions where possible.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;GenAI Attributes&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;gen_ai.system&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;gen_ai.operation.name&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;gen_ai.request.model&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;gen_ai.response.model&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;gen_ai.request.temperature&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;gen_ai.request.max_tokens&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;gen_ai.usage.input_tokens&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;gen_ai.usage.output_tokens&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Agent Attributes&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;agent.name&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;agent.version&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;agent.run_id&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;agent.session_id&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;agent.step.name&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;agent.step.index&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;agent.output.length&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;agent.input.length&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Tool Attributes&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;tool.name&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;tool.call_id&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;tool.success&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;tool.error.type&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;tool.retry_count&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Retrieval Attributes&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;retrieval.system&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;retrieval.index.name&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;retrieval.query_count&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;retrieval.document_count&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;retrieval.top_k&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Memory Attributes&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;memory.operation&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;memory.scope&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;memory.result_count&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;7. What Not To Capture&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Do not capture sensitive or high-risk data as span attributes unless your organization has explicit approval, redaction, access control, and retention policies.&lt;/p&gt;

&lt;p&gt;Avoid storing:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Full prompts&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Full model responses&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;User secrets&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;API keys or tokens&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Raw documents&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Email addresses&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Payment information&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Health data&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Authentication headers&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Full tool outputs&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Prefer safe metadata:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;prompt length&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;response length&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;token counts&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;model name&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;tool name&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;status&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;latency&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;retry count&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;document count&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;8. Metrics To Add&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Traces show what happened for one request. Metrics show aggregate behavior.&lt;/p&gt;

&lt;p&gt;Recommended metrics:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;agent.run.count&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;agent.run.duration&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;agent.run.error.count&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;llm.request.count&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;llm.request.duration&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;llm.token.input.count&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;llm.token.output.count&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;tool.call.count&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;tool.call.duration&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;tool.call.error.count&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;retrieval.query.count&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;retrieval.document.count&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;agent.handoff.count&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Start with traces first, then add metrics once your span structure is stable.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;9. Logging Setup&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Use logs for discrete application events, but correlate them with traces.&lt;/p&gt;

&lt;p&gt;Recommended log fields:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;trace_id&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;span_id&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;agent_run_id&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;agent.name&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;event.name&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;status&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;error.type&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;error.message&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Keep logs redacted. Avoid logging raw prompts, completions, credentials, or document bodies.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;10. Validation Checklist&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Use this checklist to verify the setup end to end.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Start the OpenTelemetry Collector.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Run one local agent request.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Confirm the Collector receives spans.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Confirm the root span is named agent.run.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Confirm LLM calls appear as llm.chat child spans.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Confirm tool calls appear as tool.call child spans.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Confirm errors are recorded on failed spans.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Confirm token usage attributes appear when available.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Confirm sensitive content is not exported.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Confirm traces reach your observability backend.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;11. Production Collector Example&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For production, route data through the Collector and export to your backend.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;
&lt;span class="na"&gt;otlp&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;protocols&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;http&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;grpc&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;

&lt;span class="na"&gt;processors&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;batch&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;memory_limiter&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;check_interval&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;1s&lt;/span&gt;
    &lt;span class="na"&gt;limit_mib&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;512&lt;/span&gt;
    &lt;span class="na"&gt;spike_limit_mib&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;128&lt;/span&gt;

&lt;span class="na"&gt;exporters&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;otlphttp&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;endpoint&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;https://your-observability-backend.example.com&lt;/span&gt;
    &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;api-key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${OBSERVABILITY_API_KEY}&lt;/span&gt;

&lt;span class="na"&gt;service&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;pipelines&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;traces&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;receivers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;otlp&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
      &lt;span class="na"&gt;processors&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;memory_limiter&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;batch&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
      &lt;span class="na"&gt;exporters&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;otlphttp&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Set environment variables securely through your deployment platform, secret manager, or CI/CD system.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;12. Production Hardening&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Before production rollout:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Add sampling if trace volume is high.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Redact sensitive inputs and outputs.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Add service names and versions.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Add deployment environment attributes.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Use secure OTLP endpoints.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Store backend API keys in a secret manager.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Configure retention policies.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Add alerts for agent error rate and high latency.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Track model cost through token metrics.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Validate compliance requirements before exporting AI data.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;13. Sampling Guidance&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For development, sample everything.&lt;/p&gt;

&lt;p&gt;For production, consider:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;100% sampling for errors&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;100% sampling for low-volume critical workflows&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Lower probabilistic sampling for high-volume successful requests&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Tail sampling when supported by your backend or Collector distribution&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Example parent-based trace ID ratio sampling in Python:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;opentelemetry.sdk.trace&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;TracerProvider&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;opentelemetry.sdk.trace.sampling&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ParentBased&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;TraceIdRatioBased&lt;/span&gt;

&lt;span class="n"&gt;provider&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;TracerProvider&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;sampler&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nc"&gt;ParentBased&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;TraceIdRatioBased&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.10&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This samples roughly 10% of new traces while preserving parent-child trace consistency.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;14. Framework-Specific Notes&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;LangChain&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Instrument the chain or agent executor as agent.run, then wrap model calls, retrievers, and tools as child spans. If you use callbacks, create spans inside callback handlers.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Semantic Kernel&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Trace kernel invocation as agent.run, function calls as tool.call, planner execution as agent.plan, and AI service calls as llm.chat.&lt;br&gt;
AutoGen or Multi-Agent Systems&lt;br&gt;
Use one trace for the full multi-agent workflow. Each agent turn can be represented as agent.run or agent.step, with agent.name distinguishing participants. Handoffs should be explicit spans named agent.handoff.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Custom Agents&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Create spans directly around your orchestration code. This usually gives the best signal because you know where planning, memory, retrieval, tool execution, and finalization happen.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;15. Minimal Rollout Plan&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Add the OpenTelemetry SDK to the agent service.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Send traces to a local Collector.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Add a root agent.run span.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Add child spans for LLM calls and tool calls.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Validate trace shape locally.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Add retrieval and memory spans.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Add token and latency attributes.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Add redaction rules.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Export from the Collector to your observability backend.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Add dashboards and alerts.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;16. Example Dashboard Panels&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Useful panels for agent operations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Agent run count by agent name&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Agent error rate&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Agent p50, p95, and p99 latency&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;LLM latency by model&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Input and output tokens by model&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Tool call failure rate&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Retrieval document count&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Handoff count&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Estimated model cost&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Top failing tools&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;17. Power BI Dashboard For Usage And Tokens&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Power BI should usually read from a queryable store, not directly from raw OpenTelemetry traces. Use OpenTelemetry for observability, then write a normalized usage table for reporting.&lt;/p&gt;

&lt;p&gt;Recommended flow:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
Agent Application
|
v
OpenTelemetry traces and metrics
|
v
OpenTelemetry Collector
|
v Export
v
Azure Monitor, Log Analytics, SQL, Fabric Lakehouse, or Data Warehouse
|
v Power BI semantic model
v
Power BI dashboard
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Recommended Data Source Options&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Use one of these patterns:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Azure Monitor or Log Analytics if your traces already go to Azure.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Application Insights if your application telemetry is already centralized there.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Azure SQL Database if you want simple relational reporting.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Microsoft Fabric Lakehouse or Warehouse if you want scalable analytics.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Databricks, Snowflake, or BigQuery if your organization already uses one of them.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For most teams, the easiest production setup is to store one row per LLM request in a table named agent_llm_usage.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Usage Table Schema&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Create a reporting table with stable, low-cardinality columns.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;agent_llm_usage&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;usage_id&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;trace_id&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;span_id&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;timestamp_utc&lt;/span&gt; &lt;span class="n"&gt;DATETIME2&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;environment&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;service_name&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;agent_name&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;agent_version&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;session_id&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;user_id_hash&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;model_provider&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;model_name&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;operation_name&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;input_tokens&lt;/span&gt; &lt;span class="nb"&gt;INT&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt; &lt;span class="k"&gt;DEFAULT&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;output_tokens&lt;/span&gt; &lt;span class="nb"&gt;INT&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt; &lt;span class="k"&gt;DEFAULT&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;total_tokens&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input_tokens&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;output_tokens&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;estimated_cost_usd&lt;/span&gt; &lt;span class="nb"&gt;DECIMAL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;18&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;duration_ms&lt;/span&gt; &lt;span class="nb"&gt;INT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;error_type&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;tool_name&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;retrieval_document_count&lt;/span&gt; &lt;span class="nb"&gt;INT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Do not store raw prompts, raw completions, secrets, full document text, or emails in this table. Use hashed user identifiers if user-level reporting is required.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Writing Usage Records&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;OpenTelemetry spans should still include token attributes such as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
gen_ai.usage.input_tokens
gen_ai.usage.output_tokens
gen_ai.request.model
gen_ai.system
agent.name
agent.session_id
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For Power BI, also write a compact business reporting event when each LLM call finishes. In Python, the event can be inserted into SQL, sent to Event Hubs, or written to your analytics pipeline.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;record_llm_usage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;usage_record&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;usage_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;usage_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;trace_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;trace_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;span_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;span_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;timestamp_utc&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;timestamp_utc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;environment&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;service_name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;agent-service&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;agent_name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;agent_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;agent_version&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;agent_version&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;session_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;session_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user_id_hash&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user_id_hash&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;model_provider&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;provider&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;model_name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;operation_name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;chat&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;input_tokens&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;input_tokens&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;output_tokens&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;output_tokens&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;estimated_cost_usd&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;estimated_cost_usd&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;duration_ms&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;duration_ms&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;status&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;success&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nf"&gt;insert_usage_record&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;usage_record&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Cost Calculation&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Keep model pricing in a separate table so costs can be updated without changing historical usage records.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;model_pricing&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;model_provider&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;model_name&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;effective_from_utc&lt;/span&gt; &lt;span class="n"&gt;DATETIME2&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;input_cost_per_1k_tokens&lt;/span&gt; &lt;span class="nb"&gt;DECIMAL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;18&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;output_cost_per_1k_tokens&lt;/span&gt; &lt;span class="nb"&gt;DECIMAL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;18&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The estimated cost formula is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
estimated_cost = (input_tokens / 1000 * input_price) + (output_tokens / 1000 * output_price)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can calculate this in the ingestion pipeline, SQL view, Fabric notebook, or Power BI semantic model. Pipeline or SQL calculation is usually better because all reports use the same cost logic.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Power BI Data Model&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Use a simple star schema.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fact table:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;agent_llm_usage&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Dimension tables:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;dim_date&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;dim_agent&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;dim_model&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;dim_environment&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;dim_tool&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Relationships:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;dim_date[date] -&amp;gt; agent_llm_usage[date]&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;dim_agent[agent_name] -&amp;gt; agent_llm_usage[agent_name]&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;dim_model[model_name] -&amp;gt; agent_llm_usage[model_name]&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;dim_environment[environment] -&amp;gt; agent_llm_usage[environment]&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;dim_tool[tool_name] -&amp;gt; agent_llm_usage[tool_name]&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Core DAX Measures&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Create these measures in Power BI:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
Total Requests = 
COUNTROWS(agent_llm_usage)

Successful Requests = 
CALCULATE(
    COUNTROWS(agent_llm_usage),
    agent_llm_usage[status] = "success"
)

Failed Requests = 
CALCULATE(
    COUNTROWS(agent_llm_usage),
    agent_llm_usage[status] &amp;lt;&amp;gt; "success"
)

Failure Rate = 
DIVIDE([Failed Requests], [Total Requests])

Input Tokens = 
SUM(agent_llm_usage[input_tokens])

Output Tokens = 
SUM(agent_llm_usage[output_tokens])

Total Tokens = 
[Input Tokens] + [Output Tokens]

Estimated Cost USD = 
SUM(agent_llm_usage[estimated_cost_usd])

Average Duration MS = 
AVERAGE(agent_llm_usage[duration_ms])

Average Tokens Per Request = 
DIVIDE([Total Tokens], [Total Requests])

P95 Duration MS = 
PERCENTILEX.INC(
    agent_llm_usage,
    agent_llm_usage[duration_ms],
    0.95
)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Recommended Power BI Pages&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Create these report pages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Executive Overview&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Token Usage&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Cost Analysis&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Agent Performance&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Model Performance&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Tool And Retrieval Usage&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Errors And Reliability&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Recommended visuals for the Executive Overview page:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Card: total requests&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Card: total tokens&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Card: estimated cost&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Card: failure rate&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Line chart: requests by day&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Line chart: tokens by day&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Bar chart: cost by agent&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Bar chart: tokens by model&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Table: top agents by cost, tokens, and failures&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Recommended visuals for the Token Usage page:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Line chart: input tokens and output tokens over time&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Stacked column chart: tokens by model&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Matrix: agent name by model name with total tokens&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Slicer: date range&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Recommended visuals for the Cost Analysis page:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Slicer: environment&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Slicer: agent name&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Slicer: model name&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Line chart: estimated cost by day&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Bar chart: estimated cost by model&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Bar chart: estimated cost by agent&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Table: session or hashed user groups by cost&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;KPI: cost per request&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Recommended visuals for the Errors And Reliability page:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Card: failed requests&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Card: failure rate&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Line chart: failures by day&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Bar chart: errors by model&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Bar chart: errors by tool&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Table: error type, agent name, model name, and count&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Refresh And Governance&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Recommended refresh setup:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Development: manual refresh&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Small production workload: scheduled refresh every 1 to 4 hours&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;High-volume production workload: Direct Lake, DirectQuery, or incremental refresh&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Governance recommendations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Use row-level security if teams should only see their own agents.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Store only hashed user IDs.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Keep prompt and completion content out of the reporting model.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Certify the semantic model once measures are validated.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Document model pricing assumptions and update them when provider pricing changes.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;18. Troubleshooting&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;No Spans In Collector&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Check:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Collector is running.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;App exports to &lt;a href="http://localhost:4318/v1/traces" rel="noopener noreferrer"&gt;http://localhost:4318/v1/traces&lt;/a&gt; for OTLP HTTP.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Port 4318 is reachable.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The SDK is initialized before the agent runs.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The app shuts down cleanly so batch spans are flushed.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Root Span Exists But Child Spans Are Missing&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Check:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Child spans are created inside the active context.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Async operations preserve context.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Spans are ended after work completes.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Exceptions do not skip span.end() in TypeScript.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Too Much Sensitive Data&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Check:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;No full prompts are added as attributes.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;No raw tool outputs are added as attributes.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Logs do not contain secrets.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Collector processors or backend rules redact known sensitive fields.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;High Trace Volume&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Check:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Add sampling.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Reduce span count for noisy internal steps.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Keep attributes low-cardinality.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Avoid unique values such as full user IDs, emails, or raw queries in indexed attributes.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;19. Final Recommended Baseline&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A practical first production baseline is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
Root span:
agent.run

Child spans:
agent.plan
llm.chat
tool.call
retrieval.query
memory.read
memory.write
agent.finalize

Required attributes:
service.name
deployment.environment
agent.name
agent.version
gen_ai.system
gen_ai.request.model
gen_ai.usage.input_tokens
gen_ai.usage.output_tokens
tool.name
tool.success
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This gives enough visibility to answer the most important operational questions: what happened, where time was spent, which model and tools were used, whether the run failed, and how much token usage it consumed.&lt;/p&gt;

</description>
      <category>opentelemetry</category>
      <category>python</category>
      <category>typescript</category>
      <category>ai</category>
    </item>
    <item>
      <title>Setting Up End-to-End PDF Validation in Playwright and Cucumber BDD</title>
      <dc:creator>She11 QA</dc:creator>
      <pubDate>Tue, 18 Aug 2026 13:08:36 +0000</pubDate>
      <link>https://dev.to/she11_qa/setting-up-end-to-end-pdf-validation-in-playwright-and-cucumber-bdd-5259</link>
      <guid>https://dev.to/she11_qa/setting-up-end-to-end-pdf-validation-in-playwright-and-cucumber-bdd-5259</guid>
      <description>&lt;p&gt;Validating generated PDFs in automated end-to-end tests can be tricky. Here is a comprehensive guide on how to handle PDF downloading, parsing, and structured data assertion in a Web UI automation framework using Playwright, Cucumber (BDD), pdf-parse, and ajv schema validation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Overview &amp;amp; Workflow&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The automated end-to-end PDF validation flow follows these steps:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Navigate through the UI to trigger a PDF download.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Capture the download event using Playwright and store the PDF locally in reports/downloads/pdf/.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Extract and parse text from the downloaded file.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Perform assertions using static text matches, dynamic JSON data paths, or Regex extractions verified against JSON Schemas.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;1. Prerequisites &amp;amp; Dependencies&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Ensure your project has pdf-parse and ajv installed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"dependencies"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"pdf-parse"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^1.1.1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"ajv"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^8.17.1"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Enable download handling in your Playwright configuration (setup/hooks.js):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;
&lt;span class="c1"&gt;// Browser context setup&lt;/span&gt;
&lt;span class="nx"&gt;acceptDownloads&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2. Core Utility Helper&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Create a helper module (utils/PdfHelper.js) to handle file downloads, text normalization, and schema validation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;
&lt;span class="c1"&gt;// Core functions implemented in PdfHelper.js&lt;/span&gt;
&lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nf"&gt;downloadPdfFromSelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;selector&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// Captures Playwright download event &amp;amp; saves file&lt;/span&gt;
&lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nf"&gt;parsePdf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;filePath&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nf"&gt;parsePdfBuffer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;       &lt;span class="c1"&gt;// Normalizes raw text output&lt;/span&gt;
&lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nf"&gt;assertTextContains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;expectedValues&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// Handles case-insensitive and whitespace-normalized checks&lt;/span&gt;
&lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nf"&gt;extractBySchema&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;extractionRules&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;          &lt;span class="c1"&gt;// Extracts fields using JS Regex&lt;/span&gt;
&lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nf"&gt;validateWithSchema&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fieldData&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;schemaPath&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;        &lt;span class="c1"&gt;// Validates structure with AJV&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;3. BDD Step Definitions&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Wire your Cucumber steps (step-definitions/ui/pdfValidationSteps.js) to interact with the helper and scenario context:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight gherkin"&gt;&lt;code&gt;
&lt;span class="nf"&gt;When &lt;/span&gt;the user downloads a PDF from selector &lt;span class="s"&gt;"&amp;lt;selector&amp;gt;"&lt;/span&gt;
&lt;span class="nf"&gt;When &lt;/span&gt;the user parses the downloaded PDF
&lt;span class="nf"&gt;Then &lt;/span&gt;the downloaded PDF should contain text &lt;span class="s"&gt;"&amp;lt;text&amp;gt;"&lt;/span&gt;
&lt;span class="nf"&gt;Then &lt;/span&gt;the downloaded PDF should contain values from json &lt;span class="s"&gt;"&amp;lt;jsonPath&amp;gt;"&lt;/span&gt; at path &lt;span class="s"&gt;"&amp;lt;dataPath&amp;gt;"&lt;/span&gt;
&lt;span class="nf"&gt;When &lt;/span&gt;the user extracts PDF fields using rules from &lt;span class="s"&gt;"&amp;lt;rulesPath&amp;gt;"&lt;/span&gt;
&lt;span class="nf"&gt;Then &lt;/span&gt;the extracted PDF fields should match schema &lt;span class="s"&gt;"&amp;lt;schemaPath&amp;gt;"&lt;/span&gt;
&lt;span class="nf"&gt;Then &lt;/span&gt;the downloaded PDF page count should be at least &lt;span class="nv"&gt;&amp;lt;n&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;4. Extraction Rules &amp;amp; Schema Validation Example&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For complex documents, define regex patterns to extract fields and validate them against a schema.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Regex Rules&lt;/strong&gt; (test-data/json/pdfExtractionRules.sample.json):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"rules"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"key"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"bondNumber"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"pattern"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Bond&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s2"&gt;s*Number&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s2"&gt;s*:&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s2"&gt;s*([A-Z0-9-]+)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"flags"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"i"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"group"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Feature File Integration:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight gherkin"&gt;&lt;code&gt;&lt;span class="nf"&gt;And &lt;/span&gt;the user downloads a PDF from selector &lt;span class="s"&gt;"&amp;lt;stable-selector&amp;gt;"&lt;/span&gt;
&lt;span class="nf"&gt;And &lt;/span&gt;the user parses the downloaded PDF
&lt;span class="nf"&gt;Then &lt;/span&gt;the downloaded PDF should contain values from json &lt;span class="s"&gt;"test-data/json/BondTestData.json"&lt;/span&gt; at path &lt;span class="s"&gt;"accountName"&lt;/span&gt;
&lt;span class="nf"&gt;And &lt;/span&gt;the downloaded PDF page count should be at least 1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;5. Running Tests &amp;amp; Best Practices&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Add test scripts to your package.json:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"scripts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"test:ui:pdf:dry"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"npx cucumber-js features/ui/pdfValidation.feature --import setup/hooks.js --import setup/assertions.js --import step-definitions/ui --dry-run"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"test:ui:pdf:run"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"npx cucumber-js features/ui/pdfValidation.feature --import setup/hooks.js --import setup/assertions.js --import step-definitions/ui -f progress -f json:reports/cucumber_report.json --parallel 1"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Key Recommendations:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Selectors:&lt;/strong&gt; Prefer stable data-testid attributes over text-only selectors.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Environment Safety:&lt;/strong&gt; Store expected test data in JSON files rather than hardcoding values.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Security &amp;amp; Cleanup:&lt;/strong&gt; Do not keep sensitive PDF payloads longer than necessary, and clean up downloads in post-test runs.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>javascript</category>
      <category>testing</category>
      <category>playwright</category>
      <category>cucumber</category>
    </item>
    <item>
      <title>Checklist: Onboarding End-to-End Automation Frameworks to Harness CI</title>
      <dc:creator>She11 QA</dc:creator>
      <pubDate>Tue, 18 Aug 2026 09:44:13 +0000</pubDate>
      <link>https://dev.to/she11_qa/checklist-onboarding-end-to-end-automation-frameworks-to-harness-ci-2jmb</link>
      <guid>https://dev.to/she11_qa/checklist-onboarding-end-to-end-automation-frameworks-to-harness-ci-2jmb</guid>
      <description>&lt;p&gt;Successfully onboarding an automated test suite to &lt;strong&gt;Harness CI&lt;/strong&gt; requires configuring infrastructure placeholders, secrets, pipelines, and branch protection rules.&lt;/p&gt;

&lt;p&gt;Here is a 10-step checklist to help you onboard your end-to-end (E2E) automation pipelines seamlessly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1: Replace Infrastructure Placeholders&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Ensure your pipeline YAML definitions (e.g., .harness/e2e-poc.yaml and .harness/e2e-regression-parallel.yaml) contain your specific environment values:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;ORG_ID: Harness Organization Identifier&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;PROJECT_ID: Harness Project Identifier&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;GIT_CONNECTOR: Harness Git Connector for GitHub Enterprise access&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;APP_REPO_NAME: Target repository in owner/repo format&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;K8S_CONNECTOR: Kubernetes connector for build infrastructure&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;K8S_NAMESPACE: Kubernetes namespace where build pods run&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Step 2: Configure Environment Secrets&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In Harness, set up the following runtime secrets:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;CONNECT_URL&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;CONNECT_USERNAME&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;CONNECT_PASSWORD&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Step 3: Setup PR Validation Pipeline&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Import your short-run pipeline YAML into Harness.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Save it as your PR Validation Pipeline.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Run a manual validation test using runtime overrides:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;TargetEnv = qa&lt;/li&gt;
&lt;li&gt;cucumberTags = &lt;a class="mentioned-user" href="https://dev.to/smoke"&gt;@smoke&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Step 4: Verify Artifact Generation&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Confirm that the initial execution correctly generates and uploads all required outputs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;JUnit Report:&lt;/strong&gt; reports/junit-report.xml&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Test Reports:&lt;/strong&gt; reports/**&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Failure Artifacts:&lt;/strong&gt; test-results/** (screenshots, traces)&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Step 5: Setup Nightly Parallel Pipeline&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Import your parallel pipeline YAML into Harness.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Save it as your &lt;strong&gt;Nightly Regression Pipeline.&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Run a manual validation test with target concurrency parameters:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;TargetEnv = qa&lt;/li&gt;
&lt;li&gt;cucumberTags = @regression&lt;/li&gt;
&lt;li&gt;cucumberParallel = 4&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Step 6: Configure Automated Triggers &amp;amp; Branch Protection&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;PR Trigger:&lt;/strong&gt; Configured on pull requests with cucumberTags=&lt;a class="mentioned-user" href="https://dev.to/smoke"&gt;@smoke&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Nightly Schedule Trigger:&lt;/strong&gt; Configured on a nightly cron schedule with cucumberTags=@regression and cucumberParallel=4.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;GitHub Branch Protection:&lt;/strong&gt; Enable branch protection on target branches requiring the Harness PR pipeline status check to pass before merging.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once both the PR and Nightly execution runs pass with green status checks, your pipeline onboarding is complete!&lt;/p&gt;

</description>
      <category>devops</category>
      <category>testing</category>
      <category>automation</category>
      <category>ci</category>
    </item>
    <item>
      <title>End-to-End Setup Guide: Integrating Playwright + Cucumber with Harness CI</title>
      <dc:creator>She11 QA</dc:creator>
      <pubDate>Tue, 18 Aug 2026 09:33:47 +0000</pubDate>
      <link>https://dev.to/she11_qa/end-to-end-setup-guide-integrating-playwright-cucumber-with-harness-ci-423p</link>
      <guid>https://dev.to/she11_qa/end-to-end-setup-guide-integrating-playwright-cucumber-with-harness-ci-423p</guid>
      <description>&lt;p&gt;Integrating end-to-end (E2E) automation suites into enterprise CI/CD pipelines requires robust reporting, dynamic execution controls, and seamless artifact management.&lt;/p&gt;

&lt;p&gt;Here is a guide on setting up a &lt;strong&gt;Node.js + Playwright + Cucumber.js&lt;/strong&gt; test suite using &lt;strong&gt;Harness CI&lt;/strong&gt;, configured with dual-repository dependencies, parallel execution capabilities, and dashboard-ready reporting.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key Architectural Setup&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Two-Repo Architecture:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Repository A (Application Automation Repo):&lt;/strong&gt; Contains application-specific feature files, page objects, and pipeline definitions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Repository B (Shared Framework Repo):&lt;/strong&gt; Hosts core framework utilities, custom assertions, and base drivers consumed as a pinned dependency.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Tech Stack:&lt;/strong&gt; Node.js, Playwright, Cucumber.js, Allure/JUnit reporting.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Step 1: Configure Harness Connectors &amp;amp; Secrets&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Set up these foundational resources within your Harness account:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Connectors:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;GIT_CONNECTOR: Grants access to both application and framework GitHub repositories.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;K8S_CONNECTOR: Manages the Kubernetes build infrastructure.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Secrets:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;CONNECT_URL, CONNECT_USERNAME, and CONNECT_PASSWORD (and proxy settings if required).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Step 2: Configure Pipelines&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Import your execution configurations using YAML files inside .harness/:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Standard Run&lt;/strong&gt; (.harness/e2e-poc.yaml): Used for fast PR checks.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Parallel Regression&lt;/strong&gt; (.harness/e2e-regression-parallel.yaml): Used for scheduled, high-volume regression runs.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Replace placeholders such as , , and  to map to your cluster environment.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 3: Define Pipeline Triggers&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Set up two primary execution workflows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Pull Request (PR) Trigger:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Event:&lt;/strong&gt; Pull Request to main/POC branch.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Runtime Variables:&lt;/strong&gt; cucumberTags=&lt;a class="mentioned-user" href="https://dev.to/smoke"&gt;@smoke&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Scheduled Nightly Trigger:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Event:&lt;/strong&gt; Scheduled Cron.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Runtime Variables:&lt;/strong&gt; cucumberTags=@regression, cucumberParallel=4&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Step 4: Test Report &amp;amp; Artifact Collection&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;To ensure test metrics display properly on the Harness dashboard, configure both JUnit parsing and raw artifact archiving.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Generated Outputs:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;reports/junit-report.xml (parsed by Harness for test metrics)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;reports/cucumber_report.json&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;reports/allure-results &amp;amp; HTML reports&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;test-results/screenshots &amp;amp; test-results/traces (captured on failure)&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Harness UI Configuration Steps:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Under execution reporting options, enable &lt;strong&gt;JUnit Test Report Collection&lt;/strong&gt; and point it to reports/junit-report.xml.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Enable &lt;strong&gt;Artifact Collection&lt;/strong&gt; with the following paths:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;reports/**&lt;/li&gt;
&lt;li&gt;test-results/**&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Optional: Set strictReporting=true to force pipeline failure if report outputs are missing.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Step 5: Best Practices for Headless Environments&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Browser Fallbacks:&lt;/strong&gt; Configure cross-platform handling in your hooks file to run branded browsers (like Edge) locally, while falling back to standard headless containers (e.g., Chromium) in CI Linux pods.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Branch Protection:&lt;/strong&gt; Enforce GitHub branch rules requiring status checks from Harness to pass prior to merging PRs.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Shared Framework Management:&lt;/strong&gt; For quick POCs, pin Repository B as a Git dependency in package.json. For production, publish the framework as a versioned private package.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>testing</category>
      <category>devops</category>
      <category>playwright</category>
      <category>ci</category>
    </item>
    <item>
      <title>How to Configure Parallel Execution in TestNG vs. Custom Excel Allocator</title>
      <dc:creator>She11 QA</dc:creator>
      <pubDate>Tue, 18 Aug 2026 09:23:32 +0000</pubDate>
      <link>https://dev.to/she11_qa/how-to-configure-parallel-execution-in-testng-vs-custom-excel-allocator-4lo2</link>
      <guid>https://dev.to/she11_qa/how-to-configure-parallel-execution-in-testng-vs-custom-excel-allocator-4lo2</guid>
      <description>&lt;p&gt;Optimizing test execution speed is essential for keeping build pipelines lean. Depending on how your framework is structured, you can achieve full parallel execution either natively using &lt;strong&gt;TestNG ** or dynamically using a **Custom Excel Allocator&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Here is a step-by-step guide on configuring both approaches, along with a comparison to help you choose the right strategy.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Strategy 1: Native TestNG Parallelization (Recommended for Code-Native Suites)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;TestNG natively supports parallel execution at the methods, classes, tests, or instances level using its XML configuration or Maven parameters.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Update&lt;/strong&gt; testng_regression.xml&lt;br&gt;
Modify the  tag to set the execution mode and thread pool size:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;suite&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"Regression"&lt;/span&gt; &lt;span class="na"&gt;parallel=&lt;/span&gt;&lt;span class="s"&gt;"methods"&lt;/span&gt; &lt;span class="na"&gt;thread-count=&lt;/span&gt;&lt;span class="s"&gt;"10"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2. Configure&lt;/strong&gt; pom.xml &lt;strong&gt;for Dynamic Overrides&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Allow developers and CI pipelines to override execution settings without altering XML files by adding these lines inside the  block of the maven-surefire-plugin:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;parallel&amp;gt;&lt;/span&gt;${parallel}&lt;span class="nt"&gt;&amp;lt;/parallel&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;threadCount&amp;gt;&lt;/span&gt;${threadCount}&lt;span class="nt"&gt;&amp;lt;/threadCount&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;3. Execution Commands&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Default Run:&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   mvn clean &lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="nt"&gt;-P&lt;/span&gt; runTestNGTests
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Override Thread Count Dynamically:&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   mvn clean &lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="nt"&gt;-P&lt;/span&gt; runTestNGTests &lt;span class="nt"&gt;-DthreadCount&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;15
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Full Parallel Execution (Match CPU Core Count):
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   mvn clean &lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="nt"&gt;-P&lt;/span&gt; runTestNGTests &lt;span class="nt"&gt;-Dparallel&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;methods &lt;span class="nt"&gt;-DthreadCount&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;24
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Strategy 2: Custom Allocator &amp;amp; Run Manager (For Excel-Driven Suites)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If your framework relies on an Excel-driven Run Manager to parse keyword flows and data sheets dynamically, parallelism is managed via a custom ExecutorService fixed thread pool.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Execution Command&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;mvn clean &lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="nt"&gt;-P&lt;/span&gt; runAllocator
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;How it works:&lt;/strong&gt; The allocator reads active test rows (Execute=Yes), dynamically assigns thread pools based on target thread properties, and dispatches concurrent runs.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Comparison: Allocator (Run Manager) vs. Native TestNG&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Allocator (Run Manager)&lt;/th&gt;
&lt;th&gt;TestNG Native&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Entry Point&lt;/td&gt;
&lt;td&gt;allocator.Allocator.main() via Maven Exec Plugin&lt;/td&gt;
&lt;td&gt;maven-surefire-plugin executing testng.xml&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Test Selection&lt;/td&gt;
&lt;td&gt;Reads Excel sheets via central properties&lt;/td&gt;
&lt;td&gt;Reads testng_regression.xml test classes/methods&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Thread Management&lt;/td&gt;
&lt;td&gt;Java ExecutorService (FixedThreadPool)&lt;/td&gt;
&lt;td&gt;TestNG internal thread pool (parallel + thread-count)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Command&lt;/td&gt;
&lt;td&gt;mvn clean test -P runAllocator&lt;/td&gt;
&lt;td&gt;mvn clean test -P runTestNGTests&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pros&lt;/td&gt;
&lt;td&gt;Pure data-driven control; multi-sheet aggregation; exact instance control&lt;/td&gt;
&lt;td&gt;Lighter weight; no Excel dependency; native TestNG integration&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cons&lt;/td&gt;
&lt;td&gt;Requires property tuning; multi-sheet parsing requires custom code&lt;/td&gt;
&lt;td&gt;Limited to test methods/classes; lacks Excel data loop control&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Which Approach Should You Choose?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Choose Native TestNG if you want:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Simple, code-first test execution.&lt;/li&gt;
&lt;li&gt;Faster execution loops without file parsing overhead.&lt;/li&gt;
&lt;li&gt;Standardized parallel="methods" or parallel="classes" handling.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Choose Allocator + Run Manager if you want:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Multi-sheet Excel data-driven scheduling.&lt;/li&gt;
&lt;li&gt;Fine-grained control over test iteration instances based on data rows.&lt;/li&gt;
&lt;li&gt;Integration with existing property-driven suite configs.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>java</category>
      <category>testing</category>
      <category>testng</category>
      <category>devops</category>
    </item>
  </channel>
</rss>
