質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.31%
Laravel

LaravelとはTaylor Otwellによって開発された、オープンソースなPHPフレームワークです。Laravelはシンプルで表現的なシンタックスを持ち合わせており、ウェブアプリケーション開発の手助けをしてくれます。

Q&A

解決済

1回答

1457閲覧

社内アプリのログ設計

websuke

総合スコア11

Laravel

LaravelとはTaylor Otwellによって開発された、オープンソースなPHPフレームワークです。Laravelはシンプルで表現的なシンタックスを持ち合わせており、ウェブアプリケーション開発の手助けをしてくれます。

0グッド

0クリップ

投稿2023/06/13 11:56

編集2023/06/13 12:15

実現したいこと

  • ユーザーの全ての操作ログを記録したい

前提

Laravelを使って社内で使用するアプリを作成しています。要件としてはユーザーである社員がどういった操作を行ったのかイベント単位で全てのログを記録したいです。

ただログに関しての知識があまりにもないため、どのように実装するのが良いのか判断がつかないでいます。考えていることのいずれもやりたい事自体の実現は出来るとは思っているのですがLaravelでの開発が初めてかつ、経験のある言語でもログの出力方法を検討した事がないため助言頂けないでしょうか。

考えていること

  1. 愚直にLogファサードで記述する。

  2. 以下参考にミドルウェアを作成する。

https://e-seventh.com/laravel-middleware-tracelog/

  1. 以下ライブラリでAOPを実現する。

https://github.com/ytake/Laravel-Aspect

補足情報(FW/ツールのバージョンなど)

Laravel9.x

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2023/06/20 01:31 編集

今時のログの記録は外部サーバーに任せます。例えばElasticsearchのクラスタを立てて、アプリはログを送信するだけにします。こうしないとサーバーが複数あったらそれぞれのサーバーからログを集めてこないといけません。それをマージして検索するくらいなら最初からESに送った方が良いです。 ミドルウェアは綺麗な設計になりますので良いです。ここでローカルのファイルに書き込む代わりにESに送信するようにしましょう。ESが死んでいる場合にはローカルに書き込んで、ESが復活したらESに送信すればなお良いです(ディスク・スピルと言います)。filebeatとかfluentdとかで調べてみてください。ログはJSON形式にした方が後々の拡張が楽です。 KubernetesではログはSTDOUTに書き出すのが最近の流れですね。アプリ12の設計指針のうちの11番目で「アプリケーションは、イベントストリームとしてログを生成し、実行環境に集計を委ねるべき」とあります。  https://en.wikipedia.org/wiki/Twelve-Factor_App_methodology
websuke

2023/06/20 17:05

詳細なご回答ありがとうございます。 知らない事だらけで面食らってますが頂いたリンク先の情報に私が知りたいと思っているようなことが、かなり詰まっているような気がしました。英語があまり読めないので翻訳ツールや邦訳されたものを参考に理解を深めていきたいと思います。 filebeat、fluentdについても調べておきます。 以下個人的メモとなりますのでお気になさらないでください。 - アプリ12の設計指針 https://en.wikipedia.org/wiki/Twelve-Factor_App_methodology - Best Practices in Implementing a Secure Microservices Architectureの日本語訳 https://www.cloudsecurityalliance.jp/site/wp-content/uploads/2020/11/best-practices-in-implementing-a-secure-microservices-architecture-J.pdf - Beyond the Twelve-Factor Appの所感 https://kakakakakku.hatenablog.com/entry/2020/03/09/084833
退会済みユーザー

退会済みユーザー

2023/06/20 19:20 編集

現実的にはログはローカルファイルに書き込むのが一番安全で簡単ですからまずはそれで実装して先に進むのが良いでしょう。ログをESで管理するのは一種の分散型ですが後から簡単に追加できます。 Beyond the Twelve-Factor Appいいですね。実務で実感していることが言語化されていると思いました。一般論ですがアーキテクチャのトレンドは一方向で発展しているわけでもなくて結構揺り戻しも起きますし、性能、コスト、開発のしやすさ、製品の成熟度などでトレードオフは変わりますから絶対的な正解はないと思っています。 例えばつい最近5月初旬に米国Amazonプライムビデオが分散型サーバーレスマイクロサービスからEC2でモノリスに戻るという発表がありました。皆が薄々感じていたマイクロサービス疲れに応える内容で大きな話題になりましたがAmazonが言うなら真実に違いないと思って古典的モノリスに戻る必要もないですし、どこか中間に落ち着いてまたそこから発展して行くでしょう。 https://www.f5.com/ja_jp/company/blog/monolithic-vs-microservices-architecture-microservices-are-out-monoliths-are-back https://www.publickey1.jp/blog/23/amazondhhamazonvogels.html
websuke

2023/06/21 14:15

> 現実的にはログはローカルファイルに書き込むのが一番安全で簡単ですからまずはそれで実装して先に進むのが良いでしょう。ログをESで管理するのは一種の分散型ですが後から簡単に追加できます。 後から追加もしやすいんですね。 まずはイメージが湧いているこちらの方法を試していきたいと思います。 ありがとうございます。 どこかで技術は何度もいったりきたりしているように見えて螺旋階段を登っているといったお話を聞いたことがありますが、おっしゃられていることがまさにそうだなと感じました。 半分も理解できてないと思いますがそれでもとても勉強になりました。
guest

回答1

0

自己解決

お知り合いの方からミドルウェアで実現したら良いのではと回答頂きました。
また、チャネルごとに出力レベルも変更できるということも教えてもらったので試験的に試したソースコードを記載しておきます。

コピペの継ぎ接ぎなのでそのまま使用すると良くない箇所が多々あるかもですが以下をベースにしつつ修正していけば問題ないかなっといった具合です。

参考にさせていただいたサイトは以下です。

config/logging.php

php

1'channels' => [ 2 ..... 3 4 'accessLog' => [ 5 'driver' => 'custom', 6 'via' => App\Logging\AccessLogger::class, 7 'path' => storage_path('logs/access/access.log'), 8 'level' => 'info', // ログレベル info 以上だけ出力 9 'days' => 31, // 31日分のログを保持する ※31日ではない月を考慮できてないので検討必要 10 ], 11 12 'sqlQueryLog' => [ 13 'driver' => 'custom', 14 'via' => App\Logging\CreateSQLQueryLogger::class, 15 'path' => storage_path('logs/sql/sql.log'), 16 'level' => 'debug', // ログレベル debug 以上だけ出力 ※環境ごとに切り替えれるように修正必要 17 'days' => 14, // 14日分のログを保持する 18 ],

app/Providers/AppServiceProvider.php

php

1public function boot(): void 2 { 3 \DB::listen(function($query) { 4 $str = $query->time.' ms -> '.$query->sql; 5 if ($query->bindings) { 6 $str .= "\n".'bind: '.var_export($query->bindings, true); 7 } 8 \Log::channel('sqlQueryLog')->debug($str); 9 }); 10 }

app/Logging/CreateSQLQueryLogger.php

php

1<?php 2 3namespace App\Logging; 4 5use Monolog\Logger; 6use Monolog\Handler\RotatingFileHandler; 7use Monolog\Formatter\LineFormatter; 8 9class CreateSQLQueryLogger 10{ 11 /** 12 * SQLクエリ用Monologインスタンス生成 13 * @param array $config 14 * @return \Monolog\Logger 15 */ 16 public function __invoke(array $config) 17 { 18 // 引数の $config には、config/logging.php で sqlQueryLog に設定した path とか days とかが入ってる! 19 20 // 'debug' とかの文字列をMonologが使えるログレベルに変換 21 $level = Logger::toMonologLevel($config['level']); 22 23 // 日ごとにログローテートするハンドラ作成 24 $hander = new RotatingFileHandler($config['path'], $config['days'], $level); 25 26 // 改行コードを出力する&カラのコンテキストを出力しないフォーマッタを設定 27 $hander->setFormatter(new LineFormatter(null, null, true, true)); 28 29 // Monologインスタンス作成してハンドラ設定して返却 30 $logger = new Logger('SQL'); // ロガー名は 'SQL' にした。これはログに出力される 31 $logger->pushHandler($hander); 32 return $logger; 33 } 34}

ここまででSQLのログ出力が出来るようになっている。


app/Logging/AccessLogger.php

php

1<?php 2 3namespace App\Logging; 4 5use Monolog\Logger; 6use Monolog\Handler\RotatingFileHandler; 7use Monolog\Formatter\LineFormatter; 8 9class AccessLogger 10{ 11 /** 12 * アクセスログ用Monologインスタンス生成 13 * @param array $config 14 * @return \Monolog\Logger 15 */ 16 public function __invoke(array $config) 17 { 18 // 引数の $config には、config/logging.php で accessLog に設定した path とか days とかが入ってる! 19 20 // 'debug' とかの文字列をMonologが使えるログレベルに変換 21 $level = Logger::toMonologLevel($config['level']); 22 23 // 日ごとにログローテートするハンドラ作成 24 $hander = new RotatingFileHandler($config['path'], $config['days'], $level); 25 26 // 改行コードを出力する&カラのコンテキストを出力しないフォーマッタを設定 27 $hander->setFormatter(new LineFormatter(null, null, true, true)); 28 29 // Monologインスタンス作成してハンドラ設定して返却 30 $logger = new Logger('access'); // ロガー名は 'access' にした。これはログに出力される 31 $logger->pushHandler($hander); 32 return $logger; 33 } 34}

まずはmiddlewareを以下コマンドで生成

shell

1php artisan make:middleware AccessLog

app/Http/Middleware/AccessLog.php

php

1<?php 2 3namespace App\Http\Middleware; 4 5use Closure; 6use Illuminate\Http\Request; 7use Illuminate\Support\Facades\Log; 8use Symfony\Component\HttpFoundation\Response; 9 10class AccessLog 11{ 12 /** 13 * Handle an incoming request. 14 * 15 * @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next 16 */ 17 public function handle(Request $request, Closure $next): Response 18 { 19 $response = $next($request); 20 21 // リクエスト終了時のログ 22 Log::channel('accessLog')->info('end ' . $request->url()); 23 24 return $response; 25 } 26}

ここまででアクセスログの出力が出来るようになっている。


ソースコード自体は参考サイトを基に使わせてもらっているのですが
恐らくSQLログの方もAppServiceProviderに記述するのではなくMiddlewareとして切り出せるのではないかと思うので実際のアプリケーション作成時には試してみようと思います。

投稿2023/06/13 14:58

websuke

総合スコア11

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.31%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問