雑コラBotを作ろう④: Table StorageにAzure Functionsの実行履歴を保存する

スクリプトは以下のように作成しました。

coding: utf-8

1. ライブラリの読み込み

import os, json
import imghdr
import urllib.parse
import urllib.request

import hashlib

2. バインド設定の読み込み

with open(os.path.dirname(_file_) + "/function.json", "r") as f:
bindings = json.load(f)
bindings = {bind["name"]: bind for bind in bindings["bindings"]}

print("Function Start. ID:" + os.environ["INVOCATIONID"])

def handler():

メッセージ読み込み

with open(os.environ["inputMessage"], "r") as f:
input_msg = json.load(f)

コンテンツのダウンロード

print("Downloading input image")
dlurl = inputmsg["img_url"]
req = urllib.request.Request(dl_url)
with urllib.request.urlopen(req) as res:
img_bytes = res.read()

3. 出力メッセージ用データ作成

outputpath = bindings["outputBlob"]["path"].format(blobpath=inputmsg["blobpath"]).split("/")
container = output_path[0]
blobpath = "/".join(outputpath[1:])
imgname = outputpath[-1]
imgmd5 = hashlib.md5(imgbytes).hexdigest()

画像フォーマット判定

print("Checking input image format")
imgformat = imghdr.what(None, h=imgbytes)

if img_format in ["jpeg", "png", "bmp", "gif"]:
if img_format == "jpeg":
img_format = "jpg"

画像の出力

with open(os.environ["outputBlob"], "wb") as fw:
fw.write(img_bytes)

4. 出力メッセージの設定

result = {
"status": "ok",
"message": "Image is uplaoded.",
"container": container,
"blobpath": blobpath,
"filename": img_name,
"contentmd5": imgmd5,
"input": input_msg
}

else:

4. 出力メッセージの設定

result = {
"status": "error",
"filename": img_name,
"contentmd5": imgmd5,
"input": input_msg,
}

if img_format is None:
result["message"] = "Input file is not image."
else:
result["message"] = "Unsupported image format."

5. キューメッセージの出力

print("Output Queue Message")
with open(os.environ["outputQueueItem"], "w") as fw:
json.dump(result, fw)

6. 実行履歴の書き込み

print("Insert Log to Table")
insert_entity = {key: val for key, val in result.items()}
insert_entity.update({
"PartitionKey": "results",
"RowKey": os.environ["INVOCATIONID"]
})

with open(os.environ["outputTable"], "w") as fw:
json.dump(insert_entity, fw)

return

if _name == "main_":
handler()

スクリプトの説明

1. ライブラリの読み込み

必要なライブラリを読み込みます。
実行履歴としてダウンロードしたコンテンツのMD5値を書き出すため、「hashlib」を追加しています。

2. バインド設定の読み込み

関数に設定しているバインドの設定値を読み込みます。

3. 出力メッセージ用データ作成

Queue Storage、Table Storageに出力するデータを作成します。
キューメッセージ、実行履歴に書き込むBlob Storageの出力パスとコンテンツのMD5値をここで生成します。

4. 出力メッセージの設定

Queue Storageに出力するメッセージを設定します。
後にSlack投稿用関数で使用するための必要な値を含めます。

5. キューメッセージの出力

メッセージをQueue Storageに出力します。
Blob Storageと同様に、環境変数で指定されるパスにファイルとして保存することで書き出すことができます。

6. 実行履歴の書き込み

Table Storageに実行履歴を書き込みます。

Table Storageでは「PartitonKey」、「RowKey」を設定する必要があるため、Queue Storageに出力したメッセージに「PartitonKey」、「RowKey」を追加してTable Storageに書き込みます。

実行結果

Slackの設定したチャンネルにメッセージを投稿すると、Azure Functionsにログが表示され、4秒ほどで関数が終了していることがわかります。

Table Storageを見ると、設定した名称のテーブルが作成されており、関数の終了と同時刻にテーブルにデータが書き込まれていることが確認できました。

Table Storageを見ると、設定した名称のテーブルが作成されており、関数の終了と同時刻にテーブルにデータが書き込まれていることが確認できました。

同様に、Queue Storageでも関数の終了と同時刻にメッセージが書き込まれていました。

同様に、Queue Storageでも関数の終了と同時刻にメッセージが書き込まれていました。

注意点

Table Storageの出力バインドは、データの更新に対応しておらず、PartitionKey、RowKeyの組がすでにテーブルに存在する場合には、「EntityAlreadyExists」の関数エラーが発生します。

ここで注意すべき点は、出力バインドによるデータの追加は関数の実行完了時に行われるため、出力バインドに関わるエラーを関数の例外処理で対処できないことです。出力バインドにTable Storageを使用する場合は、PartitionKey、RowKeyの組がユニークとなるように設定する必要があります。

まとめ

今回は、Azure FunctionsのTable Storageバインドを設定しました。サーバーレスとNo SQL DBは、ログ保存等によく用いられる組み合わせで、Table Storageであれば予約スループットの課金がなく安価に構成できます。

次回は、Blob Storageに保存した画像のSAS URLを発行してSlackに投稿する関数を作成し、今回作成したQueue Storageのメッセージで連携させます。

この記事を書いた人

azure-recipe-user