Azure SDK for Java Logging in Azure Functions

Vinay Gera

Vinay

In this post we will learn about Azure SDK for Java application and HTTP logging scenarios in an Azure Functions environment. We will look at the scenario of managing secrets in the Azure Key Vault with the Key Vault and Identity client libraries and how to activate and access the SDK logs in the Azure Functions environment. The Azure SDK logs are helpful to analyze the underlying behavior of the SDKs and help to debug and root cause issues experienced in the deployed production environment of Azure Functions.

What is Azure Functions?

Azure Functions is a serverless computing service which allows developers to quickly deploy their code in the cloud and run the application at scale. It simplifies the application development and takes away the hassle of managing the infrastructure of the platform on which the code will be executed. The user can also configure a variety of triggers or events to execute their function, such as HTTP requests, scheduled runs, events, or notification hubs.

Setting up Azure Functions Development Environment

VSCode and IntelliJ both offer great support to quickly get started with setting up your development environment to run Azure Functions. You can use the instructions below to setup the Azure Functions project in your preferred IDE.

VSCode

Prequisites:

  1. Java Developer Kit, version 8
  2. Apache Maven 3.0 or above
  3. Visual Studio Code
  4. Java Extension pack for Visual Studio Code
  5. Azure Functions Extension for Visual Studio Code

Use the VSCode-quickstart guide to setup your environment.

IntelliJ

Prequisites:

  1. Java Developer Kit, version 8
  2. Apache Maven 3.5.0 or above.
  3. IntelliJ IDEA Ultimate Edition or Community Edition
  4. Function Core Tools

Use the IntelliJ-quickstart guide to setup your environment.

Once you have setup the Azure Functions project in your preferred IDE, we will now begin adding the logic to connect to an Azure Key Vault using the azure-security-keyvault-secrets and azure-identity client libraries.

Maven Packages:

Add the following Azure SDK client libraries to your Azure Functions pom file:

<dependency>
    <groupId>com.azure</groupId>
    <artifactId>azure-security-keyvault-secrets</artifactId>
    <version>4.2.0</version>
</dependency>

<dependency>
    <groupId>com.azure</groupId>
    <artifactId>azure-identity</artifactId>
    <version>1.1.0</version>
</dependency> 

Latest packages for these client libraries can be found here:

  1. azure-security-keyvault-secrets.
  2. azure-identity.

Azure Functions Code Setup

In the Function Setup below, we use the Azure Identity client library to connect to the Azure Key Vault and then create a secret in the Key Vault.

@FunctionName("AzureKeyVaultFunction")
    public HttpResponseMessage run(
            @HttpTrigger(
                name = "req",
                methods = {HttpMethod.GET, HttpMethod.POST},
                authLevel = AuthorizationLevel.ANONYMOUS)
                HttpRequestMessage<Optional<String>> request,
            final ExecutionContext context) {
        // Parse query parameter
        final String vaultUrl = request.getQueryParameters().get("vaultUrl");

        if (vaultUrl == null) {
            return request.createResponseBuilder(HttpStatus.BAD_REQUEST).body("Please pass a vaultUrl on the query string or in the request body").build();
        }

        SecretClient secretClient = new SecretClientBuilder()
                .httpLogOptions(new HttpLogOptions().setLogLevel(HttpLogDetailLevel.BODY_AND_HEADERS))
                .vaultUrl(vaultUrl)
                .credential(new DefaultAzureCredentialBuilder().build())
                .buildClient();

        context.getLogger().info("Executing secret creation.");
        KeyVaultSecret secret = secretClient.setSecret("DummySecret", "DummyValue");
        context.getLogger().info(String.format("Successfully created secret with name: %s and value: %s", secret.getName(), secret.getValue()));

        return request.createResponseBuilder(HttpStatus.OK)
                .build();
    }

We use DefaultAzureCredential to authenticate SecretClient with Azure Active Directory (AAD). In the production environment where the code is deployed to the Azure Function, ManagedIdentityCredential will be used by the DefaultAzureCredential to authenticate.

SecretClient secretClient = new SecretClientBuilder()
        .vaultUrl(vaultUrl)
        .credential(new DefaultAzureCredentialBuilder().build())
        .buildClient();

Creating a secret is as simple as this one line. Once executed, then we just print out the name and value of the created secret.

  context.getLogger().info("Executing secret creation.");
  KeyVaultSecret secret = secretClient.setSecret("DummySecret", "DummyValue");
  context.getLogger().info(String.format("Successfully created secret with name: %s and value: %s", secret.getName(), secret.getValue()));

Configuring logging in Azure SDKs

HTTP logging

The HTTP logging will log all the HTTP requests being sent by the client. We will enable it in the above Azure Functions code via HttpLogOptions.

We create and configure the http log options and then specify the options in the SecretClientBuilder.

SecretClient secretClient = new SecretClientBuilder()
        .httpLogOptions(new HttpLogOptions().setLogLevel(HttpLogDetailLevel.BODY_AND_HEADERS))
        .vaultUrl(vaultUrl)
        .credential(credential)
        .buildClient();

SDK logging

Now, let’s configure logging in the Azure client libraries being used in the code. The Azure SDK support logging via SLF4J. This allows the applications to use their preferred logging framework with the Azure SDK. Detailed instructions to setup logging with Azure SDK can be found in the wiki.

We will be using log4j logging framework in this blog via following steps: 1. Add the slf4j log4j dependency to the pom file

<dependency> 
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-log4j12</artifactId>
    <version>1.7.29</version>
</dependency>
  1. Specify the log4j configuration in src/main/resources via log4j.properties file: We will be using a Rolling file appender to direct the output to a file instead of console and console output does not show up directly on Azure Functions console logs.
# Configure the root logger with appender file
log4j.rootLogger = DEBUG, FILE

# Configure the file appender
log4j.appender.FILE=org.apache.log4j.DailyRollingFileAppender

# Configure the name of the log file, it also contains the file system path of the Azure Functions environment to store the logs in this specific location. 
log4j.appender.FILE.File=D:/home/LogFiles/slf4j/log.out

# Configure the immediate flush to true (default)
log4j.appender.FILE.ImmediateFlush=true

# Configure the threshold to debug mode
log4j.appender.FILE.Threshold=debug

# Configure the append to true, should not overwrite
log4j.appender.FILE.Append=true

# Configure the DatePattern
log4j.appender.FILE.DatePattern='.' yyyy-MM-dd-HH-mm

# Configure the layout for file appender
log4j.appender.FILE.layout=org.apache.log4j.PatternLayout
log4j.appender.FILE.layout.conversionPattern=%m%n

Deploy the Azure Function

  1. Let’s now deploy the Azure Function and execute it in the cloud.

    VSCode and IntelliJ both support deploying the Azure Functions to the cloud from the IDE itself. Follow the deployment instructions in their respective deployment guides below:

  2. Once your Azure Function application is deployed, now let’s navigate to the portal.

    If your Functions app is configured to use Java 8 then click on Configuration and add a new Application Setting with name FUNCTIONS_WORKER_JAVA_LOAD_APP_LIBS and value 1

    If your Functions app is using Java 11, then you can skip this step.

  1. To execute your function, click on Functions to see all the deployed functions in the app.

  2. Select your deployed function from the drop down.

  3. Then execute your function through these steps:

    Before pressing the Run button to execute your function, add vaulUrl query parameter under Query section.

  4. A terminal window will pop up showing the output logs and trace on the screen as following:

The highlighted INFO logs are coming from the context logger in our code. The terminal in the Azure Functions will only show logs via Context logger and won’t show any HTTP logs or SDK logs which have been enabled.

context.getLogger().info("Executing secret creation.");
KeyVaultSecret secret = secretClient.setSecret("DummySecret", "DummyValue");
context.getLogger().info(String.format("Successfully created secret with name: %s and value: %s", secret.getName(), secret.getValue()));

Accessing the Log4j logs

We will use KUDU API Console to look at the SDK and HTTP logs which we enabled before via our log4j.properties previously.

Our log4j settings indicated to write the logs to the file. The KUDU API console allows us to Access the filesystem of the platform on which Azure Functions is running and we can use it to look at our logs stored in the file.

  1. Log in at the KUDU API console at this URL https://<Function-App-Name>.scm.azurewebsites.net
  2. Once logged in you’ll see the following window:

  3. Navigate to Debug console -> Powershell

  4. Navigate to the log file output path configured in log4j.properties file.

  5. Execute cat command on the log file to look at the log4j of Azure SDKs. These screenshots below display the logs in the file.

Azure Client libraries support configurable SDK and HTTP logging. The generated logs are helpful to debug and root cause issues experienced with the Azure SDKs in any environment. Enabling logging will create a negative impact on the performance of Azure client libraries, so to maximize the performance ensure that logging is disabled and only enable logging in situations where needed, for example, when debugging and investigating issues.

Azure SDK Blog Contributions

Thank you for reading this Azure SDK blog post! We hope that you learned something new and welcome you to share this post. We are open to Azure SDK blog contributions. Please contact us at azsdkblog@microsoft.com with your topic and we’ll get you setup as a guest blogger.

0 comments

Leave a comment