当开发一个现代的应用程序时,日志应该被放在优先级列表的首位。
日志提供了一种在开发和生产中可视化你的应用程序的方法,实现了透明度和可视性。有了适当的结构化日志,现代应用程序可以变得更容易维护,因为我们可以主动识别应用程序中的故障点和性能瓶颈。
Laravel框架有一个强大的日志系统,可以处理所有的障碍,配置一个正确的结构化的日志系统,开箱即用。这个在Laravel 6.5中引入的新的日志系统非常强大, 我们将在这篇文章中探讨它.
这篇文章将探讨Laravel日志的基础知识,以及为什么你应该在你的下一个项目中使用Laravel日志。我们将详细讨论结构化日志和集中式日志。此外, 我们将学习如何通过建立一个Todo应用程序来实现Laravel的日志。
如果你已经具备以下条件,你会从这篇文章中得到更多:
什么是Laravel日志?
Laravel日志是关于Laravel如何处理日志,或自动报告问题,使用一个叫Monolog的病毒式PHP日志系统。然而, 由于Laravel的理念是使用流行的现有库来实现不同的框架功能, Laravel采用Monolog来满足所有的日志需求。
Monolog是一个高度灵活和流行的PHP日志库,我们可以配置将你的日志发送到文件,套接字,数据库,和其他网络服务。Monolog提供了一个熟悉的接口,可以从标准的文本文件到先进的第三方日志管理服务来编写日志。Laravel通常设置Monolog来使用一个标准的日志配置文件。
更多关于Monolog和它的功能的信息, 请查看官方文档, 因为这已经超出了本文的范围.
在我们深入到使用Monolog配置和实现Laravel日志之前, 让我们探讨更多使用Laravel日志的原因和不同的类型。
为什么使用Laravel日志?
为什么日志是必要的?
Twelve-Factor App宣言将日志视为现代应用程序的关键问题之一, 因为日志是性能和监控的关键。
日志有助于详细了解生产中发生的错误以及它们的来源。此外,通过适当的日志结构,它可以显示特定的用户、导致错误的操作,以及可能的解决方案,以加快错误修复和维护。
结构化的日志在生产应用中是一个救命稻草,它可以帮助排查缺陷和解决生产中的问题。此外,你可以使用专门的日志工具实时监控和收集所有的日志信息,进行实时分析和报告。
由于这些原因,你需要在你的下一个现代应用项目中把结构化日志作为首要任务。
让我们来看看不同的日志风格的概述。
Laravel日志的基础知识
学习日志的基础知识将帮助你了解Laravel如何处理日志,以及如何改善你的结构化日志实践。
让我们研究一下日志中的两个基本概念,以便更好地理解如何实现我们的日志程序。
Laravel结构化日志
在软件开发中, 结构化日志是为应用程序的日志实施一个预先确定的和一致的消息格式。这种格式允许消息被视为数据,可以被监测,操作和可视化,比普通的文本格式好得多。
你必须在你的现代应用开发中实施一个结构化的日志方法,因为当你的应用在生产中发生错误时,日志文件是开发人员的重要资产。
由于Laravel使用Monolog, 开发者可以通过配置日志器来快速实现结构化日志,接收特定类型的信息,以不同的格式存储日志文件,并将日志发送到各种第三方日志管理服务,以实现可视化。
Laravel集中式日志记录
集中式日志系统是指将日志从多个来源发送到集中式日志管理(CLM)解决方案,以方便整合和可视化。然而, CLM是一个专门的日志解决方案,从不同的来源收集日志信息,并整合数据以方便处理和可视化。
除了数据收集,CLM也要支持对日志数据的分析,以及分析后对数据的清晰呈现。
结构化日志与基本日志
让我们研究一下结构化日志和基本(非结构化)日志的区别,以及为什么你应该在Laravel项目中使用结构化日志。
基本日志记录
在基本日志中, 日志文件是以原始格式存储的, 只有有限的数据可以查询和识别单个日志.
当使用基本日志时,开发者将无法使用第三方分析工具来读取, 查看和分析日志, 除非他们开发一个自定义的工具或坚持使用一个支持其日志格式的有限工具.
有三大理由可以避免使用基本日志:
- 集中式的日志管理系统在没有额外支持的情况下无法与数据一起工作。
- 需要一个定制的解决方案来读取和解析基本日志解决方案的数据。
- 由于基本日志数据是原始的、非结构化的,所以管理员要读取基本日志数据是很有挑战性的。
结构化日志记录
结构化日志通过使用支持标准日志结构的开源第三方日志分析工具来读取、查看和分析日志,为开发者节省时间。
如果日志包含下面列出的正确数据,就会有帮助,这就是结构化日志的目的。我们可以使用结构化日志中包含的数据来创建仪表盘、图形、图表和任何其他有用的可视化,以确定应用程序的健康。
这些是我们可以包含在结构化日志信息中的基本例子。此外,你可以完全定制数据以满足你的需要。
下面是一些你可以用结构化日志收集的数据的例子:
- 用来执行函数的端口
- 事件发生的日期和时间
- 客户的用户名或ID
- 事件的描述(日志信息)
- 用于执行功能的协议
- 触发事件的位置(表示API或运行中的应用)
- 唯一的事件ID
- 触发的行动的类型(日志级别)
日志应该包含足够的数据,以方便可视化解决方案或日志事件的原因。另外,请注意,你不应该在日志中存储所有类型的信息,如密码或敏感数据。
现在,我们已经瞥见了Laravel日志是怎么回事,让我们继续实现Laravel日志,通过建立一个应用程序,将日志作为一个一流的公民。
如何用Todo App实现Laravel日志记录
现在,我们将应用我们所学到的知识,创建一个新的Laravel项目并实现Laravel日志。
如果你以前没有使用过Laravel,你可以阅读一下什么是Laravel,或者看一下我们的Laravel教程列表来开始学习。
设置Laravel
首先, 我们要用下面的命令创建一个新的Laravel实例.你可以查阅官方文档以了解更多。
在运行下面的命令之前, 打开你的控制台并导航到你存储PHP项目的地方.确保Composer的安装和配置是正确的:
composer create-project laravel/laravel laravel-logging-app
cd laravel-logging-app // Change directory to current Laravel installation
php artisan serve // Start Laravel development server
配置和播种数据库
接下来,我们将设置我们的数据库,创建一个新的Todo
模型,并播种200个假数据用于测试。
打开你的数据库客户端,创建一个新的数据库。我们将做同样的事情,命名为laravel_logging_app_db
,然后在我们的**.env**文件中填入数据库凭证:
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=laravel_logging_app_db
DB_USERNAME=//DB USERNAME HERE
DB_PASSWORD=//DB PASSWORD HERE
接下来,我们将运行以下命令,同时创建迁移和Todo
模型:
php artisan make:model Todo -mc
打开新创建的迁移数据库/migrations/xxx-create-todos-xxx.php并粘贴以下代码:
<?php
use IlluminateSupportFacadesSchema;
use IlluminateDatabaseSchemaBlueprint;
use IlluminateDatabaseMigrationsMigration;
class CreateTodosTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('todos', function (Blueprint $table) {
$table->id();
$table->string('title');
$table->text('description')->nullable();
$table->boolean('is_completed')->default(false);
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('todos');
}
}
你可以通过学习使用Faker在Laravel的数据库中播种你的todos数据。
纠结于停机时间和WordPress问题?Kinsta是为节省你的时间而设计的托管解决方案
Monolog的概述
使用Laravel Monolog, 你可以流式发送结构化的日志到不同的渠道,如电子邮件,Slack,文件,套接字,收件箱,数据库,和各种网络服务。在Laravel中, 你可以通过位于config/logging.php的一个配置文件来配置日志.
该配置文件带有预定义的日志驱动程序可供选择, 默认驱动程序是一个stack
,使用single
通道来记录到存储/日志文件夹中的laravel.log文件。我们将通过使用几个Laravel的日志驱动来演示结构化日志。
Laravel提供了一些与日志交互的方法, 在TodosController.php控制器文件中很快就会展示。
在控制器中写日志信息
打开新创建的TodosController.php控制器文件, 找到app/Http/Controllers文件夹并粘贴以下代码:
<?php
namespace AppHttpControllers;
use AppModelsTodo;
use IlluminateHttpRequest;
use AppHttpControllersController;
use IlluminateSupportFacadesAuth;
use IlluminateSupportFacadesLog;
class TodosController extends Controller
{
public function index(Request $request)
{
$todos = Todo::all();
Log::warning('User is accessing all the Todos', ['user' => Auth::user()->id]);
return view('dashboard')->with(['todos' => $todos]);
}
public function byUserId(Request $request)
{
$todos = Todo::where('user_id', Auth::user()->id)->get();
Log::info('User is accessing all his todos', ['user' => Auth::user()->id]);
return view('dashboard')->with(['todos' => $todos]);
}
public function show(Request $request, $id)
{
$todo = Todo::find($id);
Log::info('User is accessing a single todo', ['user' => Auth::user()->id, 'todo' => $todo->id]);
return view('show')->with(['todo' => $todo]);
}
public function update(Request $request, $id)
{
# Validations before updating
$todo = Todo::where('user_id', Auth::user()->id)->where('id', $id)->first();
Log::warning('Todo found for updating by user', ['user' => Auth::user()->id, 'todo' => $todo]);
if ($todo) {
$todo->title = $request->title;
$todo->desc = $request->desc;
$todo->status = $request->status == 'on' ? 1 : 0;
if ($todo->save()) {
Log::info('Todo updated by user successfully', ['user' => Auth::user()->id, 'todo' => $todo->id]);
return view('show', ['todo' => $todo]);
}
Log::warning('Todo could not be updated caused by invalid todo data', ['user' => Auth::user()->id, 'todo' => $todo->id, 'data' => $request->except('password')]);
return; // 422
}
Log::error('Todo not found by user', ['user' => Auth::user()->id, 'todo' => $id]);
return; // 401
}
public function store(Request $request)
{
Log::warning('User is trying to create a single todo', ['user' => Auth::user()->id, 'data' => $request->except('password')]);
# Validations before updating
$todo = new Todo;
$todo->title = $request->title;
$todo->desc = $request->desc;
$todo->user_id = Auth::user()->id;
if ($todo->save()) {
Log::info('User create a single todo successfully', ['user' => Auth::user()->id, 'todo' => $todo->id]);
return view('show', ['todo' => $todo]);
}
Log::warning('Todo could not be created caused by invalid todo data', ['user' => Auth::user()->id, 'data' => $request->except('password')]);
return; // 422
}
public function delete(Request $request, $id)
{
Log::warning('User is trying to delete a single todo', ['user' => Auth::user()->id, 'todo' => $id]);
$todo = Todo::where('user_id', Auth::user()->id)->where('id', $id)->first();
if ($todo) {
Log::info('User deleted a single todo successfully', ['user' => Auth::user()->id, 'todo' => $id]);
$todo->delete();
return view('index');
}
Log::error('Todo not found by user for deleting', ['user' => Auth::user()->id, 'todo' => $id]);
return; // 404
}
}
在TodoController
中的每个方法中,我们添加了Log
facade,用一个特定的日志级别来定义我们要发送的错误类型。下面是一个使用
在store
方法中使用Log facade的例子:
public function store(Request $request)
{
Log::warning('User is trying to create a single todo', ['user' => Auth::user()->id, 'data' => $request->except('password')]);
# Validations before updating
$todo = new Todo;
$todo->title = $request->title;
$todo->desc = $request->desc;
$todo->user_id = Auth::user()->id;
if ($todo->save()) {
Log::info('User create a single todo successfully', ['user' => Auth::user()->id, 'todo' => $todo->id]);
return view('show', ['todo' => $todo]);
}
Log::warning('Todo could not be created caused by invalid todo data', ['user' => Auth::user()->id, 'data' => $request->except('password')]);
return; // 422
}
格式化日志信息
假设你对Laravel使用的默认LineFormatter
,它在提供可读和有用的信息方面做得很好。
在这种情况下, 你可以很容易地建立一个自定义的格式化对象来适应你的使用情况,并在整个应用程序中使用它。
官方的Monolog文档给出了一个完整的可用格式化器的列表,并可以轻松地创建一个自定义的格式化器。
在Laravel中, 你可以很容易地设置任何一个驱动程序来使用你的自定义格式化器, 就像下面位于config/logging.php的配置文件中的列表一样:
'daily' => [
'driver' => 'daily',
'path' => storage_path('logs/laravel.log'),
'level' => env('LOG_LEVEL', 'debug'),
'days' => 14,
'formatter' => MonologFormatterHtmlFormatter::class,
'formatter_with' => [
'dateFormat' => 'Y-m-d',
]
],
上面的例子在daily
驱动程序中添加了一个自定义的MonologFormatterHtmlFormatter
,使用formatter
和formatter_with
关键在daily
通道配置中来改变日期的格式。
发送日志到不同的通道
在Monolog的帮助下, Laravel可以同时发送日志到不同的渠道和多个渠道.
让我们演示一下如何按照这些简单的步骤将日志发送到我们的Slack频道。改变默认的日志通道为Slack,并在你的**.env**文件中添加Slack WebhookURL。
LOG_CHANNEL=slack
LOG_SLACK_WEBBHOOK_URL= Slack_webhook_url_here
接下来,通过使用Log
facade在你的应用程序中记录一条消息来测试你的配置,如下图所示。
Log::debug("The API instance is on fire caused by:", ['user' => 1])
你可以打开你的Slack频道,检查你在生成Webhook URL时指定的所需频道中打印的错误。
总结
日志与你的应用程序的任何其他因素一样重要,甚至更重要。这就是为什么它被 "十二要素应用宣言 "建议为任何现代应用最关键的问题之一。
通过有效的日志记录,你可以轻松地阅读、查看和可视化发生在你的生产就绪的应用程序中的错误和缺陷。为此,你必须在项目开始时就在你的应用程序中实施结构化的日志记录。
在这篇文章中, 我们已经探讨了Laravel的日志,以及为什么你应该在你的下一个项目中使用它。我们详细讨论了结构化日志和集中式日志。此外, 我们还学习了如何通过建立一个Todo应用程序来实现Laravel的日志。