Azure Functions with java - 如何获取写入函数的日志?

Posted

技术标签:

【中文标题】Azure Functions with java - 如何获取写入函数的日志?【英文标题】:Azure Functions with java - How to get the logs written within the function? 【发布时间】:2019-12-09 21:34:53 【问题描述】:

我在 Azure 中使用 Java 部署了一个队列触发的 azure 函数。我在pom.xml 中添加了logback-classiclombok 用于日志记录。 但是日志不会显示在函数的monitor > invocation details 或门户中的log-streaming service 上。 但我可以看到用context.getLogger() 编写的日志。带有 logback 记录器的日志写入器不可见。请让我知道如何检查我的函数调用日志。

以下是队列触发的天蓝色函数句柄

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);
  

以下是带有 lombok 记录器的 MySampleService

@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
    
  


下面是他logback.xml放在maven项目的resources文件夹中。

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

我在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>

编辑:附上函数调用日志的门户截图...

编辑 2:添加了本地执行的屏幕截图并在门户中部署了一个。

在本地执行时登录命令提示符

来自门户的功能的实时流媒体控制台服务,用于队列中的新消息

从门户中为队列中的新消息调用函数的详细信息

【问题讨论】:

这是否意味着appInsights 是我获取调用日志的唯一方法?该功能已经实现并且正在使用logback 来写入日志。我正在尝试使用 Azure 函数执行相同的功能。我想查看调用详细信息中的日志。见附图。但它只在那里显示上下文记录器日志。我应该在需要记录某些内容的地方使用这个上下文记录器吗?您可以看到日志语句属于不同的类。同样,执行流程中有更多日志。我得见他们。它会阻止来自其他记录器的日志条目吗? 换句话说,它在本地工作,但是当你部署到天蓝色时它不起作用? 你也一直在引用 invocation 日志。调用是什么意思? Invocation 是指调用方法的行为。这是简单的英语。 调用意味着每当有新消息添加到队列时,该函数就会被执行(它是一个队列触发函数)。我用命令mvn clean package 打包函数。命令mvn azure-functions:run 用于在本地运行它。 mvn azure-functions:deploy 用于部署它。您可以看到语句 >>>>>>>>>>> 在本地运行时可见。但是 MySampleService 类中的日志语句在其部署在门户中时不可用(在日志流或调用详细信息中不可用)。 【参考方案1】:

你需要使用 Kudu。

获取应用的 URL,如果你的应用 URL 是 xxxxxxx.azurewebsites.net,则输入 xxxxxxx.scm.azurewebsites.net 查看日志。

【讨论】:

【参考方案2】:

到目前为止,除了 java.util.logging 之外,没有其他日志框架可以工作。

我已尝试使用 log4j2,但日志不会出现在调用详细信息中因此它们也不会出现在 Application Insights 查询中

但 Log4j2 日志显示在您的任何 Functions 应用程序的 “日志流” 部分下。但同样,这并没有真正的帮助。

如果您已经使用其他日志记录框架内置了应用程序,那么您需要将所有日志记录更改为 java.util.logging 或执行以下操作。

在单独的 Maven 模块中创建以下 2 个自定义类,并将其包含到所有功能模块中。 与您使用的框架具有相同的包名称。就我而言,它的“org.apache.logging.log4j”

它们不是完美构建的自定义类。但这行得通。

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);
        
    

然后您可以使用 Azure 的执行上下文设置自定义记录器。

// 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);
    

一旦使用 Azure 的 ExecutionContext 记录器在运行方法的入口点启动/设置此记录器。您可以通过执行 LogManager.getLogger(自定义类)从任何其他类获取此记录器,因为它是静态的。

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

然后您可以在 Application Insights 中进行如下查询:

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

您还可以在以下位置查看失败/异常:ApplicationInsights > 调查部分 > 失败

【讨论】:

【参考方案3】:

现在可以通过distributed tracing 获得。您可以从与以下requests, dependencies, logs and metrics.相关的函数应用程序中查看更丰富的数据

你只需要:

    添加依赖:
<dependency>
    <groupId>com.microsoft.azure</groupId>
    <artifactId>applicationinsights-core</artifactId>
    <version>2.6.0</version>
  </dependency>
    创建一个名为 ApplicationInsights.json 的配置文件,内容如下:

  "instrumentationSettings": 
    "connectionString": "InstrumentationKey=00000000-0000-0000-0000-000000000000"
  

    创建 TelemetryClient:
private static final TelemetryClient telemetryClient = new TelemetryClient();
    使用它
  telemetryClient.trackTrace(message, SeverityLevel.Warning, properties);

【讨论】:

以上是关于Azure Functions with java - 如何获取写入函数的日志?的主要内容,如果未能解决你的问题,请参考以下文章

在 Java 中使用 Visual Studio Code 的 Azure Functions 项目

添加 Azure 存储 Blob 容器输入绑定 Azure Functions Java

将 FormData 对象读入 Java HTTP 触发的 Azure Functions

使用 Java Azure Functions SDK 处理二进制数据

Azure Java 函数 -502-Bad Gateway

基于deviceTwinTrigger的Azure Java函数:检索DeviceId