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

4

7

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

Flinch answered 1/8, 2019 at 9:57 Comment(5)
Does that mean appInsights is the only way by which I can get the invocation logs? The functionality is already implemented and is using logback to write logs. I'm trying to execute the same functionality with Azure functions. I would like to see the logs in invocation details. see image attached. But it only shows the context logger logs there. Should I use this context logger wherever I have to log something? You can see the log statements are in a different class. Similarly there are more logs in the execution flow. I've to see them. Does it blocks the log entries from other loggers?Flinch
another words, it's working locally, but when you deploy to azure it does not work?Thynne
also you keep referring to invocation logs. what does invocation mean?Thynne
Invocation refers to the act of invoking a method. It's plain english.Jemmie
The invocation means the function gets executed whenever a new message is added to the queue(Its a queue triggered function). I pack the function with command mvn clean package. The command mvn azure-functions:run is used to run it locally. mvn azure-functions:deploy is used to deploy it. You can see that the statement <<<<<<<<<<<< INSIDE THE SERVICE HANDLE >>>>>>>>>>>> is visible when its run locally. But the log statements in the class MySampleService is not available when its deployed in portal (not available in log streaming or invocation details).Flinch
P
3

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

Pastorale answered 10/8, 2020 at 17:18 Comment(1)
This is no longer true. AppInsights supports SLF4J out of the box. Check: https://mcmap.net/q/1625763/-azure-function-app-logs-into-app-insights-using-slf4jCastillo
M
2

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);
Macromolecule answered 5/10, 2020 at 13:4 Comment(0)
C
0

Updated in 2022

Application Insights agent supports SLF4j logs (for log4j2, logback, and jul) out of the box. Quoting from https://learn.microsoft.com/en-us/azure/azure-monitor/app/java-standalone-config#auto-collected-logging

Log4j, Logback, and java.util.logging are auto-instrumented, and logging performed via these logging frameworks is auto-collected. Logging is only captured if it first meets the level that is configured for the logging framework, and second, also meets the level that is configured for Application Insights.

For more details, refer to a similar question: https://mcmap.net/q/1625763/-azure-function-app-logs-into-app-insights-using-slf4j

Castillo answered 8/2, 2022 at 19:10 Comment(0)
A
-1

You need to use Kudu.

Get the URL for the application, if your application URL is xxxxxxx.azurewebsites.net, then input xxxxxxx.scm.azurewebsites.net to see the logs.

Apgar answered 25/2, 2020 at 22:9 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.