Knative serving でのロギングと表示

このページでは、Knative serving で使用可能なログと、ログの表示および作成方法について説明します。

Knative serving には、次の 2 種類のログがあります。

  • リクエストログ: Knative serving サービスに送信されたリクエストのログ。これらのログは自動的に作成されます。
  • コンテナログ: コンテナ インスタンス(通常は独自のコード)から出力されたログです。コンテナログを作成するで説明されているように、サポートされているロケーションに書き込まれます。

ログを有効にする

Google Cloud の場合、ログは Cloud Logging に自動的に送信されます。Google Distributed Cloud の場合は、まずログを有効にする必要があります

ログの表示

サービスのログはいくつかの方法で表示できます。

  • Google Cloud コンソールで Knative serving のページを使用する。
  • Google Cloud コンソールで Cloud Logging のログ エクスプローラを使用する。

どちらの表示方法でも、Cloud Logging に保存されている同じログを調べることができますが、Cloud Logging のログ エクスプローラでは、より詳細な情報が表示されます。また、より多くのフィルタリング機能を使用できます。

Knative serving でのログの表示

Knative serving のページでログを表示するには:

  1. Knative serving に移動

  2. 表示されたリストで、必要なサービスをクリックします。

  3. このサービスのすべてのリビジョンのリクエストログとコンテナログを取得するには、[ログ] タブをクリックします。ログの重大度でフィルタリングできます。

Cloud Logging でのログの表示

Cloud Logging のログ エクスプローラで Knative serving ログを表示するには:

  1. Google Cloud コンソールの [ログ エクスプローラ] ページに移動します。

  2. ページの上部で既存の Google Cloud プロジェクトを選択するか、新しいプロジェクトを作成します。

  3. プルダウン メニューからリソース(Kubernetes コンテナ)を選択します。

詳細については、ログ エクスプローラの使用をご覧ください。

Cloud Code でのログの表示

Cloud Code でログを表示するには、IntelliJVisual Studio Code のガイドをご覧ください。

プログラムでログを読み取る

プログラムでログを読み取るには、次のいずれかの方法を使用します。

コンテナログを作成する

サービスからログを作成する場合、ログの出力先が次のいずれかであれば、Cloud Logging によってログが自動的に取得されます。

ほとんどのデベロッパーは、ログの出力先として標準出力と標準エラーを想定しています。

これらのサポートされているロケーションに作成されたコンテナログは、Knative serving サービス、リビジョン、ロケーションに自動的に関連付けられます。

ログでの単純なテキストと構造化 JSON の使用を比較する

ログに出力するときに、単純なテキスト文字列を送信することも、シリアル化された JSON(構造化データ)の 1 行を送信することもできます。これは Cloud Logging によって取得され、解析された後、jsonPayload に配置されます。単純なテキスト メッセージは textPayload に配置されます。

構造化ログへの書き込み

次のスニペットは、構造化ログエントリを書き込む方法を示しています。また、ログメッセージを対応するリクエストログと関連付ける方法も示しています。

Node.js


// Uncomment and populate this variable in your code:
// const project = 'The project ID of your function or Cloud Run service';

// Build structured log messages as an object.
const globalLogFields = {};

// Add log correlation to nest all log messages beneath request log in Log Viewer.
// (This only works for HTTP-based invocations where `req` is defined.)
if (typeof req !== 'undefined') {
  const traceHeader = req.header('X-Cloud-Trace-Context');
  if (traceHeader && project) {
    const [trace] = traceHeader.split('/');
    globalLogFields['logging.googleapis.com/trace'] =
      `projects/${project}/traces/${trace}`;
  }
}

// Complete a structured log entry.
const entry = Object.assign(
  {
    severity: 'NOTICE',
    message: 'This is the default display field.',
    // Log viewer accesses 'component' as 'jsonPayload.component'.
    component: 'arbitrary-property',
  },
  globalLogFields
);

// Serialize to a JSON string and output.
console.log(JSON.stringify(entry));

Python

# Uncomment and populate this variable in your code:
# PROJECT = 'The project ID of your Cloud Run service';

# Build structured log messages as an object.
global_log_fields = {}

# Add log correlation to nest all log messages.
# This is only relevant in HTTP-based contexts, and is ignored elsewhere.
# (In particular, non-HTTP-based Cloud Functions.)
request_is_defined = "request" in globals() or "request" in locals()
if request_is_defined and request:
    trace_header = request.headers.get("X-Cloud-Trace-Context")

    if trace_header and PROJECT:
        trace = trace_header.split("/")
        global_log_fields[
            "logging.googleapis.com/trace"
        ] = f"projects/{PROJECT}/traces/{trace[0]}"

# Complete a structured log entry.
entry = dict(
    severity="NOTICE",
    message="This is the default display field.",
    # Log viewer accesses 'component' as jsonPayload.component'.
    component="arbitrary-property",
    **global_log_fields,
)

print(json.dumps(entry))

Go

各ログエントリの構造は、Entry タイプによって指定されます。


// Entry defines a log entry.
type Entry struct {
	Message  string `json:"message"`
	Severity string `json:"severity,omitempty"`
	Trace    string `json:"logging.googleapis.com/trace,omitempty"`

	// Logs Explorer allows filtering and display of this as `jsonPayload.component`.
	Component string `json:"component,omitempty"`
}

// String renders an entry structure to the JSON format expected by Cloud Logging.
func (e Entry) String() string {
	if e.Severity == "" {
		e.Severity = "INFO"
	}
	out, err := json.Marshal(e)
	if err != nil {
		log.Printf("json.Marshal: %v", err)
	}
	return string(out)
}

Entry 構造体がログに記録されると、String メソッドが呼び出され、Cloud Logging で想定されている JSON 形式にマーシャリングされます。


func init() {
	// Disable log prefixes such as the default timestamp.
	// Prefix text prevents the message from being parsed as JSON.
	// A timestamp is added when shipping logs to Cloud Logging.
	log.SetFlags(0)
}

func indexHandler(w http.ResponseWriter, r *http.Request) {
	// Uncomment and populate this variable in your code:
	// projectID = "The project ID of your Cloud Run service"

	// Derive the traceID associated with the current request.
	var trace string
	if projectID != "" {
		traceHeader := r.Header.Get("X-Cloud-Trace-Context")
		traceParts := strings.Split(traceHeader, "/")
		if len(traceParts) > 0 && len(traceParts[0]) > 0 {
			trace = fmt.Sprintf("projects/%s/traces/%s", projectID, traceParts[0])
		}
	}

	log.Println(Entry{
		Severity:  "NOTICE",
		Message:   "This is the default display field.",
		Component: "arbitrary-property",
		Trace:     trace,
	})

	fmt.Fprintln(w, "Hello Logger!")
}

Java

logback.xml の構成で Logstash JSON Encoder を有効にして、LogbackSLF4J での JSON ロギングを有効にします。

// Build structured log messages as an object.
Object globalLogFields = null;

// Add log correlation to nest all log messages beneath request log in Log Viewer.
// TODO(developer): delete this code if you're creating a Cloud
//                  Function and it is *NOT* triggered by HTTP.
String traceHeader = req.headers("x-cloud-trace-context");
if (traceHeader != null && project != null) {
  String trace = traceHeader.split("/")[0];
  globalLogFields =
      kv(
          "logging.googleapis.com/trace",
          String.format("projects/%s/traces/%s", project, trace));
}
// -- End log correlation code --

// Create a structured log entry using key value pairs.
// For instantiating the "logger" variable, see
// https://cloud.google.com/run/docs/logging#run_manual_logging-java
logger.error(
    "This is the default display field.",
    kv("component", "arbitrary-property"),
    kv("severity", "NOTICE"),
    globalLogFields);
<configuration>
  <appender name="jsonConsoleAppender" class="ch.qos.logback.core.ConsoleAppender">
    <encoder class="net.logstash.logback.encoder.LogstashEncoder">
      <!-- Ignore default logging fields -->
      <fieldNames>
        <timestamp>[ignore]</timestamp>
        <version>[ignore]</version>
        <logger>[ignore]</logger>
        <thread>[ignore]</thread>
        <level>[ignore]</level>
        <levelValue>[ignore]</levelValue>
      </fieldNames>
    </encoder>
  </appender>
  <root level="INFO">
    <appender-ref ref="jsonConsoleAppender"/>
  </root>
</configuration>

メッセージ内の特別な JSON フィールド

特殊フィールドのドキュメントで説明されているように、構造化ログを JSON ディクショナリとして提供すると、いくつかの特殊フィールドが jsonPayload から削除され、生成された LogEntry の対応フィールドに書き込まれます。

たとえば、JSON に severity プロパティが含まれている場合、jsonPayload から削除され、代わりにログエントリの severity として表示されます。message プロパティが存在する場合は、ログエントリのメイン表示テキストとして使用されます。特別なプロパティの詳細については、以下のロギング リソース セクションをご覧ください。

コンテナログとリクエストログを関連付けする

ログ エクスプローラでは、同じ trace で関連付けられたログを「親子」形式で表示できます。リクエスト ログエントリの左側にある三角形のアイコンをクリックすると、該当するリクエストに関連付けられたコンテナログが、リクエストログの下にネストされた状態で表示されます。

Cloud Logging クライアント ライブラリを使用しない限り、コンテナログは自動的にリクエストログに関連付けられません。クライアント ライブラリを使用せずにコンテナログをリクエストログに関連付けるには、上記の構造化ロギングのサンプルで示した X-Cloud-Trace-Context ヘッダーから抽出された、トレース ID を持つ logging.googleapis.com/trace フィールドを含む構造化 JSON ログの行を使用します。

リクエストログのリソース使用量を制御する

リクエストログは自動的に作成されます。Knative serving から直接リクエストログの量を制御することはできませんが、Cloud Logging のログの除外機能を利用できます。

ロギング エージェントに関する注意事項

Compute Engine などの特定の Google Cloud プロダクトで Cloud Logging を使用している場合は、Cloud Logging のロギング エージェントを使用した可能性があります。ログ収集のサポートが組み込まれているため、Knative serving ではロギング エージェントを使用しません。

ロギング リソース

ログ エクスプローラでログエントリをクリックすると、JSON 形式のログエントリが表示され、必要な詳細を確認できます。

タイムスタンプ、重大度、httpRequest など、ログエントリ内のすべてのフィールドが標準で表示されます。詳しくは、ログエントリのドキュメントをご覧ください。

ただし、Knative serving に固有のラベルやリソースラベルがあります。以下にサンプルを示します。

{
 httpRequest: {}
 insertId:  "5c82b3d1000ece0000000000"
 labels: {
  instanceId:  "00bf4bf00000fb59c906a00000c9e29c2c4e06dce91500000000056008d2b6460f163c0057b97b2345f2725fb2423ee5f0bafd36df887fdb1122371563cf1ff453717282afe000001"
 }
 logName:  "projects/my-project/logs/kubernetes-engine/enterprise/knative-serving/.googleapis.com%2Frequests"
 receiveTimestamp:  "2019-03-08T18:26:25.981686167Z"
 resource: {
  labels: {
   configuration_name:  "myservice"
   location:  "us-central1"
   project_id:  "my-project"
   revision_name:  "myservice-00002"
   service_name:  "myservice"
  }
  type:  "cloud_run_revision"
 }
 severity:  "INFO"
 timestamp:  "2019-03-08T18:26:25.970397Z"
}
フィールド 値とメモ
instanceId リクエストを処理したコンテナ インスタンス。
logName リクエストログ、標準エラー、標準出力など、ログの種類を表します。
configuration_name リクエストを処理したリビジョンを作成した構成リソース。
location サービスの GCP ロケーション。
project_id サービスがデプロイされているプロジェクト。
revision_name リクエストを処理したリビジョン。
service_name リクエストを処理したサービス。
type cloud_run_revision。Knative serving リソースタイプ。