如何在PHP中保存日志

6
我该如何在PHP中保存日志?是否有任何“神奇”的函数或库可以做到这一点?还是每次都需要使用fopen打开文件并将其转储到其中?我想将我的日志保存在文本文件中。谢谢!

我只想要print_r的数据。有什么方法吗? - HenryW
5个回答

11

如果您不想使用自己的实现或只是进行fopen操作,可以使用内置函数error_log('要记录的字符串');。这将把所需的字符串写入您的服务器软件的错误日志中。


3
PHP允许您使用error_log php.ini设置配置默认记录器的设置:http://php.net/error-log - Powerlord

10

我写了一个简单的类来实现这个功能,也许你会觉得它有用。

class Log
  {
  public function __construct($log_name,$page_name)
    {
    if(!file_exists('/your/directory/'.$log_name)){ $log_name='a_default_log.log'; }
    $this->log_name=$log_name;

    $this->app_id=uniqid();//give each process a unique ID for differentiation
    $this->page_name=$page_name;

    $this->log_file='/your/directory/'.$this->log_name;
    $this->log=fopen($this->log_file,'a');
    }
  public function log_msg($msg)
    {//the action
    $log_line=join(' : ', array( date(DATE_RFC822), $this->page_name, $this->app_id, $msg ) );
    fwrite($this->log, $log_line."\n");
    }
  function __destruct()
    {//makes sure to close the file and write lines when the process ends.
    $this->log_msg("Closing log");
    fclose($this->log);
    }
  }

 $log=new Log('file_name','my_php_page');
 $log->log_msg('fizzy soda : 45 bubbles remaining per cubic centimeter');

1
我使用进程ID来保持日志中每行的来源清晰。它将在日志中每个页面上是唯一的,因此假设您有5个人同时访问您的页面 - 日志中将只有一堆行。该ID可以帮助您知道哪些行来自同一进程。如果您不需要它,您总是可以将其删除。 - JAL
这似乎是PHP的error_log($msg, 3, $log_name);非常复杂的版本。 - Powerlord
@R. Bemrose 这并不复杂。到目前为止,我唯一不理解的是传递给哈希函数的第二个参数(microtime(1).(time()/147))). - TCM
@R. Bemrose 不,这不是真的。它提供了额外的便利,比如检查日志文件是否存在并提供默认值(实际上应该检查是否可写),自动添加进程 ID、页面名称、日期和换行符到您的消息后面。我本可以使用 error_log 而不是 fopen/fwrite/fclose,但如果你不是记录错误的话,它似乎相当不语义化。我可能会像这样包装 error_log 来提供前述功能。 - JAL
@Nitesh Panchal 说实话,real_unique_id函数有点多余...那部分可以用简单的uniqid()替换。实际上,这来自于我拥有的其他代码。我已经编辑过它,使它更简单了。 - JAL
显示剩余4条评论

6
如果您不想使用其他回答中提到的PHP错误处理函数(http://www.php.net/manual/en/ref.errorfunc.php),那么这里有一个非常简单的Logger类,我以前用过。请注意,我没有在高风险应用程序或重负载站点上使用它(尽管应该没问题)。
<?
class Logger
{
  private static function addEntry($str)
  {
    $handle = fopen('./services.log', 'a');
    fwrite($handle, sprintf("%s %s\n", date('c'), $str));
    fclose($handle);
  }

  public static function warn($str)
  {
    self::addEntry("WARNING $str");
  }

  public static function info($str)
  {
    self::addEntry("INFO $str");
  }

  public static function debug($str)
  {
    self::addEntry("DEBUG $str");
  }
}
?>

然后你可以像这样使用它:
<?php
require('Logger.php');
Logger::debug('test');
Logger::warn('bwah');
Logger::info('omg');
?>

非常简单,可以添加更多的功能(如Logger :: error()),存储文件处理程序,这样每次想要记录日志时就不必重新打开它(即,在私有静态类范围变量中存储$handle变量,并在运行addEntry()时检查是否设置并运行fopen(),如果没有设置则运行),或者更改记录日志的格式。
干杯。

很高兴你喜欢它,感谢你的支持。是的,这是使用静态的经典案例。干杯。 - Sam Bisbee

4

最后一个回答是在2010年,需要更新一下。我与大家分享一下我在程序中使用的php类。

我采用了与上面@Sam-Bisbee答案类似的方法,因此可以轻松添加新功能和函数,但我还使它更加灵活,适用于不同的程序/脚本。

我将我的类放在Logg.php文件中,您也可以这样做。

我的类

<?php
class Logg
{
    public static $fileName = 'new_log';
    public static $filePath = '/';
    public static $fileType = '.log';
    
    public static function addInfo($logMsg, $fName = null, $fPath = null)
    {
        $fName = $fName ?: self::$fileName;
        $fPath = $fPath ?: self::$filePath;
        self::addData($logMsg, $fName, $fPath, ' |  INFO   |');
    }
    
    public static function addWarn($logMsg, $fName = null, $fPath = null)
    {
        $fName = $fName ?: self::$fileName;
        $fPath = $fPath ?: self::$filePath;
        self::addData($logMsg, $fName, $fPath, ' | WARNING |');
    }
    
    public static function addErr($logMsg, $fName = null, $fPath = null)
    {
        $fName = $fName ?: self::$fileName;
        $fPath = $fPath ?: self::$filePath;
        self::addData($logMsg, $fName, $fPath, ' |  ERROR  |');
    }
    
    public static function addConf($logMsg, $fName = null, $fPath = null)
    {
        $fName = $fName ?: self::$fileName;
        $fPath = $fPath ?: self::$filePath;
        self::addData($logMsg, $fName, $fPath, ' | CONFIG  |');
    }
    
    private static function addData($logMsg, $fName, $fPath, $type)
    {
        $handle = fopen($fPath . $fName . self::$fileType, 'a');
        
        if (gettype($logMsg) == 'array') {
            if (count($logMsg)%2 == 0) {
                $logText = '';
                for ($i = 0; $i < count($logMsg); $i+=2) {
                    $logText .= str_pad($logMsg[$i], $logMsg[$i+1], " ", STR_PAD_BOTH).'|';
                }
                fwrite($handle, sprintf("| %s%s", date("Y-m-d H:i:s"), $type.$logText.PHP_EOL));
            } else {
                throw new Exception('Wrong arguments for Array-type log.');
            }
        } else {
            fwrite($handle, sprintf("| %s%s", date("Y-m-d H:i:s"), $type.' '.$logMsg.PHP_EOL));
        }
        
        fclose($handle);
    }
    
    public static function addLine($lines = 1, $fName = null, $fPath = null)
    {
        $fName = $fName ?: self::$fileName;
        $fPath = $fPath ?: self::$filePath;
        $handle = fopen($fPath . $fName . self::$fileType, 'a');
        fwrite($handle, str_repeat(PHP_EOL, $lines));
        fclose($handle);
    }
}
?>

使用示例

包含类 - 指定日志文件的名称和路径:

<?php 
require '/Logg.php';

//specifying logs file name and path - best do this right after include
Logg::$fileName = 'ExampleName_'.date("Y_m_d");             //if not specified, default value = 'new_log'
Logg::$filePath = 'C:/Apache24/htdocs/public_html/logs/';   //if not specified, default value = '/'

你可以稍后在程序中再次更改这些值,如果需要的话。 简单用法:
//Logging simple messages
Logg::addInfo("FooBar");            //sample information log message
Logg::addWarn("Sample warning");    //sample warning log message
Logg::addErr ("Sample error");      //sample error log message
Logg::addConf("Sample config log"); //sample config change log message

//Adding empty lines
Logg::addLine();                            //adds empty line
Logg::addInfo("Log after empty line");      //some log
Logg::addLine(3);                           //adds 3 lines instead of one
Logg::addInfo("Log after 3 empty lines");   //some log


您也可以使用一个 Array(建议)代替 String 作为您的“日志信息”,就像下面的示例一样。这将使您的日志消息格式化为指定的长度,以使它们更易读:
Logg::addLine();
Logg::addInfo(['Goo', 5]);          //logs 'Goo' as char of length 5
Logg::addInfo(['', 10, 'Goo', 5]);  //logs empty space of length 10 and a 'Goo' of length 5 after that
Logg::addInfo(['Foo',       10 , 'Bar', 5, 'Sample message with some length formatted to 75 chars',              75]); //more examples
Logg::addInfo(['FooBar',    10 , 'Baz', 5, 'Another sample message with different length formatted to 75 chars', 75]);
Logg::addLine();
Logg::addInfo(['Different message of length 65 - trying to format it to length 50', 50]);   //in this case the message will keep it initial length so we wont be loosing any of the information we are logging

执行上述代码后,会在指定的文件路径中创建一个名为"ExampleName_2021_08_31.log"的文件,并且该文件包含以下内容:
| 2021-08-31 11:28:52 |  INFO   | FooBar
| 2021-08-31 11:28:52 | WARNING | Sample warning
| 2021-08-31 11:28:52 |  ERROR  | Sample error
| 2021-08-31 11:28:52 | CONFIG  | Sample config log

| 2021-08-31 11:28:52 |  INFO   | Log after empty line



| 2021-08-31 11:28:52 |  INFO   | Log after 3 empty lines

| 2021-08-31 11:28:52 |  INFO   | Goo |
| 2021-08-31 11:28:52 |  INFO   |          | Goo |
| 2021-08-31 11:28:52 |  INFO   |   Foo    | Bar |           Sample message with some length formatted to 75 chars           |
| 2021-08-31 11:28:52 |  INFO   |  FooBar  | Baz |    Another sample message with different length formatted to 75 chars     |

| 2021-08-31 11:28:52 |  INFO   |Different message of length 65 - trying to format it to length 50|


更改文件类型

可以通过更改我Logg类的$fileType属性来更改为不同的文件类型。默认文件类型为".log"

例如:

//changing file type, can be put after class include
Logg::$fileType = ".txt";

//logging some example messages
Logg::addInfo("Log message now in .txt file");
Logg::addWarn("Everything will be saved to this file from now on!");

现在已经创建了名为"ExampleName_2021_08_31.txt"的文件,并包含以下内容:
| 2021-08-31 09:35:20 |  INFO   | Log message now in .txt file
| 2021-08-31 09:35:20 | WARNING | Everything will be saved to this file from now on!

额外的函数参数

每个公共的“添加”函数都有两个额外的参数 - 文件名文件路径。指定这些参数将暂时覆盖先前指定的Logg::$fileNameLogg::$filePath类变量的值。 如果您想要将一些信息记录到具有不同名称或不同路径的文件中,但不想为程序中使用的每个日志更改文件路径或文件名,则这可能非常有用。

用法:

//Additional parameters
Logg::addErr("New error log");                                                          //no additional parameters - uses filename and path from "Logg::$fileName" and "Logg::$filePath"
Logg::addErr("New error log in different file", "<file name>");                         //one additional parameter - uses provided filename instead of "Logg::$fileName"
Logg::addErr("New error log in different file and path", '<file name>', '<file path>'); //two additional parameters - uses provided filename and path instead of "Logg::$fileName" and "Logg::$filePath"

例子:

//this:
{
    //logging information to different file in different path
    Logg::addInfo(['Some info', 15], 'AnotherFileName', '/some/different/path/');
}
//is equal to this:
{
    //changing filename and path
    Logg::$fileName = 'AnotherFileName';
    Logg::$filePath = '/some/different/path/';

    //logging information
    Logg::addInfo(['Some info', 15]);

    //changing filename and path back to its original values
    Logg::$fileName = 'ExampleName_'.date("Y_m_d");
    Logg::$filePath = 'C:/Apache24/htdocs/public_html/logs/';
}

我正在等待您的评论和建议。祝您有愉快的一天。

4

一切都取决于您要记录什么。默认情况下,您已经拥有一个错误日志,它基本上是一个纯文本文件。如果您想记录代码中的事件以进行调试或跟踪脚本中的活动,则需要为此编写自己的日志处理程序,但这非常简单。正如另一个帖子所说,您可以使用error_log()函数将内容推送到错误日志中,但这会导致一些非常难以管理的日志文件。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接