Python のアプリケーションで、Cloud logger にログを出力したいときに

  1. 標準のPython logging モジュールを利用して、ログを出力する
  2. Python Cloud Logging package を使用する

上記の2つの方法があります。

不必要にパッケージを増やしたくはないので、1の標準モジュールでCloud Logger へ出力できないか試してみました。

標準のPython logging モジュールを試す

標準のlogging モジュールでログを出力したいときに

import logging
 
logger = logging.getLogger(__name__)
 
def hoge(): 
	logger.info('logging Start 2021')

と、logging.info() を仕込んで、Cloud logger にログを出力してみると、logger.info() で出しているはずなのに、Cloud logger 上ではすべてエラーとして扱われてしまっています。

原因を特定するために、logger のログを見てみると logger.info() がすべて stderr標準エラーストリームへ出力されてしまっています。

{
  "textPayload": "2021-02-20 21:26:51,012 - root:predict:36 - INFO:  logging Start\n",
	...
  },
  "timestamp": "2021-02-20T12:26:51.013213826Z",
  "severity": "ERROR",
  "labels": {
	...
  },
  "logName": "projects/.../logs/stderr",
  "receiveTimestamp": "2021-02-20T12:26:55.050911180Z"
}

そのため、明示的に標準出力へ出力先を変更してみました。

import logging
import sys
 
logger = logging.getLogger(__name__)
handler = logging.StreamHandler(sys.stdout)
logger.addHandler(handler)
 
def hoge(): 
	logger.info('logging Start 2021')

これで解決できているはずと、Cloud Loggerを見てみると

GKE%20%E4%B8%8A%E3%81%A6%E3%82%99Python%E3%81%A6%E3%82%99%20logger%20info()%20%E3%82%92%E8%A1%8C%E3%81%86%E3%81%A8Cloud%20logger%E4%B8%8A%E3%81%A6%E3%82%99%20%204de7ffc30dc349e2b3b61b14a2103f46/Untitled.png

なぜか、stderr への出力は残ったまま、stdoutへの出力が新たに増えました。このままでもINFO レベルでログは残せているので目的は達成できていますが、stderrへの出力が残ってしまっているせいでCloud logger 上でエラーが発生しているようになり問題です。なので、これを避けるために2つ目の方法であるPython Cloud Logging package を利用してみます。

Python Cloud Logging package を使用する

まずPython Client for Cloud Logging のチュートリアルをすすめて行きます。

Cloud logging のPython package のQuick Start にてわかりやすく導入方法が紹介されています。ここで混乱しやすいのが、GCPのLogging Client Libraries のドキュメントではまずService Account を作って、GOOGLE_APPLICATION_CREDENTIALS を使用してCloud Logging の認証を行ってくださいと書かれています。

ですが、

と本元のパッケージのドキュメントでは書かれており、こちらのほうが誤解が少なくていいですね。(公式ドキュメントだと、SA作成時に Project > Owner を指定しろと書かれているんだけど、これって権限過多なのでは無いのだろうか??)

ちなみにGCP 上の認証過程はこちらのドキュメントが詳しく書かれているので、気になった方は御覧ください。

Authenticating as a service account

まとめると

  1. GOOGLE_APPLICATION_CREDENTIALS を参照する
  2. ADC (Application Default Credentials) が、コードに紐付けられているSAを使用する
  3. ADCは各サービスのSAを利用する
  4. 1-3 が使用できなかった場合、認証エラーが発生する

その後、以下のコードで無事に INFO のログがstderr に吐き出されることなく、stdout にINFOとして出力されるようになります。

import logging
import sys
# NOTE: GKE ではCotainerEngineHandler が必要
from google.cloud.logging.handlers import ContainerEngineHandler
 
logger = logging.getLogger(__name__)
# NOTE: stream で stdout を指定する
logger.addHandler(ContainerEngineHandler(name=__name__, stream=sys.stdout))
# NOTE: ログが重複して出力されるので、propagate を切る
logger.propagate = False
 
logger.info('Hello Cloud Logging.')

Appendix