🌻

コピペとコマンド実行のみでSentryのIssuesをCSV形式で書き出す

2024/08/02に公開

こんにちは
It's summerですね🍉

導入

みなさまエラー監視ツールは何を使われてますでしょうか。
弊社はモバイルアプリにSentryを使っています。
まだ部分的にしか適用しておらず、エラー数もそこまで多くないためDeveloperプラン($0)で利用しています。

当該プランの場合、コンソール画面からのCSV書き出しに対応していないためAPI経由で取得する必要があります。

事前準備

正味、ChatGPT先生がはき出してくれたコードなので大きな顔はできませんが備忘録として

api_token, organization_slug, project_slugはSentryのSettingsから確認し、置き換えてください。

api_token

https://your_org.sentry.io/settings/account/api/auth-tokens/
の新しいトークンを生成から生成

organization_slug

https://your_org.sentry.io/settings/organization/
Organization Slug

project_slug

https://your_org.sentry.io/settings/projects/your_project_name/
A name for this project

具体的な話

  1. sentry_issues_export.pyとか適当にファイルを作り下記の内容で保存します
  2. 当該ファイルのあるディレクトリで以下を実行します
python3 export_sentry_issues.py
sentry_issues_export.py
import requests
import csv
from datetime import datetime, timedelta

# Sentry APIの設定
api_token = 'your_api_token'
organization_slug = 'your_org_slug'
project_slug = 'your_project_slug'
sentry_api_url = f'https://sentry.io/api/0/projects/{organization_slug}/{project_slug}/issues/'

# ヘッダーの設定
headers = {
    'Authorization': f'Bearer {api_token}',
    'Content-Type': 'application/json',
}

# 対象期間の設定(例:過去30日間)
end_date = datetime.utcnow()
start_date = end_date - timedelta(days=30)
start = start_date.isoformat() + 'Z'
end = end_date.isoformat() + 'Z'

# Issueを取得する
issues = []
cursor = None

while True:
    params = {
        'limit': 100,
        'start': start,
        'end': end,
        'query': 'is:unresolved',  # ここでは未解決のIssueをフィルタリング
    }
    if cursor:
        params['cursor'] = cursor
    response = requests.get(sentry_api_url, headers=headers, params=params)
    data = response.json()
    issues.extend(data)

    # 次のページがあるかどうか確認
    link_header = response.headers.get('Link')
    if 'results="false"' in link_header:
        break
    cursor = link_header.split('; rel="next",')[1].split('cursor=')[1].split('&')[0]

# Issueごとにイベントを集計する関数
def get_event_count(issue_id):
    url = f'https://sentry.io/api/0/issues/{issue_id}/events/'
    events = []
    cursor = None

    while True:
        params = {
            'limit': 100,
            'start': start,
            'end': end,
        }
        if cursor:
            params['cursor'] = cursor
        response = requests.get(url, headers=headers, params=params)
        data = response.json()
        events.extend(data)

        link_header = response.headers.get('Link')
        if 'results="false"' in link_header:
            break
        cursor = link_header.split('; rel="next",')[1].split('cursor=')[1].split('&')[0]

    return len(events)

# 各Issueのイベント数を集計
for issue in issues:
    issue['event_count'] = get_event_count(issue['id'])

# CSVに書き出す
with open('sentry_issues.csv', 'w', newline='') as csvfile:
    fieldnames = ['id', 'title', 'status', 'culprit', 'permalink', 'firstSeen', 'lastSeen', 'event_count']
    writer = csv.DictWriter(csvfile, fieldnames=fieldnames)
    writer.writeheader()
    for issue in issues:
        writer.writerow({
            'id': issue['id'],
            'title': issue['title'],
            'status': issue['status'],
            'culprit': issue['culprit'],
            'permalink': issue['permalink'],
            'firstSeen': issue['firstSeen'],
            'lastSeen': issue['lastSeen'],
            'event_count': issue['event_count'],
        })

取得項目

まぁ見たまんまやんけみたいな感じですが、以下の項目を取得するようにしてます

  • id
    • IssueのID
  • title
    • Issueのタイトル
      • e.g.App Hanging: App hanging for at least 2000 ms.とか
  • status
    • Issueのステータス
      • e.g.unresolved(未解決)とか
  • culprit
    • Issueの発生場所的な
      • e.g.logger.dart in Logger.errorとか
  • permalink
    • Issueへのリンク
      • e.g.https://your_org.sentry.io/issues/123456xxx/とか
  • firstSeen, lastSeen
    • Issueの初回発生と直近発生のタイムスタンプ
      • e.g.2024-07-05T08:29:53.046000Zとか
  • event_count
    • Issueのイベント数
      • countだと初回発生からの累計発生回数を取得してきてしまったので、私の場合はイベント数で取得するようにしました

間違いやここもっと良くできるよ的なことがあればコメントくださいませ🙏🏻

Discussion