Administrar y monitorear los logs es fundamental para garantizar el buen funcionamiento de las aplicaciones y los servicios. Si bien es cierto que Azure es una plataforma potente para las aplicaciones, también es cierto que puede ser difícil de configurar. Por otro lado, depurar con los logs puede ser problemático. Los logs en Azure pueden estar dispersos en distintos lugares y pueden implicar problemas: almacenarlos puede resultar caro y su visibilidad podría no ser suficiente. 

En esta guía paso a paso aprenderás a capturar los logs de Azure a través de Azure Event Hubs y Azure Functions. Enviarás estos logs a New Relic con nuestra API de logs. Al completar esta guía, contarás con una integración de extremo a extremo entre Azure y New Relic, lo que te permitirá monitorear tus logs de manera fácil y eficaz. 

Prerrequisitos

Para seguir este ejemplo y que te resulte útil, verifica lo que necesitas:

Configurar los servicios de Azure

Para comenzar, vamos a configurar los servicios de Azure y el reenvío de logs a New Relic. Para ello, usarás estos tres servicios: 

  1. App Service para logs de diagnóstico
  2. Azure Event Hubs
  3. Azure Functions

En este ejemplo, enviarás logs para una aplicación Node alojada en App Service. Observa que de manera predeterminada los logs de diagnóstico —que en Azure se denominan registros de diagnóstico— para los servicios de aplicaciones están desactivados en Azure, por lo que tendrás que activarlos manualmente desde cada recurso. 

Configuración del servicio

1. Crea un namespace de Azure Event Hubs. Sigue la guía del video para aprender a crear uno.

2. Después de desplegar correctamente el namespace, crea un Event Hub de Azure con las instrucciones del siguiente video.

3. Luego, activa la configuración de diagnóstico para los recursos de Azure. La configuración de diagnóstico te permite especificar qué categorías de logs deseas capturar y dónde enviarlas. Tienes que seleccionar Event Hubs como el destino de los logs y proporcionar el nombre del namespace y el Event Hub que creaste en el paso uno. Sigue las instrucciones del video. 

4. Crea claves de acceso compartido, como se muestra en el siguiente video. Para que nuestra Azure Functions App se pueda comunicar con el Event Hub para escuchar los eventos entrantes, necesitamos crear una clave de acceso compartido que solo tenga permisos Listen (para escuchar).

5. Crea una Function App (aplicación de función) de Azure. Elige Code como modo de ejecución de la aplicación y Node.js como el stack de tiempo de ejecución, tal como se muestra en el siguiente video. Puedes elegir Windows o Linux para tus proyectos personales, pero el stack de tiempo de ejecución para este ejemplo debe ser Node.js porque la lógica de la función se escribirá en JavaScript.

6. Crear una “nueva clave de conexión” durante la creación de la función puede ocasionar problemas. Por ejemplo, puede ocurrir que una “clave de conexión” no se agregue a la Function App durante la creación de la función, o bien puede ser que se esté usando una convención de nombre que no es válida para el nombre de clave de acceso generado automáticamente, lo que puede ocasionar el mal funcionamiento de la función.

7. Agrega una función en el contenedor de la Function App. Selecciona Azure Event Hub trigger como disparador y escribe el nombre en el Event Hub (no el namespace) que creaste en el paso dos. Event Hub Connection debe rellenarse automáticamente con la clave NRLOGS_EVENTHUB_CONNECTION como una opción de conexión. Esta es la misma clave que acabas de crear en el paso anterior. 

Nota: El campo de grupo de consumidor distingue entre mayúsculas y minúsculas y debe quedar como $Default

Antes de agregar código en la función, crea un secreto llamado NR_INGEST_API_KEY  para tu clave de licencia de New Relic en la configuración Application a nivel de la Function App.

8. Agrega código a la función. Cuando se activa la función, el Event Hub recibe todos los eventos y estos se pasan como un parámetro a la función. Copia el fragmento que sigue y agrégalo a la función que creaste en el paso anterior. 

   const https = require("https");
  
   // 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",
       /* ADD YOUR NR INGEST LICENSE TO THE Application Settings of Functions App */
       "Api-Key": process.env.NR_INGEST_API_KEY,
     },
   };
  
   module.exports = function (context, eventHubMessages) {
     const parsedMessages = JSON.parse(eventHubMessages);
  
     try {
       if (parsedMessages.hasOwnProperty("records")) {
         parsedMessages.records.forEach(async (message, index) => {
           /**
           * capture only the console.log() of our app from the message
           * ignoring all other metadata
           */
           let logMessage = JSON.parse(message.resultDescription);
  
           /**
           * Setup the payload for New Relic with decoded message from EventHub
           * with "message", "logtype" as attributes
           */
           let logPayload = {
             message: logMessage,
             logtype: "MSAzure_AppServiceConsoleLogs",
           };
  
           let response = await SendToNR(logPayload);
  
           console.log(
             `Processed message ${message} with response => ${response}`
           );
         });
       }
     } catch (error) {
       console.error(`POST to New Relic Failed ${error}`);
     }
  
     context.done();
   };
  
   function SendToNR(payload) {
     return new Promise((resolve, reject) => {
       const req = https.request(options, (res) => {
         if (res.statusCode < 200 || res.statusCode > 299) {
           return reject(new Error(`HTTP status code ${res.statusCode}`));
         }
  
         const body = [];
         res.on("data", (chunk) => body.push(chunk));
         res.on("end", () => {
           const resString = Buffer.concat(body).toString();
           resolve(resString);
         });
       });
  
       req.on("error", (err) => {
         reject(err);
       });
  
       req.on("timeout", () => {
         req.destroy();
         reject(new Error("Request time out"));
       });
  
       req.write(JSON.stringify(payload));
       req.end();
     });
   }

Observa cómo puedes acceder a los datos de logs a través del parámetro de entrada eventHubMessages. eventHubMessages es una matriz de objetos EventData que tienen propiedades como records. Estos records o registros muestran el contenido de los logs, tal como EnqueuedTimeUtc que muestra la hora en que el log fue enviado al Event Hub.

Observa también que el código crea una carga personalizada con un atributo personalizado, logType, para diferenciar entre las distintas fuentes de los logs. 

9.  Guarda y despliega la función. Después de desplegar la función, espera que tu aplicación genere algunos logs. Para comprobar si los logs fueron creados y reenviados, mira la IU de New Relic.

10. Verifica que los logs aparezcan en la IU de New Relic. En New Relic, verás que la implementación está funcionando y podrás explorar los logs reenviados. En la siguiente imagen, observa cómo puedes ver la fuente de los logs en el atributo logType

Resolución de problemas

Estos son algunos de los problemas que suelen ocurrir durante la configuración de esta solución: 

La función no se está ejecutando en el disparador Event Hub. Verifica que la función esté bien configurada con la clave de conexión de Event Hub y que se proporcione el nombre del Event Hub correcto. Consulta y sigue las instrucciones del paso cinco de este blog. Además, asegúrate de que tu grupo de consumidor esté configurado en $Default usando las mayúsculas y minúsculas correctamente porque este nombre hace una distinción entre ellas. Si has configurado otro grupo de consumidor para tu Event Hub, asegúrate de que la configuración sea la correcta y vuelve a examinar el paso cinco

No puedo ver los logs en New Relic. En algunos casos, los datos pueden demorarse unos minutos en aparecer. Si después de un tiempo de espera aún no ves los datos, asegúrate de haber usado la NR_INGEST_LICENSE correcta. 

Resumen

Vamos a resumir lo que has aprendido en este blog ahora que ya has completado los pasos del ejemplo. 

Primero activaste la depuración en Azure a través de la función de logs (registros) de diagnóstico. Esto te dio acceso a los logs detallados de tu aplicación.

Luego configuraste el streaming de logs a través de los servicios nativos de Azure. Una vez que pudiste hacer streaming de los logs a un lugar central, se hizo más fácil acceder a ellos y analizarlos en una variedad de recursos de Azure. Esta función resulta útil para aplicaciones de gran escala ya que identifica tendencias y patrones en los datos.

Por último, estableciste el reenvío de logs a New Relic a través de los mismos servicios nativos. Esto ayuda a analizar los logs en tiempo real, lo que permite acelerar el tiempo para detectar y corregir problemas en nuestra aplicación.

Los logs (registros) de diagnóstico de Azure, el streaming de logs y el reenvío de logs a New Relic se pueden usar para todas las aplicaciones de Azure, lo que hace que sea una forma económica y eficiente de administrar los logs.