Azure Blob Storage に Append Block でログ追記する話
こんにちは、ソリューション開発部の柴崎です。
前回は Azure Event Hubs を利用して Azure Blob Storage に書き込みを試みました。今回は Azure Blob Storage の Append Block 操作を利用してメッセージを追記することについて調査しましたのでご紹介します。
BLOB サービスには Append Blobs があり、Append Block 操作によってファイルの末尾に追記することができます。これは追記を続けるログファイルとして使うことができそうです。今回は Pepper から送信したログを直接 Blob に出力してみます。
Blob を作成する
最初から追記の API だけを利用したいところですが、追記対象となるリソースが存在しない場合は 404 エラー (The specified blob does not exist.) となってしまいます。そのため事前に Put Blob 操作により新規に Blob を作成する必要があります。
Blob が存在しない場合のみ新規作成し Blob を上書きしないようにする
既に Blob が存在する場合に上書きしてしまうと困る場合があります。Blob サービス操作の条件ヘッダーの指定をすることで、既に Blob が存在する場合に影響を与えないようにすることができます。具体的には、リクエストに以下のヘッダーを付与することで、既に Blob が存在する場合は 409 エラー (The specified blob already exists.) となりリソースに影響を与えなくなります。
If-None-Match: *
Blob に追記する
前述の Put Blob 操作で作成した Blob に対し Append Block 操作により追記することができます。
メッセージを送りつける Python コード
コードを実行し、Blob に出力されていることを確認しましょう。
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import urllib2
import base64
import hmac,hashlib
import datetime
def request(account_name, access_key, blob_path, content = ''):
u'''指定の content を追記します。指定の content が空で BLOB が存在しない場合はは空の BLOB を作成します。'''
content_encoding = 'UTF-8'
content = content.encode(content_encoding)
content_length = len(content)
content_md5 = base64.b64encode(hashlib.md5(content).digest())
content_type = 'text/plain'
x_ms_blob_type = 'AppendBlob'
x_ms_date = '{:%a, %d %b %Y %H:%M:%S GMT}'.format(datetime.datetime.utcnow())
x_ms_version = '2015-02-21'
request_url_format = 'https://{}.blob.core.windows.net{}'
if content:
request_url_format += '?comp=appendblock'
string_to_sign_format = 'PUT\n{}\n\n{}\n{}\n{}\n\n\n\n{}\n\n\nx-ms-blob-type:{}\nx-ms-date:{}\nx-ms-version:{}\n/{}{}{}'
string_to_sign = string_to_sign_format.format(
content_encoding,
content_length if 0 < content_length else '',
content_md5,
content_type,
'*' if not content else '',
x_ms_blob_type,
x_ms_date,
x_ms_version,
account_name,
blob_path,
'\ncomp:appendblock' if content else ''
)
signature = base64.encodestring(hmac.new(base64.decodestring(access_key), string_to_sign, hashlib.sha256).digest()).rstrip('\n')
req = urllib2.Request(request_url_format.format(account_name, blob_path), content)
req.get_method = lambda: 'PUT'
req.add_header("Authorization", 'SharedKey {}:{}'.format(account_name, signature))
req.add_header("Content-Encoding", content_encoding)
req.add_header("Content-Length", content_length)
req.add_header("Content-MD5", content_md5)
req.add_header("Content-Type", content_type)
if not content:
req.add_header("If-None-Match", '*')
req.add_header("x-ms-blob-type", x_ms_blob_type)
req.add_header("x-ms-date", x_ms_date)
req.add_header("x-ms-version", x_ms_version)
if not content:
try:
return urllib2.urlopen(req)
except urllib2.HTTPError, e:
if e.code == 409:
# If-None-Match ヘッダにより既に BLOB がある場合は要求を受け付けない
return e
else:
raise
else:
return urllib2.urlopen(req)
if __name__ == '__main__':
account_name = 'STORAGE_ACCOUNT_NAME'
access_key = 'STORAGE_ACCESS_KEY'
blob_path = '/CONTAINER/PATH/TO/BLOB.log'
# BLOB が無ければ作成する
res = request(account_name, access_key, blob_path)
print res.code, res.msg
# 追記する
res = request(account_name, access_key, blob_path, u'おはよう\r\n')
print res.code, res.msg
res = request(account_name, access_key, blob_path, u'こんにちは\r\n')
print res.code, res.msg
res = request(account_name, access_key, blob_path, u'こんばんは\r\n')
print res.code, res.msg
このコードを実行すると、以下の内容が見事に Blob に出力されました。
おはよう
こんにちは
こんばんは
上記のコードを再度実行しても既存の Blob が消されることなく追加されていきます。
まとめ
ログの追記などであれば Azure Blob Storage の Append Block が有用であることが分かりました。Azure Storage のスケーラビリティおよびパフォーマンスのターゲットを確認し利用できるかどうか検討してみてください。