#!/usr/bin/env python3 """ nginxログ分析: チェックイン・画像アップロード機能の使用状況を確認 """ import subprocess import re from collections import defaultdict, Counter from datetime import datetime def analyze_provided_logs(): """ ユーザーが提供したログデータを分析 """ print("🔍 提供されたログデータの分析") print("=" * 50) # ユーザーが提供したログデータ log_data = """ nginx-1 | 172.18.0.1 - - [05/Sep/2025:17:25:15 +0000] "GET /api/new-events/ HTTP/1.0" 200 22641 "-" "Dart/3.9 (dart:io)" "202.215.43.20" nginx-1 | 172.18.0.1 - - [05/Sep/2025:17:25:15 +0000] "GET /api/entry/ HTTP/1.0" 200 11524 "-" "Dart/3.9 (dart:io)" "202.215.43.20" nginx-1 | 172.18.0.1 - - [05/Sep/2025:17:25:15 +0000] "GET /api/teams/ HTTP/1.0" 200 674 "-" "Dart/3.9 (dart:io)" "202.215.43.20" nginx-1 | 172.18.0.1 - - [05/Sep/2025:17:25:15 +0000] "GET /api/categories/ HTTP/1.0" 200 2824 "-" "Dart/3.9 (dart:io)" "202.215.43.20" nginx-1 | 172.18.0.1 - - [05/Sep/2025:17:25:18 +0000] "GET /api/user/current-entry-info/ HTTP/1.0" 200 512 "-" "Dart/3.9 (dart:io)" "202.215.43.20" nginx-1 | 172.18.0.1 - - [05/Sep/2025:17:25:19 +0000] "PATCH /api/entries/897/update-status/ HTTP/1.0" 200 1281 "-" "Dart/3.9 (dart:io)" "202.215.43.20" nginx-1 | 172.18.0.1 - - [05/Sep/2025:17:25:31 +0000] "GET /api/entry/ HTTP/1.0" 200 11523 "-" "Dart/3.9 (dart:io)" "202.215.43.20" nginx-1 | 172.18.0.1 - - [05/Sep/2025:17:25:31 +0000] "GET /api/teams/ HTTP/1.0" 200 674 "-" "Dart/3.9 (dart:io)" "202.215.43.20" nginx-1 | 172.18.0.1 - - [05/Sep/2025:17:25:31 +0000] "GET /api/new-events/ HTTP/1.0" 200 22641 "-" "Dart/3.9 (dart:io)" "202.215.43.20" nginx-1 | 172.18.0.1 - - [05/Sep/2025:17:25:31 +0000] "GET /api/categories/ HTTP/1.0" 200 2824 "-" "Dart/3.9 (dart:io)" "202.215.43.20" """.strip() analyze_log_content(log_data.split('\n')) def analyze_log_content(lines): """ ログ内容を分析する共通関数 """ print(f"📊 ログ行数: {len(lines)}") # チェックイン・画像関連のエンドポイント checkin_endpoints = { '/api/checkinimage/': '画像アップロード', '/gifuroge/checkin_from_rogapp': 'チェックイン登録', '/api/bulk_upload_checkin_photos/': '一括写真アップロード', '/api/user/current-entry-info/': 'ユーザー参加情報', '/api/entries/': 'エントリー操作', '/api/new-events/': 'イベント情報', '/api/teams/': 'チーム情報', '/api/categories/': 'カテゴリ情報' } # 分析結果 endpoint_counts = defaultdict(int) methods = Counter() status_codes = Counter() user_agents = Counter() client_ips = Counter() dart_requests = 0 for line in lines: if not line.strip(): continue # ログパターンマッチング # nginx-1 | IP - - [timestamp] "METHOD path HTTP/1.0" status size "-" "user-agent" "real-ip" match = re.search(r'"(\w+)\s+([^"]+)\s+HTTP/[\d\.]+"\s+(\d+)\s+(\d+)\s+"[^"]*"\s+"([^"]*)"\s+"([^"]*)"', line) if match: method = match.group(1) path = match.group(2) status = match.group(3) size = match.group(4) user_agent = match.group(5) real_ip = match.group(6) methods[method] += 1 status_codes[status] += 1 user_agents[user_agent] += 1 client_ips[real_ip] += 1 # Dartクライアント(スマホアプリ)の検出 if 'Dart/' in user_agent: dart_requests += 1 # エンドポイント別カウント for endpoint in checkin_endpoints: if endpoint in path: endpoint_counts[endpoint] += 1 # 結果表示 print(f"\n📱 Dartクライアント(スマホアプリ)リクエスト: {dart_requests}件") print(f"\n🎯 関連APIエンドポイントの使用状況:") print("-" * 60) found_activity = False for endpoint, description in checkin_endpoints.items(): count = endpoint_counts[endpoint] if count > 0: print(f"✅ {description:20s} ({endpoint}): {count}回") found_activity = True else: print(f"❌ {description:20s} ({endpoint}): アクセスなし") print(f"\n📊 HTTPメソッド別:") for method, count in methods.most_common(): print(f" {method}: {count}回") print(f"\n📊 ステータスコード別:") for status, count in status_codes.most_common(): print(f" HTTP {status}: {count}回") print(f"\n📊 User Agent:") for ua, count in user_agents.most_common(): ua_short = ua[:50] + "..." if len(ua) > 50 else ua print(f" {ua_short}: {count}回") print(f"\n📊 クライアントIP:") for ip, count in client_ips.most_common(): print(f" {ip}: {count}回") # チェックイン・画像機能の判定 print(f"\n🎯 機能使用状況の判定:") print("-" * 40) checkin_active = endpoint_counts['/gifuroge/checkin_from_rogapp'] > 0 image_upload_active = endpoint_counts['/api/checkinimage/'] > 0 bulk_upload_active = endpoint_counts['/api/bulk_upload_checkin_photos/'] > 0 print(f"チェックイン登録機能: {'✅ 使用中' if checkin_active else '❌ 未使用'}") print(f"画像アップロード機能: {'✅ 使用中' if image_upload_active else '❌ 未使用'}") print(f"一括写真アップロード機能: {'✅ 使用中' if bulk_upload_active else '❌ 未使用'}") print(f"スマホアプリ(Dartクライアント): {'✅ アクティブ' if dart_requests > 0 else '❌ 非アクティブ'}") if dart_requests > 0: print(f"\n📱 スマホアプリの動作状況:") print(f" • アプリは正常に動作している") print(f" • イベント情報、エントリー情報、チーム情報を取得中") print(f" • エントリーステータスの更新も実行中") print(f" • ただし、チェックインや画像アップロードは確認されていない") return found_activity """ nginxログを分析してチェックイン・画像関連の活動を確認 """ print("🔍 nginx ログ分析: チェックイン・画像アップロード機能") print("=" * 70) # nginxログを取得 try: result = subprocess.run( ['docker-compose', 'logs', '--tail=500', 'nginx'], capture_output=True, text=True ) if result.returncode != 0: print(f"❌ ログ取得エラー: {result.stderr}") return log_lines = result.stdout.split('\n') except Exception as e: print(f"❌ 実行エラー: {e}") return # 分析用パターン patterns = { 'checkin_api': re.compile(r'(POST|GET).*/(checkin_from_rogapp|checkin|addCheckin)', re.I), 'image_api': re.compile(r'(POST|GET).*/checkinimage', re.I), 'bulk_upload': re.compile(r'(POST|GET).*/bulk_upload', re.I), 'dart_client': re.compile(r'"Dart/[\d\.]+ \(dart:io\)"'), 'api_access': re.compile(r'"(GET|POST|PUT|PATCH|DELETE) (/api/[^"]+)'), 'status_codes': re.compile(r'" (\d{3}) \d+') } # 分析結果 results = { 'checkin_requests': [], 'image_requests': [], 'bulk_upload_requests': [], 'dart_requests': [], 'api_endpoints': Counter(), 'status_codes': Counter(), 'client_ips': Counter() } # ログ行の解析 for line in log_lines: if not line.strip() or 'nginx-1' not in line: continue # 各パターンをチェック if patterns['checkin_api'].search(line): results['checkin_requests'].append(line) if patterns['image_api'].search(line): results['image_requests'].append(line) if patterns['bulk_upload'].search(line): results['bulk_upload_requests'].append(line) if patterns['dart_client'].search(line): results['dart_requests'].append(line) # APIエンドポイント集計 api_match = patterns['api_access'].search(line) if api_match: method, endpoint = api_match.groups() results['api_endpoints'][f"{method} {endpoint}"] += 1 # ステータスコード集計 status_match = patterns['status_codes'].search(line) if status_match: results['status_codes'][status_match.group(1)] += 1 # クライアントIP集計 ip_match = re.search(r'(\d+\.\d+\.\d+\.\d+) - -', line) if ip_match: results['client_ips'][ip_match.group(1)] += 1 # 結果表示 print_analysis_results(results) def print_analysis_results(results): """ 分析結果を表示 """ # 1. チェックインAPI使用状況 print(f"\n📍 チェックインAPI アクセス状況") print("-" * 50) if results['checkin_requests']: print(f"件数: {len(results['checkin_requests'])}件") for req in results['checkin_requests'][-5:]: # 最新5件 print(f" {extract_log_info(req)}") else: print("❌ チェックインAPIへのアクセスなし") # 2. 画像アップロードAPI使用状況 print(f"\n🖼️ 画像アップロードAPI アクセス状況") print("-" * 50) if results['image_requests']: print(f"件数: {len(results['image_requests'])}件") for req in results['image_requests'][-5:]: # 最新5件 print(f" {extract_log_info(req)}") else: print("❌ 画像アップロードAPIへのアクセスなし") # 3. 一括アップロードAPI使用状況 print(f"\n📤 一括アップロードAPI アクセス状況") print("-" * 50) if results['bulk_upload_requests']: print(f"件数: {len(results['bulk_upload_requests'])}件") for req in results['bulk_upload_requests'][-5:]: # 最新5件 print(f" {extract_log_info(req)}") else: print("❌ 一括アップロードAPIへのアクセスなし") # 4. Dartクライアント(スマホアプリ)の活動 print(f"\n📱 スマホアプリ(Dart)アクセス状況") print("-" * 50) if results['dart_requests']: print(f"件数: {len(results['dart_requests'])}件") # Dartクライアントが使用しているAPIエンドポイント dart_endpoints = Counter() for req in results['dart_requests']: api_match = re.search(r'"(GET|POST|PUT|PATCH|DELETE) (/api/[^"]+)', req) if api_match: method, endpoint = api_match.groups() dart_endpoints[f"{method} {endpoint}"] += 1 print("主要なAPIエンドポイント:") for endpoint, count in dart_endpoints.most_common(10): print(f" {endpoint}: {count}回") else: print("❌ スマホアプリからのアクセスなし") # 5. 全体のAPI使用状況(Top 10) print(f"\n🌐 API使用状況 (Top 10)") print("-" * 50) for endpoint, count in results['api_endpoints'].most_common(10): print(f" {endpoint}: {count}回") # 6. HTTPステータスコード分布 print(f"\n📊 HTTPステータスコード分布") print("-" * 50) for status, count in results['status_codes'].most_common(): status_emoji = get_status_emoji(status) print(f" {status_emoji} {status}: {count}回") # 7. クライアントIP分布 print(f"\n🌍 アクセス元IP分布") print("-" * 50) for ip, count in results['client_ips'].most_common(5): ip_type = "🏠 ローカル" if ip.startswith(('192.168', '172.', '127.')) else "🌐 外部" print(f" {ip_type} {ip}: {count}回") def extract_log_info(log_line): """ ログ行から重要な情報を抽出 """ # 時刻を抽出 time_match = re.search(r'\[([^\]]+)\]', log_line) time_str = time_match.group(1) if time_match else "Unknown" # メソッドとパスを抽出 method_match = re.search(r'"(GET|POST|PUT|PATCH|DELETE) ([^"]+)', log_line) method_path = method_match.groups() if method_match else ("Unknown", "Unknown") # ステータスコードを抽出 status_match = re.search(r'" (\d{3}) \d+', log_line) status = status_match.group(1) if status_match else "Unknown" # User Agentを抽出 ua_match = re.search(r'"([^"]+)" "[^"]*"$', log_line) user_agent = ua_match.group(1) if ua_match else "Unknown" return f"{time_str} | {method_path[0]} {method_path[1][:50]}... | {status} | {user_agent[:20]}..." def get_status_emoji(status_code): """ HTTPステータスコードに対応する絵文字を返す """ if status_code.startswith('2'): return '✅' elif status_code.startswith('3'): return '🔀' elif status_code.startswith('4'): return '❌' elif status_code.startswith('5'): return '💥' else: return '❓' def analyze_nginx_logs(): """ Dockerコンテナからnginxログを取得・分析 """ print("🔍 Dockerからnginxログを取得中...") print("=" * 50) try: # docker-compose logsでnginxのログを取得 result = subprocess.run( ['docker-compose', 'logs', '--tail=100', 'nginx'], capture_output=True, text=True, check=True ) lines = result.stdout.split('\n') analyze_log_content(lines) except subprocess.CalledProcessError as e: print(f"❌ ログ取得エラー: {e}") print(f"stderr: {e.stderr}") print("\n💡 Dockerコンテナが起動していることを確認してください") print(" docker-compose ps") except FileNotFoundError: print("❌ docker-composeコマンドが見つかりません") print("💡 Dockerがインストールされていることを確認してください") def main(): print("🚀 スマホアプリのnginxログ解析ツール") print("=" * 50) choice = input("\n分析方法を選択してください:\n1. Dockerからログを取得\n2. 提供されたログデータを分析\n選択 (1/2): ") try: if choice == "1": analyze_nginx_logs() elif choice == "2": analyze_provided_logs() else: print("無効な選択です。提供されたログデータを分析します。") analyze_provided_logs() print(f"\n✅ 分析完了") print(f"\n💡 結論:") print(f" ログから、チェックイン・画像アップロード機能の実際の使用状況を確認できます") print(f" スマホアプリ(Dart)の活動状況も把握可能です") except Exception as e: print(f"❌ エラー: {e}") import traceback traceback.print_exc() if __name__ == '__main__': main()