【Azure Spring Cloud】Azure Spring Cloud服务,如何获取应用程序日志文件呢?

问题描述

在使用Azure Spring Cloud服务时,如果要收集应用程序的日志。有控制台输出(实时流日志),也可以配置Log Analytics服务。

日志流式处理

可以通过以下命令在 Azure CLI 中使用日志流式处理。

az spring-cloud app logs -n hellospring -s yourspringcloudname -g <resource group name> --lines 100 -f

Log Analytics

在Azure Spring Cloud门户页面,转到“服务 | 概述”页,然后在“监视”部分中选择“日志” 。 选择 Azure Spring Cloud 的一个示例查询上的“运行”。

但是,如果应用中需要自行把日志写入到日志文件中,那么如果应用部署到Azure Spring Cloud 上后,如何来查看并获取到应用程序自身专门用于记录日志的文件呢? 

 

问题解答

Azure Spring Cloud 与 App Service有较大的区别。App Service可以通过Kudu工具访问应用的文件系统,就可以在Home目录下直接看见应用程序生成的日志文件并下载。

而Spring Cloud服务,需要通过文件挂载(Mount)的方式,把一个存储账号(Storage Account)挂载到Spring Cloud的App上。  

挂载Storage Account以及在代码中配置挂载后的日志路径步骤:

第一步:为Azure Spring Cloud服务添加一个Storage

  • Storage name:为自定义名称,根据自己情况设定,如 springstorage01

  • Account name:为存储账号(Storage Account)的名称,需要从Azure Storage Account的页面中获取

  • Account Key:从Azure Storage Account的Access Key页面中获取。注意:此处只需要填写Access Key就可以,不需要完整的Conneciton String

 

第二步:为Spring Cloud App添加挂载

在Spring Cloud页面,点击“Apps”列举出所有的App。选中需要配置日志文件路径的应用。然后选择“Configuration” --> "Persistent Storage"

  • Storage Name:为第一步中自定义的Storage Name。

  • Share Name:为在Azure Storage Account的文件共享中所创建的一个共享文件夹。可以自定义文件夹名称。

  • Mount Path: 所挂载Spring Cloud App所运行实例上的文件路径。这一步的内容也是将在应用为日志文件所配置的存放路径。非常关键!如使用 /app/logs

 

第三步:在Java Spring应用中重新配置日志生成路径

修改应用中的日志保存路径。如本实例中使用的 logback-spring.xml 。 修改文件输出路径为: <file>/app/logs/test.log</file>

重新生成Jar文件并再次发布。

 

第四步: 在存储账号中检查应用日志

在Azure门户中,进入Storage Account中,查看Spring Cloud App的运行日志。如下:

 

 

 

附录:Spring Cloud应用示例代码

POM.XML 依赖包文件

&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"&gt;
    &lt;modelVersion&gt;4.0.0&lt;/modelVersion&gt;
    &lt;parent&gt;
        &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
        &lt;artifactId&gt;spring-boot-starter-parent&lt;/artifactId&gt;
        &lt;version&gt;2.6.11&lt;/version&gt;
        &lt;relativePath/&gt; &lt;!-- lookup parent from repository --&gt;
    &lt;/parent&gt;
    &lt;groupId&gt;com.example&lt;/groupId&gt;
    &lt;artifactId&gt;hellospring&lt;/artifactId&gt;
    &lt;version&gt;0.0.1-SNAPSHOT&lt;/version&gt;
    &lt;name&gt;hellospring&lt;/name&gt;
    &lt;description&gt;Demo project for Spring Boot&lt;/description&gt;
    &lt;properties&gt;
        &lt;java.version&gt;1.8&lt;/java.version&gt;
        &lt;spring-cloud.version&gt;2021.0.3&lt;/spring-cloud.version&gt;
    &lt;/properties&gt;
    &lt;dependencies&gt;
        &lt;dependency&gt;
            &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
            &lt;artifactId&gt;spring-boot-starter-actuator&lt;/artifactId&gt;
        &lt;/dependency&gt;
        &lt;dependency&gt;
            &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
            &lt;artifactId&gt;spring-boot-starter-web&lt;/artifactId&gt;
        &lt;/dependency&gt;
        &lt;dependency&gt;
            &lt;groupId&gt;org.springframework.cloud&lt;/groupId&gt;
            &lt;artifactId&gt;spring-cloud-starter-config&lt;/artifactId&gt;
        &lt;/dependency&gt;
        &lt;dependency&gt;
            &lt;groupId&gt;org.springframework.cloud&lt;/groupId&gt;
            &lt;artifactId&gt;spring-cloud-starter-netflix-eureka-client&lt;/artifactId&gt;
        &lt;/dependency&gt;

        &lt;dependency&gt;
            &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
            &lt;artifactId&gt;spring-boot-starter-test&lt;/artifactId&gt;
            &lt;scope&gt;test&lt;/scope&gt;
        &lt;/dependency&gt;
        &lt;dependency&gt;
            &lt;groupId&gt;net.logstash.logback&lt;/groupId&gt;
            &lt;artifactId&gt;logstash-logback-encoder&lt;/artifactId&gt;
            &lt;version&gt;6.5&lt;/version&gt;
        &lt;/dependency&gt;
    &lt;/dependencies&gt;
    &lt;dependencyManagement&gt;
        &lt;dependencies&gt;
            &lt;dependency&gt;
                &lt;groupId&gt;org.springframework.cloud&lt;/groupId&gt;
                &lt;artifactId&gt;spring-cloud-dependencies&lt;/artifactId&gt;
                &lt;version&gt;${spring-cloud.version}&lt;/version&gt;
                &lt;type&gt;pom&lt;/type&gt;
                &lt;scope&gt;import&lt;/scope&gt;
            &lt;/dependency&gt;
        &lt;/dependencies&gt;
    &lt;/dependencyManagement&gt;

    &lt;build&gt;
        &lt;plugins&gt;
            &lt;plugin&gt;
                &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
                &lt;artifactId&gt;spring-boot-maven-plugin&lt;/artifactId&gt;
            &lt;/plugin&gt;
        &lt;/plugins&gt;
    &lt;/build&gt;

&lt;/project&gt;

 

application.properties 配置文件

spring.cloud.config.enabled=false

 

logback-spring.xml配置文件

&lt;configuration&gt;
    &lt;appender name="stdout" class="ch.qos.logback.core.ConsoleAppender"&gt;
        &lt;encoder class="net.logstash.logback.encoder.LoggingEventCompositeJsonEncoder"&gt;
            &lt;providers&gt;
                &lt;timestamp&gt;
                    &lt;fieldName&gt;timestamp&lt;/fieldName&gt;
                    &lt;timeZone&gt;UTC&lt;/timeZone&gt;
                &lt;/timestamp&gt;
                &lt;loggerName&gt;
                    &lt;fieldName&gt;logger&lt;/fieldName&gt;
                &lt;/loggerName&gt;
                &lt;logLevel&gt;
                    &lt;fieldName&gt;level&lt;/fieldName&gt;
                &lt;/logLevel&gt;
                &lt;threadName&gt;
                    &lt;fieldName&gt;thread&lt;/fieldName&gt;
                &lt;/threadName&gt;
                &lt;nestedField&gt;
                    &lt;fieldName&gt;mdc&lt;/fieldName&gt;
                    &lt;providers&gt;
                        &lt;mdc /&gt;
                    &lt;/providers&gt;
                &lt;/nestedField&gt;
                &lt;stackTrace&gt;
                    &lt;fieldName&gt;stackTrace&lt;/fieldName&gt;
                    &lt;!-- maxLength - limit the length of the stack trace --&gt;
                    &lt;throwableConverter class="net.logstash.logback.stacktrace.ShortenedThrowableConverter"&gt;
                        &lt;maxDepthPerThrowable&gt;200&lt;/maxDepthPerThrowable&gt;
                        &lt;maxLength&gt;14000&lt;/maxLength&gt;
                        &lt;rootCauseFirst&gt;true&lt;/rootCauseFirst&gt;
                    &lt;/throwableConverter&gt;
                &lt;/stackTrace&gt;
                &lt;message /&gt;
                &lt;throwableClassName&gt;
                    &lt;fieldName&gt;exceptionClass&lt;/fieldName&gt;
                &lt;/throwableClassName&gt;
            &lt;/providers&gt;
        &lt;/encoder&gt;
    &lt;/appender&gt;
    &lt;appender name="files" class="ch.qos.logback.core.FileAppender"&gt;
        &lt;file&gt;/app/logs/test.log&lt;/file&gt;
        &lt;append&gt;true&lt;/append&gt;
        &lt;encoder class="net.logstash.logback.encoder.LoggingEventCompositeJsonEncoder"&gt;
            &lt;providers&gt;
                &lt;timestamp&gt;
                    &lt;fieldName&gt;timestamp&lt;/fieldName&gt;
                    &lt;timeZone&gt;UTC&lt;/timeZone&gt;
                &lt;/timestamp&gt;
                &lt;loggerName&gt;
                    &lt;fieldName&gt;logger&lt;/fieldName&gt;
                &lt;/loggerName&gt;
                &lt;logLevel&gt;
                    &lt;fieldName&gt;level&lt;/fieldName&gt;
                &lt;/logLevel&gt;
                &lt;threadName&gt;
                    &lt;fieldName&gt;thread&lt;/fieldName&gt;
                &lt;/threadName&gt;
                &lt;nestedField&gt;
                    &lt;fieldName&gt;mdc&lt;/fieldName&gt;
                    &lt;providers&gt;
                        &lt;mdc /&gt;
                    &lt;/providers&gt;
                &lt;/nestedField&gt;
                &lt;stackTrace&gt;
                    &lt;fieldName&gt;stackTrace&lt;/fieldName&gt;
                    &lt;!-- maxLength - limit the length of the stack trace --&gt;
                    &lt;throwableConverter class="net.logstash.logback.stacktrace.ShortenedThrowableConverter"&gt;
                        &lt;maxDepthPerThrowable&gt;200&lt;/maxDepthPerThrowable&gt;
                        &lt;maxLength&gt;14000&lt;/maxLength&gt;
                        &lt;rootCauseFirst&gt;true&lt;/rootCauseFirst&gt;
                    &lt;/throwableConverter&gt;
                &lt;/stackTrace&gt;
                &lt;message /&gt;
                &lt;throwableClassName&gt;
                    &lt;fieldName&gt;exceptionClass&lt;/fieldName&gt;
                &lt;/throwableClassName&gt;
            &lt;/providers&gt;
        &lt;/encoder&gt;
    &lt;/appender&gt;
    &lt;root level="info"&gt;
        &lt;appender-ref ref="stdout" /&gt;
        &lt;appender-ref ref="files" /&gt;
    &lt;/root&gt;
&lt;/configuration&gt;

 

HelloController.java 代码

package com.example.hellospring;

import org.springframework.web.bind.annotation.RestController;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.RequestMapping;

@RestController
public class HelloController {

    org.slf4j.Logger logger = LoggerFactory.getLogger(getClass());

    @RequestMapping("/")
    public String index() {

        logger.info("Loging into Storage Folder.... request from index page.... test by lb @09-02");

        return "Greetings from Azure Spring Cloud!";
    }

}

 

HellospringApplication.java 代码

package com.example.hellospring;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class HellospringApplication {

    public static void main(String[] args) {
        SpringApplication.run(HellospringApplication.class, args);
    }

}

 

 

参考资料

快速入门:在 Azure Spring Cloud 中部署你的第一个应用程序https://docs.azure.cn/zh-cn/spring-cloud/quickstart?tabs=Azure-CLI#build-and-deploy-the-app

日志https://docs.azure.cn/zh-cn/spring-cloud/quickstart-logs-metrics-tracing?tabs=Azure-CLI

 

正在加载评论...