Giter Club home page Giter Club logo

analog's Introduction

Analog - Minimal PHP logging library

GitHub Workflow Status GitHub Packagist Version Packagist PHP Version Support Packagist Downloads

A minimal PHP logging package based on the idea of using closures for configurability and extensibility. It functions as a static class, but you can completely control the writing of log messages through a closure function (aka anonymous functions), or use the Analog\Logger wrapper that implements the PSR-3 specification.

Installation

Install the latest version with:

$ composer require analog/analog

Usage

Basic Usage

<?php

use Analog\Analog;
use Analog\Handler\FirePHP;

Analog::handler (FirePHP::init ());

Analog::log ('Take me to your browser');

Usage with PSR-3

<?php

use Analog\Logger;
use Analog\Handler\Variable;

$logger = new Logger;

$log = '';

$logger->handler (Variable::init ($log));

$logger->alert ('Things are really happening right now!');

// With context
$logger->debug ('Testing {0}:{1}', [__FILE__, __LINE__]);

var_dump ($log);

Usage with a custom handler

<?php

use Analog\Analog;

// Default logging to /tmp/analog.txt
Analog::log ('Log this error');

// Log to a MongoDB log collection
Analog::handler (function ($info) {
	static $conn = null;
	if (! $conn) {
		$conn = new Mongo ('localhost:27017');
	}
	$conn->mydb->log->insert ($info);
});

// Log an alert
Analog::log ('The sky is falling!', Analog::ALERT);

// Log some debug info
Analog::log ('Debugging info', Analog::DEBUG);

Usage without composer

Analog uses a simple autoloader internally, so if you don't have access to composer you can clone this repository and include it like this:

<?php

require 'analog/lib/Analog.php';

Analog::handler (Analog\Handler\Stderr::init ());

Analog::log ('Output to php://stderr');

For more examples, see the examples folder.

Logging Options

By default, this class will write to a file named sys_get_temp_dir() . '/analog.txt' using the format "machine - date - level - message\n", making it usable with no customization necessary.

Analog also comes with dozens of pre-written handlers in the Analog/Handlers folder, with examples for each in the examples folder. These include:

  • Amon - Send logs to the Amon server monitoring tool
  • Apprise - Send notifications through the apprise command line tool
  • Buffer - Buffer messages to send all at once (works with File, Mail, Stderr, and Variable handlers)
  • ChromeLogger - Sends messages to Chrome Logger browser plugin
  • EchoConsole - Echo output directly to the console
  • File - Append messages to a file
  • FirePHP - Send messages to FirePHP browser plugin
  • GELF - Send message to the Graylog2 log management server
  • IFTTT - Trigger webhooks via the IFTTT service
  • Ignore - Do nothing
  • LevelBuffer - Buffer messages and send only if sufficient error level reached
  • LevelName - Convert log level numbers to names in log output
  • Mail - Send email notices
  • Mongo - Save to MongoDB collection
  • Multi - Send different log levels to different handlers
  • PDO - Send messages to any PDO database connection (MySQL, SQLite, PostgreSQL, etc.)
  • Post - Send messages over HTTP POST to another machine
  • Redis - Save messages to Redis key using RPUSH
  • Slackbot - Post messages to Slack via Slackbot
  • Stderr - Send messages to STDERR
  • Syslog - Send messages to syslog
  • Threshold - Only writes log messages above a certain threshold
  • Variable - Buffer messages to a variable reference
  • WPMail - Send email notices using Wordpress wp_mail()

So while it's a micro class, it's highly extensible and very capable out of the box too.

Rationale

I wrote this because I wanted something very small and simple like KLogger, and preferably not torn out of a wider framework if possible. After searching, I wasn't happy with the single-purpose libraries I found. With KLogger for example, I didn't want an object instance but rather a static class, and I wanted more flexibility in the back-end.

I also found some that had the flexibility also had more complexity, for example Monolog is dozens of source files (not incl. tests). With closures, this seemed to be a good balance of small without sacrificing flexibility.

What about Analog, the logfile analyzer? Well, since it hasn't been updated since 2004, I think it's safe to call a single-file PHP logging class the same thing without it being considered stepping on toes :)

analog's People

Contributors

diogoko avatar jbroadway avatar lord2800 avatar lucasmichel avatar lux avatar mzaweb avatar nhymxu avatar polyfractal avatar sponno avatar spronkey avatar tessus avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

analog's Issues

Ignored context argument

<?php

require 'vendor/autoload.php';

use Analog\Logger;
use Analog\Handler;

$logger = new Logger();
$logger->handler(Handler\File::init('test.log'));

$logger->debug('Test', [__FILE__, __LINE__]); // context is ignored, only 'Test' is going to logs, i.e. localhost - 2023-06-01 23:40:14 - 7 - Test

Meanwhile it should work this way according to the function declaration:

	/**
	 * Detailed debug information.
	 */
	public function debug ($message, array $context = array ()) {

Could not lock file

Hello, I'm sometimes throwing the following error:
exception 'RuntimeException' with message 'Could not lock file' in /home/travis/build/galette/galette/galette/includes/Analog-1.0.0.git876d8a3bb/Analog/Handler/File.php:27

That file should not be locked, it is used by Analog only.

I do not know how to reproduce it, I had it a very few times using my application; but that regulary fail on my travis unit tests.

Of course, I could disable file logging during tests to avoid that; but I'm afraid my users may throw the same issue on some configurations.

Any idea how to fix that?

Mix Multi and Threshold handlers

Hello,

Using Mutli or Threshold handler separately works fine; but I can't achieve to get them working together. I'm using files to log, and I'd like to get two separate files:

  • one for notices, warnings, errors, etc
  • another for info and debug messages (because those ones may be very verbose).

Giving the the following example:

$errors = "Errors:\n";
$debug = "Debug:\n";

\Analog\Analog::handler(
    \Analog\Handler\Multi::init(
        array (
            \Analog\Analog::NOTICE  => \Analog\Handler\Threshold::init(
                \Analog\Handler\Variable::init($errors),
                \Analog\Analog::NOTICE
            ),
            Analog::DEBUG       => \Analog\Handler\Threshold::init(
                \Analog\Handler\Variable::init($debug),
                \Analog\Analog::DEBUG
            )
        )
    )
);

\Analog\Analog::log(
    'An error',
    \Analog\Analog::ERROR
);


\Analog\Analog::log(
    'A debug message',
    \Analog\Analog::DEBUG
);

var_dump($errors);
var_dump($debug);

The output is:

string 'Errors:
' (length=8)

string 'Debug:
127.0.0.1 - 2016-11-16 21:32:13 - 3 - An error
127.0.0.1 - 2016-11-16 21:32:13 - 7 - A debug message
' (length=108)

But what I was expecting was "An error" to be written in $errors, and "A debug message" to $debug.

Did I miss something?

Thank you very much!

Unit tests may fail

I execute unit tests on Analog during the construction of the package for Fedora; a build system will ran the tests, and the build will fail if unit tests are not ok.

Unit tests ran well almost all the time, I had once the following:
+ phpunit tests
PHPUnit 3.7.9 by Sebastian Bergmann.
Configuration read from /builddir/build/BUILD/analog-1.0.0-git876d8a3bb/phpunit.xml.dist
^[[41;37mF^[[0m^[[36;1mS^[[0m^[[36;1mS^[[0m
Time: 0 seconds, Memory: 3.00Mb
There was 1 failure:
1) AnalogTest::test_default
Failed asserting that two strings are equal.
--- Expected
+++ Actual
@@ @@
-'localhost - 2012-12-30 12:15:21 - 3 - Foo
+'localhost - 2012-12-30 12:15:20 - 3 - Foo
'

Not a big issue, but that would cause rejects from the build system, I had to disable tests (well, I actually make them quiet on error).

Maybe a solution would be to use something like:
$this->assertStringMatchesFormat ("localhost - %d-%d-%d %d:%d:%d - 3 - Foo\n", file_get_contents (Analog::handler ());

You can find a git formatted patch with this change at:
http://odysseus.x-tnd.be/fedora/php-Analog/0001-Tests-may-fail-on-seconds.patch

Analog use gmdate

Gmdate() is used to print current date.
Why don't you use date() ?
Can you add a way to configure this ?

Direct output

Is there a way to output logs directly (to the console, etc.)?

Null is reserved in PHP 7

  PHP Fatal error:  Cannot use 'Null' as class name as it is reserved in xxx/Analog/Handler/Null.php on line 14

Class Mail. Request best config

Hi.

On this days it's best way to send email trought authenticate SMTP )spam system, security)
It's possible add funcionality on nexts versions?

Apreciate you time.

Send log to multiple separate handlers (mongo & slack)?

It appears I can send all ERRORS to one handler (Slack) and all DEBUG to Mongodb using the multi handler, but not to both simultaneously.

Ultimately, I'm looking for the ability to choose which handler is used during the log call.

In other logging libraries you can simply instantiate two versions of class, one for each destination like:

$mongo_logger = new Logger('mongo', 'db', 'info');
$slack_logger = new Logger('slack', 'api', 'info');

$mongo_logger->log('error', 'this is an error');
$slack_logger->log('error', 'this is an error');

When I try to do the same in Analog, all the log messages use the last handler set:

require 'vendor/autoload.php';
$_logger_mongo = new \Analog\Logger();
$_logger_mongo->handler(Analog\Handler\Mongo::init(
  MONGODB_HOST,
  MONGODB_LOG_DB_NAME,
  MONGODB_LOG_COLLECTION_NAME
));
$_logger_slack = new \Analog\Logger();
$_logger_slack->handler(Analog\Handler\Slackbot::init('team', 'token', 'channel'));

$_logger_slack->debug(['message' => 'Debug info for slack', 'data' => [1, 4, 6]]);
$_logger_mongo->debug(['message' => 'Debug info for mongo', 'data' => [1, 4, 6]]);

Both messages are sent to slack.

What am I doing wrong?

I'm using "dev-master" inside my composer.json

Thanks!

Uncaught Error: Undefined class constant 'ATTR_ERRMODE' in analog/analog/lib/Analog/Handler/PDO.php:52

Uncaught Error: Undefined class constant 'ATTR_ERRMODE' in ..../vendor/analog/analog/lib/Analog/Handler/PDO.php:52

Because your class named PDO(as global) but your had no CONST NAMES!!!!! set \PDO const names instead PDO you forgot a slash sign, like:

class PDO {
	public static function init ($pdo, $table) {
		if (is_array ($pdo)) {
			$pdo = new \PDO ($pdo[0], $pdo[1], $pdo[2], [\PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION]);
		}

cause if i send $pdo as PDO object, it all works well, untill i send as array, and all executed as (without slash)

class PDO {
	public static function init ($pdo, $table) {
		if (is_array ($pdo)) {
			$pdo = new \PDO ($pdo[0], $pdo[1], $pdo[2], [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]);
		}

Maximum execution time exceeded (not really a bug)

That is not really an issue, I made a mistake in my code, causing Analog to excess maximum execution time; using multi handler.

My stuff is wrong, not Analog one; but it was quite difficult to figure out what was happening :-)

I get something like:

Analog::log(
    'My message',
    OLD_CONSTANT
);

The "OLD_CONSTANT" value is the issue, since it no longer exists, PHP use constant name as a text value. Once in Analog/Handler/Multi.php file, we go through the while instruction that never ends because of the string instead of integer.

Maybe it would be useful to check if $info['level'] is an integer before entering the while, and either throw an exception or take a default arbitrary value.

Please tag releases (e.g. major.minor.patch)

Hi there =)

Would love it if you can tag releases, including patch updates, so that they are usable through Composer. As it stands, Analog's composer.json points to dev-master, which means it is automatically "dev" stability.

If a second library requires Analog, the end-user has to either specify 'dev' stability for all packages (not good), or specifically include Analog. Stability is not inherited (e.g. Library B which requires Analog cannot specify the stability), and so things break because everything is just thrown into dev-master.

Ultimately, this leads to composer files like this (where other/library requires Analog):

{
    "require": {
        "other/library" : "0.1.*",
        "analog/analog": "dev-master"
    }
}

When really it should just be:

{
    "require": {
        "other/library" : "0.1.*"
    }
}

Support multiple logger instances out of the box

If we want multiple logger instances for multiple parts of the system then we need to extend this class and copy-paste getStruct as it's based on self:: not static:: calls and getStruct is private not protected.
I.e. this way it will work with file loggers:

$subSystemOneLogger = createLogger('sys-one');
$subSystemTwoLogger = createLogger('sys-two');

$subSystemOneLogger->info('Something about subsystem one that will be logged to sys-one.debug.log and sys-one.info.log ');
...
$subSystemTwoLogger->info('Something about subsystem two that will be logged to sys-two.debug.log and sys-two.info.log ');


function createLogger($prefix = ''): AppLogger
{
    $logger = new AppLogger();
    $prefix = empty($prefix) ? '' : "$prefix.";
    $monthDir = LOG_DIR . date('Y/m');
    if (!is_dir($monthDir)) {
        mkdir($monthDir, 0775, true);
    }
    $monthDir = "$monthDir/";
    $logger->setHandler(Handler\Multi::init([
        Analog::INFO => [
            Handler\File::init(LOG_DIR . "{$prefix}debug.log"),
            Handler\File::init(LOG_DIR . "{$prefix}info.log"),
            Handler\File::init($monthDir . "{$date}_{$prefix}debug.log"),
            Handler\File::init($monthDir . "{$date}_{$prefix}info.log")
        ],
        Analog::DEBUG => [
            Handler\File::init(LOG_DIR . "{$prefix}debug.log"),
            Handler\File::init($monthDir . "{$date}_{$prefix}debug.log")
        ]
    ]));
    return $logger;
}

class AppLogger implements LoggerInterface
{
    private ?Closure $handler = null;

    public function debug($message, array $context = []): void
    {
        $this->write($this->getStruct($message, \Analog\Analog::DEBUG));
    }

    public function info($message, array $context = []): void
    {
        $this->write($this->getStruct($message, \Analog\Analog::INFO));
    }
// ... similar methods
    public function setHandler(Closure $handler): void
    {
        $this->handler = $handler;
    }

    private function write($struct): void
    {
        if (!$this->handler instanceof Closure) {
            $this->handler = \Analog\Handler\File::init(LOG_DIR . 'default.log');
        }
        call_user_func($this->handler, $struct);
    }

    private function getStruct($message, $level): array
    {
        $dt = new DateTime ('now', new DateTimeZone (\Analog\Analog::$timezone));
        return array(
            'machine' => '',
            'date' => $dt->format(\Analog\Analog::$date_format),
            'level' => $level,
            'message' => $message
        );
    }
}

My proposition is to make it less complex or add example similar to this one to achieve the same result.

PHP Parse error on Echo handler

PHP version: 7.1.33

Example

<?php

require 'vendor/autoload.php';

use \Analog\Analog;

Analog::handler (Analog\Handler\Echo::init ());
Analog::log ('Log me');

Error

PHP Parse error:  syntax error, unexpected 'Echo' (T_ECHO), expecting identifier (T_STRING) or '{' in /path/to/your/script.php on line 7

Parse error: syntax error, unexpected 'Echo' (T_ECHO), expecting identifier (T_STRING) or '{' in /path/to/your/script.php on line 7

MariaDB/MySQL support

It would be useful if you supported logging to SQL out of the box.

While Mongo is cool, MySQL is still the 'M' in LAMP for a reason.. lots more php based software systems use this as the databae

-FT

Composer can't find analog

Hi there,

I seem to be unable to install analog on my server. I have several other libraries installed via composer (Stripe, PayPal, Google API, Medoo, etc. but can't seem to find yours.
I've included the console log:

-bash-4.2$ composer require jbroadway/analog
  [InvalidArgumentException]
  Could not find a matching version of package jbroadway/analog. Check the package spelling, your version constraint and that the package is available in a stability which matches your minimum-stability (stable).
require [--dev] [--prefer-source] [--prefer-dist] [--no-progress] [--no-suggest] [--no-update] [--no-scripts] [--update-no-dev] [--update-with-dependencies] [--update-with-all-dependencies] [--ignore-platform-reqs] [--prefer-stable] [--prefer-lowest] [--sort-packages] [-o|--optimize-autoloader] [-a|--classmap-authoritative] [--apcu-autoloader] [--] [<packages>]...

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    πŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. πŸ“ŠπŸ“ˆπŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❀️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.