Python のアプリケーションで、Cloud logger にログを出力したいときに
- 標準のPython logging モジュールを利用して、ログを出力する
- 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を見てみると
-を行うとCloud-logger上で--4de7ffc30dc349e2b3b61b14a2103f46/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 の認証を行ってくださいと書かれています。
ですが、
- GCP上でのVMでパッケージを使用するなら認証は必要ない 。ただし、デフォルトのSAを使いたくない場合や、GCP外などで動かしたい場合はSAが必要
と本元のパッケージのドキュメントでは書かれており、こちらのほうが誤解が少なくていいですね。(公式ドキュメントだと、SA作成時に Project > Owner を指定しろと書かれているんだけど、これって権限過多なのでは無いのだろうか??)
ちなみにGCP 上の認証過程はこちらのドキュメントが詳しく書かれているので、気になった方は御覧ください。
Authenticating as a service account
まとめると
- GOOGLE_APPLICATION_CREDENTIALS を参照する
- ADC (Application Default Credentials) が、コードに紐付けられているSAを使用する
- ADCは各サービスのSAを利用する
- 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.')