The extension I am learning today is an extension related to logging. For the logging application of PHP, in addition to its own error_log() and syslog(), monolog will often be seen in most frameworks. . Of course, what we are talking about today is not monolog, but an extended log component that needs to be installed by yourself.

About SeasLog

The first thing to note is that the SeasLog extension is developed by our Chinese, Neeke. And this extension is also included in the official documentation. Below is the link to his Zhihu homepage. You can learn more from the big guys.

Architect Neeke: https://www.zhihu.com/people/ciogao

For the two built-in log functions of PHP, that is, error_log() and syslog(), although they are powerful and have excellent performance, there is no error level setting, no fixed format, and no module records can be distinguished. However, logging programs such as monolog and log4php have some slight shortcomings in performance. It is precisely because of these various reasons that Neeke has developed this SeasLog extension to solve the above log-related system problems.

Because it was developed by our countrymen, its Chinese documentation is very friendly. There are detailed Chinese documentation in Gibhub and official documentation, which is very convenient for us to use. The installation process is no different from ordinary PHP extensions, and no other special software support is needed.

Log record format

Let's first understand the logging format of SeasLog. Its log format template is configured in the php.ini file and cannot be modified dynamically. You need to configure the seaslog.default_template option. The default value is "%T | %L | %P | %Q | %t | %M". Among them, %T stands for time, %L stands for log level, %P stands for process ID, %Q stands for request ID, %t stands for timestamp, and %M stands for log information.

Of course, in addition to these default parameters, it also has some other parameters, which are described in detail in the document, and we will mention two more in the following article. You can refer to the specific content in the official document by yourself, so I won't talk about it here.

Related attribute parameter settings

In addition to some option parameters that need to be configured in php.ini, SeasLog can also configure and obtain some configuration information while the program is running.

Log root directory

// 获取设置日志根目录
var_dump(SeasLog::getBasePath()); // string(12) "/var/log/www"
SeasLog::setBasePath("./");
var_dump(SeasLog::getBasePath()); // string(2) "./"

This is the file directory information to be recorded in the log. We can get the currently configured file directory information through getBasePath(). By default, it is the directory "/var/log/www". Of course, we can also configure the seaslog.default_basepath option in php.ini to modify the default directory. In the code, we can modify this directory through the setBasePath() function. For example, we have modified the log directory to the current directory in the above test code.

Logger information

// 获取设置日志 Logger 名称
var_dump(SeasLog::getLastLogger()); // string(7) "default"

SeasLog::info("Test Logger Default");
// ./default/20200106.log
// 2021-01-06 01:01:53 | INFO | 5038 | 5ff50c019ebe9 | 1609894913.650 | Test Logger Default

SeasLog::setLogger("mylog");
var_dump(SeasLog::getLastLogger()); // string(5) "mylog"

SeasLog::info("Test Logger MyLog");
// ./mylog/20200106.log
// 2021-01-06 01:01:53 | INFO | 5038 | 5ff50c019ebe9 | 1609894913.650 | Test Logger MyLog

Logger information can be regarded as a log category, and it will put the logs under this category in the same folder. For example, our Logger is default by default. If you use info() to record logs directly by default, a default directory will be generated in the current log root directory and a log file named after the current date will be created.

If we use setLogger() to set a new Logger, then when logging twice, a new Logger directory will be created and the log will be recorded to this directory.

Date format

// 获取设置日志日期格式
var_dump(SeasLog::getDatetimeFormat()); // string(11) "Y-m-d H:i:s"

SeasLog::info("Test Datetime Default");
// 2021-01-06 01:04:44 …………

SeasLog::setDatetimeFormat("Y/m/d His");
var_dump(SeasLog::getDatetimeFormat()); // string(9) "Y/m/d His"

SeasLog::info("Test Datetime New");
// 2021/01/06 010444 …………

The modification of the date format is actually for the %T parameter in our log format. This is not much to explain, the code has been demonstrated very clearly, use getDatetimeFormat() to get the currently set date information, and the setDatetimeFormat() method is to set the date format.

Request id

var_dump(SeasLog::getRequestID()); // string(13) "5ff50e4721a06"
SeasLog::setRequestID("new_request_id" . uniqid());
var_dump(SeasLog::getRequestID()); // string(27) "new_request_id5ff50e6519202"
SeasLog::info("Test New Request ID");
// 2021/01/06 011216 | INFO | 5250 | new_request_id5ff50e70337b8 | 1609895536.210 | Test New Request ID

The request ID is for the content of the %Q parameter. By default, it is a unique ID data generated by calling uniqid(). We can use setRequestID() to specify the content of the request ID to be generated for the current log.

Other log variables

In addition to the above fixed-function functions, we can also set some other request parameter information.

ar_dump(SeasLog::getRequestVariable(SEASLOG_REQUEST_VARIABLE_DOMAIN_PORT));
var_dump(SeasLog::getRequestVariable(SEASLOG_REQUEST_VARIABLE_REQUEST_URI));
var_dump(SeasLog::getRequestVariable(SEASLOG_REQUEST_VARIABLE_REQUEST_METHOD));
var_dump(SeasLog::getRequestVariable(SEASLOG_REQUEST_VARIABLE_CLIENT_IP));
// string(3) "cli"
// string(5) "1.php"
// string(9) "/bin/bash"
// string(5) "local"

// seaslog.default_template="%T | %L | %P | %Q | %t | %M | %H | %m"

SeasLog::info("Test Other Request Variable");
// 2021/01/06 012512 | INFO | 5496 | new_request_id5ff5117888f86 | 1609896312.561 | Test Other Request Variable | localhost.localdomain | /bin/bash

SeasLog::setRequestVariable(SEASLOG_REQUEST_VARIABLE_REQUEST_METHOD, "Get");
var_dump(SeasLog::getRequestVariable(SEASLOG_REQUEST_VARIABLE_REQUEST_METHOD)); // string(3) "Get"
SeasLog::info("Test New Other Request Variable");
// 2021/01/06 012625 | INFO | 5520 | new_request_id5ff511c1367c2 | 1609896385.223 | Test New Other Request Variable | localhost.localdomain | Get

The getRequestVariable() and setRequestVariable() methods are the methods used to obtain and set other request variable information. They only support four constants, which are demonstrated in the code, which represent the requested port number, URI, method, and client IP. However, these contents are not configured in the default log template, so we need the default log template to view these contents.

In this code, we tested the addition of %H and %m to the default template. %H represents the host name, and %m represents the requested method, which is the request method. Among them, %H cannot be set and modified. This is just to test the configuration of the format, and the focus is on the content of %m. By default, the request method recorded in the log is /bin/bash, in fact, because we are using the command line to run the test code, its request method is the command environment bash. At this time, we use setRequestVariable() to modify the value of the request method to "Get", and all the %m parameters in the subsequent log records will be output as Get.

Logging

Through the above study, we have seen the use of an info() method. It is a function to record common log information, and the %L information in the log information recorded by it will be displayed as "info". There are other log type methods later, let's take a look at what else this info() method can dig deeper.

SeasLog::info("Test Info Log");
// 2021/01/06 013018 | INFO | 5583 | new_request_id5ff512aaafcde | 1609896618.720 | Test Info Log | localhost.localdomain | Get

SeasLog::info("Test {name} Log", ['name'=>'Info1']);
// 2021/01/06 013018 | INFO | 5583 | new_request_id5ff512aaafcde | 1609896618.720 | Test Info1 Log | localhost.localdomain | Get

SeasLog::info("Test {name} Log", ['name'=>'Info1'], 'default');
// ./default/20210106.log
// 2021/01/06 013140 | INFO | 5609 | new_request_id5ff512fc264f3 | 1609896700.156 | Test Info1 Log | localhost.localdomain | Get

SeasLog::info("Test {name} Log", ['name'=>'Info1'], 'defaultNew');
// defaultNew/20210106.log
// 2021/01/06 013230 | INFO | 5631 | new_request_id5ff5132e3e143 | 1609896750.254 | Test Info1 Log | localhost.localdomain | Get

That's right, besides the first parameter of the info() method, which is the content information of the log, it also has two parameters.

The function of the second parameter is to define an array to replace some placeholders in the first parameter. Does it feel similar to precompiled placeholders in the database? The third parameter is to directly specify a Logger, which is the log category directory to be recorded. This parameter is convenient. Our current test environment is the "mysql" set earlier, so the first two logs are recorded in the log file under the mysql directory. The latter two are one record in the default directory, and the other one is in a new defaultNew directory. We did not create this new Logger directory through setLogger(), but here we will also create new directories and log files directly like setLogger(), which is very convenient.

In addition to info(), common log types include notice, warning, error, alter, debug, critical, and emergency. I believe students who have used framework development or monolog will not be unfamiliar. In SeasLog, these corresponding methods and functions also exist, and all the parameters and info() are the same.

SeasLog::alert("Test {name} Log", ['name'=>'Alert'], 'defaultNew');
SeasLog::error("Test {name} Log", ['name'=>'Error'], 'defaultNew');
SeasLog::debug("Test {name} Log", ['name'=>'Debug'], 'defaultNew');
SeasLog::critical("Test {name} Log", ['name'=>'Critical'], 'defaultNew');
SeasLog::emergency("Test {name} Log", ['name'=>'Emergency'], 'defaultNew');
SeasLog::notice("Test {name} Log", ['name'=>'Notice'], 'defaultNew');
SeasLog::warning("Test {name} Log", ['name'=>'Warning'], 'defaultNew');
// 2021/01/06 014106 | ALERT | 5762 | new_request_id5ff5153200b30 | 1609897266.2 | Test Alert Log | localhost.localdomain | Get
// 2021/01/06 014106 | ERROR | 5762 | new_request_id5ff5153200b30 | 1609897266.2 | Test Error Log | localhost.localdomain | Get
// 2021/01/06 014106 | DEBUG | 5762 | new_request_id5ff5153200b30 | 1609897266.2 | Test Debug Log | localhost.localdomain | Get
// 2021/01/06 014106 | CRITICAL | 5762 | new_request_id5ff5153200b30 | 1609897266.2 | Test Critical Log | localhost.localdomain | Get
// 2021/01/06 014106 | EMERGENCY | 5762 | new_request_id5ff5153200b30 | 1609897266.2 | Test Emergency Log | localhost.localdomain | Get
// 2021/01/06 014106 | NOTICE | 5762 | new_request_id5ff5153200b30 | 1609897266.2 | Test Notice Log | localhost.localdomain | Get
// 2021/01/06 014106 | WARNING | 5762 | new_request_id5ff5153200b30 | 1609897266.2 | Test Warning Log | localhost.localdomain | Get

As you can see, the %L content in the log information they record corresponds to itself. Of course, in addition to these fixed method functions, we also have a general logging method.

SeasLog::log(SEASLOG_INFO, "Test log() {name} Log", ['name'=>"Info"], 'defaultNew');
// 2021/01/06 014330 | INFO | 5809 | new_request_id5ff515c2ddd5d | 1609897410.908 | Test log() Info Log | localhost.localdomain | Get

This is the log() method. Compared with the info() methods above, it has one more parameter that needs to specify the current log type, and this parameter also provides a constant.

Log information view

The next step is to view some data of the log information. Just like in the above test, we have actually recorded a lot of log data back and forth. Is there any method or function to conveniently view the content of these log information?

SeasLog::setLogger('defaultNew');
var_dump(SeasLog::analyzerCount());
// array(8) {
//     ["DEBUG"]=>
//     int(40)
//     ["INFO"]=>
//     int(80)
//     ["NOTICE"]=>
//     int(40)
//     ["WARNING"]=>
//     int(40)
//     ["ERROR"]=>
//     int(40)
//     ["CRITICAL"]=>
//     int(40)
//     ["ALERT"]=>
//     int(40)
//     ["EMERGENCY"]=>
//     int(40)
//   }

The first is the analyzerCount(), which is used to count the number of logs. You can see that by default it returns an array and displays the number of logs recorded by all log types. Of course, it can add parameters, but if you increase the parameter to specify the log type, it returns a common numeric value, which is the quantity information of the specified log type.

var_dump(SeasLog::analyzerCount(SEASLOG_WARNING)); // int(10)
var_dump(SeasLog::analyzerCount(SEASLOG_ERROR, null)); // int(10)
var_dump(SeasLog::analyzerCount(SEASLOG_ERROR, null, "1609897995.939")); // int(1)

In addition, it has two other parameters, the second parameter is the path information of the specified log, that is, if there are other directories in the current Logger directory, you can specify this parameter, if it is set to null, ignore this function. The third parameter is filtering information, which is a bit like the concept of fuzzy search. For example, we only search for the log with the specified timestamp here, and the returned result is only one.

var_dump(SeasLog::analyzerDetail(SEASLOG_ALL, 'secpath/', null, 1, 2, SEASLOG_DETAIL_ORDER_DESC));
// array(2) {
//     [0]=>
//     string(125) "2021/01/06 020212 | INFO | 6840 | new_request_id5ff51a248e1e2 | 1609898532.582 | Test Info1 Log | localhost.localdomain | Get"
//     [1]=>
//     string(124) "2021/01/06 020212 | INFO | 6840 | new_request_id5ff51a248e1e2 | 1609898532.582 | Test Info Log | localhost.localdomain | Get"
//   }

analyzerDetail() can display specific log information according to conditions. It can also specify the log type, path directory, and keyword information. In addition, it can also specify the number of returned items, sorting, etc., that is, it has its own range search and page turning functions. Is it very tough? Like analyzerCount(), its parameters can also be set to null, which means that the current parameter is not enabled to go to the default value. For example, null in our test code is the parameter position of the keyword, which means that there is no need to fuzzy match the keyword.

Memory buffer log

In addition to directly recording file logs, SeasLog can also be configured in php.ini to send files to a specified tcp or ftp server. You can understand this yourself. The next thing to learn is the operation of caching logs in memory. Through this configuration, we can further improve the performance of SeasLog.

If each log is written to disk or sent externally in real time, it will undoubtedly bring additional disk IO or network IO overhead. In order to further improve performance, the memory buffer capability provided in SeasLog can help us solve the problem. It actually means to save the log in the memory first, and then write it to the disk or send it to the outside at one time after reaching a certain amount.

var_dump(SeasLog::getBufferEnabled()); // bool(false)

// seaslog.use_buffer=1
// seaslog.buffer_disabled_in_cli=0
// seaslog.buffer_size=100
var_dump(SeasLog::getBufferEnabled()); // bool(true)

We need to configure seaslog.use_buffer to 1 in php.ini, which is to turn on the memory buffer function. If it is a command line script to run, you also need to set seaslog.buffer_disabled_in_cli to 0 to turn off the "memory buffer is not enabled in the cli environment" function. Finally, set the seaslog.buffer_size, which is how many pieces of data the buffer holds. When these settings are completed, use getBufferEnabled() to see that the returned content is true, that is, we have enabled the memory buffering capability.

Next, use the getBuffer() method to see all the log information currently in the memory buffer.

var_dump(SeasLog::getBuffer());
// array(3) {
//     [".//default/20210106.log"]=>
//     array(2) {
//       [0]=>
//       string(125) "2021-01-06 02:21:43 | INFO | 8006 | 5ff51eb7e1a54 | 1609899703.924 | Test Logger Default | localhost.localdomain | /bin/bash
//   "
//     ……………………
//     }
//     [".//mylog/20210106.log"]=>
//     array(8) {
//       [0]=>
//       string(123) "2021-01-06 02:21:43 | INFO | 8006 | 5ff51eb7e1a54 | 1609899703.924 | Test Logger MyLog | localhost.localdomain | /bin/bash
//   "
//       ……………………
//     }
//     [".//defaultNew/20210106.log"]=>
//     array(9) {
//       [0]=>
//       string(126) "2021/01/06 022143 | INFO | 8006 | new_request_id5ff51eb7e1b30 | 1609899703.924 | Test Info1 Log | localhost.localdomain | Get
//   "
//       ……………………
//     }
//   }

When the memory buffer function is turned on, all logging operations in our previous test code will record the logs in the memory, so there will be a lot of data in the array returned by the getBuffer() method. When the script finishes running or requests to terminate, the data in the memory will be written to disk or sent away. Of course, we can also manually flush these data to disk.

// 刷新内存缓冲区
SeasLog::flushBuffer();
var_dump(SeasLog::getBuffer());
// array(0) {
// }

You can manually flush the memory data by calling the flushBuffer() method, and then call getBuffer() and there will be no data. At the same time, all log data is written into our test file at one time.

Summarize

It has to be said that through this extended learning it seems that a big treasure has been discovered. This kind of logging system operates on the underlying extensions, and there is definitely no problem in efficiency, but the trouble is that you need to install the underlying extensions, unlike monolog and the like, you can directly use Composer to complete the installation and use. Things always have pros and cons, and which one is suitable depends on in-depth analysis and judgment based on our business scenarios.

Test code:

https://github.com/zhangyue0503/dev-blog/blob/master/php/2021/01/source/2. Learn to understand the SeasLog log extension in PHP. php

Reference documents:

https://www.php.net/manual/zh/book.seaslog.php

Github document
[https://github.com/SeasX/SeasLog/blob/master/README_zh.md]


硬核项目经理
90 声望18 粉丝