【原创】yii2配置各种监控日志发送邮件及时处理

yii2日志自动发送邮件网上已经有很多例子了,这里会把踩坑和别的说说。

同样先把基本的说说,先配置项目公用邮箱配置common/config/main-local.php,自己用qq邮箱开通smtp就可以了。

'components' => [
   'db' => [
      'class' => 'yii\db\Connection',
      'dsn' => 'mysql:host=localhost;dbname=yii2advanced',
      'username' => 'root',
      'password' => 'root',
      'charset' => 'utf8',
   ],
   'mailer' => [
      'class' => 'yii\swiftmailer\Mailer',
      'viewPath' => '@common/mail',
      // send all mails to a file by default. You have to set
      // 'useFileTransport' to false and configure a transport
      // for the mailer to send real emails.
      //if ($this->useFileTransport) {
      // $isSuccessful = $this->saveMessage($message);
      //} else {
      // $isSuccessful = $this->sendMessage($message);
      //}
      'useFileTransport' => false,//这句一定有,false发送邮件,true只是生成邮件在runtime文件夹下,不发邮件
      'transport' => [
         'class' => 'Swift_SmtpTransport',
         'host' => 'smtp.qq.com',  //每种邮箱的host配置不一样
         'username' => 'admin@netpc.com.cn',
         'password' => 'xxx',
         'port' => '465',
         'encryption' => 'ssl',
      ],
      'messageConfig' => [
         'charset' => 'UTF-8',
         'from' => ['admin@netpc.com.cn' => 'admin']
      ],
   ],
],

以上配置成功后就能发送邮件了,

$mail= Yii::$app->mailer->compose();
$mail->setTo('admin@netpc.com.cn');
$mail->setSubject($title);
//$mail->setTextBody('text');   //发布纯文字文本
$mail->setHtmlBody("<br>商品:<a href='".$url."' target='_blank'>地址</a>");    //发布可以带html标签的文本
if($mail->send())
   return "success";
else
   return "failse";

这样只是自己能发邮件,但是日志错误是不会自动发送的,这时候需要配置项目内容,这里用api项目举例,文件api/config/main.php。

'log' => [
   'traceLevel' => YII_DEBUG ? 3 : 0,
   //'flushInterval' => 1,
   'targets' => [
      [
         //'class' => 'yii\log\EmailTarget',//默认邮件处理类
         'class' => 'api\components\EmailTargetKen',//自定义日志处理类
         //'levels' => ['error', 'warning', 'trace', 'info'],//各种等级区分,根据需要使用
         'levels' => ['error'],
         'categories' => ['email_log'],
         'mailer' => 'mailer',
         'message' => [
            'from' => ['admin@netpc.com.cn' => 'admin'],
            'to' => ['admin@netpc.com.cn'],
            'subject' => 'Log message',

         ],
         'except' => ['yii\web\HttpException:404'],  // 排除404,不然的话你会发现你的邮箱里全塞满了这些邮件
         'exportInterval' => 1,//阀值一个错误的时候就执行输出
         'logVars' => [],
      ],
      [
         'class' => 'yii\log\FileTarget',//默认文件处理类
         'levels' => ['error', 'warning'],
         'exportInterval' => 1,
         'categories' => ['myinfo'],
         //'categories' => ['yii\*'],//$categories the message categories to filter by. If empty, it means all categories are allowed.
         'logVars' => ['*'],//记录最基本的 []赋值也可以
         //'logFile' => '@runtime/logs/order.log'.date('Ymd'),//用日期方式记录日志
      ],
   ],
],

这里说说坑了,假如你配置了categories这个属性,那么只有在自己代码里面执行Yii::error这样代码才能触发文件记录和发送邮件错误日志,而且是必需程序能正常完全执行完毕才能触发,例如代码有个语法输错array(少了一个括号,有没有配置一个全局没有categories分类的日志配置,那么以下Yii::error的日志都不会被触发,除非加多一个没有categories的全局配置。

Yii::error('error_test1');
Yii::error('error_test2','myinfo');//只会记录到myinfo分类文件日志里
Yii::error('error_test3','email_log');//只会记录到email_log分类邮件日志里

我就是按照网上配置了categories这个属性,结果没有用YII:error去触发错误,一直都没办法查看到日志和邮件。

我建议如果你需要分类保存日志,也需要配置一个全局的,因为只有全局没有限制分类的存在才能记录到所有的错误,那怕你没有配置分类,也能传分类参数用分类记录日志。

接下来说说api\components\EmailTargetKen这个自定义处理类,看看源码包括数据库记录什么都可以自己搞出来加工处理整合在一起。

我为什么做处理是因为在做测试的时候,故意写错语法,界面提示{“name”:”PHP Parse Error”,”message”:”syntax error, unexpected ‘]'”,”code”:4,”type”:”yii\\base\\ErrorException”,”file”:后面省略,可是日志文件里面却记录的不一样,少了”name”:”PHP Parse Error”这句语法错误提示,翻看源码后发现记录和提示的确是不一样。所以只能自己加工了。

namespace api\components;
public function export()
{
    // moved initialization of subject here because of the following issue
    // https://github.com/yiisoft/yii2/issues/1446
    if (empty($this->message['subject'])) {
        $this->message['subject'] = 'Application Log';
    }

    $error = Yii::$app->errorHandler->exception;
    $messages = array_map([$this, 'formatMessage'], $this->messages);

    //不喜欢它70个字符截断换行的方式    
    //$body = wordwrap(implode("\n", $messages), 70);
    //如果捕获的到错误就加上错误名称
    if($error){
        $body = $error->getName().implode("\n", $messages);
    }else{
        $body = implode("\n", $messages);
    }
    $message = $this->composeMessage($body);
    if (!$message->send($this->mailer)) {
        throw new LogRuntimeException('Unable to export log through email!');
    }
}

这样就能带上错误名称,有名称一看提示就能清楚大概是什么问题导致的。

yii2自动发送错误日志邮件有什么问题给我留言。

发表在 服务器 | 标签为 | 留下评论

谷歌浏览器https证书过期更新后依然错误

今天有个域名的免费https证书过期了,然后重新申请更新后,谷歌浏览器上显示不安全,可是火狐没问题。

谷歌按F12,然后切换到“Security”,查看该网站使用的ssl证书情况,显示页面不安全。

This page is not secure.
Active content with certificate errors
You have recently allowed content loaded with certificate errors (such as scripts or iframes) to run on this site.

网上https过期,一般就是查看证书是否过期,电脑时间是否正确,最后我测试用一个文本文件访问都出现同样的错误提示,那样肯定没有脚本或框架。

考虑应该是证书缓存,晚上找了删除方法,还是不行。

最后很简单,什么问题都没有,就是重启下浏览器,哎,表示无语,重启重启重启,3遍。

 

发表在 服务器 | 标签为 , | 留下评论

[原创]yii2搭建restful风格api接口强制返回json数据格式

yii2搭建了一个restful风格api接口,想要通过配置文件直接就强制所有action直接返回json数据格式,结果发现移动只要继承use yii\rest\ActiveController;控制器配置文件bootstrap和response的配置就会失效。最后看源码发现ActiveController继承\vendor\yiisoft\yii2\rest\Controller.php控制器里面通过behaviors行为重写了返回信息的数据格式。那么如果选择继承ActiveController控制器的话就只能在当前的行为重写返回数据格式规则,这样就需要每个控制器都去写规则了,如果想全局只能从配置文件里在response里执行到最后截取要出去数据根据自己的需求定义再输出,但是这样又不够灵活,自己看着办吧。参考如下配置内容根据自己需要修改:

return [
   'id' => 'app-api',
   'basePath' => dirname(__DIR__),
   'controllerNamespace' => 'api\controllers',
   'bootstrap' => [
      'log',
      [
         'class' => 'yii\filters\ContentNegotiator',
         'formats' => [
            'application/json' => yii\web\Response::FORMAT_JSON,
            //'text/html' => yii\web\Response::FORMAT_JSON,
            //'application/xml' => yii\web\Response::FORMAT_XML,
         ],
      ]
   ],
   'modules' => [
      'v1' => [  //module 与gii生成module配置的ModuleID 一致
         'class' => 'api\modules\v1\Module',
      ],
   ],
   'components' => [
      'request' => [
         'csrfParam' => '_csrf-api',
         'parsers' => [  //返回的数据格式为json
            'application/json' => 'yii\web\JsonParser',
            'text/json' => 'yii\web\JsonParser',
         ],
      ],
      //'response' => [
      // 'on beforeSend' => function($event) {
      //    if(is_array($event->sender->data)){
      //       $event->sender->format = 'json';
      //    }
      // },
      //],
      'response' => [
         'format' => 'json',//全局controller 返回json 不包括 ActiveController 会被行为重写
         //'charset' => 'UTF-8',
      ],
      'response' => [
         'format' => 'json',
         'formatters' => [
            \yii\web\Response::FORMAT_JSON => [
               'class' => 'yii\web\JsonResponseFormatter',
               'prettyPrint' => YII_DEBUG, // use "pretty" output in debug mode
               'encodeOptions' => JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE,
               // ...
            ],
         ],
      ],
      'response' => [
         'class' => 'yii\web\Response',
         'on beforeSend' => function ($event) {
            $response = $event->sender;
            $response->data = [
               'success' => $response->isSuccessful,
               'code' => $response->getStatusCode(),
               'message' => $response->statusText,
               'data' => $response->data,
            ];
            $response->statusCode = 200;
         },
      ],

在当前控制器里重写ActiveController控制器里的行为内容,参考着用。

class DefaultController extends ActiveController
{
   /**
    * Renders the test view for the module
    * @return string
    */
   //public function behaviors()
   //{
   // return [
   //    [
   //       'class' => 'yii\filters\ContentNegotiator',
   //       'only' => ['view', 'index'],  // in a controller
   //       // if in a module, use the following IDs for user actions
   //       // 'only' => ['user/view', 'user/index']
   //       'formats' => [
   //          'application/json' => Response::FORMAT_JSON,
   //       ],
   //       'languages' => [
   //          'en',
   //          'de',
   //       ],
   //    ],
   // ];
   //}
   public function behaviors()
   {
      $behaviors = parent::behaviors();
      $behaviors['contentNegotiator']['formats']['text/html'] = Response::FORMAT_JSON;
      //print_r($behaviors);exit;
      return $behaviors;
   }
发表在 服务器 | 标签为 | 留下评论

svn导出checkout后更新没有账号密码提示框

今天配置了个新svn库,passwd也配置了密码,能导出版本库,提交的时候却提示svn: Commit failed (details follow): svn: Authorization failed.

提交失败,仅仅是因为配置了密码,可是你没让它用起来,所以库只有只读的权限,没有写权限。

修改svnserve.conf:
[general]
anon-access = read
auth-access = write
password-db = passwd
authz-db = authz

发表在 服务器 | 标签为 | 留下评论

yii2如何加载Asset资源管理,为什么assets随机生成目录文件?

一般刚开始使用yii2的时候就会疑惑为什么项目有个\web\assets目录会有一些随机名称的子目录,而且一删除它还会时不时生成。

其实它就是yii2前端页面加载需要资源包,一般包括了静态的css、js、视频、图片、字体等。

一般资源包因为都是放在http无法访问的目录上,所以它才会根据组件和系统配置集中打包生成到\web\assets可访问目录下。

一切从main.php页面中使用use backend\assets\AppAsset;开始,

资源包指定为继承 yii\web\AssetBundle 的 PHP 类, 包名为可自动加载的 PHP 类名, 在资源包类中,要指定资源所在位置, 包含哪些 CSS 和 JavaScript 文件以及和其他包的依赖关系。

<?php

namespace app\assets;

use yii\web\AssetBundle;

class AppAsset extends AssetBundle
{
    public $basePath = '@webroot';
    public $baseUrl = '@web';
    public $css = [
        'css/site.css',
        ['css/print.css', 'media' => 'print'],
    ];
    public $js = [
    ];
    public $depends = [//列出该资源包依赖的其他资源包
         'yii\web\YiiAsset',
 'yii\bootstrap\BootstrapAsset',
];
}

如上 AppAsset 类指定资源文件放在 @webroot 目录下,对应的 URL 为 @web,资源包中包含一个 CSS 文件 css/site.css,没有 JavaScript 文件, 依赖其他两个包 yii\web\YiiAsset 和 yii\bootstrap\BootstrapAsset。

\vendor\yiisoft\yii2\web\YiiAsset.php内容如下,YiiAsset又依赖了yii\web\JqueryAsset

class YiiAsset extends AssetBundle
{
    public $sourcePath = '@yii/assets';
    public $js = [
        'yii.js',
    ];
    public $depends = [
        'yii\web\JqueryAsset',
    ];
}

\vendor\yiisoft\yii2\web\JqueryAsset.php内容如下,可以看到最终加载了\vendor\bower-asset\jquery\dist目录下的jquery.js文件,并复制生成到了\web\assets目录下。

class JqueryAsset extends AssetBundle
{
    public $sourcePath = '@bower/jquery/dist';
    public $js = [
        'jquery.js',
    ];
}

这里顺便说点信息,一般\web\assets会有更新问题,可以使用配置linkAssets属性来让它使用快捷方式指定到资源目录位置,而不是复制多一份方式。不使用就记得更新了文件记得删除让它重新生成。

如果默认的jquery版本不合适,就使用覆盖’yii\web\JqueryAsset’方式调用不同版本的jquery。

    'components' => [
        'assetManager'=> [
            'linkAssets'=> true,//在项目/web/assets目录使用快捷方式指定到资源目录位置
            'bundles' => [
                //'yii\web\JqueryAsset' => false,//禁用
                'yii\web\JqueryAsset' => [
/*                    'js' => [
                        //YII_ENV_DEV ? 'jquery.js' : 'jquery.min.js',
                        '//lib.sinaapp.com/js/jquery/2.2.4/jquery-2.2.4.min.js'
                    ]*/
                ],
            ],

        ],
    ],

其它具体内容可以看看官方文档:https://www.yiichina.com/doc/guide/2.0/structure-assets

 

发表在 服务器 | 标签为 | 留下评论

yii2资源Asset加载jquery最版本导致组件无法正常使用

最近yii2安装2.0.34最新AdminLTE后台模板和yii2-admin集成rbac管理组件。

composer require dmstr/yii2-adminlte-asset “2.*”

composer require mdmsoft/yii2-admin “2.x-dev”

发现在yii2-admin添加才当时候界面不能正常弹出下拉选择框,最后排查得知是因为yii2最新版Asset引用了最新的jquery-3.4.1.js。

前端脚本调用的时候出现Uncaught TypeError: elem.getClientRects is not a function错误提示。

该函数只能是jquery2才能正常使用,替换jquery即可。

发表在 服务器 | 标签为 | 留下评论

加速访问GitHub速度慢

今天访问github巨慢无比,发现连ping都不通了。

C:\Users\Administrator>ping www.github.com -t

正在 Ping github.com [13.229.188.59] 具有 32 字节的数据:
请求超时。
请求超时。
请求超时。

dns给我分配了一个访问新加坡服务器的ip,这时候就需要更换访问GitHub服务器ip,那么那个快呢?

访问:http://tool.chinaz.com/dns?type=1&host=github.com&ip=

记录:TTL值比较小的那个地址

访问:https://github.com.ipaddress.com/

记录:IP Address的地址

以上方式选一个,搜索系统盘中的host文件,修改指定固定ip地址。

这里以windows为例,一般为C:\Windows\System32\drivers\etc\hosts,在文件后面添加以下内容。

140.82.112.3 github.com
185.199.108.153 assets-cdn.github.com
185.199.109.153 assets-cdn.github.com
185.199.110.153 assets-cdn.github.com
185.199.111.153 assets-cdn.github.com
199.232.69.194 github.global.ssl.fastly.net

保存,运行cmd,执行ipconfig /flushdns刷新dns缓存,再试试看。

发表在 服务器 | 标签为 | 留下评论

磁盘满Mysql同步状态显示正常实际不同步

今天有台mysql从数据库服务器因为磁盘满了而导致同步失败,访问也失败,清理空间后重启mysql一切都显示正常,查看同步专业如下:

mysql> show slave status\G;
*************************** 1. row ***************************
Slave_IO_State: Waiting for master to send event
Master_Host: 10.10.10.1
Master_User: repluser
Master_Port: 3306
Connect_Retry: 60
Master_Log_File: master-bin.006842
Read_Master_Log_Pos: 67309612
Relay_Log_File: mysqld-relay-bin.019867
Relay_Log_Pos: 66793478
Relay_Master_Log_File: master-bin.006833
Slave_IO_Running: Yes
Slave_SQL_Running: Yes

同步的两个slave都是yes,也就没留意它了,到后来才发现它实际上停止的,没有任何错误提示,想想取消同步,再开启说不定就可以了,结果一执行stop slave就卡死了。

mysql> stop slave;
ERROR 2006 (HY000): MySQL server has gone away
No connection. Trying to reconnect…
Connection id: 15367
Current database: *** NONE ***

接下来就什么都操作不了了,kill mysql重新启动重复操作都不行。

应该是磁盘满的时候同步的日志文件没有写完整,导致同步的时候卡死。最后查看mysql任务列表发现有个卡了1865618秒的Waiting for an event from Coordinator复制进程任务,决定kill它,应该是它卡了很久。

mysql> show processlist;
ERROR 2006 (HY000): MySQL server has gone away
No connection. Trying to reconnect…
Connection id: 18126
Current database: *** NONE ***

+——-+————-+———–+——+———+———+———————————————–+——————+
| Id | User | Host | db | Command | Time | State | Info |
+——-+————-+———–+——+———+———+———————————————–+——————+
| 16549 | system user | | NULL | Connect | 685 | Waiting for master to send event | NULL |
| 16550 | system user | | NULL | Connect | 685 | Waiting for Slave Worker to release partition | NULL |
| 16551 | system user | | NULL | Connect | 1865618 | Waiting for an event from Coordinator | NULL |
| 18126 | root | localhost | NULL | Query | 0 | init | show processlist |
+——-+————-+———–+——+———+———+———————————————–+——————+
4 rows in set (0.00 sec)

kill后确实可以stop slave同步了,可是一启动,又出现了,而且同样实际没有同步,会卡死所有的别的操作,最后没办法,只能老办法关闭多线程复制,改成单线程复制试试看。

mysql> set global slave_parallel_workers = 0;

重新kill,再开启同步,结果发现复活了。不知道为什么开始多并发同步就会卡死在一些错误的地方。同样暂时没有详细去寻找答案。

发表在 服务器 | 标签为 | 留下评论

php使用md5字符编码gbk和utf8有所不同

在一套新老系统上使用md5加密的方式读取相同用户表密码的时候,出现了md5加密后不一致的结果,最后查询是因为字符编码不一致导致的,php使用md5字符编码的时候要注意中文字符在gbk和utf8两种编码进行转换后是有所不同,而纯英文字符和数字却不会有这个问题,小心让你翻车。^_^

 

发表在 服务器 | 标签为 | 留下评论

composer.json和composer.lock有什么区别?

composer install - 如有 composer.lock 文件,直接安装,否则从 composer.json 安装最新扩展包和依赖;
composer update - 从 composer.json 安装最新扩展包和依赖;
composer update vendor/package - 从 composer.json 或者对应包的配置,并更新到最新;
composer require new/package - 添加安装 new/package, 可以指定版本,如: composer require kartik-v/yii2-date-range "*"

当一个项目需要多人合作的时候,为了让所有人都使用与您正在使用的完全相同的依赖项。因此,为了保证这一点,Composer官方是建议把composer.lock提交到git上。

每当去更新依赖的时候,都是基于composer.lock,去更新的。composer.lock文件阻止您自动获取最新版本的依赖项。

所有依赖要更新到最新版本,请使用composer update命令,但一般不要使用update命令,因为update命令将更新所有的依赖到最新,这个动作是危险的,这样很有可能导致,最新类库和你以前写的东西冲突,你以前的代码不可用。

发表在 服务器 | 标签为 | 留下评论