# 16.2.2 查看配置详细信息

除了获取有关应用程序的一般信息之外，了解应用程序的配置信息也很有用。应用程序上下文中有哪些 bean？哪些自动配置条件通过或失败？应用程序可以使用哪些环境属性？HTTP 请求如何映射到控制器？一个或多个包或类设置为怎样的日志级别？

这些问题由 Actuator 的 `/beans`、`/conditions`、`/env`、`/configprops`、`/mappings` 和 `/loggers` 端点回答。 在某些情况下，例如 `/env` 和 `/loggers`，您甚至可以动态调整正在运行的应用程序的配置。从 `/beans` 端点开始，我们将查看每一个端点，以深入了解正在运行的应用程序的配置。

**获取 Bean Wiring 报告**

探索 Spring 应用程序上下文最重要的端点是 `/beans` 端点。此端点返回一个 JSON 文档，描述应用程序上下文中的每个 bean、它的 Java 类型以及它注入的任何其他 bean。

对 `/beans` 的 GET 请求所得到的完整响应信息非常多，可以轻松地填满本章。作为完整响应的替代，让我们考虑以下片段，它专注于单个 bean 条目：

```
{
    "contexts": {
        "application-1": {
            "beans": {
   ...
                "ingredientsController": {
                    "aliases": [],
                    "scope": "singleton",
                    "type": "tacos.ingredients.IngredientsController",
                    "resource": "file [/Users/habuma/Documents/Workspaces/
                    TacoCloud/ingredient-service/target/classes/tacos/ingredients/
                    IngredientsController.class]",
                    "dependencies": [
                        "ingredientRepository"
                    ]
                },
        ...
            },
            "parentId": null
        }
    }
}
```

响应的根是 contexts 元素，它包含应用程序中每个 Spring 应用程序上下文的一个子元素。在每个应用程序上下文中都有一个 beans 元素，其中包含应用程序上下文中所有 bean 的详细信息。

在前面的示例中，显示的 bean 是名称为 `ingredientsController` 的 bean。您可以看到它没有别名，范围为单例，并且类型为 `tacos.ingredients.IngredientsController`。此外，资源属性给出了定义 bean 的类文件的路径。并且 dependencies 属性列出了注入给定 bean 的所有其他 bean。 在这种情况下，`ingredientsController` bean 被注入了一个名称为 `ingredientRepository` 的 bean。

**解释自动配置**

如您所见，自动配置是 Spring Boot 提供的最强大的功能之一。但是，有时您可能想知道为什么会自动配置某些内容。或者您可能期望某些东西被自动配置，想知道它为什么没有被自动配置。在这种情况下，您可以向 `/conditions` 发出 GET 请求，以了解自动配置的情况。

`/conditions` 返回的自动配置报告分为三部分：positiveMatches（条件配置通过）、negativeMatches（条件配置失败）和 unconditionalClasses（无条件类）。以下是 `/conditions` 请求的响应的片段，示例显示了每个部分：

```
{
  "contexts": {
    "application-1": {
      "positiveMatches": {
      ...
        "MongoDataAutoConfiguration#mongoTemplate": [
          {
            "condition": "OnBeanCondition",
            "message": "@ConditionalOnMissingBean (types
            :org.springframework.data.mongodb.core.MongoTemplate;
            SearchStrategy: all) did not find any beans"
          }
        ],
      ...
    },
    "negativeMatches": {
      ...
      "DispatcherServletAutoConfiguration": {
        "notMatched": [
          {
            "condition": "OnClassCondition",
            "message": "@ConditionalOnClass did not find required
            class 'org.springframework.web.servlet.
            DispatcherServlet'"
          }
        ],
        "matched": []
      },
      ...
    },
    "unconditionalClasses": [
      ...
      "org.springframework.boot.autoconfigure.context.
      ConfigurationPropertiesAutoConfiguration",
      ...
    ]
  }
  }
}
```

在 positiveMatches 部分，您会看到 MongoTemplate bean 是由自动配置配置的，且因为它不存在。导致这种情况的自动配置是因为有 `@ConditionalOnMissingBean` 注释，如果尚未显式配置要配置的 bean，则它会传递要配置的 bean。在这种情况下，由于没有找到 MongoTemplate 类型的 bean，因此自动配置介入并配置了一个。

在 negativeMatches 下，Spring Boot 自动配置考虑配置一个 DispatcherServlet。但是 @ConditionalOnClass 条件注解失败了，因为找不到 DispatcherServlet。

最后，如 unconditionalClasses 部分所示，配置了 ConfigurationPropertiesAutoConfiguration bean。配置属性是 Spring Boot 运行的基础，因此任何与配置属性相关的配置，都应该在没有任何条件的情况下自动配置。

**检查环境和配置属性**

除了了解您的应用程序 bean 如何连接在一起之外，您可能还对了解哪些环境属性可用，以及哪些配置属性注入了 bean 中感兴趣。

当您向 `/env` 端点发出 GET 请求时，您将收到一个相当长的，来自 Spring 应用程序正在运行的，所有属性来源的属性列表。这包括来自环境变量的属性、JVM 系统属性、`application.properties` 和 `application.yml` 文件，甚至 Spring Cloud Config Server（如果应用程序是 Config Server 的客户端）。

下面的清单显示了，您可能从 `/env` 端点获得的响应数据的一个大大简化的示例，让您对它提供的信息有所了解。

{% code title="清单 16.1 来自 /env 端点的数据" %}

```bash
$ curl localhost:8081/actuator/env
{
  "activeProfiles": [
    "development"
  ],
  "propertySources": [
  ...
  {
    "name": "systemEnvironment",
    "properties": {
      "PATH": {
        "value": "/usr/bin:/bin:/usr/sbin:/sbin",
        "origin": "System Environment Property \"PATH\""
      },
      ...
      "HOME": {
        "value": "/Users/habuma",
        "origin": "System Environment Property \"HOME\""
      }
    }
  },
  {
    "name": "applicationConfig: [classpath:/application.yml]",
    "properties": {
      "spring.application.name": {
        "value": "ingredient-service",
        "origin": "class path resource [application.yml]:3:11"
      },
      "server.port": {
        "value": 8081,
        "origin": "class path resource [application.yml]:9:9"
      },
      ...
    }
  },
  ...
  ]
}
```

{% endcode %}

尽管 `/env` 的完整响应提供了更多信息，但清单 16.1 中包含了那些需要关注的元素。首先，请注意响应顶部是一个名为 activeProfiles 的字段。现在的配置表明 development 配置文件处于活动状态。 如果任何其他配置文件处于活动状态，也将列出这些配置文件。

接下来，propertySources 字段是一个数组，其中包含 Spring 应用程序环境中每个属性源的条目。在清单 16.1 中，只显示了 systemEnvironment 和引用 application.yml 文件的 applicationConfig 属性源。

在每个属性源中，列出了该源提供的所有属性，并与其值配对。在 application.yml 属性源的情况下，每个属性的 origin 字段准确说明属性设置的位置，包括在 application.yml 中的行和列。

当该属性的名称作为路径的第二个元素给出时，`/env` 端点还可用于获取特定属性。例如，要检查 server.port 属性，请提交对 `/env/server.port` 的 GET 请求，如下所示：

```bash
$ curl localhost:8081/actuator/env/server.port
{
  "property": {
    "source": "systemEnvironment", "value": "8081"
  },
  "activeProfiles": [ "development" ],
  "propertySources": [
    { "name": "server.ports" },
    { "name": "mongo.ports" },
    { "name": "systemProperties" },
    { 
      "name": "systemEnvironment",
      "property": {
        "value": "8081",
        "origin": "System Environment Property \"SERVER_PORT\""
      }
    },
    { "name": "random" },
    {
      "name": "applicationConfig: [classpath:/application.yml]",
      "property": {
        "value": 0,
        "origin": "class path resource [application.yml]:9:9"
      }
    },
    { "name": "springCloudClientHostInfo" },
    { "name": "refresh" },
    { "name": "defaultProperties" },
    { "name": "Management Server" }
  ]
}
```

如您所见，所有属性源仍被表示，但只有那些设置了指定属性的源才会包含附加信息。在这种情况下，系统环境属性源和 application.yml 属性源都具有 `server.port` 属性的值。因为 systemEnvironment 属性源优先于它下面列出的任何属性源，所以它的值 8081 胜出。获胜值反映在属性字段下方的顶部附近。

`/env` 端点不仅可以用于读取属性值。通过向 /env 端点提交 POST 请求以及带有名称和值字段的 JSON 数据，您还可以在正在运行的应用程序中设置属性。例如，要将名为 `tacocloud.discount.code` 的属性设置为 TACOS1234，您可以使用 curl 在命令行提交 POST 请求，如下所示：

```bash
$ curl localhost:8081/actuator/env \
  -d'{"name":"tacocloud.discount.code","value":"TACOS1234"}' \
  -H "Content-type: application/json"
{"tacocloud.discount.code":"TACOS1234"}
```

提交属性后，新设置的属性及其值将在响应中返回。稍后，如果您决定不再需要该属性，您可以向 `/env` 端点提交 DELETE 请求，以删除通过该端点创建的所有属性：

```bash
$ curl localhost:8081/actuator/env -X DELETE
{"tacocloud.discount.code":"TACOS1234"}
```

与通过 Actuator 的 API 设置属性一样重要，要注意，使用 `/env` 端点的 POST 请求设置的任何属性，仅适用于接收请求的应用程序实例，是临时的，并且会在应用程序重新启动时丢失.

**浏览 HTTP 请求映射**

尽管 Spring MVC（和 Spring WebFlux 的）编程模型，通过简单地使用请求映射注释，对方法进行注释来轻松处理 HTTP 请求，但有时很难全面了解，应用程序可以处理的所有类型的 HTTP 请求，以及处理这些请求的组件类型。

Actuator 的 `/mappings` 端点提供了应用程序中每个 HTTP 请求处理程序的一站式视图，无论是来自 Spring MVC 控制器还是 Actuator 自己的端点之一。要获取 Spring Boot 应用程序中所有端点的完整列表，请向 `/mappings` 端点发出 GET 请求，您可能会收到类似于下面显示的简化响应的内容。

{% code title="清单 16.2 如 /mappings 端点所示的 HTTP 请求映射" %}

```bash
$ curl localhost:8081/actuator/mappings | jq
{
  "contexts": {
    "application-1": {
      "mappings": {
        "dispatcherHandlers": {
          "webHandler": [
            ...
            {
              "predicate": "{[/ingredients],methods=[GET]}",
              "handler": "public
              reactor.core.publisher.Flux<tacos.ingredients.Ingredient>
              tacos.ingredients.IngredientsController.allIngredients()",
              "details": {
                "handlerMethod": {
                  "className": "tacos.ingredients.IngredientsController",
                  "name": "allIngredients",
                  "descriptor": "()Lreactor/core/publisher/Flux;"
                },
                "handlerFunction": null,
                "requestMappingConditions": {
                  "consumes": [],
                  "headers": [],
                  "methods": [
                    "GET"
                  ],
                  "params": [],
                  "patterns": [
                    "/ingredients"
                  ],
                  "produces": []
                }
              }
            },
            ...
            ]
          }
        },
        "parentId": "application-1"
      },
      "bootstrap": {
        "mappings": {
          "dispatcherHandlers": {}
        },
        "parentId": null
      }
    }
}
```

{% endcode %}

为简洁起见，此响应已被删节以仅显示单个请求处理程序。具体来说，它表明对 `/ingredients` 的 GET 请求将由 IngredientsController 的 allIngredients() 方法处理。

**管理日志级别**

日志记录对任何应用程序来说都是非常重要的功能。日志记录可以提供一种审计手段，以及一种粗略的调试手段。

设置日志记录级别是需要进行反复权衡的行为。如果日志级别设置得低，日志中可能会出现过多的噪音，可能很难找到有用的信息。另一方面，如果您将日志记录级别设置得太高，则日志对于了解应用程序正在执行的操作可能没有多大价值。

日志级别通常应用在包级别上。如果您想知道正在运行的 Spring Boot 应用程序中设置了哪些日志记录级别，您可以向 `/loggers` 端点发出 GET 请求。以下 JSON 显示了对 `/loggers` 的响应的摘录：

```bash
{
  "levels": [ "OFF", "ERROR", "WARN", "INFO", "DEBUG", "TRACE" ],
  "loggers": {
    "ROOT": {
      "configuredLevel": "INFO", "effectiveLevel": "INFO"
    },
    ...
    "org.springframework.web": {
      "configuredLevel": null, "effectiveLevel": "INFO"
    },
    ...
    "tacos": {
      "configuredLevel": null, "effectiveLevel": "INFO"
    },
    "tacos.ingredients": {
      "configuredLevel": null, "effectiveLevel": "INFO"
    },
    "tacos.ingredients.IngredientServiceApplication": {
      "configuredLevel": null, "effectiveLevel": "INFO"
    }
  }
}
```

响应数据以所有有效日志记录级别的列表开始。之后，loggers 元素会列出应用程序中每个包的日志记录级别的详细信息。 configureLevel 属性显示已显式配置的日志记录级别（如果尚未显式配置，则为 null）。 EffectiveLevel 属性给出了有效的日志级别，它可能是从父包或根记录器继承的。

此示例仅显示了根记录器和四个包的日志记录级别，完整的响应会包括应用程序中每个包的日志记录级别条目，包含所有使用到的库中的包。如果您希望查看特定包，您可以将包名称指定为请求路径中的一部分。

例如，如果您只想知道为 tacocloud.ingredients 设置了哪些日志记录级别。您可以向 `/loggers/tacos.ingredients` 发出请求：

```bash
{
  "configuredLevel": null,
  "effectiveLevel": "INFO"
}
```

除了返回应用程序包的日志级别外，`/loggers` 端点还允许您通过发出 POST 请求来更改配置的日志级别。例如，假设您要将 `tacocloud.ingredients` 包的日志记录级别设置为 DEBUG。用以下 curl 命令将实现这一点：

```bash
$ curl localhost:8081/actuator/loggers/tacos/ingredients \
  -d'{"configuredLevel":"DEBUG"}' \
  -H"Content-type: application/json"
```

现在日志级别已更改，您可以向 `/loggers/tacos/ingredients` 发出 GET 请求以查看更改：

```bash
{
  "configuredLevel": "DEBUG",
  "effectiveLevel": "DEBUG"
}
```

请注意，configuredLevel 以前为 null，现在是 DEBUG。这种变化也传递到了 effectiveLevel。但最重要的是，如果该包中的任何代码在调试级别记录任何内容，日志文件将包含该调试级别信息。
