- 后端
- 认证鉴权
- 单点登录
【免费下载链接】cas
Apereo CAS - Identity & Single Sign On for all earthlings and beyond.
导读
本文基于 Apereo CAS 的cas-server-support-gcp-logging模块,系统讲解如何将 CAS 的运行日志接入 Google Cloud Logging(Cloud Logging),实现云端集中化日志收集、检索与分析。你将掌握三种落地路径:使用JsonTemplateLayout的 JSON 模板布局、使用 CAS 提供的专用GoogleCloudAppender自定义 Appender,以及通过 Actuator 端点gcpLogs在运行时直接拉取 GCP 上的日志条目;同时深入理解 CAS 如何自动把X-B3-TraceId、X-Cloud-Trace-Context等分布式追踪头信息关联到每条日志,为生产环境的排障与链路追踪提供完整闭环。
集成总览
Cloud Logging)与它对接。从源码结构看(cas-server-support-gcp-logging 源码目录),该集成由四个核心组件组成:
| 组件 | 职责 | 源码位置 |
|---|---|---|
GoogleCloudAppender | Log4j2 自定义 Appender,将日志事件转换为符合 GCP 结构的 JSON 输出 | GoogleCloudAppender.java |
GoogleCloudLoggingWebInterceptor | Spring MVC 拦截器,把请求的 Trace ID 与 URL 写入 MDC(ThreadContext) | GoogleCloudLoggingWebInterceptor.java |
GoogleCloudLogsEndpoint | Actuator 端点gcpLogs,按条件查询并返回 GCP 中的日志条目 | GoogleCloudLogsEndpoint.java |
CasGoogleCloudLoggingAutoConfiguration | Spring Boot 自动配置,装配拦截器、端点与 GCPLogging服务 Bean | CasGoogleCloudLoggingAutoConfiguration.java |
其中自动配置类在模块的META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports中注册(见 AutoConfiguration.imports),并通过@ConditionalOnFeatureEnabled(feature = CasFeatureModule.FeatureCatalog.Logging, module = "gcp")条件开启,即只有显式引入该模块并启用logging.gcp特性时才生效。
启用方式:WAR Overlay 中添加依赖
在 CAS WAR Overlay 中启用该集成的第一步是引入模块依赖(对应原文档的casmodule引用,Maven 坐标为org.apereo.cas:cas-server-support-gcp-logging):
<dependency> <groupId>org.apereo.cas</groupId> <artifactId>cas-server-support-gcp-logging</artifactId> <version>${cas.version}</version> </dependency>引入后,build.gradle 会带入 Spring Cloud GCP Logging Starter 与 Google Cloud Logging 客户端库(同时排除了 Logback 相关依赖,因为 CAS 使用 Log4j2),并在编译期生成 Log4j2 插件元数据,使GoogleCloudAppender在log4j2.xml中可直接被识别。
凭据与项目 ID:环境变量优先原则
原文档特别强调了一个易踩的坑:由于日志输出链路的特殊性,CAS 属性(cas.logging.gcp.*)中定义的 Google Cloud project ID 和凭据并不会被写日志的 Appender 使用。正确的做法是设置环境变量:
export GOOGLE_CLOUD_PROJECT="your-gcp-project-id" export GOOGLE_APPLICATION_CREDENTIALS="/path/to/service-account-key.json"这一行为在源码中可以得到印证:GoogleCloudAppender的构造逻辑中,只有当 XML 属性projectId未显式指定时,才会回退到DefaultGcpProjectIdProvider解析项目 ID(见 GoogleCloudAppender.java),而该 Provider 正是从GOOGLE_CLOUD_PROJECT环境变量读取。因此有两条等效途径:
- 通过环境变量
GOOGLE_CLOUD_PROJECT/GOOGLE_APPLICATION_CREDENTIALS提供项目 ID 与凭据; - 直接在日志配置文件(
log4j2.xml)的<GoogleCloudAppender projectId="...">中显式写入项目 ID。
需要说明的是,cas.logging.gcp.project-id属性并非没有用途——它被自动配置类用于构建查询端点使用的 GCPLogging服务 Bean(LoggingOptions.newBuilder().setProjectId(projectId).build().getService(),见 CasGoogleCloudLoggingAutoConfiguration.java),供gcpLogs端点检索日志时使用。
方案一:JsonTemplateLayout JSON 模板布局
如果不想引入 CAS 专用模块,可以仅使用 Log4j2 自带的JsonTemplateLayout生成结构化的 JSON 日志。它是一种可定制、高效且无 GC 压力(garbage-free)的 JSON 生成布局,会按照所提供 JSON 模板的结构对 LogEvent 进行编码:
<JsonTemplateLayout eventTemplateUri="classpath:GcpLayout.json"/>GcpLayout.json是一个由部署方放置于应用 classpath 下的 JSON 模板文件,模板中定义了日志字段到 GCP 日志结构的映射(如时间戳、严重级别、logging.googleapis.com/*保留字段等)。该方案适合希望保持最小依赖、完全自定义 JSON 输出的场景。
方案二(推荐):CAS 专用 GoogleCloudAppender
完整配置示例
这是原文档提供的 log4j2 配置,结合 log4j2-test.xml 中的真实测试配置,整理为可直接落地的完整版本:
<Configuration> <Appenders> <!-- 内嵌的 Console Appender,负责把日志渲染为 GCP 认可的 JSON 结构 --> <Console name="Console" target="SYSTEM_OUT"> <JsonLayout locationInfo="false" includeStacktrace="true" objectMessageAsJsonObject="true" compact="true" properties="false" eventEol="true" includeTimeMillis="false"> <KeyValuePair key="time" value="$${event:timestamp:-}"/> <KeyValuePair key="timestampSeconds" value="$${ctx:timestampSeconds:-}"/> <KeyValuePair key="timestampNanos" value="$${ctx:timestampNanos:-}"/> <KeyValuePair key="severity" value="$${ctx:severity:-}"/> <KeyValuePair key="logging.googleapis.com/insertId" value="$${ctx:insertId:-}"/> <KeyValuePair key="logging.googleapis.com/spanId" value="$${ctx:spanId:-}"/> <KeyValuePair key="logging.googleapis.com/trace" value="$${ctx:traceId:-}"/> </JsonLayout> </Console> <!-- CAS 专用 Appender,包一层引用了上面的 Console Appender --> <!-- 更新 projectId,或删除该属性交由 CAS 自动解析 --> <GoogleCloudAppender name="GoogleCloudAppender" flattenMessage="true" projectId="..."> <AppenderRef ref="Console"/> </GoogleCloudAppender> </Appenders> <Loggers> <Logger name="org.apereo.cas" includeLocation="true" level="INFO" additivity="false"> <AppenderRef ref="GoogleCloudAppender"/> </Logger> </Loggers> </Configuration>注意配置中的<AppenderRef ref="casConsole"/>与原文档保持一致(实际引用名须与你定义的 Console Appender 的name匹配,示例中为Console)。
GoogleCloudAppender 参数详解
从 GoogleCloudAppender.java 的@PluginFactory签名可以提取出完整的可配置属性:
| 属性 | 默认值 | 说明 |
|---|---|---|
name | 必填 | Appender 名称,供 Logger 的AppenderRef引用 |
projectId | 空 | GCP 项目 ID。为空时由DefaultGcpProjectIdProvider从环境自动解析;属性标记为sensitive,不会在日志配置转储中暴露明文 |
labels | application=cas | 逗号分隔的key=value标签列表,会被写入日志的labels字段,便于在 GCP 中过滤 |
flattenMessage | false | 为true时消息以扁平字符串输出(ObjectMessage(formattedMessage));为false时消息被结构化为{text, ...参数}对象 |
requiresLocation | true | 是否要求记录源码位置(类、方法、文件、行号),与 Logger 的includeLocation配合 |
AppenderRef | 必填 | 指向内嵌的 Console/JsonLayout Appender 引用 |
Filter | 可选 | Log4j2 过滤器 |
Appender 内部做了什么
GoogleCloudAppender.append()的核心流程(GoogleCloudAppender.java)在转发日志前依次向上下文数据(MDC)补充以下 GCP 专用字段:
insertId:由 Log4j2 配置的NanoClock的纳秒时间生成(collectInsertId),保证每条日志在 GCP 中拥有唯一插入 ID;labels:写入labels及每个label-{key}字段(collectLabels);sourceLocation:写入sourceLocation对象及sourceLocation-{class,function,file,line}字段(collectSourceLocation),这是requiresLocation=true的前提;httpRequest:若 MDC 中存在requestUrl,则组装requestMethod、requestUrl、protocol、userAgent、remoteIp等 HTTP 请求元数据(collectHttpRequest);timestampSeconds/timestampNanos:由日志事件时间戳换算的秒与纳秒(collectTimestamps);traceId:GCP 格式的完整 Trace 名称(见下节)。
此外,日志消息会经过 CAS 的MessageSanitizer消毒处理后再输出(见buildLogMessage),避免敏感信息直接进入云端日志;参数化消息(如 Map、POJO 对象)会被结构化为消息负载字段。
分布式 Trace 自动关联机制
原文档指出,集成会自动把 Web 请求的 Trace ID 与对应日志条目关联起来——通过从 MDC 中检索X-B3-TraceId或X-Cloud-Trace-Context头值实现。这一机制由两个组件协作完成:
1. 请求侧采集:GoogleCloudLoggingWebInterceptor(GoogleCloudLoggingWebInterceptor.java)是一个注册在/**路径上的 Spring MVC 拦截器(并以RefreshableHandlerInterceptor包装,同时注册进 Webflow 执行计划)。它优先使用CloudTraceIdExtractor解析X-Cloud-Trace-Context头,若不存在则回退读取X-B3-TraceId头,随后写入 ThreadContext 的traceId与requestUrl(含 query string)。
2. 输出侧组装:GoogleCloudAppender.collectTraceId()(GoogleCloudAppender.java)依次检查 MDC 中的StackdriverTraceConstants.MDC_FIELD_TRACE_ID(即traceId)、X-B3-TraceId、traceparent、X-Cloud-Trace-Context字段,取第一个非空值,并通过StackdriverTraceConstants.composeFullTraceName(projectId, traceId)组装成 GCP 要求的完整格式projects/{projectId}/traces/{traceId}写入traceId上下文。日志 JSON 中对应的logging.googleapis.com/trace字段即可被 Cloud Logging 识别。
一个值得注意的细节是 64 位 Trace ID 的兼容处理:formatTraceId()(GoogleCloudAppender.java)会在 16 字符(64 位)的 Trace ID 前补齐 16 个0,将其规范化为 GCP 要求的 128 位格式,避免因位数不足导致 Trace 关联失效。
这一行为有测试用例覆盖:GoogleCloudAppenderTests.java 构造携带X-B3-TraceId头的POST /login请求调用preHandle,随后验证不同消息类型(普通字符串、POJO 参数化消息、Map 消息等)都能经由GoogleCloudAppender正常输出。
Actuator 端点:gcpLogs
CAS 提供gcpLogsActuator 端点用于在运行时直接查询 GCP 中的日志。端点默认访问级别为Access.NONE,需通过management.endpoint.gcpLogs.access或 Spring Security 配置放行后才可访问。
端点行为
GoogleCloudLogsEndpoint(GoogleCloudLogsEndpoint.java)暴露GET /actuator/gcpLogs/stream,支持两个查询参数:
| 参数 | 默认值 | 说明 |
|---|---|---|
count | 50 | 返回日志条目的最大数量 |
level | INFO | 过滤的日志级别(severity),如INFO、WARNING、ERROR |
端点构造的过滤表达式为severity={level},并在此基础上叠加cas.logging.gcp.log-name(AND logName={logName})与cas.logging.gcp.labels(AND resource.labels.{key}={value}),随后调用 GCPLogging.listLogEntries按时间戳降序拉取(SortingField.TIMESTAMP, SortingOrder.DESCENDING),最终返回包含message、timestamp、level、labels的 JSON 列表。
调用示例:
# 拉取最近 100 条 ERROR 级别的日志 curl -H "Authorization: Bearer ${ACCESS_TOKEN}" \ "https://cas.example.org/actuator/gcpLogs/stream?count=100&level=ERROR"该端点行为由 GoogleCloudLogsEndpointTests.java 验证,其测试配置中给出了完整的属性组合:
cas.logging.gcp.log-name=projects/cas-project-id/logs/cas-server cas.logging.gcp.project-id=hello-gkej2ee-test3 cas.logging.gcp.labels.namespace_name=cas-idp-0-develop management.endpoint.gcpLogs.access=UNRESTRICTED management.endpoints.web.exposure.include=*配置属性:cas.logging.gcp.*
CAS 将 GCP 日志相关配置归入cas.logging.gcp.*命名空间,对应属性模型 GoogleCloudLogsProperties.java(由 LoggingProperties.java 中的gcp嵌套属性承载):
| 属性 | 必填 | 说明 |
|---|---|---|
cas.logging.gcp.log-name | 是 | GCP 日志名称,用于定位具体的日志。语法为projects/[PROJECT_ID]/logs/[LOG_ID],例如projects/cas-project-id/logs/cas-server |
cas.logging.gcp.project-id | 是 | GCP 项目 ID,全局唯一,用于构建查询端点的Logging服务 |
cas.logging.gcp.labels | 否 | 资源标签 Map(labels.{key}={value}),用于在端点查询时过滤日志条目,例如cas.logging.gcp.labels.namespace_name=cas-idp-0-develop |
再次强调:这些属性服务于gcpLogs查询端点;写日志路径的项目 ID 与凭据仍以环境变量或 log4j2 XML 中projectId属性为准(见上文"环境变量优先原则")。
进阶:为 JSON 补充 K8s / Docker / Spring 元数据
测试配置 log4j2-test.xml 展示了比原文档示例更丰富的字段注入方式:在JsonLayout中通过 Lookup 表达式补充容器与平台元数据,例如:
<KeyValuePair key="kubernetes.podName" value="$${k8s:podName:-}"/> <KeyValuePair key="kubernetes.namespaceName" value="$${k8s:namespaceName:-}"/> <KeyValuePair key="spring.application.name" value="$${spring:spring.application.name:-}"/> <KeyValuePair key="docker.containerId" value="$${docker:containerId:-}"/>这些字段在 GCP 中可以配合资源标签与resource.labels过滤条件做集群维度、应用维度的日志下钻,适合 CAS 以容器/K8s 方式部署在 Google Cloud 的场景。
总结与排查建议
- 启用路径:Overlay 引入
cas-server-support-gcp-logging→ 设置GOOGLE_CLOUD_PROJECT与GOOGLE_APPLICATION_CREDENTIALS环境变量 → 在log4j2.xml配置GoogleCloudAppender(或使用JsonTemplateLayout)→ 可选启用gcpLogs端点。 - Trace 关联不上时:检查请求是否携带
X-B3-TraceId/X-Cloud-Trace-Context头,且拦截器是否被注册(自动配置开启前提是logging.gcp特性可用);同时确认日志 JSON 中的logging.googleapis.com/trace字段是projects/{projectId}/traces/{traceId}完整格式。 - 日志不出现时:核对
cas.logging.gcp.*仅作用于查询端点,写日志侧的项目 ID 与凭据必须来自环境变量或 XML 中的projectId属性;并确认 Appender 内嵌引用的 Console Appender 名称与AppenderRef一致。 - 字段缺失时:
sourceLocation需要requiresLocation=true且 Logger 声明includeLocation="true";httpRequest需要 MDC 中存在requestUrl。
如需了解 CAS 日志体系的其他对接方式(MDC 机制、Logback 与 Log4j2 配置、Syslog、Splunk 等),可继续阅读 Logging 目录 下的相关文档,如 Logging-MDC.md 与 Logging-Logback.md。
- 后端
- 认证鉴权
- 单点登录
【免费下载链接】cas
Apereo CAS - Identity & Single Sign On for all earthlings and beyond.
相关推荐
Apereo CAS 集成 Google Cloud Secret Manager:Spring Cloud 配置数据源实战指南
Apereo CAS 集成 Google Cloud Secret Manager:Spring Cloud 配置数据源实战指南 导读 本指南围绕 Apereo
后端认证鉴权单点登录Microsoft.UI.Xaml MapControl 设计规格与实现剖析:基于 WebView2 与 Azure Maps 的 WinUI 3 地图控件
Microsoft.UI.Xaml MapControl 设计规格与实现剖析:基于 WebView2 与 Azure Maps 的 WinUI 3 地图控件 本
后端认证鉴权单点登录gh_mirrors/cas/cas容器化日志管理:Docker Log Driver与集中式收集
gh_mirrors/cas/cas容器化日志管理:Docker Log Driver与集中式收集 在容器化部署环境中,gh_mirrors/cas/cas(以
后端认证鉴权单点登录
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考