duckstery / process-analyzer
Dependency free process analyzer for PHP
Requires
- php: >=8.0
Requires (Dev)
- friendsofphp/php-cs-fixer: ^3.35
- phpunit/phpunit: 10
- vimeo/psalm: ^6.0@dev
This package is auto-updated.
Last update: 2024-04-28 09:27:43 UTC
README
Table of Contents
Feature
- Provide
Analyzer
that can measure the amount of time and memory of blocks of code - Report measured amount through file or console
- Support multiple Profile for multiple metrics
For example, you can do this:
// Start of code $uid = Analyzer::start("Do a simple math"); // Calculating $output = 1 + 1; // Print output to screen echo $output; // End of code Analyzer::stop($uid); // Flush to get report Analyzer::flush();
After that, you will get a report like this
Default -------------------- ╭───────────────┬──────────────────┬─────────────┬────────┬────────────┬────────────┬───────────╮ │ Uid │ Name │ Time │ Memory │ Start peak │ Stop peak │ Diff peak │ ├───────────────┼──────────────────┼─────────────┼────────┼────────────┼────────────┼───────────┤ │ 654af62889e08 │ Do a simple math │ 2006.472 ms │ 2.5 KB │ 16502872 B │ 16502872 B │ 0 B │ ╰───────────────┴──────────────────┴─────────────┴────────┴────────────┴────────────┴───────────╯ ----------------------------
The report can be printed to file or console. Moreover, you can decide to grab the report result and print it to wherever you want
Installation
For PHP 8+
Run script to install
composer require --dev duckstery/process-analyzer
For Laravel
Warning
This integration only work properly while handling request individually (1 request at a time) because it'll flush everything out at the end of the request.
Run script to install
composer require --dev duckstery/laravel-process-analyzer
Package's ServiceProvider will be auto required by Laravel. If you don't use auto-discovery, you need to manually add ServiceProvider
Duckstery\Laravel\Analyzer\ProcessAnalyzerServiceProvider::class
to the providers array in config/app.php
/* |-------------------------------------------------------------------------- | Autoloaded Service Providers |-------------------------------------------------------------------------- | | The service providers listed here will be automatically loaded on the | request to your application. Feel free to add your own services to | this array to grant expanded functionality to your applications. | */ 'providers' => ServiceProvider::defaultProviders()->merge([ /* * Package Service Providers... */ Duckstery\Laravel\Analyzer\ProcessAnalyzerServiceProvider::class, // ** // /* * Application Service Providers... */ App\Providers\AppServiceProvider::class, App\Providers\AuthServiceProvider::class, // App\Providers\BroadcastServiceProvider::class, App\Providers\EventServiceProvider::class, App\Providers\RouteServiceProvider::class, ])->toArray(),
Then, you can publish config file for better customization
php artisan vendor:publish --provider="Duckstery\Laravel\Analyzer\ProcessAnalyzerServiceProvider"
Configuration
How to config
Before use, you should config package to match your needs.
To config, create a class and extend AnalyzerConfig
like this
<?php use Duckstery\Analyzer\AnalyzerConfig; class MyAnalyzerConfig extends AnalyzerConfig { // Todo }
Then, you can override AnalyzerConfig
's properties or functions and change their value
<?php use Duckstery\Analyzer\AnalyzerConfig; class MyAnalyzerConfig extends AnalyzerConfig { // Property override protected bool $prettyPrint; // Function override public function prettyPrint(): bool { return true; } }
Beware that config with function override will ignore the property's value. If you config logic is big, you should use function override instead.
After creating config class, init Analyzer
with your class's instance
<?php use Duckstery\Analyzer\Analyzer; Analyzer::tryToInit(new MyAnalyzerConfig());
Options
enable
: This option will enable and disable Analyzer
. With this option, you can include this package in
production. If you need it to measure your process in production, you can switch it on. But remember to switch it off
after
- Type:
bool
- Default: true
defaultProfile
: Define default Profile name
- Type:
string
- Default: "Default"
defaultRecordGetter
: Define getter method name. This method will return Record's default name
- Type:
array
|string
|null
- Default:
profileExtras
: Define extra metrics for Profile. These metrics can be retrieved at the start or end of execution.
After that, Analyzer
can calculate difference or format them
- Type:
array
- Default:
[ "Default" => [ "peak" => [ // Metrics method name's or callback array "handler" => "memory_get_peak_usage", // Formatter "formatter" => [Utils::class, "appendB"], // Get at the start "start" => true, // Get at the end "stop" => true, // Calculate difference "diff" => true ] ] ]
profile
: Profile class. You can customize by create a new class that extend IAProfile
- Type:
string
- Default:
Duckstery\Analyzer\Structures\AnalysisProfile::class
record
: Record class
- Type:
string
- Default:
Duckstery\Analyzer\Structures\AnalysisRecord::class
printer
: Printer class
- Type:
string
- Default:
Duckstery\Analyzer\AnalysisPrinter::class
prettyPrint
: Print report in table
- Type:
bool
- Default: true
- Example:
$prettyPrint = true; // Default -------------------- // ╭───────────────┬──────────────────┬─────────────┬────────┬────────────┬────────────┬───────────╮ // │ Uid │ Name │ Time │ Memory │ Start peak │ Stop peak │ Diff peak │ // ├───────────────┼──────────────────┼─────────────┼────────┼────────────┼────────────┼───────────┤ // │ 654af62889e08 │ Do a simple math │ 2006.472 ms │ 2.5 KB │ 16502872 B │ 16502872 B │ 0 B │ // ╰───────────────┴──────────────────┴─────────────┴────────┴────────────┴────────────┴───────────╯ // ---------------------------- $prettyPrint = false; // Default -------------------- // [654af6159585c] Do a simple math: // Time ⇒ [2000.605 ms]; // Memory ⇒ [2.5 KB]; // Start peak ⇒ [16501872 B]; // Stop peak ⇒ [16501872 B]; // Diff peak ⇒ [0 B]; // ----------------------------
oneLine
: Print each Record in report in a line. Ignored if prettyPrint
: true
- Type:
bool
- Default:
- Example:
$oneLine = true; // Default -------------------- // [654b2b522090f] Do a simple math: Time ⇒ [2009.288 ms]; Memory ⇒ [2.5 KB]; Start peak ⇒ [16501904 B]; Stop peak ⇒ [16501904 B]; Diff peak ⇒ [0 B]; // ---------------------------- $oneLine = false; // Default -------------------- // [654af6159585c] Do a simple math: // Time ⇒ [2000.605 ms]; // Memory ⇒ [2.5 KB]; // Start peak ⇒ [16501872 B]; // Stop peak ⇒ [16501872 B]; // Diff peak ⇒ [0 B]; // ----------------------------
showUID
: Show Record's UID in report
- Type:
bool
- Default: true
useFile
: Define path to directory that holds report file. Report file will be created each day. If useFile
is false, report won't be printed to any file
- Type:
string
- Default: "logs"
useConsole
: Print result to console. If useConsole
is false, report won't be printed to console
- Type:
bool
- Default: true
profilePrefix
: Define Profile's name prefix
- Type:
string
- Default: ""
profileSuffix
: Define Profile's name suffix
- Type:
string
- Default: ""
recordPrefix
: Define Record's name prefix
- Type:
string
- Default: ""
recordSuffix
: Define Record's suffix
- Type:
string
- Default: ""
timeUnit
: Define unit of time
- Type:
string
- Default: "ms"
timeFormatter
: Define a callback to modify main time metrics. Ignored timeUnit
if this option is defined
- Type:
array
|string
|null
- Default: null
memUnit
: Define unit of memory
- Type:
string
- Default: "KB"
memFormatter
: Define a callback to modify main memory metrics. Ignored memUnit
if this option is defined
- Type:
array
|string
|null
- Default:
topLeftChar
: Define top left corner character
- Type:
string
- Default: "╭"
topRightChar
: Define top right corner character
- Type:
string
- Default: "╮"
bottomLeftChar
: Define bottom left corner character
- Type:
string
- Default: "╰"
bottomRightChar
: Define bottom right corner character
- Type:
string
- Default: "╯"
topForkChar
: Define top fork character
- Type:
string
- Default: "┬"
rightForkChar
: Define right fork character
- Type:
string
- Default: "┤"
bottomForkChar
: Define bottom fork character
- Type:
string
- Default: "┴"
leftForkChar
: Define left fork character
- Type:
string
- Default: "├"
crossChar
: Define cross character
- Type:
string
- Default: "┼"
Printer's hooks
There are some printer's hooks that allow you to interact with report data. To use these hooks, create a class and
extend AnalysisPrinter
Duckstery\Analyzer\AnalysisPrinter::class
Then, override methods like this
<?php use Duckstery\Analyzer\AnalysisPrinter; use Duckstery\Analyzer\Interfaces\IAProfile; class MyPrinter extends AnalysisPrinter { public function onPreprocessProfile(IAProfile $profile): void { // Todo } }
These are some hooks that you can use:
onPreprocessProfile
: Execute before process Profile
- Param:
IAProfile
: Profile can be modified at this hook
onPreprocessRecord
: Execute on each Record and before process Record
- Param:
IARecord
: Record can be modified at this hook
onEachPreprocessedRecord
: Execute on each Record and after process Record
- Param:
array
- Example: Without Profile's extras
$example = [ "uid" => "654af6159585c", "name" => "handle", "time" => "2000.605 ms", "memory" => "2.5 KB", ]
- Example: With Profile's extras (peak)
$example = [ "uid" => "654af6159585c", "name" => "handle", "time" => "2000.605 ms", "memory" => "2.5 KB", "start peak" => ..., // If start = true "stop peak" => ..., // If stop = true "diff peak" => ..., // If start = stop = diff = true ]
onEachRecordString
: Execute on each Record and after convert Record to string
- Param:
string
onPrintProfileString
: Execute after complete the report
- Param:
string
: This is the final report - Note: Modify this won't change your file or console result. If you want to send your report elsewhere, you should
disable
useFile
anduseConsole
and define your logic in this hook instead.
Usage
These are some examples to instruct you to use this package. You will be provided with a static class.
Duckstery\Analyzer\Analyzer::class
Analyzer
will only measure execution time and memory of your execution. It'll exclude self execution time and
memory out of final result.
The basic approach is placing your logic inside start
and stop
. When everything is done, call flush
so Analyzer can generate the report for you.
The Laravel integration has a specific middleware that will execute flush
at the end of request. So you don't need to flush
while using that integration. But in most case, you have to flush
at the end of your program (or at least at the end of the process that you desired to measure).
For any unmentioned situation, you can issue me for more detail.
Basic
Analyzer
only measure execution time and memory of your execution
<?php use Duckstery\Analyzer\Analyzer; public class SomeController { public function handle(): void { // Use Profile: SomeController and start recording Analyzer::profile("SomeController")->start("handle"); // Execute process A $this->processA(); // Execute process B $this->processB(); // Execute process C $this->processC(); // Stop the latest recording of Profile Analyzer::profile("SomeController")->stop(); // Flush Analyzer::flush("SomeController"); // Or Analyzer::flush(); to flush all Profile } public function processA(): void { // Use Profile: SomeController and start recording Analyzer::profile("SomeController")->start("processA"); // Use 5kb // Stop the latest recording of Profile Analyzer::profile("SomeController")->stop(); } public function processB(): void { // Use Profile: SomeController and start recording Analyzer::profile("SomeController")->start("processA"); // Use 0kb // Stop the latest recording of Profile Analyzer::profile("SomeController")->stop(); } public function processC(): void { // Use Profile: SomeController and start recording Analyzer::profile("SomeController")->start("processA"); // Executed for 5s // Stop the latest recording of Profile Analyzer::profile("SomeController")->stop(); } }
Report
SomeController -------------------- ╭───────────────┬──────────┬─────────────┬────────╮ │ Uid │ Name │ Time │ Memory │ ├───────────────┼──────────┼─────────────┼────────┤ │ 654af62889e08 │ handle │ 5036.472 ms │ 5 KB │ │ 654af62889e09 │ processA │ 6.472 ms │ 0 KB │ │ 654af62889e10 │ processB │ 6.472 ms │ 5 KB │ │ 654af62889e11 │ processC │ 5000.472 ms │ 0 KB │ ╰───────────────┴──────────┴─────────────┴────────╯ ------------------------------
Use default Profile name and extra metrics
Config
$profileExtras = [ "Default" => [ "peak" => [ // Metrics method name's or callback array "handler" => "memory_get_peak_usage", // Formatter "formatter" => [Utils::class, "appendB"], // Get at the start "start" => true, // Get at the end "stop" => true, // Calculate difference "diff" => true ] ] ]
Capture metrics by using default Profile
<?php use Duckstery\Analyzer\Analyzer; public class SomeController { public function handle(): void { $uid = Analyzer::start("SomeController::handle"); // Or Analyzer::start("SomeController::handle"); // Todo Analyzer::stop($uid); // Or Analyzer::stop(); // Flush Analyzer::flush(); } }
Report
Default -------------------- ╭───────────────┬────────────────────────┬─────────────┬────────┬────────────┬────────────┬───────────╮ │ Uid │ Name │ Time │ Memory │ Start peak │ Stop peak │ Diff peak │ ├───────────────┼────────────────────────┼─────────────┼────────┼────────────┼────────────┼───────────┤ │ 654af62889e08 │ SomeController::handle │ 2006.472 ms │ 0 KB │ 16502872 B │ 16502872 B │ 0 B │ ╰───────────────┴────────────────────────┴─────────────┴────────┴────────────┴────────────┴───────────╯ ----------------------------
Use default Record name
Capture metrics by using default Record name
<?php use Duckstery\Analyzer\Analyzer; public class SomeController { public function handle(): void { $uid = Analyzer::start(); // Or Analyzer::start(); // Todo Analyzer::stop($uid); // Or Analyzer::stop(); // Flush Analyzer::flush(); } }
Report
Default -------------------- ╭───────────────┬──────────────────┬─────────────┬────────┬────────────┬────────────┬───────────╮ │ Uid │ Name │ Time │ Memory │ Start peak │ Stop peak │ Diff peak │ ├───────────────┼──────────────────┼─────────────┼────────┼────────────┼────────────┼───────────┤ │ 654af62889e08 │ Function: handle │ 2006.472 ms │ 0 KB │ 16502872 B │ 16502872 B │ 0 B │ ╰───────────────┴──────────────────┴─────────────┴────────┴────────────┴────────────┴───────────╯ ----------------------------
Use multiple Profile
Capture metrics with multiple Profile
<?php use Duckstery\Analyzer\Analyzer; public class SomeController { public function handle(): void { $uid = Analyzer::startProfile("Profile 1"); // Or Analyzer::startProfile("Profile 1"); $this->todo(); Analyzer::stopProfile("Profile 1", $uid); // Or Analyzer::stopProfile("Profile 1"); // Flush Analyzer::flush(); } public function todo(): void { $uid = Analyzer::startProfile("Profile 2"); // Or Analyzer::startProfile("Profile 2"); $this->todo(); Analyzer::stopProfile("Profile 2", $uid); // Or Analyzer::stopProfile("Profile 2"); } }
Report
Profile 1 -------------------- ╭───────────────┬──────────────────┬─────────────┬────────╮ │ Uid │ Name │ Time │ Memory │ ├───────────────┼──────────────────┼─────────────┼────────┤ │ 654af62889e08 │ Function: handle │ 2006.472 ms │ 0 KB │ ╰───────────────┴──────────────────┴─────────────┴────────╯ ------------------------------ Profile 2 -------------------- ╭───────────────┬────────────────┬─────────────┬────────╮ │ Uid │ Name │ Time │ Memory │ ├───────────────┼────────────────┼─────────────┼────────┤ │ 654af62889e09 │ Function: todo │ 2006.472 ms │ 0 KB │ ╰───────────────┴────────────────┴─────────────┴────────╯ ------------------------------
Testing
For testing
composer run-script test
For coverage
composer run-script test-coverage
Issue
If you discover any security-related issues, bugs or ideas, please feel free to create an issue.
License
The MIT License (MIT). Please see License File for more information.