# PHP实现把日志保存到mongodb中

`monolog`可以说是PHP中应用最为广泛的日志组件，使用它可以方便的实现多种日志记录需求，本文主要利用`monolog`的`MongoDBHandler`类，将日志保存至`mongodb`中。

## 环境

本文使用`webman`框架，用法基本与`laravel`大同小异。

## 准备工作

### 需要为php安装mongodb扩展

```sh
pecl install mongodb
```

> 也可以使用已经做好环境的docker镜像：`docker pull wandoubaba517/workerman:8.1`

### monolog版本要在3.0以上

先创建一个全新的`webman`项目：

```sh
composer create-project workerman/webman
cd webman
```

`webman`中默认已经内置了`monolog`，不过我们要确保`monolog`的版本要在3.0以上：

```sh
compose require monolog/monolog:^3.0
```

### 用docker快速启动一下mongodb服务

```sh
mkdir -p log/db
touch mongod.conf
```

`mongod.conf`文件中的内容类似下面的样子：

```yml
storage:
  dbPath: /data/db
net:
  port: 27017
  bindIp: 0.0.0.0
```

`docker-compose.yml`文件中添加`log`服务，类似下面的样子（把root和password换成你自己想要设置的）：

```yml
version: "3.1"

services:
  log:
    image: mongo:7.0
    container_name: mongodb-log
    restart: always
    environment:
      MONGO_INITDB_ROOT_USERNAME: root
      MONGO_INITDB_ROOT_PASSWORD: password
      MONGO_INITDB_DATABASE: log
    volumes:
      - ./log/db:/data/db
      - ./log/mongod.conf:/etc/mongod.conf
    network_mode: host
    command: ['mongod', '--config', '/etc/mongod.conf']
```

执行`docker compose up -d`就可以启动mongodb服务了。

### 引入`mongodb/laravel-mongodb`组件

```sh
composer require mongodb/laravel-mongodb
```

## 实现代码

### `config/database.php`文件中配置mongodb连接

```php
return [
    'default' => 'pgsql',
    'connections' => [
        'pgsql' => [
            /// ...
        ],
        'mongo' => [
            'driver'   => 'mongodb',
            'host'     => '127.0.0.1',
            'port'     => 27017,
            'database' => 'log',
            'username' => 'root',
            'password' => 'password',
            'options' => [
                'appname' => 'homestead'
            ],
        ],
        /// ...
    ]
];
```

### `config/log.php`文件中配置日志通道

```php
return [
    'mongo' => [
        'handlers' => [
            [
                'class' => Monolog\Handler\MongoDBHandler::class,
                'constructor' => [
                    new Mongodb\Client('mongodb://root:password@localhost:27017'),
                    'log',      // 数据库名
                    'default',  // 集合名（相当于表名）
                    Monolog\Level::Debug,   // 最低日志等级（默认就是Debug）
                ],
            ],
        ],
    ],
    /// ...
];
```

### 业务代码中应用

以`app/controller/IndexController.php`中的`log`方法为例：

```php
<?php

namespace app\controller;

use support\Request;
use support\Db;
use support\Log;

class IndexController
{
    public function log(Request $request)
    {
        // 随便记录点什么日志
        Log::channel('mongo')->info(__CLASS__ . DIRECTORY_SEPARATOR . __FUNCTION__, $request->all());
        $logs = Db::connection('mongo')->collection('default')->get();
        return json($logs);
    }
}
```

### 运行测试

启动服务，然后用浏览器或者接口调试工具访问`http://IP:port/index/log`，控制器方法会先记录一条日志，然后再把整所有已经保存的日志全部都输出回来。

## 与文本日志对比

主要优势：方便查询（文本日志文件的最小粒度是一天一个文件，当日志量具大时，在几百M或者几个G甚至更大的一个文本文件中定位某行记录，简直就是灾难）。

主要劣势：需要自行处理过期记录（文本日志可以自动删除过期的文件）。

## 推荐

本文作者建好了一个开箱即用的webman+mongodb代码仓库：<https://git.wandoubaba.com/wandoubaba/docker-webman-mongolog>
