文件

§組態記錄

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 中預設啟用。

安全性標記也允許安全性失敗觸發或篩選與一般記錄不同的部分。例如,若要停用設定安全性標記的所有記錄,請將下列行新增到 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 檔中定義的屬性,你可以將 play.logger.includeConfigProperties=true 新增到你的 application.conf 檔。當應用程式啟動時,組態中定義的所有屬性都將可供記錄器使用

<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,但只要有 SLF4J 轉接器,就可以設定 Play 使用其他記錄架構。為此,必須使用 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


在此文件發現錯誤?此頁面的原始程式碼可以在 這裡 找到。在閱讀 文件指南 後,請隨時提交拉取請求。有問題或建議要分享?請前往 我們的社群論壇 與社群展開對話。