背景
编写了一个常驻进程的脚本结果发现内存在不断上涨最后导致溢出,经过排查发现是日志的问题,之后做了一个测试如下:
for ($i = 0; $i < 1000; $i++) {
Log::info('[INFO]:' . $i);
Log::error('[ERROR]:' . $i);
Log::notice('[NOTICE]:' . $i);
Log::debug('[DEBUG]:' . $i);
if ($i%100==0) {
var_dump(memory_get_usage());
}
}
var_dump(memory_get_usage());
运行结果
4846848
5008736
5118264
5235984
5321256
5422592
5573080
5658032
5742984
5828256
5945368
由结果可以看出内存在不断上涨。
解决办法:
先附上解决办法执行:
一劳永逸
composer remove --dev facade/ignitio
手动释放
app()->make(LogRecorder::class)->reset();
参考地址: https://learnku.com/laravel/t/53565
分析过程如下:
1. 先上一个Xhprof对系统进行初筛
附 Xhprof 结果:
由此分析得出结果是由于logger导致的且与monolog无关。
2. 代码分析
2.1 大胆猜测
根据上图得知问题是由\Illuminate\Log\Logger::writeLog
导致
protected function writeLog($level, $message, $context)
{
$this->logger->{$level}($message = $this->formatMessage($message), $context);
$this->fireLogEvent($level, $message, $context);
}
这里有一个事件触发,那么有可能是事件触发导致的。
protected function fireLogEvent($level, $message, array $context = [])
{
// If the event dispatcher is set, we will pass along the parameters to the
// log listeners. These are useful for building profilers or other tools
// that aggregate all of the log messages for a given "request" cycle.
if (isset($this->dispatcher)) {
$this->dispatcher->dispatch(new MessageLogged($level, $message, $context));
}
}
2.2 细致排查
根据fireLogEvent
方法得知是对MessageLogged
的监听
全局搜索MessageLogged有关的监听逐一排查
应该是LogRecorder
的问题
public function register(): self
{
$this->app['events']->listen(MessageLogged::class, [$this, 'record']);
return $this;
}
发现有回调record
public function record(MessageLogged $event): void
{
if ($this->shouldIgnore($event)) {
return;
}
$this->logMessages[] = LogMessage::fromMessageLoggedEvent($event);
}
先将其注释掉看看结果
结果发现内存不再增高,排查问题结束!
2.3 得出结论
结论
$this->logMessages[] = LogMessage::fromMessageLoggedEvent($event);
导致内存增高应该是没有地方释放,再看这个类存在reset
public function reset(): void
{
$this->logMessages = [];
}
2.3.1 方案一:手动释放
那么调用到这个应该就可以了。
app()->make(LogRecorder::class)->reset();
OK,问题解决,但这样不是长久之计。
2.3.1 方案二:一劳永逸
那么需要查找LogRecorder
的调用。
在\Facade\Ignition\IgnitionServiceProvider
public function boot()
{
if ($this->app->runningInConsole()) {
$this->publishes([
__DIR__.'/../config/flare.php' => config_path('flare.php'),
], 'flare-config');
$this->publishes([
__DIR__.'/../config/ignition.php' => config_path('ignition.php'),
], 'ignition-config');
}
$this
->registerViewEngines()
->registerHousekeepingRoutes()
->registerLogHandler()
->registerCommands();
if ($this->app->bound('queue')) {
$this->setupQueue($this->app->get('queue'));
}
$this->app->make(QueryRecorder::class)->register();
$this->app->make(LogRecorder::class)->register();
$this->app->make(DumpRecorder::class)->register();
}
protected function setupQueue(QueueManager $queue)
{
$queue->looping(function () {
$this->app->get(Flare::class)->reset();
if (config('flare.reporting.report_queries')) {
$this->app->make(QueryRecorder::class)->reset();
}
$this->app->make(LogRecorder::class)->reset();
$this->app->make(DumpRecorder::class)->reset();
});
}
发现只有Queue
才会释放内存。。。。
查找了一圈除了改源码就剩手动不断释放了。
查找这个包的作用,最后发现只是报错展示使用,决定将包删除,问题彻底解决。