文件

§記錄 API

在應用程式中使用記錄功能有助於監控、除錯、錯誤追蹤和商業情報。Play 使用 SLF4J 作為記錄外觀,並以 Logback 作為預設記錄引擎。

§記錄架構

記錄 API 使用一組元件,協助您實作有效的記錄策略。

§記錄器

您的應用程式可以定義記錄器來傳送記錄訊息要求。每個記錄器都有名稱,會顯示在記錄訊息中,並用於設定。

記錄器遵循基於其命名的階層繼承結構。如果記錄器的名稱加上一個點是後代記錄器名稱的前綴,則該記錄器稱為另一個記錄器的祖先。例如,稱為「com.foo」的記錄器是稱為「com.foo.bar.Baz」的記錄器的祖先。所有記錄器都繼承自根記錄器。記錄器繼承允許您透過設定共同祖先來設定一組記錄器。

我們建議為每個類別建立個別命名的記錄器。遵循此慣例,Play 函式庫使用在「play」下命名空間的記錄器,許多第三方函式庫的記錄器會基於其類別名稱。

§記錄層級

記錄層級用於分類記錄訊息的嚴重性。當您撰寫記錄要求陳述式時,您會指定嚴重性,這會顯示在產生的記錄訊息中。

以下是可用記錄層級的集合,依嚴重性遞減排列。

除了對訊息分類之外,記錄等級還用於設定記錄器和附加程式上的嚴重性閾值。例如,設定為 資訊 等級的記錄器將記錄 資訊 或更高等級 (資訊警告錯誤) 的任何要求,但會忽略較低嚴重性的要求 (偵錯追蹤)。使用 關閉 將忽略所有記錄要求。

§附加程式

記錄 API 允許記錄要求列印到稱為「附加程式」的一個或多個輸出目的地。附加程式在組態中指定,且針對主控台、檔案、資料庫和其他輸出有選項。

附加程式與記錄器結合使用,可以幫助您路由和篩選記錄訊息。例如,您可以使用一個附加程式針對記錄分析有用資料的記錄器,並使用另一個由操作團隊監控的錯誤附加程式。

注意:有關架構的更多資訊,請參閱 Logback 文件

§使用記錄器

首先匯入 記錄器 類別

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

§建立記錄器

您可以使用 記錄器工廠名稱 參數建立新的記錄器

final Logger accessLogger = LoggerFactory.getLogger("access");

記錄應用程式事件的常見策略是使用類別名稱針對每個類別使用不同的記錄器。記錄 API 支援此功能,並提供採用類別參數的工廠方法

final Logger log = LoggerFactory.getLogger(this.getClass());

然後您可以使用 記錄器 來撰寫記錄陳述式

// Log some debug info
logger.debug("Attempting risky calculation.");

try {
  final int result = riskyCalculation();

  // Log result if successful
  logger.debug("Result={}", result);
} catch (Throwable t) {
  // Log error with message and Throwable.
  logger.error("Exception with riskyCalculation", t);
}

使用 Play 的預設記錄設定,這些陳述式會產生類似以下的控制台輸出

[debug] c.e.s.MyClass - Attempting risky calculation.
[error] c.e.s.MyClass - Exception with riskyCalculation
java.lang.ArithmeticException: / by zero
    at controllers.Application.riskyCalculation(Application.java:20) ~[classes/:na]
    at controllers.Application.index(Application.java:11) ~[classes/:na]
    at Routes$$anonfun$routes$1$$anonfun$applyOrElse$1$$anonfun$apply$1.apply(routes_routing.scala:69) [classes/:na]
    at Routes$$anonfun$routes$1$$anonfun$applyOrElse$1$$anonfun$apply$1.apply(routes_routing.scala:69) [classes/:na]
    at play.core.Router$HandlerInvoker$$anon$8$$anon$2.invocation(Router.scala:203) [play_2.10-2.3-M1.jar:2.3-M1]

請注意,這些訊息具有記錄層級、記錄器名稱(在本例中為類別名稱,以縮寫形式顯示)、訊息,以及在記錄要求中使用 Throwable 時的堆疊追蹤。

還有 play.Logger 靜態方法,可讓您存取名為 application 的記錄器,但在 Play 2.7.0 及以上版本中,它們的使用已過時。您應該使用上述定義的策略之一來宣告自己的記錄器執行個體。

§使用標記

SLF4J API 有標記的概念,其作用是豐富記錄訊息並標示出具有特殊意義的訊息。標記對於觸發和篩選特別有用,例如 OnMarkerEvaluator 可以看見標記時傳送電子郵件,或可以將特定流程標示到自己的附加程式。

標記非常有用,因為它們可以為記錄器提供額外的內容文字資訊。例如,使用 Logstash Logback Encoder,可以自動將要求資訊編碼到記錄陳述式中

import static net.logstash.logback.marker.Markers.append;

private Marker requestMarker(Http.Request request) {
  return append("host", request.host()).and(append("path", request.path()));
}

public Result index(Http.Request request) {
  logger.info(requestMarker(request), "Rendering index()");
  return ok("foo");
}

請注意,標記對於「追蹤子彈」風格的記錄也很有用,您可以在不顯式變更記錄層級的情況下,針對特定要求進行記錄。例如,您可以在滿足特定條件時才新增標記

public class JavaTracerController extends Controller {

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

  private static final Marker tracerMarker = org.slf4j.MarkerFactory.getMarker("TRACER");

  private Marker tracer(Http.Request request) {
    Marker marker = MarkerFactory.getDetachedMarker("dynamic"); // base do-nothing marker...
    request.queryString("trace").ifPresent(s -> marker.add(tracerMarker));
    return marker;
  }

  public Result index(Http.Request request) {
    logger.trace(tracer(request), "Only logged if queryString contains trace=true");
    return ok("hello world");
  }
}

然後在 logback.xml 中使用以下 TurboFilter 觸發記錄

<turboFilter class="ch.qos.logback.classic.turbo.MarkerFilter">
  <Name>TRACER_FILTER</Name>
  <Marker>TRACER</Marker>
  <OnMatch>ACCEPT</OnMatch>
</turboFilter>

此時,您可以動態設定偵錯陳述式以回應輸入。

如需瞭解如何在記錄中使用標記的更多資訊,請參閱 Logback 手冊中的 TurboFilters基於標記的觸發 部分。

§記錄模式

有效使用記錄器可協助您使用相同的工具達成許多目標

import java.util.concurrent.CompletionStage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import play.mvc.Action;
import play.mvc.Controller;
import play.mvc.Http;
import play.mvc.Result;
import play.mvc.With;

public class Application extends Controller {

  private static final Logger logger = LoggerFactory.getLogger(Application.class);

  @With(AccessLoggingAction.class)
  public Result index() {
    try {
      final int result = riskyCalculation();
      return ok("Result=" + result);
    } catch (Throwable t) {
      logger.error("Exception with riskyCalculation", t);
      return internalServerError("Error in calculation: " + t.getMessage());
    }
  }

  private static int riskyCalculation() {
    return 10 / (new java.util.Random()).nextInt(2);
  }
}

class AccessLoggingAction extends Action.Simple {

  private static final Logger accessLogger = LoggerFactory.getLogger(AccessLoggingAction.class);

  public CompletionStage<Result> call(Http.Request request) {
    accessLogger.info(
        "method={} uri={} remote-address={}",
        request.method(),
        request.uri(),
        request.remoteAddress());

    return delegate.call(request);
  }
}

此範例使用 動作組成 來定義一個 AccessLoggingAction,它會將請求資料記錄到名為「access」的記錄器。Application 控制器使用此動作,並使用它自己的記錄器(以其類別命名)記錄應用程式事件。在組態中,您接著可以將這些記錄器路由到不同的附加程式,例如存取記錄和應用程式記錄。

如果您只想針對特定動作記錄請求資料,上述設計會很好用。若要記錄所有請求,最好使用 篩選器 或延伸 play.http.DefaultHttpRequestHandler

§組態

有關組態的詳細資訊,請參閱 組態記錄

下一頁:進階主題


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