文档

§配置日志记录

Play 使用 SLF4J 进行日志记录,默认情况下由 Logback 作为其日志记录引擎。有关配置的详细信息,请参阅 Logback 文档

§默认配置

在开发模式下,Play 使用以下默认配置

<?xml version="1.0" encoding="UTF-8" ?>
<!--
   Copyright (C) from 2022 The Play Framework Contributors <https://github.com/playframework>, 2011-2021 Lightbend Inc. <https://www.lightbend.com>
-->

<!DOCTYPE configuration>

<!-- The default logback configuration that Play uses in dev mode if no other configuration is provided -->
<configuration>
  <import class="ch.qos.logback.classic.encoder.PatternLayoutEncoder"/>
  <import class="ch.qos.logback.core.ConsoleAppender"/>

  <appender name="STDOUT" class="ConsoleAppender">
    <encoder class="PatternLayoutEncoder">
      <pattern>%highlight(%-5level) %logger{15} - %message%n%xException{10}</pattern>
    </encoder>
  </appender>

  <logger name="play" level="INFO"/>
  <logger name="com.gargoylesoftware.htmlunit.javascript" level="OFF"/>

  <root level="WARN">
    <appender-ref ref="STDOUT"/>
  </root>

</configuration>

Play 在生产环境中使用以下默认配置

<?xml version="1.0" encoding="UTF-8" ?>
<!--
   Copyright (C) from 2022 The Play Framework Contributors <https://github.com/playframework>, 2011-2021 Lightbend Inc. <https://www.lightbend.com>
-->

<!DOCTYPE configuration>

<!-- The default logback configuration that Play uses if no other configuration is provided -->
<configuration>
  <import class="ch.qos.logback.classic.encoder.PatternLayoutEncoder"/>
  <import class="ch.qos.logback.classic.AsyncAppender"/>
  <import class="ch.qos.logback.core.ConsoleAppender"/>

  <appender name="STDOUT" class="ConsoleAppender">
    <encoder class="PatternLayoutEncoder">
      <pattern>%highlight(%-5level) %logger{15} - %message%n%xException{10}</pattern>
    </encoder>
  </appender>

  <appender name="ASYNCSTDOUT" class="AsyncAppender">
    <!-- increases the default queue size -->
    <queueSize>512</queueSize>
    <!-- don't discard messages -->
    <discardingThreshold>0</discardingThreshold>
    <!-- block when queue is full -->
    <neverBlock>false</neverBlock>
    <appender-ref ref="STDOUT"/>
  </appender>

  <logger name="play" level="INFO"/>
  <logger name="com.gargoylesoftware.htmlunit.javascript" level="OFF"/>

  <root level="WARN">
    <appender-ref ref="ASYNCSTDOUT"/>
  </root>

</configuration>

关于这些配置,需要注意以下几点

要添加文件记录器,请将以下追加器添加到您的 conf/logback.xml 文件中

<appender name="FILE" class="ch.qos.logback.core.FileAppender">
    <file>${application.home:-.}/logs/application.log</file>
    <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
        <pattern>%date [%level] from %logger in %thread - %message%n%xException</pattern>
    </encoder>
</appender>

可以选择使用异步追加器来包装 FileAppender

<appender name="ASYNCFILE" class="ch.qos.logback.classic.AsyncAppender">
    <appender-ref ref="FILE" />
</appender>

将必要的追加器添加到根节点

<root level="WARN">
    <appender-ref ref="ASYNCFILE" />
    <appender-ref ref="ASYNCSTDOUT" />
</root>

§安全日志记录

Play 中已为安全相关操作添加了一个安全标记,现在失败的安全检查将在 WARN 级别记录,并设置安全标记。这确保开发人员始终知道特定请求失败的原因,这对于现在 Play 中默认启用安全过滤器非常重要。

安全标记还允许安全故障触发或过滤,与正常日志记录区分开来。例如,要禁用所有带有 SECURITY 标记的日志记录,请将以下行添加到 logback.xml 文件中

<turboFilter class="ch.qos.logback.classic.turbo.MarkerFilter">
    <Marker>SECURITY</Marker>
    <OnMatch>DENY</OnMatch>
</turboFilter>

此外,使用安全标记的日志事件还可以触发向安全信息和事件管理 (SEIM) 引擎发送消息,以进行进一步处理。

§使用自定义应用程序加载器

请注意,当使用不扩展默认 GuiceApplicationLoader 的自定义应用程序加载器时(例如,当使用 编译时依赖注入 时),需要手动调用 LoggerConfigurator 来获取您的自定义配置。您可以使用以下代码来完成此操作

class MyApplicationLoaderWithInitialization extends ApplicationLoader {
  def load(context: Context) = {
    LoggerConfigurator(context.environment.classLoader).foreach {
      _.configure(context.environment, context.initialConfiguration, Map.empty)
    }
    new MyComponents(context).application
  }
}

§自定义配置

对于任何自定义配置,您都需要指定自己的 Logback 配置文件。

§使用项目源中的配置文件

您可以通过提供一个名为 conf/logback.xml 的文件来提供默认的日志记录配置。

§使用外部配置文件

您还可以通过系统属性指定配置文件。这在生产环境中特别有用,在生产环境中,配置文件可能在您的应用程序源代码之外进行管理。

注意:日志记录系统最优先考虑系统属性指定的配置文件,其次是 conf 目录中的文件,最后是默认文件。这使您可以自定义应用程序的日志记录配置,并仍然可以为特定环境或开发人员设置覆盖它。

§使用 -Dlogger.resource

指定要从类路径加载的配置文件

$ start -Dlogger.resource=prod-logger.xml

§使用 -Dlogger.file

指定要从文件系统加载的配置文件

$ start -Dlogger.file=/opt/prod/logger.xml

注意:要查看正在使用的文件,您可以设置一个系统属性来调试它:-Dlogback.debug=true

§示例

以下是一个使用滚动文件追加器以及用于输出访问日志的单独追加器的配置示例

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration>

<configuration>
  <import class="ch.qos.logback.classic.boolex.OnMarkerEvaluator"/>
  <import class="ch.qos.logback.classic.encoder.PatternLayoutEncoder"/>
  <import class="ch.qos.logback.core.rolling.RollingFileAppender"/>
  <import class="ch.qos.logback.core.filter.EvaluatorFilter"/>
  <import class="ch.qos.logback.core.FileAppender"/>
  <import class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"/>

  <appender name="FILE" class="RollingFileAppender">
    <file>${application.home:-.}/logs/application.log</file>
    <rollingPolicy class="TimeBasedRollingPolicy">
      <!-- Daily rollover with compression -->
      <fileNamePattern>${application.home:-.}/logs/application-log-%d{yyyy-MM-dd}.gz</fileNamePattern>
      <!-- keep 30 days worth of history -->
      <maxHistory>30</maxHistory>
    </rollingPolicy>
    <encoder class="PatternLayoutEncoder">
      <pattern>%date{yyyy-MM-dd HH:mm:ss ZZZZ} [%level] from %logger in %thread - %message%n%xException</pattern>
    </encoder>
  </appender>

  <appender name="SECURITY_FILE" class="FileAppender">
    <filter class="EvaluatorFilter">
      <evaluator class="OnMarkerEvaluator">
        <marker>SECURITY</marker>
      </evaluator>
      <onMismatch>DENY</onMismatch>
      <onMatch>ACCEPT</onMatch>
    </filter>
    <file>${application.home:-.}/logs/security.log</file>
    <encoder class="PatternLayoutEncoder">
      <pattern>%date [%level] [%marker] from %logger in %thread - %message%n%xException</pattern>
    </encoder>
  </appender>

  <appender name="ACCESS_FILE" class="RollingFileAppender">
    <file>${application.home:-.}/logs/access.log</file>
    <rollingPolicy class="TimeBasedRollingPolicy">
      <!-- daily rollover with compression -->
      <fileNamePattern>${application.home:-.}/logs/access-log-%d{yyyy-MM-dd}.gz</fileNamePattern>
      <!-- keep 1 week worth of history -->
      <maxHistory>7</maxHistory>
    </rollingPolicy>
    <encoder class="PatternLayoutEncoder">
      <pattern>%date{yyyy-MM-dd HH:mm:ss ZZZZ} %message%n</pattern>
      <!-- this quadruples logging throughput -->
      <immediateFlush>false</immediateFlush>
    </encoder>
  </appender>

  <!-- additivity=false ensures access log data only goes to the access log -->
  <logger name="access" level="INFO" additivity="false">
    <appender-ref ref="ACCESS_FILE"/>
  </logger>

  <root level="INFO">
    <appender-ref ref="FILE"/>
    <appender-ref ref="SECURITY_FILE"/>
  </root>

</configuration>

这演示了一些有用的功能

注意file 标签是可选的,如果要避免文件重命名,可以省略它。有关更多信息,请参阅Logback 文档

§包含属性

默认情况下,只有application.home 属性被导出到日志记录框架,这意味着文件可以相对于 Play 应用程序进行引用

 <file>${application.home:-}/example.log</file>

如果要引用在application.conf 文件中定义的属性,可以在application.conf 文件中添加play.logger.includeConfigProperties=true。应用程序启动时,配置中定义的所有属性都将对记录器可用

<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
    <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
        <pattern>context = ${my.property.defined.in.application.conf} %message%n</pattern>
    </encoder>
</appender>

§Pekko 日志记录配置

Pekko 系统日志记录可以通过将org.apache.pekko 记录器更改为 INFO 来完成。

<!-- Set logging for all Pekko library classes to INFO -->
<logger name="org.apache.pekko" level="INFO" />
<!-- Set a specific actor to DEBUG -->
<logger name="actors.MyActor" level="DEBUG" />

您可能还想为 Pekko 记录器配置一个附加器,其中包含有用的属性,例如线程和参与者地址。有关配置 Pekko 日志记录的更多信息,包括有关 Logback 和 Slf4j 集成的详细信息,请参阅Pekko 文档

§使用自定义日志记录框架

Play 默认使用 Logback,但可以配置 Play 使用另一个日志记录框架,只要有该框架的 SLF4J 适配器即可。为此,必须使用disablePlugins 禁用PlayLogback sbt 插件

lazy val root = (project in file("."))
  .enablePlugins(PlayScala)
  .disablePlugins(PlayLogback)

从那里,可以使用自定义日志记录框架。这里,Log4J 2 用作示例。

libraryDependencies ++= Seq(
  "org.apache.logging.log4j" % "log4j-slf4j-impl" % "2.19.0",
  "org.apache.logging.log4j" % "log4j-api" % "2.19.0",
  "org.apache.logging.log4j" % "log4j-core" % "2.19.0"
)

加载库和 SLF4J 适配器后,可以在命令行上照常设置log4j.configurationFile 系统属性。

如果需要根据 Play 的模式进行自定义配置,可以使用 LoggerConfigurator 进行额外的自定义。为此,请将 logger-configurator.properties 添加到类路径中,其中包含

play.logger.configurator=Log4J2LoggerConfigurator

然后用任何自定义扩展 LoggerConfigurator

Java
import com.typesafe.config.Config;
import com.typesafe.config.ConfigFactory;
import java.io.File;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import org.slf4j.ILoggerFactory;
import org.slf4j.LoggerFactory;
import play.Environment;
import play.LoggerConfigurator;
import play.Mode;
import play.api.PlayException;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.core.*;
import org.apache.logging.log4j.core.config.Configurator;

public class JavaLog4JLoggerConfigurator implements LoggerConfigurator {

  private ILoggerFactory factory;

  @Override
  public void init(File rootPath, Mode mode) {
    Map<String, String> properties = new HashMap<>();
    properties.put("application.home", rootPath.getAbsolutePath());

    String resourceName = "log4j2.xml";
    URL resourceUrl = this.getClass().getClassLoader().getResource(resourceName);
    configure(properties, Optional.ofNullable(resourceUrl));
  }

  @Override
  public void configure(Environment env) {
    Map<String, String> properties =
        LoggerConfigurator.generateProperties(env, ConfigFactory.empty(), Collections.emptyMap());
    URL resourceUrl = env.resource("log4j2.xml");
    configure(properties, Optional.ofNullable(resourceUrl));
  }

  @Override
  public void configure(
      Environment env, Config configuration, Map<String, String> optionalProperties) {
    // LoggerConfigurator.generateProperties enables play.logger.includeConfigProperties=true
    Map<String, String> properties =
        LoggerConfigurator.generateProperties(env, configuration, optionalProperties);
    URL resourceUrl = env.resource("log4j2.xml");
    configure(properties, Optional.ofNullable(resourceUrl));
  }

  @Override
  public void configure(Map<String, String> properties, Optional<URL> config) {
    try {
      LoggerContext loggerContext = (LoggerContext) LogManager.getContext(false);
      loggerContext.setConfigLocation(config.get().toURI());

      factory = LoggerFactory.getILoggerFactory();
    } catch (URISyntaxException ex) {
      throw new PlayException(
          "log4j2.xml resource was not found",
          "Could not parse the location for log4j2.xml resource",
          ex);
    }
  }

  @Override
  public ILoggerFactory loggerFactory() {
    return factory;
  }

  @Override
  public void shutdown() {
    LoggerContext loggerContext = (LoggerContext) LogManager.getContext();
    Configurator.shutdown(loggerContext);
  }
}
Scala
import java.io.File
import java.net.URI
import java.net.URL

import play.api.{Mode, Configuration, Environment, LoggerConfigurator}

import org.slf4j.ILoggerFactory

import org.apache.logging.log4j.LogManager
import org.apache.logging.log4j.core._
import org.apache.logging.log4j.core.config.Configurator

import org.slf4j.ILoggerFactory
import org.slf4j.LoggerFactory
import play.api.Configuration
import play.api.Environment
import play.api.LoggerConfigurator
import play.api.Mode

class Log4J2LoggerConfigurator extends LoggerConfigurator {
  private var factory: ILoggerFactory = _

  override def init(rootPath: File, mode: Mode): Unit = {
    val properties   = Map("application.home" -> rootPath.getAbsolutePath)
    val resourceName = "log4j2.xml"
    val resourceUrl  = Option(this.getClass.getClassLoader.getResource(resourceName))
    configure(properties, resourceUrl)
  }

  override def shutdown(): Unit = {
    val context = LogManager.getContext().asInstanceOf[LoggerContext]
    Configurator.shutdown(context)
  }

  override def configure(env: Environment): Unit = {
    val properties  = LoggerConfigurator.generateProperties(env, Configuration.empty, Map.empty)
    val resourceUrl = env.resource("log4j2.xml")
    configure(properties, resourceUrl)
  }

  override def configure(
      env: Environment,
      configuration: Configuration,
      optionalProperties: Map[String, String]
  ): Unit = {
    // LoggerConfigurator.generateProperties enables play.logger.includeConfigProperties=true
    val properties  = LoggerConfigurator.generateProperties(env, configuration, optionalProperties)
    val resourceUrl = env.resource("log4j2.xml")
    configure(properties, resourceUrl)
  }

  override def configure(properties: Map[String, String], config: Option[URL]): Unit = {
    val context = LogManager.getContext(false).asInstanceOf[LoggerContext]
    context.setConfigLocation(config.get.toURI)

    factory = LoggerFactory.getILoggerFactory
  }

  override def loggerFactory: ILoggerFactory = factory
}

下一步: 配置 WS SSL


发现本手册中的错误?此页面的源代码可在 此处 找到。阅读完 文档指南 后,请随时贡献拉取请求。有疑问或建议要分享?请访问 我们的社区论坛,与社区开始对话。