Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Azure Functions with java - How to get the logs written within the function?

I've deployed a queue triggered azure function with Java in Azure. I've added logback-classic and lombok in pom.xml for logging. But the logs are not displayed on the function's monitor > invocation details or the log-streaming service in portal. But I could see the logs written with context.getLogger(). The logs writter with logback logger is not visible. Please let me know how to check my logs in function invocation.

Following is the queue triggerred azure function handle

public class QueueHandlerFunction {

  @FunctionName("queuetriggertest")
  public void queueMessageHandler(@QueueTrigger(name = "msg",
      queueName = "my-test-queue", connection = "MyQStorage") final String payload,
      final ExecutionContext context) {

    //Logs with this logger is visible
    context.getLogger().info("Received Message From my-test-queue : " + payload);
    
    MySampleService.handleQueueMessage(payload);
  }
}

Following is the MySampleService class with lombok logger

@Slf4j
public class MySampleService {

  public static void handleQueueMessage(final String payload) {

    log.info("<<<<<<<<<<<< INSIDE THE SERVICE HANDLE >>>>>>>>>>>>");
    if (StringUtils.isNotBlank(payload)) {
        log.info("Received Payload : {}", payload); //This log not available
        // TODO Work with incoming payload
    } else {
      log.info("Message payload is Blank."); //This log not available
    }
  }

}

Following is he logback.xml placed in the resources folder of the maven project.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE xml>
<configuration>
   <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
      <encoder>
         <pattern>%d{E MMM dd yyyy hh:mm:ss a} [%thread] %-5level %logger{36}
                - %msg%n</pattern>
      </encoder>
   </appender>
   
   <logger name="com.howayig.test" level="INFO" />
   <root level="INFO">
      <appender-ref ref="STDOUT" />
   </root>
</configuration>

And I've the following dependencies in the pom.xml

<dependencies>
        <dependency>
            <groupId>com.microsoft.azure.functions</groupId>
            <artifactId>azure-functions-java-library</artifactId>
            <version>1.3.0</version>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>3.7</version>
        </dependency>
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <version>1.2.3</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.16.20</version>
        </dependency>
    </dependencies>

EDIT: Attached portal screenshot for the function invocation logs...

Attached portal screenshot for the function invocation logs

EDIT 2: Added screenshots of local execution and deployed one in portal.

  • Logs in command prompt when is executed locally Command prompt when executed the function locally

  • Live streaming console service of the function from the portal for a new message in queue Portal Console with live streaming

  • Invocation details of the function from the portal for a new message in queue Portal Invocation details

like image 686
Master Po Avatar asked Aug 01 '19 09:08

Master Po


2 Answers

None of the logging framework works other than java.util.logging till this date.

I have tried with log4j2, but logs don't come up in invocations details and hence they don't show up in Application Insights query either.

But Log4j2 logs appear under "Log stream" section of any of your Functions Apps. But again, that's not really helpful.

If you have already built-in application with some other logging framework, then either you need to change all of your logging to java.util.logging or do below.

Create below 2 custom classes in a separate maven module and include that to all of your function modules. Have same package name as the framework that you are using. In my case its "org.apache.logging.log4j"

They are not perfectly built custom classes. But this works.

LogManager.java

package org.apache.logging.log4j;

public class LogManager {

    static Logger logger = new Logger();

    public static Logger getLogger() {
        return logger;
    }

    public static void setLogger(Logger logger1) {
        logger = logger1;
    }

    public static Logger getLogger(Class<?> class1) {
        return logger;
    }

    public static Logger getLogger(String name) {
        return logger;
    }
}

Logger.java

package org.apache.logging.log4j;

import java.util.logging.Level;

public class Logger {

    private java.util.logging.Logger logger;

    public void setLogger(java.util.logging.Logger logger) {
        this.logger = logger;
    }

    public void debug(Object... obj) {
        printLog(obj, Level.FINE);
    }

    public void info(Object... obj) {
        printLog(obj, Level.INFO);
    }

    public void error(Object... obj) {
        printLog(obj, Level.SEVERE);
    }

    public void log(Object level, Object... obj) {
        printLog(obj, Level.FINEST);
    }

    public void warn(Object... obj) {
        printLog(obj, Level.WARNING);
    }

    private void printLog(Object[] obj, Level level) {
        if (obj != null) {

            String logString = obj[0] != null ? obj[0].toString() : "";

            if (obj.length > 1) {
                for (int i = 1; i < obj.length; i++) {
                    if (obj[i] != null) {
                        logString = logString.replaceFirst("\\{}", obj[i].toString());
                    }
                }                   
            }
            logger.log(level, logString);
        }
    }
}

Then you can set your custom logger using Azure's Execution Context.

// LOG var Global to class    
private final Logger LOG = LogManager.getLogger(BgdInfoAnalyticsHandler.class);

@FunctionName("func-name-fn")
    public void run(
            @ServiceBusQueueTrigger(name = "TriggerName", queueName = "sbq-use-queue-name", connection = "AzureWebJobsServiceBus") String input,
            ExecutionContext context) {
        LOG.setLogger(context.getLogger());
        LogManager.setLogger(LOG);
        String fnInput = HostServiceUtil.getBlobData(input);
        LOG.info("fnInput : {} ", fnInput);
        invokeService(fnInput);
    }

Once this Logger is initiated/set in the entry point of your run method with Azure's ExecutionContext logger. You get this logger from any other class by doing LogManager.getLogger (Custom Class) as it is static.

Note: Static vars are being shared by different invocations or functions (if they resides in same function app)

Then you can do query in Application Insights as below:

union traces
| union exceptions | union requests
// | where timestamp > ago(2d)
// | where cloud_RoleName =~ 'fun-use-function-app-name' and operation_Name =~ 'function-name-fn' //and operation_Id =~ "7303edd79433b0468f934c80a88e5f77"
// | where innermostMessage contains "Exception" or message contains "Exception"
| project timestamp, message = iff(message != '', message, 
    iff(innermostMessage != '', innermostMessage, customDimensions.['prop__{OriginalFormat}'])), logLevel = customDimensions.['LogLevel']
    , operation_Name, operation_Id, cloud_RoleName, invocationId=customDimensions['InvocationId']
| order by timestamp asc
| take 3000

You can also see Failure/Exception under: ApplicationInsights > Investigate section > Failures

like image 162
Harsh Goswami Avatar answered Oct 02 '22 10:10

Harsh Goswami


It's now available via distributed tracing. You can view richer data from your function applications pertaining to the following requests, dependencies, logs and metrics.

All you need is:

  1. Add dependency:
<dependency>
    <groupId>com.microsoft.azure</groupId>
    <artifactId>applicationinsights-core</artifactId>
    <version>2.6.0</version>
  </dependency>
  1. Create a configuration file named ApplicationInsights.json with the following content:
{
  "instrumentationSettings": {
    "connectionString": "InstrumentationKey=00000000-0000-0000-0000-000000000000"
  }
}
  1. Create a TelemetryClient:
private static final TelemetryClient telemetryClient = new TelemetryClient();
  1. Use it
  telemetryClient.trackTrace(message, SeverityLevel.Warning, properties);
like image 24
Helay Avatar answered Oct 02 '22 12:10

Helay