<?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: Zameer Fouzan</title>
    <description>The latest articles on DEV Community by Zameer Fouzan (@zmrfzn).</description>
    <link>https://dev.to/zmrfzn</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.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F438514%2F574094e4-f1be-4786-a7a7-3b7792879b91.jpeg</url>
      <title>DEV Community: Zameer Fouzan</title>
      <link>https://dev.to/zmrfzn</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/zmrfzn"/>
    <language>en</language>
    <item>
      <title>Monitoring PM2 in production</title>
      <dc:creator>Zameer Fouzan</dc:creator>
      <pubDate>Fri, 01 Nov 2024 13:30:00 +0000</pubDate>
      <link>https://dev.to/newrelic/monitoring-pm2-in-production-eak</link>
      <guid>https://dev.to/newrelic/monitoring-pm2-in-production-eak</guid>
      <description>&lt;p&gt;In large-scale Node.js production environments, monitoring multiple applications can become challenging. The New Relic APM agent for Node.js helps capture logs, traces, and in-depth performance metrics from individual applications. But what about the overall health and resource consumption of all Node.js processes themselves, and critical process-level metrics like CPU and memory usage?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://pm2.io/" rel="noopener noreferrer"&gt;PM2&lt;/a&gt; is a popular process manager for Node.js applications, designed to simplify deployment and ensure reliability. It provides robust features, including automatic application restarts, load balancing, and monitoring capabilities, making it an essential tool for managing production-grade Node.js environments.&lt;/p&gt;

&lt;p&gt;PM2's monitoring API provides easy access to detailed telemetry data, like active processes, and resource consumption, along with additional helpful details like Git commit, active branch, Node.js version, and entry script. These insights can be invaluable when troubleshooting and identifying the root cause of performance issues.&lt;/p&gt;

&lt;h2&gt;
  
  
  What can I monitor from PM2?
&lt;/h2&gt;

&lt;p&gt;PM2 API provides generous information that can be captured. Ideally, all the metrics that can be visualized using the PM2’s monitor API on your host can be captured and exported to New Relic.&lt;/p&gt;

&lt;p&gt;&lt;a href="PM2%20monitor%20Live%20View%20Gif"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftc1bddnts2vuoylzd9wy.gif" alt="PM2 monitor view" width="800" height="488"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this blog post, we’ll focus on several key metrics that are important for monitoring PM2. &lt;/p&gt;

&lt;h3&gt;
  
  
  Application identity and host information
&lt;/h3&gt;

&lt;p&gt;Application details, including the host ID, for applications currently running as part of PM2, make it easy to pinpoint which instance is associated with each application. This is especially helpful in environments with multiple services, as you can easily identify specific processes and manage them effectively.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Metrics&lt;/strong&gt;: &lt;code&gt;appName&lt;/code&gt;, &lt;code&gt;hostId&lt;/code&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Resource metrics
&lt;/h3&gt;

&lt;p&gt;Metrics from each process, like CPU and memory usage, provide essential insights into the resource consumption and performance of your application. With &lt;code&gt;axm_monitor&lt;/code&gt;, you can also track HTTP latency, active requests, and event loop latency to monitor responsiveness in real time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Metrics&lt;/strong&gt;: &lt;code&gt;monit.cpu&lt;/code&gt;, &lt;code&gt;monit.memory&lt;/code&gt;, &lt;code&gt;axm_monitor&lt;/code&gt; (for example, HTTP latency, active requests, event loop latency)&lt;/p&gt;

&lt;h3&gt;
  
  
  Process information
&lt;/h3&gt;

&lt;p&gt;Process-level details such as process ID, status, and uptime help monitor each application's health and lifecycle. Tracking when a process was last updated or started helps you identify issues like frequent restarts or unusually high uptime.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Metrics&lt;/strong&gt;: &lt;code&gt;pid&lt;/code&gt;, &lt;code&gt;pm2_env.status&lt;/code&gt;, &lt;code&gt;pm2_env.pm_uptime&lt;/code&gt;, &lt;code&gt;pm2_env.created_at&lt;/code&gt;, &lt;code&gt;timestamp&lt;/code&gt;, &lt;code&gt;pm2_env.update_time&lt;/code&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Debug pointers: Logs, errors, restarts, and crashes
&lt;/h3&gt;

&lt;p&gt;Debugging details like log paths, error codes, and restart counts allow quick troubleshooting. Having both the standard output and error logs, alongside information on restarts, helps pinpoint stability issues or potential bugs within applications.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Metrics&lt;/strong&gt;: &lt;code&gt;pm2_env.error_file&lt;/code&gt;, &lt;code&gt;pm2_env.out_file&lt;/code&gt;, &lt;code&gt;pm2_env.exit_code&lt;/code&gt;, &lt;code&gt;pm2_env.unstable_restarts&lt;/code&gt;, &lt;code&gt;pm2_env.restart_time&lt;/code&gt;, &lt;code&gt;pm2_env.status&lt;/code&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Version control and deployment insights
&lt;/h3&gt;

&lt;p&gt;Deployment details, including Git commit history and Node.js version, make it easy to track deployed versions. PM2 captures branch, revision, and commit data, giving clear visibility into which version is running and simplifying root cause analysis after code changes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Metrics&lt;/strong&gt;: &lt;code&gt;pm2_env.node_version&lt;/code&gt;, &lt;code&gt;pm2_env.version&lt;/code&gt;, &lt;code&gt;pm2_env.versioning.*&lt;/code&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  How do I capture insights from PM2?
&lt;/h2&gt;

&lt;p&gt;Currently, there’s no direct integration between New Relic agents and PM2, but we can &lt;a href="https://docs.newrelic.com/docs/infrastructure/host-integrations/host-integrations-list/flex-integration-tool-build-your-own-integration/" rel="noopener noreferrer"&gt;build our own integration using Flex&lt;/a&gt;. Flex is an easy and agentless option to build integrations between your data source and New Relic. &lt;/p&gt;

&lt;h3&gt;
  
  
  Configuration
&lt;/h3&gt;

&lt;p&gt;Create a new configuration file named &lt;code&gt;pm2_monit.yml&lt;/code&gt;, then add the following configuration.&lt;/p&gt;

&lt;p&gt;This configuration is simply calling the &lt;code&gt;PM2 jlist&lt;/code&gt; API and sanitizing the JSON output data using &lt;a href="https://jqlang.github.io/jq/" rel="noopener noreferrer"&gt;JQ&lt;/a&gt;. JQ is a command line utility that's used to process, query, and transform JSON data. Flex has support for JQ built in, which makes sanitizing and transforming the data easier. &lt;/p&gt;

&lt;p&gt;As the original output can also include additional, sensitive data, which could be unnecessary, it can be easily dropped with the JQ and &lt;code&gt;remove_keys&lt;/code&gt; functions of Flex.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Basic configuration&lt;/strong&gt;&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;integrations&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nri-flex&lt;/span&gt;
    &lt;span class="na"&gt;timeout&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;60s&lt;/span&gt;
    &lt;span class="na"&gt;interval&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;30s&lt;/span&gt;
    &lt;span class="na"&gt;config&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;PM2status&lt;/span&gt;
      &lt;span class="na"&gt;apis&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;PM2Process&lt;/span&gt;
          &lt;span class="na"&gt;event_type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;PM2Sample&lt;/span&gt;
          &lt;span class="na"&gt;commands&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# Linux specific command&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;USER=ubuntu; su - $USER bash -c "pm2 jlist"&lt;/span&gt;
        &lt;span class="c1"&gt;# command for windows/mac&lt;/span&gt;
        &lt;span class="c1"&gt;# - run: npx pm2 jlist&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;JSON transformation with JQ&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
At this stage, the output from &lt;code&gt;pm2 jlist&lt;/code&gt; is a raw JSON. Let’s sanitize and transform the JSON output with JQ to retain only the necessary fields, using the &lt;code&gt;remove_keys&lt;/code&gt; and &lt;code&gt;rename_keys&lt;/code&gt; functions to streamline the data further:&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="c1"&gt;# Sanitize and transform the JSON output to required format   &lt;/span&gt;
          &lt;span class="na"&gt;jq&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;&amp;gt;-&lt;/span&gt; 
              &lt;span class="s"&gt;[] | { &lt;/span&gt;
                  &lt;span class="s"&gt;pid,&lt;/span&gt;
                  &lt;span class="s"&gt;name,&lt;/span&gt;
                  &lt;span class="s"&gt;pm2_env: {&lt;/span&gt;
                    &lt;span class="s"&gt;script: .pm2_env.script?,&lt;/span&gt;
                    &lt;span class="s"&gt;out_file: .pm2_env.out_file?,&lt;/span&gt;
                    &lt;span class="s"&gt;error_file: .pm2_env.error_file?,&lt;/span&gt;
                    &lt;span class="s"&gt;watch: .pm2_env.watch?,&lt;/span&gt;
                    &lt;span class="s"&gt;exit_code: .pm2_env.exit_code?,&lt;/span&gt;
                    &lt;span class="s"&gt;node_version: .pm2_env.node_version?,&lt;/span&gt;
                    &lt;span class="s"&gt;versioning: .pm2_env.versioning?,&lt;/span&gt;
                    &lt;span class="s"&gt;version: .pm2_env.version?,&lt;/span&gt;
                    &lt;span class="s"&gt;unstable_restarts: .pm2_env.unstable_restarts?,&lt;/span&gt;
                    &lt;span class="s"&gt;restart_time: .pm2_env.restart_time?,&lt;/span&gt;
                    &lt;span class="s"&gt;created_at: .pm2_env.created_at?,&lt;/span&gt;
                    &lt;span class="s"&gt;pm_uptime: .pm2_env.pm_uptime?,&lt;/span&gt;
                    &lt;span class="s"&gt;status: .pm2_env.status?,&lt;/span&gt;
                    &lt;span class="s"&gt;unique_id: .pm2_env.unique_id?&lt;/span&gt;
                  &lt;span class="s"&gt;},&lt;/span&gt;
                  &lt;span class="s"&gt;pm_id,&lt;/span&gt;
                  &lt;span class="s"&gt;monit&lt;/span&gt;
                &lt;span class="s"&gt;} | del(.pm2_env.versioning.remotes) &lt;/span&gt;
          &lt;span class="na"&gt;remove_keys&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;pm_id&lt;/span&gt;
          &lt;span class="na"&gt;rename_keys&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;appName&lt;/span&gt;
          &lt;span class="na"&gt;custom_attributes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;hostId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;localhost&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;strong&gt;Switching user context&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;command&lt;/code&gt; block in Flex configuration switches the user context to where PM2 processes are running, which is necessary, as Flex runs under the root (sudo) user by default. If you're running PM2 locally on Windows or Mac, use &lt;code&gt;npx pm2 jlist&lt;/code&gt; command block instead of the Linux-specific command.&lt;/p&gt;

&lt;p&gt;The complete configuration should look like this:&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;integrations&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nri-flex&lt;/span&gt;
    &lt;span class="na"&gt;timeout&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;60s&lt;/span&gt;
    &lt;span class="na"&gt;interval&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;30s&lt;/span&gt;
    &lt;span class="na"&gt;config&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;PM2status&lt;/span&gt;
      &lt;span class="na"&gt;apis&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;PM2Process&lt;/span&gt;
          &lt;span class="na"&gt;event_type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;PM2Sample&lt;/span&gt;
          &lt;span class="na"&gt;commands&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;USER=ubuntu; su - $USER bash -c "pm2 jlist"&lt;/span&gt;
        &lt;span class="c1"&gt;# - run: npx pm2 jlist&lt;/span&gt;
       &lt;span class="c1"&gt;# Sanitize and transform the JSON output to required format   &lt;/span&gt;
          &lt;span class="na"&gt;jq&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;&amp;gt;-&lt;/span&gt; 
              &lt;span class="s"&gt;[] | { &lt;/span&gt;
                  &lt;span class="s"&gt;pid,&lt;/span&gt;
                  &lt;span class="s"&gt;name,&lt;/span&gt;
                  &lt;span class="s"&gt;pm2_env: {&lt;/span&gt;
                    &lt;span class="s"&gt;script: .pm2_env.script?,&lt;/span&gt;
                    &lt;span class="s"&gt;out_file: .pm2_env.out_file?,&lt;/span&gt;
                    &lt;span class="s"&gt;error_file: .pm2_env.error_file?,&lt;/span&gt;
                    &lt;span class="s"&gt;watch: .pm2_env.watch?,&lt;/span&gt;
                    &lt;span class="s"&gt;exit_code: .pm2_env.exit_code?,&lt;/span&gt;
                    &lt;span class="s"&gt;node_version: .pm2_env.node_version?,&lt;/span&gt;
                    &lt;span class="s"&gt;versioning: .pm2_env.versioning?,&lt;/span&gt;
                    &lt;span class="s"&gt;version: .pm2_env.version?,&lt;/span&gt;
                    &lt;span class="s"&gt;unstable_restarts: .pm2_env.unstable_restarts?,&lt;/span&gt;
                    &lt;span class="s"&gt;restart_time: .pm2_env.restart_time?,&lt;/span&gt;
                    &lt;span class="s"&gt;created_at: .pm2_env.created_at?,&lt;/span&gt;
                    &lt;span class="s"&gt;pm_uptime: .pm2_env.pm_uptime?,&lt;/span&gt;
                    &lt;span class="s"&gt;status: .pm2_env.status?,&lt;/span&gt;
                    &lt;span class="s"&gt;unique_id: .pm2_env.unique_id?&lt;/span&gt;
                  &lt;span class="s"&gt;},&lt;/span&gt;
                  &lt;span class="s"&gt;pm_id,&lt;/span&gt;
                  &lt;span class="s"&gt;monit&lt;/span&gt;
                &lt;span class="s"&gt;} | del(.pm2_env.versioning.remotes) &lt;/span&gt;
          &lt;span class="na"&gt;remove_keys&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;pm_id&lt;/span&gt;
          &lt;span class="na"&gt;rename_keys&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;appName&lt;/span&gt;
          &lt;span class="na"&gt;custom_attributes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;hostId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;localhost&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Validation
&lt;/h3&gt;

&lt;p&gt;Once the configuration is ready, we can validate it with Flex's debug mode. If you’re using the standalone binary mode of Flex, use the following command to test your configuration:&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="nb"&gt;sudo&lt;/span&gt; ./nri-flex &lt;span class="nt"&gt;-config_file&lt;/span&gt; pm2_monit.yml &lt;span class="nt"&gt;--pretty&lt;/span&gt; &lt;span class="nt"&gt;--verbose&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To test the configuration using New Relic's infrastructure agent Flex integration, execute the following command:&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="nb"&gt;sudo&lt;/span&gt; /var/db/newrelic-infra/newrelic-integrations/bin/nri-flex &lt;span class="nt"&gt;--verbose&lt;/span&gt; &lt;span class="nt"&gt;--pretty&lt;/span&gt; &lt;span class="nt"&gt;--config_file&lt;/span&gt; ./pm2_monit.yml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Read more about Flex testing and debugging in &lt;a href="https://docs.newrelic.com/docs/infrastructure/host-integrations/host-integrations-list/flex-integration-tool-build-your-own-integration" rel="noopener noreferrer"&gt;this&lt;/a&gt; &lt;a href="https://docs.newrelic.com/docs/infrastructure/host-integrations/host-integrations-list/flex-integration-tool-build-your-own-integration/" rel="noopener noreferrer"&gt;document&lt;/a&gt;. &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Upon successful execution of your configuration, you should expect an output similar to the one displayed below, without the Flex debug output:&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;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"com.newrelic.nri-flex"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"protocol_version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"3"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"integration_version"&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.15.2"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"data"&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;"metrics"&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;"appName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"expressApp-otel"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="nl"&gt;"event_type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"PM2Sample"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="nl"&gt;"hostId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ec2-webserver"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="nl"&gt;"integration_name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"com.newrelic.nri-flex"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="nl"&gt;"integration_version"&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.15.2"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="nl"&gt;"monit.cpu"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="nl"&gt;"monit.memory"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;46641152&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="nl"&gt;"pid"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1304&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="nl"&gt;"pm2_env.created_at"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1728589283166&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="nl"&gt;"pm2_env.error_file"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"logs/error.log"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="nl"&gt;"pm2_env.exit_code"&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="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="nl"&gt;"pm2_env.node_version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"16.17.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="nl"&gt;"pm2_env.out_file"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"logs/app.log"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="nl"&gt;"pm2_env.pm_uptime"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1729058602005&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="nl"&gt;"pm2_env.restart_time"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="nl"&gt;"pm2_env.script"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"./server.js"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="nl"&gt;"pm2_env.status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"online"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="nl"&gt;"pm2_env.unique_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"858b1616-bfd7-41c0-8861-de5babcfe106"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="nl"&gt;"pm2_env.unstable_restarts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="nl"&gt;"pm2_env.version"&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.0.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="nl"&gt;"pm2_env.versioning.ahead"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"false"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="nl"&gt;"pm2_env.versioning.branch"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"master"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="nl"&gt;"pm2_env.versioning.branch_exists_on_remote"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"true"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="nl"&gt;"pm2_env.versioning.comment"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"update: Added query param to fetch mapped category in response&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;fix: fallback envvar value&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&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;"pm2_env.versioning.next_rev"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\u&lt;/span&gt;&lt;span class="s2"&gt;003cnil&lt;/span&gt;&lt;span class="se"&gt;\u&lt;/span&gt;&lt;span class="s2"&gt;003e"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="nl"&gt;"pm2_env.versioning.prev_rev"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"f195db41aec0592142ef478c424ec1722c7318a4"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="nl"&gt;"pm2_env.versioning.remote"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"origin"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="nl"&gt;"pm2_env.versioning.repo_path"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/home/ubuntu/workspace/node-express-app"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="nl"&gt;"pm2_env.versioning.revision"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"8d31b501b38229171be428db1dc7e2b412694116"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="nl"&gt;"pm2_env.versioning.type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"git"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="nl"&gt;"pm2_env.versioning.unstaged"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"true"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="nl"&gt;"pm2_env.versioning.update_time"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2024-10-16T06:03:22.325Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="nl"&gt;"pm2_env.versioning.url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"git@github.com:zmrfzn/node-express-app.git"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="nl"&gt;"pm2_env.watch"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"false"&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;"event_type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"flexStatusSample"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="nl"&gt;"flex.Hostname"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ip-172-31-30-74"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="nl"&gt;"flex.IntegrationVersion"&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.15.2"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="nl"&gt;"flex.counter.ConfigsProcessed"&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="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="nl"&gt;"flex.counter.EventCount"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="nl"&gt;"flex.counter.EventDropCount"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="nl"&gt;"flex.counter.PM2Sample"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="nl"&gt;"flex.time.elapsedMs"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;263&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="nl"&gt;"flex.time.endMs"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1730119876587&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="nl"&gt;"flex.time.startMs"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1730119876324&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;"inventory"&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;"events"&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="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;Here’s the simplified data that we’re capturing with Flex from the PM2 processes after transformations with JQ:&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;"appName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"expressApp-otel"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"hostId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ec2-webserver"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"integration_name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"com.newrelic.nri-flex"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"integration_version"&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.15.2"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"monit.cpu"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"monit.memory"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;80068608&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"pid"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1310&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"pm2_env.created_at"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1728589676754&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"pm2_env.error_file"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"logs/error.log"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"pm2_env.exit_code"&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="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"pm2_env.node_version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"16.17.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"pm2_env.out_file"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"logs/app.log"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"pm2_env.pm_uptime"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1729058602010&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"pm2_env.restart_time"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"pm2_env.script"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"./server.js"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"pm2_env.status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"online"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"pm2_env.unique_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"83e7e3d5-17d7-41b0-87fe-5201148c826a"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"pm2_env.unstable_restarts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"pm2_env.version"&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.0.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"pm2_env.versioning.ahead"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"false"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"pm2_env.versioning.branch"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"otel"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"pm2_env.versioning.branch_exists_on_remote"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"true"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"pm2_env.versioning.comment"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"chore: update OTEL SDK packages to latest"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"pm2_env.versioning.next_rev"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;nil&amp;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;"pm2_env.versioning.prev_rev"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"f9fb795ec5ec07d24bd858b55287cbffda44b365"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"pm2_env.versioning.remote"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"origin"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"pm2_env.versioning.repo_path"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/home/ubuntu/workspace/otel/node-express-app"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"pm2_env.versioning.revision"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"aad10ae5445469718cd2da5041d140e39be8ef78"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"pm2_env.versioning.type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"git"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"pm2_env.versioning.unstaged"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"true"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"pm2_env.versioning.update_time"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2024-10-16T06:03:22.319Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"pm2_env.versioning.url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"git@github.com:zmrfzn/node-express-app.git"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"pm2_env.watch"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"false"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"timestamp"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1729513381684&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;h3&gt;
  
  
  Verification
&lt;/h3&gt;

&lt;p&gt;Flex sends all processed data via the New Relic &lt;a href="https://docs.newrelic.com/docs/data-apis/ingest-apis/event-api/introduction-event-api/" rel="noopener noreferrer"&gt;events API&lt;/a&gt;, which allows efficient handling of various types of event data. In this configuration, we’re naming our event PM2Sample, which helps clearly identify and differentiate it from other events in the system. &lt;/p&gt;

&lt;p&gt;All the data associated with this event can be easily queried using New Relic Query Language (NRQL) on this table itself.&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;FROM&lt;/span&gt; &lt;span class="n"&gt;PM2Sample&lt;/span&gt; &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;SINCE&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt; &lt;span class="n"&gt;MINUTES&lt;/span&gt; &lt;span class="n"&gt;AGO&lt;/span&gt; &lt;span class="k"&gt;LIMIT&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7x27blmw8vxkdlo315ch.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7x27blmw8vxkdlo315ch.png" alt="PM2 Verify" width="800" height="270"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Visualization
&lt;/h3&gt;

&lt;p&gt;Once the data is accessible on the New Relic platform, you can easily query specific metrics relevant to your needs and create tailored visualizations. This allows you to analyze performance trends, monitor process health, and also gather versioning details for the individual applications currently running with PM2.&lt;/p&gt;

&lt;p&gt;With customizable visualizations, you can present the data in a way that best suits your objectives and enhances your understanding of complex information. Additionally, these metrics can also be used to set up personalized alerts. &lt;/p&gt;

&lt;p&gt;Let’s dive into the &lt;code&gt;PM2Sample&lt;/code&gt;. Now we can effortlessly query the average CPU utilization from individual applications:&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;FROM&lt;/span&gt; &lt;span class="n"&gt;PM2Sample&lt;/span&gt; &lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="n"&gt;average&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;monit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cpu&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="s1"&gt;'CPU Usage %'&lt;/span&gt; &lt;span class="n"&gt;facet&lt;/span&gt; &lt;span class="n"&gt;appName&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fstpl3rye80xcscminook.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fstpl3rye80xcscminook.png" alt="PM2Sample CPU Utilization by application" width="800" height="394"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can also capture time series data, showcasing how memory consumption compares to CPU usage over time for all your applications under PM2, which are outside of your APM metrics.&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;FROM&lt;/span&gt; &lt;span class="n"&gt;PM2Sample&lt;/span&gt; &lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="n"&gt;average&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;monit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;memory&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;1048576&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="s1"&gt;'Avg Memory M/b'&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;average&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;monit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cpu&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="s1"&gt;'Avg CPU%'&lt;/span&gt; &lt;span class="n"&gt;EXTRAPOLATE&lt;/span&gt; &lt;span class="n"&gt;TIMESERIES&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk7ea8dqgad6r8mqkafhf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk7ea8dqgad6r8mqkafhf.png" alt="PM2Sample memory vs CPU timeseries" width="800" height="260"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;NRQL provides an easy way to query and visualize metrics, with powerful dashboard capabilities. Below is an example of a custom dashboard setup specifically designed for PM2 monitoring. It captures various metrics, including memory usage per application, CPU vs. memory for all active applications, and the latest app revision details of the active processes with PM2.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fftqd038sf53423l6hpi5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fftqd038sf53423l6hpi5.png" alt="Custom Pre-built Dashboard for PM2Sample" width="800" height="444"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Import this dashboard using the JSON from this location: &lt;a href="https://raw.githubusercontent.com/zmrfzn/nri-flex/refs/heads/master/examples/pm2-monitor-dashboard.json" rel="noopener noreferrer"&gt;PM2_monit_dashboard&lt;/a&gt;. Make sure to replace all the placeholder account Ids (&lt;code&gt;1234567)&lt;/code&gt; with your New Relic account ID before importing it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;PM2’s telemetry offers process-level insights, capturing essential metrics like CPU, memory, and application logs. Flex’s customizable integration with New Relic allows you to bring these data points into a single, unified view. This combination not only enhances visibility into application performance, but also simplifies root-cause analysis and proactive monitoring&lt;/p&gt;

&lt;h2&gt;
  
  
  What Next?
&lt;/h2&gt;

&lt;p&gt;For more information, visit the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://newrelic.com/blog/how-to-relic/what-is-flex" rel="noopener noreferrer"&gt;Build your own integrations with Flex&lt;/a&gt; [link: &lt;a href="https://newrelic.com/blog/how-to-relic/what-is-flex" rel="noopener noreferrer"&gt;https://newrelic.com/blog/how-to-relic/what-is-flex&lt;/a&gt;]
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://newrelic.com/blog/best-practices/nodejs-application-monitoring" rel="noopener noreferrer"&gt;Explore the New Relic APM agent for Node.js&lt;/a&gt;  [link: &lt;a href="https://newrelic.com/blog/best-practices/nodejs-application-monitoring" rel="noopener noreferrer"&gt;https://newrelic.com/blog/best-practices/nodejs-application-monitoring&lt;/a&gt;]&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>apm</category>
      <category>pm2</category>
      <category>extendnewrelic</category>
      <category>node</category>
    </item>
    <item>
      <title>Instrumenting AWS Lambda functions with OpenTelemetry SDKs</title>
      <dc:creator>Zameer Fouzan</dc:creator>
      <pubDate>Sat, 25 Nov 2023 12:30:00 +0000</pubDate>
      <link>https://dev.to/newrelic/instrumenting-aws-lambda-functions-with-opentelemetry-sdks-3614</link>
      <guid>https://dev.to/newrelic/instrumenting-aws-lambda-functions-with-opentelemetry-sdks-3614</guid>
      <description>&lt;p&gt;In this blog post, we'll explore how to use OpenTelemetry to instrument and monitor serverless applications running on AWS Lambda. We'll look at the Open Telemetry SDK, a component that simplifies the configuration and deployment of OpenTelemetry agents for Lambda functions.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;What is OpenTelemetry?&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;OpenTelemetry is an open-source project that provides a unified framework for collecting, processing, and exporting observability data from distributed systems. Observability data includes metrics, traces, and logs that help developers and operators understand the behavior and performance of their applications.&lt;/p&gt;

&lt;p&gt;OpenTelemetry supports multiple languages, platforms, and vendors, and offers a vendor-neutral API and SDK for instrumenting applications. It also provides a set of exporters that allow users to send their data to various backends, such as New Relic, Jaeger, Zipkin, AWS X-Ray, and many more.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Why use OpenTelemetry for serverless?&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Serverless applications are composed of ephemeral, event-driven functions that run on demand in response to triggers. This makes them scalable, cost-effective, and easy to deploy but also introduces new challenges for observability.&lt;/p&gt;

&lt;p&gt;For example, serverless functions have short lifespans and may not have access to persistent storage or network resources. This means traditional methods of collecting and exporting observability data, such as using agents or sidecars, may not work well for serverless functions. Additionally, serverless functions may have complex invocation patterns and dependencies, which make it difficult to trace the end-to-end execution flow and identify bottlenecks or errors.&lt;/p&gt;

&lt;p&gt;OpenTelemetry addresses these challenges by providing a lightweight and flexible solution for instrumenting serverless functions. With OpenTelemetry you can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Add minimal code changes to the functions to enable automatic instrumentation of common libraries and frameworks.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Configure the sampling rate and span attributes to control the amount and quality of trace data collected.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Choose from a variety of exporters to send their data to the backend of their choice.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Leverage the Open Telemetry Lambda Collector to simplify the deployment and configuration of OpenTelemetry agents for Lambda functions (not covered in this blog).&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We'll focus on the automatic instrumentation of the Lambda function using the OpenTelemetry libraries and SDKs for Node.js.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Pre-requisites&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;What do you need to follow this guide successfully?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;New Relic ingest license key. (Don’t have an account? Sign up for a &lt;a href="https://newrelic.com/signup" rel="noopener noreferrer"&gt;free New Relic account&lt;/a&gt;).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Basic knowledge of OpenTelemetry&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Libraries overview&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;These are the few key libraries and APIs that are essential in instrumenting the AWS Lambda function with the Node.js runtime.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;@opentelemetry/instrumentation-aws-lambda:&lt;/strong&gt; This module provides automatic instrumentation for the &lt;a href="https://docs.aws.amazon.com/lambda/latest/dg/nodejs-handler.html" rel="noopener noreferrer"&gt;&lt;strong&gt;AWS Lambda&lt;/strong&gt;&lt;/a&gt; module, and it's' included in the &lt;code&gt;@opentelemetry/auto-instrumentations-node&lt;/code&gt; bundle as a plug-in. This is necessary to instrument our AWS Lambda functions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;@opentelemetry/sdk-trace-node:&lt;/strong&gt; This module provides automated instrumentation and tracing for Node.js applications. It exposes an API called &lt;code&gt;NodeTracerProvider&lt;/code&gt;, which can be used with the &lt;code&gt;registerInstrumentations&lt;/code&gt; API from &lt;code&gt;@opentelemetry/instrumentation&lt;/code&gt; to load individual instrumentation plugins.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;OTLPTraceExporter:&lt;/strong&gt; This is a common API provided in the &lt;code&gt;@opentelemetry/exporter-trace-otlp-http&lt;/code&gt; and &lt;code&gt;@opentelemetry/exporter-trace-otlp-proto&lt;/code&gt; packages for exporting the collected telemetry to a collector or a choice of backend. We'll use New Relic's native &lt;a href="https://docs.newrelic.com/docs/more-integrations/open-source-telemetry-integrations/opentelemetry/get-started/opentelemetry-set-up-your-app/#review-settings" rel="noopener noreferrer"&gt;OpenTelemetry Protocol (OTLP) endpoint&lt;/a&gt; to export our telemetry data.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Instrumenting our serverless functions with OpenTelemetry&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;In this example, we're using the &lt;a href="https://www.serverless.com/" rel="noopener noreferrer"&gt;serverless framework&lt;/a&gt; to quickly set up the Lambda function along with an API gateway for the entry point. The lambda function is a simple &lt;a href="https://koajs.com/" rel="noopener noreferrer"&gt;Koa&lt;/a&gt; REST API with a few functional endpoints.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Step 1: Install packages&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;First, install all required packages for Node.js and Node runtime on the Lambda function. Copy the command below and run it in the root of your Node app&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; &lt;span class="nt"&gt;--save&lt;/span&gt; 
@opentelemetry/auto-instrumentations-node
@opentelemetry/sdk-trace-node
@opentelemetry/exporter-trace-otlp-proto
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;strong&gt;Step 2: Configure SDK&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Next, create a new file with the name &lt;code&gt;otel-wrapper.js&lt;/code&gt; in the &lt;em&gt;root&lt;/em&gt; or &lt;em&gt;src&lt;/em&gt; folder of your Function in the serverless framework project. Now, copy and paste the following code into the newly created file:&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="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;NodeTracerProvider&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@opentelemetry/sdk-trace-node&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="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;registerInstrumentations&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@opentelemetry/instrumentation&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="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;getNodeAutoInstrumentations&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@opentelemetry/auto-instrumentations-node&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="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;BatchSpanProcessor&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@opentelemetry/sdk-trace-base&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="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Resource&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@opentelemetry/resources&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="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;SemanticResourceAttributes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@opentelemetry/semantic-conventions&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="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="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@opentelemetry/exporter-trace-otlp-proto&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="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;CompositePropagator&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;W3CBaggagePropagator&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;W3CTraceContextPropagator&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@opentelemetry/core&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// For troubleshooting, set the log level to DiagLogLevel.DEBUG&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;diag&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;DiagConsoleLogger&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;DiagLogLevel&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&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="c1"&gt;// var logger = diag.createComponentLogger(DiagLogLevel.WARN);&lt;/span&gt;
&lt;span class="nx"&gt;diag&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setLogger&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;DiagConsoleLogger&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nx"&gt;DiagLogLevel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;WARN&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;COLLECTOR_STRING&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;OTEL_EXPORTER_OTLP_ENDPOINT&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/v1/traces`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="cm"&gt;/**
 * The `newRelicExporter` is an instance of OTLPTraceExporter
 * configured to send traces to New Relic's OTLP-compatible backend.
 * Make sure you have added your New Relic Ingest License to NR_LICENSE env-var
 */&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;newRelicExporter&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;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="nx"&gt;COLLECTOR_STRING&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&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;api-key&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;NR_LICENSE&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;provider&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;NodeTracerProvider&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;resource&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;Resource&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;SemanticResourceAttributes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;SERVICE_NAME&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;OTEL_SERVICE_NAME&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="nx"&gt;provider&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addSpanProcessor&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;BatchSpanProcessor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;newRelicExporter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="c1"&gt;//Optional BatchSpanProcessor Configurations&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// The maximum queue size. After the size is reached spans are dropped.&lt;/span&gt;
      &lt;span class="na"&gt;maxQueueSize&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="c1"&gt;// The maximum batch size of every export. It must be smaller or equal to maxQueueSize.&lt;/span&gt;
      &lt;span class="na"&gt;maxExportBatchSize&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="c1"&gt;// The interval between two consecutive exports&lt;/span&gt;
      &lt;span class="na"&gt;scheduledDelayMillis&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="c1"&gt;// How long the export can run before it is cancelled&lt;/span&gt;
      &lt;span class="na"&gt;exportTimeoutMillis&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;30000&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="nx"&gt;provider&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;register&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;propagator&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;CompositePropagator&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;propagators&lt;/span&gt;&lt;span class="p"&gt;:&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;W3CBaggagePropagator&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;W3CTraceContextPropagator&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="nf"&gt;registerInstrumentations&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;instrumentations&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="cm"&gt;/**
     * AutoInstrumentations META package for Node.js
     * contains bundled libraries for most libraries and frameworks
     */&lt;/span&gt;
    &lt;span class="nf"&gt;getNodeAutoInstrumentations&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@opentelemetry/instrumentation-fs&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="na"&gt;enabled&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ENABLE_FS_INSTRUMENTATION&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;requireParentSpan&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="p"&gt;},&lt;/span&gt;
      &lt;span class="c1"&gt;// Enable instrumentation for KOA framework&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@opentelemetry/instrumentation-koa&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="na"&gt;enabled&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="p"&gt;},&lt;/span&gt;
      &lt;span class="cm"&gt;/**
       * Enable instrumentation for AWS Lambda framework
       * Use the requestHook and responseHook to
       * add additional attributes to the traces
       */&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@opentelemetry/instrumentation-aws-lambda&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="na"&gt;enabled&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="na"&gt;disableAwsContextPropagation&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="na"&gt;requestHook&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="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt; &lt;span class="p"&gt;})&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;faas.name&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;functionName&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

          &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;requestContext&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;requestContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;http&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;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;faas.http.method&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;requestContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;method&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;faas.http.target&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;requestContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;
            &lt;span class="p"&gt;);&lt;/span&gt;
          &lt;span class="p"&gt;}&lt;/span&gt;

          &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;queryStringParameters&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;faas.http.queryParams&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;queryStringParameters&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="na"&gt;responseHook&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="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="p"&gt;})&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;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt; &lt;span class="k"&gt;instanceof&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;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;faas.error&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
          &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;res&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;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;faas.http.status_code&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;statusCode&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="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;Highlighting a few key things in the instrumentation in &lt;code&gt;otel-wrapper.js&lt;/code&gt;, under the configurations for &lt;code&gt;instrumentation-aws-lambda&lt;/code&gt;, the property &lt;code&gt;disableAwsContextPropagation&lt;/code&gt; has been set to &lt;code&gt;TRUE&lt;/code&gt;. This is required to skip &lt;strong&gt;Amazon X-Ray&lt;/strong&gt; parent extraction, which may conflict with the OpenTelemetry &lt;code&gt;traceparent&lt;/code&gt; headers.&lt;/p&gt;

&lt;p&gt;Most Node.js OpenTelemetry libraries offer two methods, &lt;code&gt;requestHook&lt;/code&gt; and &lt;code&gt;responseHook&lt;/code&gt;, for adding custom attributes to spans. These hooks can also be used to add conditional attributes to spans. Take note of the sample &lt;code&gt;responseHook&lt;/code&gt; below, which adds an extra attribute to the span called &lt;code&gt;faas.error&lt;/code&gt; containing the error message thrown by the code.&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="nx"&gt;responseHook&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="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="p"&gt;})&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;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt; &lt;span class="k"&gt;instanceof&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;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;faas.error&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
          &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;res&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;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;faas.http.status_code&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;statusCode&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;h3&gt;
  
  
  &lt;strong&gt;Step 3: Configure environment&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;In the code above in the &lt;code&gt;otel-wrapper.js&lt;/code&gt;, there are a couple of environment variables that are being referred to. We need to define these in our function configuration. Since we're using the serverless framework in this example, we need to add them to our &lt;code&gt;serverless.yml&lt;/code&gt; configuration file.&lt;/p&gt;

&lt;p&gt;Most importantly, to start our function with the OpenTelemetry instrumentations, we need to launch our function a little differently. Notice the &lt;code&gt;NODE_OPTIONS&lt;/code&gt; environment variable in the list of all the defined variables. We're setting the argument to use our &lt;code&gt;otel-wrapper.js&lt;/code&gt; as the module to be initialized to invoke our function.&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;functions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;api&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;handler&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;index.handler&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;NODE_OPTIONS&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--require&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;./otel-wrapper"&lt;/span&gt;
      &lt;span class="na"&gt;OTEL_SERVICE_NAME&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Node-Lambda-Otel-SDK"&lt;/span&gt;
            &lt;span class="c1"&gt;# External API for Distributed traces&lt;/span&gt;
      &lt;span class="na"&gt;EXPRESS_OTEL_API_ENDPOINT&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http://3.230.230.121/v3/api"&lt;/span&gt;
            &lt;span class="c1"&gt;# License key stored in SSM parameter.&lt;/span&gt;
      &lt;span class="na"&gt;NR_LICENSE&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${ssm:/nr_experiments_ingest_key}&lt;/span&gt;
      &lt;span class="na"&gt;OTEL_EXPORTER_OTLP_ENDPOINT&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;https://otlp.nr-data.net:4317&lt;/span&gt;
            &lt;span class="s"&gt;# Disabling FS instrumentation to reduce noise&lt;/span&gt;
      &lt;span class="na"&gt;ENABLE_FS_INSTRUMENTATION&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;strong&gt;Step 4: Explore data&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Once your function is deployed, it's time to explore the telemetry data from the Lambda in the New Relic platform.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffrcszhm0q1wvssnohl8g.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffrcszhm0q1wvssnohl8g.png" title="Lambda requests metrics from Spans" alt="Lambda requests metrics from Spans"&gt;&lt;/a&gt;&lt;em&gt;Lambda requests metrics from Spans&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6nia9dq60n5ubvg5vlvm.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6nia9dq60n5ubvg5vlvm.jpeg" title="Attributes from Distributed Traces" alt="Attributes from Distributed Traces"&gt;&lt;/a&gt;&lt;em&gt;Attributes from Distributed Traces&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Look at the highlighted attributes in the screenshot above: &lt;code&gt;faas.http.method&lt;/code&gt;, &lt;a href="http://faas.http.target" rel="noopener noreferrer"&gt;&lt;code&gt;faas.http.target&lt;/code&gt;&lt;/a&gt;, and &lt;a href="http://faas.name" rel="noopener noreferrer"&gt;&lt;code&gt;faas.name&lt;/code&gt;&lt;/a&gt;. These attributes were added by the &lt;code&gt;requestHook&lt;/code&gt; method, but not all the attributes are present in this trace; that's because there are conditional attributes depending on the incoming request.&lt;/p&gt;

&lt;p&gt;Our Lambda has two different endpoints which are bridged via API gateway, &lt;code&gt;/path&lt;/code&gt; and &lt;code&gt;/weather&lt;/code&gt;. Let's take a look at the traces from the second endpoint, which is &lt;code&gt;/weather?location=bangalore&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;/weather&lt;/code&gt; endpoint in the Lambda function calls an external entity that's also instrumented with OpenTelemetry. As a result, the distributed traces capture both the entities and the New Relic platform automatically generates a service map that shows the complete journey of the request.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://fast.wistia.net/embed/iframe/4wvq9ihxxx" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8xfoap4kybjtvty17bot.jpg" alt="Lambda distributed traces and service map"&gt;&lt;/a&gt;&lt;em&gt;Lambda distributed traces and service map&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;In this specific trace, the additional attribute &lt;code&gt;faas.http.queryParams&lt;/code&gt; is now visible in the list of attributes. Refer to the screenshot below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzcx26rrt25lmtyvlbont.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzcx26rrt25lmtyvlbont.jpg" title="Conditional attributes from requestHook" alt="Conditional attributes from requestHook"&gt;&lt;/a&gt;&lt;em&gt;Conditional attributes from requestHook&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;When exploring the spans in the New Relic UI, you can also check the attributes of the called entity from the distributed traces screen.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy43aq3ru8b6mqbzi57y9.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy43aq3ru8b6mqbzi57y9.jpg" title="Attributes from the called OTEL entity" alt="Attributes from the called OTEL entity"&gt;&lt;/a&gt;&lt;em&gt;Attributes from the called OTEL entity&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Conclusion&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;We've seen how OpenTelemetry can help us instrument and monitor serverless applications running on AWS Lambda. Instrumenting AWS Lambda functions with OpenTelemetry SDKs and exporting traces to New Relic can greatly enhance the observability of serverless applications. By following the steps outlined in this blog post, you can gain valuable insights into the performance, behavior, and dependencies of your Lambda functions. By exporting traces from OpenTelemetry to New Relic, we can take advantage of its powerful features, including service map visualizations, transactions, alerting, and much more.&lt;/p&gt;

&lt;blockquote&gt;
&lt;h2&gt;
  
  
  &lt;strong&gt;Next steps?&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Check out the source code for the application used in this blog on this GitHub repository: &lt;a href="https://github.com/zmrfzn/lambda-opentelemetry" rel="noopener noreferrer"&gt;github.com/zmrfzn/lambda-opentelemetry&lt;/a&gt;   &lt;/p&gt;

&lt;p&gt;If you're interested in learning more about OpenTelemetry and serverless observability, you can check out these resources:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://opentelemetry.io/docs/faas/" rel="noopener noreferrer"&gt;OpenTelemetry Functions as a Service&lt;/a&gt; &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/open-telemetry/opentelemetry-lambda" rel="noopener noreferrer"&gt;OpenTelemetry AWS Lambda repository&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://newrelic.com/blog/how-to-relic/opentelemetry-full-stack-javascript" rel="noopener noreferrer"&gt;Set up OpenTelemetry for full-stack JavaScript development&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

</description>
      <category>aws</category>
      <category>node</category>
      <category>opentelemetry</category>
      <category>serverless</category>
    </item>
    <item>
      <title>How to import Google Cloud logs without an agent</title>
      <dc:creator>Zameer Fouzan</dc:creator>
      <pubDate>Fri, 10 Mar 2023 06:30:21 +0000</pubDate>
      <link>https://dev.to/newrelic/how-to-import-google-cloud-logs-without-an-agent-14j4</link>
      <guid>https://dev.to/newrelic/how-to-import-google-cloud-logs-without-an-agent-14j4</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Push metrics, events, logs, and traces into New Relic without using an installed agent&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;With New Relic, you can collect, process, explore, query, and create alerts on your log data with scalability for your needs. While there are various options for getting logs into New Relic using an agent or one of the open source plug-ins like FluentBit, in the world of serverless architecture it’s challenging to rely on install-based agents to forward the logs. This is when an agentless approach is really helpful—You can import all your logs to New Relic without installing any agent!&lt;/p&gt;

&lt;p&gt;In this blog post, we’ll use all of the Google Cloud Platform native services to set up an agentless log forwarder. By default, Google Cloud services write logs to Google Cloud Logging (&lt;a href="https://cloud.google.com/products/operations" rel="noopener noreferrer"&gt;formerly Stackdriver&lt;/a&gt;). With the logs router, we’ll configure a sink with a destination that is a &lt;a href="https://cloud.google.com/pubsub/docs/overview" rel="noopener noreferrer"&gt;Cloud Pub/Sub&lt;/a&gt; topic. Whenever a log entry is generated and sent to this topic, it triggers a linked Cloud Function that is configured through the push-based subscription on the topic. The Cloud Function accepts all the logs and forwards them to New Relic through the New Relic Log API.&lt;/p&gt;

&lt;h2&gt;
  
  
  Before you begin
&lt;/h2&gt;

&lt;p&gt;Before you start this tutorial, make sure you have completed these prerequisites:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Enable the Google Cloud APIs for the following services:

&lt;ul&gt;
&lt;li&gt;Cloud Pub/Sub&lt;/li&gt;
&lt;li&gt;Cloud Functions&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Get your New Relic Insert API Key, which is different from your Ingest License Key.&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;During the configuration, make sure you do these tasks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Set up the correct inclusion and exclusion filters on the Google Cloud sink (also known as the logs router).&lt;/li&gt;
&lt;li&gt;Create runtime ENV variables to store your New Relic license key for security.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now you're ready to get started implementing and configuring the log forwarding from Google Cloud to New Relic.&lt;/p&gt;

&lt;h2&gt;
  
  
  Configure log forwarding from Google Cloud to New Relic
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Create a Cloud Function in Google Cloud. Enter basic details like this example:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4411ye8v44wc8itqhocb.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4411ye8v44wc8itqhocb.jpg" alt="Screenshot of Create function screen in Google Cloud Functions"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Select PubSub as the trigger for your Cloud Function, and create a new topic, as shown in this clip.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdvrocpgog4qgelb2genz.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdvrocpgog4qgelb2genz.gif" alt="Pub/Sub Topic Creation video"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;In the Cloud Function configuration, add a runtime environment variable named API_KEY. This is where you add your Insight Insert Key, which is important so our license is not exposed in our Cloud Function code. Generate this key by selecting the highlighted link on the right panel on the &lt;strong&gt;Account Settings &amp;gt; API Keys&lt;/strong&gt; page. &lt;/p&gt;

&lt;p&gt;Note: This Insight Insert Key is not the Insights License or Browser API Key on the &lt;strong&gt;Account Settings &amp;gt; API&lt;/strong&gt; page.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fa0cb3lonxfju3sm3ds89.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fa0cb3lonxfju3sm3ds89.gif" alt="Insight Insert Key selection video"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Now, copy this Node.js code sample and paste it into your Google Cloud Functions source.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const https = require("https");

exports.nrLogForwarder = (message, context) =&amp;gt; {
  const pubSubMessage = Buffer.from(message.data, "base64").toString();

  /* Setup the payload for New Relic with decoded message from Pub/Sub
      with "message", "logtype" as atrributes
  */
  const logPayload = {
    message: pubSubMessage,
    logtype: "gcpStackdriverLogs",
  };

  // configure the New Relic Log API http options for POST
  const options = {
    hostname: "log-api.newrelic.com",
    port: 443,
    path: "/log/v1",
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      "Api-Key":
        process.env
          .API_KEY /* ADD YOUR NR INSIGHTS INSERT LICENSE TO THE RUNTIME ENV VAR */,
    },
  };

  // HTTP Request with the configured options
  const req = https.request(options, (res) =&amp;gt; {
    console.log(`statusCode: ${res.statusCode}`);
    const body = [];

    res.on("data", (d) =&amp;gt; {
      body.push(d);
    });
    res.on("end", () =&amp;gt; {
      const resString = Buffer.concat(body).toString();
      console.log(`res: ${resString}`);
    });
  });

  req.on("error", (error) =&amp;gt; {
    console.error(error);
    callback(null, "error!");
  });

  // write the payload to our request
  req.write(JSON.stringify(logPayload));

  req.end();
};
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;In Google Cloud Functions, this code accepts the message from the Pub/Sub topic and then removes the Pub/Sub formatting. This is necessary because Pub/Sub might add additional information to the message, such as metadata or specific formatting, that’s not relevant in the context of logs. After the messages is decoded, the extracted logs are then forwarded through the &lt;a href="https://docs.newrelic.com/docs/logs/log-api/introduction-log-api?utm_source=devto&amp;amp;utm_medium=community&amp;amp;utm_campaign=global-fy23-q4-google-cloud-logs-agentless" rel="noopener noreferrer"&gt;New Relic Log API&lt;/a&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Ensure that the entry point is updated with the same exact name as the function name in the code. Then deploy the Google Cloud Function.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuup8s0csg1r1y8cw01xw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuup8s0csg1r1y8cw01xw.png" alt="Screenshot for Verify entry point"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Verify that the deployment of your function is successful. Hint: look for green check. This can take up to a few minutes after you selected &lt;strong&gt;Deploy&lt;/strong&gt; in the previous step.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flo3kpu0cmvkntyn9gxal.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flo3kpu0cmvkntyn9gxal.jpg" alt="Screenshot for deployment verification"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;After the function is deployed, switch to Google Cloud Logs Explorer under Logging. This example video shows how to create a sink here to forward the logs to the Pub/Sub topic that we created earlier.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fag11puq10em80nc3rr3r.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fag11puq10em80nc3rr3r.gif" alt="Video showing sink creation"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;On the next screen, let’s add details to the sink and configure it to forward the logs. When you fill in the sink details for NRLogForwarder, it should look like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvkpage8lt3o2nacrwxse.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvkpage8lt3o2nacrwxse.jpg" alt="Screenshot showing sink details"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;For the Sink Destination, choose &lt;strong&gt;Cloud Pub/Sub Topic&lt;/strong&gt; and then select the topic we created in step 2.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftkcghf7ofc7jh8ojreb1.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftkcghf7ofc7jh8ojreb1.gif" alt="video for sink destination configuration"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Optionally, you can add an inclusion filter. By default, the sink is configured to forward all the logs that appear in the Logs Explorer.&lt;/p&gt;

&lt;p&gt;You can configure it to forward only the logs for the services that you want to use with New Relic. This is also helpful when you have many services running on your Google Cloud.&lt;/p&gt;

&lt;p&gt;For example, you might have a microservices architecture, as &lt;a href="https://images.ctfassets.net/hspc7zpa5cvq/6szXHVXaOaCxZD2ORnZ0FY/73f0df8b11694cc3274d9483a99e7f11/Microservices_Diagram.jpg" rel="noopener noreferrer"&gt;in this sample&lt;/a&gt;, and you want to capture all the logs generated by these services. These services could be running on a serverless model, such as Cloud Functions or Cloud Run, or Google App Engine. These logs could provide valuable insights into your customers’ journeys on your platform, which can help you debug and identify any bottlenecks.&lt;/p&gt;

&lt;p&gt;In this example, I used &lt;a href="https://github.com/winstonjs/winston" rel="noopener noreferrer"&gt;Winston logger&lt;/a&gt; with my Node.js apps and wanted to forward all logs that are from the &lt;a href="https://github.com/googleapis/nodejs-logging-winston#readme" rel="noopener noreferrer"&gt;Google Cloud Winston Logger transport&lt;/a&gt; to New Relic. &lt;/p&gt;

&lt;p&gt;You’ll need to update your Project ID or project name and copy this snippet:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;log_name="projects/&amp;lt;Your Project ID or Project Name&amp;gt;/logs/winston_log"
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;Then add in your inclusion filter under Sink Configuration like this example:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6zwv7mqftni130q2m4wa.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6zwv7mqftni130q2m4wa.jpg" alt="Screenshot for inclusion filter"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Required step&lt;/strong&gt;: Add an exclusion filter.&lt;/p&gt;

&lt;p&gt;If you don’t complete this step, your sink, Cloud Functions, and Pub/Sub will go in an infinite loop of capturing and forwarding the logs, increasing your billing.&lt;/p&gt;

&lt;p&gt;Because Cloud Function execution also generates logs, you'll also need to add an exclusion filter under the sink configurations to exclude the logs generated by our Cloud Function, which forwards the log.    &lt;/p&gt;

&lt;p&gt;Copy this code:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;resource.labels.function_name="my-nr-logforwarder”
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;Add the code in your exclusion filter, replacing &lt;code&gt;my-nr-logforwarder&lt;/code&gt; with your function’s name, like this example:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxcc6glsn7zrr7clnharl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxcc6glsn7zrr7clnharl.png" alt="Screenshot for exclusion filter"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Now let's go to our &lt;a href="https://one.newrelic.com/?utm_source=devto&amp;amp;utm_medium=community&amp;amp;utm_campaign=global-fy23-q4-google-cloud-logs-agentless" rel="noopener noreferrer"&gt;New Relic&lt;/a&gt; account, verify the implementation, and explore the forwarded logs!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmwrgayjd6lvm1n2rwyd4.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmwrgayjd6lvm1n2rwyd4.gif" alt="Video for log verification in New Relic"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The messages are published as base64 by Pub/Sub, and the code we added in the Cloud Function is responsible for converting it into a readable string format. Using the inherent parsing mechanism on New Relic for the logs, the message is parsed as a JSON. This allows us to gain access to a range of attributes, such as &lt;code&gt;authorizationinfo&lt;/code&gt;, &lt;code&gt;emailid&lt;/code&gt;, and &lt;code&gt;method names&lt;/code&gt;, which we can then use to query the logs. With this data, we can efficiently monitor and track the progress of our services, making sure they are running optimally, and troubleshoot any issues that arise.&lt;/p&gt;

&lt;p&gt;You’ve seen that by setting up and using native services from Google Cloud, you can forward all your logs to New Relic. Then you can enjoy the benefits of enhanced log monitoring and explore other capabilities of New Relic.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Read the full blog post at New Relic: &lt;a href="https://dev.to?utm_source=devto&amp;amp;utm_medium=community&amp;amp;utm_campaign=global-fy23-q4-google-cloud-logs-agentless"&gt;How to import Google Cloud logs without an agent&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Not an existing New Relic user? &lt;a href="https://newrelic.com/signup?utm_source=devto&amp;amp;utm_medium=community&amp;amp;utm_campaign=global-fy23-q4-google-cloud-logs-agentless" rel="noopener noreferrer"&gt;Sign up for a free account&lt;/a&gt; to get started!&lt;/em&gt; 👨‍💻&lt;/p&gt;

</description>
      <category>agentless</category>
      <category>logs</category>
      <category>newrelic</category>
      <category>googlecloud</category>
    </item>
  </channel>
</rss>
