Compare commits

...

86 commits

Author SHA1 Message Date
netkas
dc9b831534 Fixed FileLogging issue by setting the write permission to 0666 for the log file if it doesn't exist. 2025-01-13 13:24:38 -05:00
netkas
49bb26a063 Bumped version 2025-01-10 04:24:50 -05:00
netkas
9958bdd205 Prevent file logging in web environments. 2025-01-10 04:24:45 -05:00
netkas
0507fbb9d1 Refactor file locking to return status and handle failure. 2025-01-09 15:42:44 -05:00
netkas
808baec53c Bumped version to 2.0.5 2025-01-09 15:39:52 -05:00
netkas
284cd4f52a Minor bug fix 2024-12-04 00:29:23 -05:00
netkas
359afdcbe0 Bug fix, bumped to version 2.0.4 2024-12-04 00:26:03 -05:00
netkas
e927c7a8ec Refactor getTraceString and add call type determination. 2024-11-05 15:48:19 -05:00
netkas
b6417cba97 Refactor exception handling to Runtime class 2024-11-05 15:48:10 -05:00
netkas
ae1c71fec0 Add BacktraceParser class to LogLib 2024-11-05 15:48:01 -05:00
netkas
0fdf36661e Bumped to version 2.0.3 2024-11-05 14:09:31 -05:00
netkas
163a163b96 Updated CHANGELOG.md 2024-10-30 00:15:01 -04:00
netkas
7c99edddbd Implement enhanced error and exception handling 2024-10-30 00:14:51 -04:00
netkas
9435841987 Refactor exception handling in FileLogging 2024-10-30 00:14:33 -04:00
netkas
9fdedc5dea Bumped version to 2.0.2 2024-10-29 22:16:24 -04:00
netkas
ccc3378774 This update introduces a critical bug fix where Console logging was enabled in web environments 2024-10-29 14:18:30 -04:00
netkas
73c333e6e6 Updated CHANGELOG.md 2024-10-29 00:48:00 -04:00
netkas
519777a922 Minor correction 2024-10-29 00:44:38 -04:00
netkas
4cc5a8450d Removed black/grey colors for black background terminals 2024-10-29 00:42:33 -04:00
netkas
83066f5315 Updated .gitignore 2024-10-29 00:40:18 -04:00
netkas
1b8813314e Added PhpUnit test files 2024-10-29 00:40:04 -04:00
netkas
92107d4018 Updated LogTest 2024-10-29 00:38:37 -04:00
netkas
1e7a74d6ef Minor improvement 2024-10-29 00:38:31 -04:00
netkas
c2ce54139c Removed console formatting for traces when handling file logging events 2024-10-29 00:37:25 -04:00
netkas
5c5d06446a Initialize static properties in ConsoleLogging 2024-10-29 00:33:34 -04:00
netkas
72ddaffc4f Refactor file handling to improve robustness 2024-10-29 00:33:22 -04:00
netkas
3e88913042 Fix log file naming format 2024-10-28 20:01:20 -04:00
netkas
dd78dccab2 Refactor directory creation error handling 2024-10-28 19:59:34 -04:00
netkas
6f61db996c Add error and shutdown handlers 2024-10-28 19:56:13 -04:00
netkas
735dc7b33e Removed unused LoggingException 2024-10-28 19:47:20 -04:00
netkas
7e779ab41b Refactor logging utility functions 2024-10-28 19:43:10 -04:00
netkas
709729df14 Added documentation to FileLogging 2024-10-28 19:38:20 -04:00
netkas
8f9333a273 Removed unused LogHandlerType 2024-10-28 19:37:51 -04:00
netkas
15f55b870d Updated CHANGELOG.md 2024-10-28 19:36:45 -04:00
netkas
3bf6b70526 Added Logger object for constructing a logging instance for an application 2024-10-28 19:36:26 -04:00
netkas
e35eb46873 Updated CHANGELOG.md 2024-10-28 15:33:17 -04:00
netkas
81ee234540 Refactor logging methods and utility functions 2024-10-28 15:29:57 -04:00
netkas
dec96d85e1 Add LogHandlerInterface and related types 2024-10-28 15:29:46 -04:00
netkas
9d0dbad846 Add FileLogging and FileLock classes 2024-10-28 15:29:14 -04:00
netkas
f3d0412525 Add console logging handler 2024-10-28 15:28:51 -04:00
netkas
e5b0bb6012 Remove unused BRIGHT_COLORS constant from ConsoleColors 2024-10-28 15:28:34 -04:00
netkas
d6e94ce52e Removed unused Options & RuntimeOptions 2024-10-28 15:28:20 -04:00
netkas
a272396ef9 Add Application class for LogLib 2024-10-28 15:28:11 -04:00
netkas
b08b878e9a Removed Console.php 2024-10-28 15:27:51 -04:00
netkas
38fc3295c2 Bumped version to 2.0.0 due to the many major changes 2024-10-28 13:05:11 -04:00
netkas
7b0de0cb7f Updated CHANGELOG.md 2024-10-26 00:29:24 -04:00
netkas
bd9bf431e7 Refactor to use RuntimeOptions in Console 2024-10-26 00:29:15 -04:00
netkas
8f413a9aae Updated CHANGELOG.md 2024-10-26 00:18:42 -04:00
netkas
fa27ed6405 Rename classes to enums and update usage 2024-10-26 00:18:08 -04:00
netkas
2dd8f912b2 Updated php.xml 2024-10-25 20:10:44 -04:00
netkas
e5ce8cdc01 Set default log level to 'info' in Utilities. instead of using null due to deprecation error 2024-10-25 20:10:40 -04:00
netkas
cf4041f47b Bumped version to 1.1.2 2024-10-25 20:10:05 -04:00
8d5d443792 Updated build system 2024-10-13 16:40:55 -04:00
edcd01d4df Bumped version to 1.1.1 2024-10-13 16:40:12 -04:00
a74a9664da Updated project.json 2024-10-13 16:39:34 -04:00
91c79ab3de Merge branch 'master' into dev 2024-10-13 16:36:45 -04:00
0f50faee8f Updated Build System 2024-10-13 16:36:35 -04:00
e8fb624e19
Updated README.md 2023-10-12 23:45:34 -04:00
951bdb325e
"Refactor logging logic to improve log level and backtrace information
The code has been updated to enhance the way log levels and backtrace information is handled. Changes have been done to ensure the backtrace ignores any calls coming from the LogLib namespace, improving clarity and reducing noise in the log output. Additionally, changes were made to 'colorize' and 'log' methods to adjust the typing of the log level from string to integer, enhancing the consistency of log level usage throughout the code. Removed the Backtrace class that was not serving any purpose after these updates. Tests are also updated to ensure proper functionality."
2023-10-12 23:40:05 -04:00
7f2332e228
Fix 2023-10-12 17:16:02 -04:00
3e9606c184
Updated .gitlab-ci.yml 2023-10-12 00:35:39 -04:00
26ef269e91
Updated CHANGELOG.md 2023-10-11 18:53:26 -04:00
f4890f90b9
Updated project.json 2023-10-11 18:50:33 -04:00
42bd16d5ee
Updated README.md formatting 2023-10-11 18:49:54 -04:00
d5831486eb
Updated LICENSE formatting 2023-10-11 18:46:27 -04:00
adfef954d1
Remove NCC Build GitHub workflow 2023-10-11 18:44:40 -04:00
7b8b636d37
Update .gitlab-ci.yml to simplify build stage
Removed the 'ncc project install' script from the build stage in the '.gitlab-ci.yml'. Debugging showed it was not necessary for the building process when not building statically
2023-10-11 18:38:48 -04:00
1a4d2ff726
Streamline CI pipeline by explicit project installation
This commit removes an unused 'prepare' stage from '.gitlab-ci.yml' and includes 'ncc project install' in the build scripts. The intention is to improve the CI/CD flow by installing project dependencies directly in the build phases, preventing possible build failures due to missing dependencies.
2023-10-11 18:35:01 -04:00
0311fcf3f0
Remove unused run configuration and update CI pipeline
This commit removes 'Install.xml' run configuration, which is no longer used or necessary in the project. The '.gitlab-ci.yml' file has also been updated to include a new 'prepare' stage in the pipeline. This modification was done to streamline and improve the CI/CD process by explicitly installing necessary project dependencies before the build phase begins.
2023-10-11 17:49:15 -04:00
4fa87c349c
Remove unused ConfigException class, make code style adjustments
Deleted ConfigException class as it was not being used anywhere in the project.
Code style adjustments were made to conform with the PSR-12 coding standard. This includes property and variable naming conventions, use of strict equality operators, reformatting of code and removal of unnecessary comments. The .idea/php.xml file was also updated to a newer version of the library.
Log functions are updated to throw exceptions for non-existent message and invalid level. Also, LogLib is now registered/unregistered  as an exception handler.
Other adjustments were made to achieve consistency in the codebase including renaming properties for clarity, moving magic strings into constants, improving code readability and adding descriptive comments.
2023-10-10 23:29:26 -04:00
892c4c7ad7
Bumped version to 1.1.0 and updated project.json 2023-09-29 02:22:07 -04:00
56b06dee4f
Updated CHANGELOG.md 2023-07-06 18:43:18 -04:00
0d92d2a838
Fixed mistake in \LogLib\Classes > Console > outException() where the function attempts to print out a previous exception by calling getPrevious() as an array instead of a function call. 2023-07-06 18:42:25 -04:00
Zi Xing
881145c1cb
Update ncc.yml
Final changes to ncc.yml
2023-03-05 14:26:20 -05:00
Zi Xing
0eccff4144
Update ncc.yml
Removed extensions
2023-03-05 14:20:53 -05:00
Zi Xing
d73bb9a6c7
Update ncc.yml
Trying sudo
2023-03-05 14:19:09 -05:00
Zi Xing
f66425596a
Created ncc.yml
Test for CI integrations in GitHub
2023-03-05 14:17:20 -05:00
Netkas
f06d06f581 Updated CHANGELOG.md 2023-03-01 20:30:08 -05:00
Netkas
1b6da9c607 Updated Makefile 2023-03-01 20:29:21 -05:00
Netkas
a22a9a02a6 Minor correction 2023-03-01 20:29:15 -05:00
Netkas
31ac793273 Minor update 2023-03-01 20:22:35 -05:00
Netkas
30ad2ee1ab Updated Timestamp for analyzing program ticks 2023-03-01 20:22:04 -05:00
Netkas
eb4a1d4350 Added Run Configurations 2023-03-01 20:21:48 -05:00
Netkas
77f2a7ef3d Bumped version to 1.0.2 2023-03-01 20:12:16 -05:00
Netkas
f3ad99769f Bumped version to 1.0.1 2023-03-01 20:10:41 -05:00
Netkas
baf2ee794b Bumped version to 1.0.1 2023-03-01 20:10:32 -05:00
43 changed files with 2748 additions and 1012 deletions

552
.github/workflows/ncc_workflow.yml vendored Normal file
View file

@ -0,0 +1,552 @@
name: CI
on:
push:
branches:
- '**'
release:
types: [created]
workflow_dispatch:
jobs:
release:
runs-on: ubuntu-latest
container:
image: php:8.3
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Install dependencies
run: |
apt update -yqq
apt install git libpq-dev libzip-dev zip make wget gnupg -yqq
- name: Install phive
run: |
wget -O phive.phar https://phar.io/releases/phive.phar
wget -O phive.phar.asc https://phar.io/releases/phive.phar.asc
gpg --keyserver hkps://keys.openpgp.org --recv-keys 0x9D8A98B29B2D5D79
gpg --verify phive.phar.asc phive.phar
chmod +x phive.phar
mv phive.phar /usr/local/bin/phive
- name: Install phab
run: |
phive install phpab --global --trust-gpg-keys 0x2A8299CE842DD38C
- name: Install latest version of NCC
run: |
git clone https://git.n64.cc/nosial/ncc.git
cd ncc
make redist
NCC_DIR=$(find build/ -type d -name "ncc_*" | head -n 1)
if [ -z "$NCC_DIR" ]; then
echo "NCC build directory not found"
exit 1
fi
php "$NCC_DIR/INSTALL" --auto
cd .. && rm -rf ncc
- name: Build project
run: |
ncc build --config release --build-source --log-level debug
- name: Upload build artifact
uses: actions/upload-artifact@v4
with:
name: release
path: build/release/net.nosial.loglib.ncc
release-compressed:
runs-on: ubuntu-latest
container:
image: php:8.3
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Install dependencies
run: |
apt update -yqq
apt install git libpq-dev libzip-dev zip make wget gnupg -yqq
- name: Install phive
run: |
wget -O phive.phar https://phar.io/releases/phive.phar
wget -O phive.phar.asc https://phar.io/releases/phive.phar.asc
gpg --keyserver hkps://keys.openpgp.org --recv-keys 0x9D8A98B29B2D5D79
gpg --verify phive.phar.asc phive.phar
chmod +x phive.phar
mv phive.phar /usr/local/bin/phive
- name: Install phab
run: |
phive install phpab --global --trust-gpg-keys 0x2A8299CE842DD38C
- name: Install latest version of NCC
run: |
git clone https://git.n64.cc/nosial/ncc.git
cd ncc
make redist
NCC_DIR=$(find build/ -type d -name "ncc_*" | head -n 1)
if [ -z "$NCC_DIR" ]; then
echo "NCC build directory not found"
exit 1
fi
php "$NCC_DIR/INSTALL" --auto
cd .. && rm -rf ncc
- name: Build project
run: |
ncc build --config release-compressed --build-source --log-level debug
- name: Upload build artifact
uses: actions/upload-artifact@v4
with:
name: release-compressed
path: build/release/net.nosial.loglib.gz.ncc
debug-compressed:
runs-on: ubuntu-latest
container:
image: php:8.3
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Install dependencies
run: |
apt update -yqq
apt install git libpq-dev libzip-dev zip make wget gnupg -yqq
- name: Install phive
run: |
wget -O phive.phar https://phar.io/releases/phive.phar
wget -O phive.phar.asc https://phar.io/releases/phive.phar.asc
gpg --keyserver hkps://keys.openpgp.org --recv-keys 0x9D8A98B29B2D5D79
gpg --verify phive.phar.asc phive.phar
chmod +x phive.phar
mv phive.phar /usr/local/bin/phive
- name: Install phab
run: |
phive install phpab --global --trust-gpg-keys 0x2A8299CE842DD38C
- name: Install latest version of NCC
run: |
git clone https://git.n64.cc/nosial/ncc.git
cd ncc
make redist
NCC_DIR=$(find build/ -type d -name "ncc_*" | head -n 1)
if [ -z "$NCC_DIR" ]; then
echo "NCC build directory not found"
exit 1
fi
php "$NCC_DIR/INSTALL" --auto
cd .. && rm -rf ncc
- name: Build project
run: |
ncc build --config debug-compressed --build-source --log-level debug
- name: Upload build artifact
uses: actions/upload-artifact@v4
with:
name: debug-compressed
path: build/debug/net.nosial.loglib.gz.ncc
release-executable:
runs-on: ubuntu-latest
container:
image: php:8.3
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Install dependencies
run: |
apt update -yqq
apt install git libpq-dev libzip-dev zip make wget gnupg -yqq
- name: Install phive
run: |
wget -O phive.phar https://phar.io/releases/phive.phar
wget -O phive.phar.asc https://phar.io/releases/phive.phar.asc
gpg --keyserver hkps://keys.openpgp.org --recv-keys 0x9D8A98B29B2D5D79
gpg --verify phive.phar.asc phive.phar
chmod +x phive.phar
mv phive.phar /usr/local/bin/phive
- name: Install phab
run: |
phive install phpab --global --trust-gpg-keys 0x2A8299CE842DD38C
- name: Install latest version of NCC
run: |
git clone https://git.n64.cc/nosial/ncc.git
cd ncc
make redist
NCC_DIR=$(find build/ -type d -name "ncc_*" | head -n 1)
if [ -z "$NCC_DIR" ]; then
echo "NCC build directory not found"
exit 1
fi
php "$NCC_DIR/INSTALL" --auto
cd .. && rm -rf ncc
- name: Build project
run: |
ncc build --config release-executable --build-source --log-level debug
- name: Upload build artifact
uses: actions/upload-artifact@v4
with:
name: release-executable
path: build/release/release_executable_gz
release-compressed-executable:
runs-on: ubuntu-latest
container:
image: php:8.3
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Install dependencies
run: |
apt update -yqq
apt install git libpq-dev libzip-dev zip make wget gnupg -yqq
- name: Install phive
run: |
wget -O phive.phar https://phar.io/releases/phive.phar
wget -O phive.phar.asc https://phar.io/releases/phive.phar.asc
gpg --keyserver hkps://keys.openpgp.org --recv-keys 0x9D8A98B29B2D5D79
gpg --verify phive.phar.asc phive.phar
chmod +x phive.phar
mv phive.phar /usr/local/bin/phive
- name: Install phab
run: |
phive install phpab --global --trust-gpg-keys 0x2A8299CE842DD38C
- name: Install latest version of NCC
run: |
git clone https://git.n64.cc/nosial/ncc.git
cd ncc
make redist
NCC_DIR=$(find build/ -type d -name "ncc_*" | head -n 1)
if [ -z "$NCC_DIR" ]; then
echo "NCC build directory not found"
exit 1
fi
php "$NCC_DIR/INSTALL" --auto
cd .. && rm -rf ncc
- name: Build project
run: |
ncc build --config release-compressed-executable --build-source --log-level debug
- name: Upload build artifact
uses: actions/upload-artifact@v4
with:
name: release-compressed-executable
path: build/release/release_compressed_executable
debug-compressed-executable:
runs-on: ubuntu-latest
container:
image: php:8.3
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Install dependencies
run: |
apt update -yqq
apt install git libpq-dev libzip-dev zip make wget gnupg -yqq
- name: Install phive
run: |
wget -O phive.phar https://phar.io/releases/phive.phar
wget -O phive.phar.asc https://phar.io/releases/phive.phar.asc
gpg --keyserver hkps://keys.openpgp.org --recv-keys 0x9D8A98B29B2D5D79
gpg --verify phive.phar.asc phive.phar
chmod +x phive.phar
mv phive.phar /usr/local/bin/phive
- name: Install phab
run: |
phive install phpab --global --trust-gpg-keys 0x2A8299CE842DD38C
- name: Install latest version of NCC
run: |
git clone https://git.n64.cc/nosial/ncc.git
cd ncc
make redist
NCC_DIR=$(find build/ -type d -name "ncc_*" | head -n 1)
if [ -z "$NCC_DIR" ]; then
echo "NCC build directory not found"
exit 1
fi
php "$NCC_DIR/INSTALL" --auto
cd .. && rm -rf ncc
- name: Build project
run: |
ncc build --config debug-compressed-executable --build-source --log-level debug
- name: Upload build artifact
uses: actions/upload-artifact@v4
with:
name: debug-compressed-executable
path: build/debug/debug_compressed_executable
# Checking for phpunit.xml
check-phpunit:
runs-on: ubuntu-latest
outputs:
phpunit-exists: ${{ steps.check.outputs.phpunit-exists }}
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Check for phpunit.xml
id: check
run: |
if [ -f phpunit.xml ]; then
echo "phpunit-exists=true" >> $GITHUB_OUTPUT
else
echo "phpunit-exists=false" >> $GITHUB_OUTPUT
fi
# Checking for phpdoc.dist.xml
check-phpdoc:
runs-on: ubuntu-latest
outputs:
phpdoc-exists: ${{ steps.check.outputs.phpdoc-exists }}
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Check for phpdoc.dist.xml
id: check
run: |
if [ -f phpdoc.dist.xml ]; then
echo "phpdoc-exists=true" >> $GITHUB_OUTPUT
else
echo "phpdoc-exists=false" >> $GITHUB_OUTPUT
fi
generate-phpdoc:
needs: [release, check-phpdoc]
runs-on: ubuntu-latest
container:
image: php:8.3
if: needs.check-phpdoc.outputs.phpdoc-exists == 'true'
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Install dependencies
run: |
apt update -yqq
apt install git libpq-dev libzip-dev zip make wget gnupg -yqq
- name: Download PHPDocumentor
run: |
wget https://phpdoc.org/phpDocumentor.phar
chmod +x phpDocumentor.phar
- name: Generate PHPDoc
run: |
php phpDocumentor.phar -d src -t docs
- name: Archive PHPDoc
run: |
zip -r docs.zip docs
- name: Upload PHPDoc
uses: actions/upload-artifact@v4
with:
name: documentation
path: docs.zip
test:
needs: [release, release-compressed, debug-compressed, release-executable, release-compressed-executable, debug-compressed-executable, check-phpunit]
runs-on: ubuntu-latest
container:
image: php:8.3
if: needs.check-phpunit.outputs.phpunit-exists == 'true'
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Download build artifacts
uses: actions/download-artifact@v4
with:
name: release
path: release
- name: Install dependencies
run: |
apt update -yqq
apt install git libpq-dev libzip-dev zip make wget gnupg -yqq
curl -sSLf -o /usr/local/bin/install-php-extensions https://github.com/mlocati/docker-php-extension-installer/releases/latest/download/install-php-extensions
chmod +x /usr/local/bin/install-php-extensions
install-php-extensions zip
- name: Install phive
run: |
wget -O phive.phar https://phar.io/releases/phive.phar
wget -O phive.phar.asc https://phar.io/releases/phive.phar.asc
gpg --keyserver hkps://keys.openpgp.org --recv-keys 0x9D8A98B29B2D5D79
gpg --verify phive.phar.asc phive.phar
chmod +x phive.phar
mv phive.phar /usr/local/bin/phive
- name: Install phab
run: |
phive install phpab --global --trust-gpg-keys 0x2A8299CE842DD38C
- name: Install latest version of NCC
run: |
git clone https://git.n64.cc/nosial/ncc.git
cd ncc
make redist
NCC_DIR=$(find build/ -type d -name "ncc_*" | head -n 1)
if [ -z "$NCC_DIR" ]; then
echo "NCC build directory not found"
exit 1
fi
php "$NCC_DIR/INSTALL" --auto
cd .. && rm -rf ncc
- name: Install NCC packages
run: |
ncc package install --package="release/net.nosial.loglib.ncc" --build-source --reinstall -y --log-level debug
- name: Run PHPUnit tests
run: |
wget https://phar.phpunit.de/phpunit-11.3.phar
php phpunit-11.3.phar --configuration phpunit.xml --log-junit reports/junit.xml --log-teamcity reports/teamcity --testdox-html reports/testdox.html --testdox-text reports/testdox.txt
- name: Upload test reports
uses: actions/upload-artifact@v4
with:
name: reports
path: reports
release-documentation:
needs: generate-phpdoc
permissions: write-all
runs-on: ubuntu-latest
container:
image: php:8.3
if: github.event_name == 'release'
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Download documentation artifact
uses: actions/download-artifact@v4
with:
name: documentation
path: documentation
- name: Upload documentation artifact
uses: softprops/action-gh-release@v1
with:
files: |
documentation/*
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
release-artifacts:
needs: [release, release-compressed, debug-compressed, release-executable, release-compressed-executable, debug-compressed-executable]
permissions: write-all
runs-on: ubuntu-latest
container:
image: php:8.3
if: github.event_name == 'release'
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Download release artifact
uses: actions/download-artifact@v4
with:
name: release
path: release
- name: Upload release artifact to release
uses: softprops/action-gh-release@v1
with:
files: |
release/*
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Download release-compressed artifact
uses: actions/download-artifact@v4
with:
name: release-compressed
path: release-compressed
- name: Upload release-compressed artifact to release
uses: softprops/action-gh-release@v1
with:
files: |
release-compressed/*
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Download debug-compressed artifact
uses: actions/download-artifact@v4
with:
name: debug-compressed
path: debug-compressed
- name: Upload debug-compressed artifact to release
uses: softprops/action-gh-release@v1
with:
files: |
debug-compressed/*
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Download release-executable artifact
uses: actions/download-artifact@v4
with:
name: release-executable
path: release-executable
- name: Upload release-executable artifact to release
uses: softprops/action-gh-release@v1
with:
files: |
release-executable/*
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Download release-compressed-executable artifact
uses: actions/download-artifact@v4
with:
name: release-compressed-executable
path: release-compressed-executable
- name: Upload release-compressed-executable artifact to release
uses: softprops/action-gh-release@v1
with:
files: |
release-compressed-executable/*
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Download debug-compressed-executable artifact
uses: actions/download-artifact@v4
with:
name: debug-compressed-executable
path: debug-compressed-executable
- name: Upload debug-compressed-executable artifact to release
uses: softprops/action-gh-release@v1
with:
files: |
debug-compressed-executable/*
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

2
.gitignore vendored
View file

@ -1 +1,3 @@
build/ build/
/.idea/phpunit.xml
/.phpunit.result.cache

View file

@ -1,43 +1,43 @@
image: php:8.1 image: repo.n64.cc:443/nosial/ncc:latest
before_script: stages:
# Install some stuff that the image doesn't come with - build
- apt update -yqq - publish
- apt install git libpq-dev libzip-dev zip make wget gnupg -yqq
# Install phive variables:
- wget -O phive.phar https://phar.io/releases/phive.phar PACKAGE_NAME: $CI_COMMIT_REF_NAME
- wget -O phive.phar.asc https://phar.io/releases/phive.phar.asc
- gpg --keyserver hkps://keys.openpgp.org --recv-keys 0x9D8A98B29B2D5D79
- gpg --verify phive.phar.asc phive.phar
- chmod +x phive.phar
- mv phive.phar /usr/local/bin/phive
# Install phab
- phive install phpab --global --trust-gpg-keys 0x2A8299CE842DD38C
# Install the latest version of ncc (Nosial Code Compiler)
- git clone https://git.n64.cc/nosial/ncc.git
- cd ncc
- make redist
- php build/src/INSTALL --auto --install-composer
- cd .. && rm -rf ncc
build: build:
stage: build
script: script:
- ncc build --config release --log-level debug - ncc build --config release --log-level debug -o "build/release/net.nosial.loglib.ncc"
artifacts: artifacts:
paths: paths:
- build/ - "build/release/net.nosial.loglib.ncc"
rules:
- if: $CI_COMMIT_BRANCH
release: build_static:
stage: deploy stage: build
script: script:
- ncc build --config release --log-level debug - ncc project install
- ncc build --config release_static --log-level debug -o "build/release/net.nosial.loglib_static.ncc"
artifacts: artifacts:
paths: paths:
- build/ - "build/release/net.nosial.loglib_static.ncc"
rules:
- if: $CI_COMMIT_TAG publish:
stage: publish
before_script:
- 'if [ "$CI_COMMIT_REF_NAME" == "master" ]; then PACKAGE_NAME="latest"; fi'
script:
- |
curl --header "JOB-TOKEN: $CI_JOB_TOKEN" --upload-file build/release/net.nosial.loglib.ncc \
"https://$CI_SERVER_HOST/api/v4/projects/$CI_PROJECT_ID/packages/generic/${PACKAGE_NAME}/${CI_COMMIT_SHA}/net.nosial.loglib.ncc"
publish_static:
stage: publish
before_script:
- 'if [ "$CI_COMMIT_REF_NAME" == "master" ]; then PACKAGE_NAME="latest"; fi'
script:
- |
curl --header "JOB-TOKEN: $CI_JOB_TOKEN" --upload-file build/release/net.nosial.loglib_static.ncc \
"https://$CI_SERVER_HOST/api/v4/projects/$CI_PROJECT_ID/packages/generic/${PACKAGE_NAME}/${CI_COMMIT_SHA}/net.nosial.loglib_static.ncc"

View file

@ -19,8 +19,12 @@
<option value="X-Args-3" /> <option value="X-Args-3" />
<option value="X-Args-4" /> <option value="X-Args-4" />
<option value="X-Args-5" /> <option value="X-Args-5" />
<option value="X-Temperature" />
<option value="X-Model" />
<option value="X-OPENAI-API-KEY" />
</set> </set>
</option> </option>
</inspection_tool> </inspection_tool>
<inspection_tool class="PhpDocRedundantThrowsInspection" enabled="true" level="INFORMATION" enabled_by_default="true" />
</profile> </profile>
</component> </component>

20
.idea/php-inspections-ea-ultimate.xml generated Normal file
View file

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="EAUltimateProjectSettings">
<categories>
<STRICTNESS_CATEGORY_SECURITY enabled="yes" />
<STRICTNESS_CATEGORY_PROBABLE_BUGS enabled="yes" />
<STRICTNESS_CATEGORY_PERFORMANCE enabled="yes" />
<STRICTNESS_CATEGORY_ARCHITECTURE enabled="yes" />
<STRICTNESS_CATEGORY_CONTROL_FLOW enabled="yes" />
<STRICTNESS_CATEGORY_LANGUAGE_LEVEL_MIGRATION enabled="yes" />
<STRICTNESS_CATEGORY_CODE_STYLE enabled="yes" />
<STRICTNESS_CATEGORY_UNUSED enabled="yes" />
<STRICTNESS_CATEGORY_PHPUNIT enabled="yes" />
</categories>
<settings>
<ANALYZE_ONLY_MODIFIED_FILES value="no" />
<PREFER_YODA_COMPARISON_STYLE value="no" />
</settings>
</component>
</project>

View file

@ -5,7 +5,7 @@
<tool tool_name="PHPUnit"> <tool tool_name="PHPUnit">
<cache> <cache>
<versions> <versions>
<info id="Local" version="PHPUnit version can't be detected. Default PHP interpreter is not local" /> <info id="Local/home/netkas/phar/phpunit.phar" version="11.3.5" />
</versions> </versions>
</cache> </cache>
</tool> </tool>

5
.idea/php.xml generated
View file

@ -11,9 +11,8 @@
</component> </component>
<component name="PhpIncludePathManager"> <component name="PhpIncludePathManager">
<include_path> <include_path>
<path value="/var/ncc/packages/net.nosial.optslib=1.1.2" />
<path value="/usr/share/php" /> <path value="/usr/share/php" />
<path value="/etc/ncc" />
<path value="/var/ncc/packages/net.nosial.optslib=1.0.0" />
</include_path> </include_path>
</component> </component>
<component name="PhpProjectSharedConfiguration" php_language_level="8.2" /> <component name="PhpProjectSharedConfiguration" php_language_level="8.2" />
@ -125,7 +124,7 @@
</component> </component>
<component name="PhpUnit"> <component name="PhpUnit">
<phpunit_settings> <phpunit_settings>
<PhpUnitSettings load_method="PHPUNIT_PHAR" custom_loader_path="$USER_HOME$/phpunit-9.5.phar" phpunit_phar_path="$USER_HOME$/phpunit-9.5.phar" /> <PhpUnitSettings load_method="PHPUNIT_PHAR" custom_loader_path="$USER_HOME$/phpunit-9.5.phar" phpunit_phar_path="$USER_HOME$/phar/phpunit.phar" />
</phpunit_settings> </phpunit_settings>
</component> </component>
<component name="PsalmOptionsConfiguration"> <component name="PsalmOptionsConfiguration">

10
.idea/runConfigurations/Build.xml generated Normal file
View file

@ -0,0 +1,10 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Build" type="MAKEFILE_TARGET_RUN_CONFIGURATION" factoryName="Makefile">
<makefile filename="$PROJECT_DIR$/Makefile" target="build" workingDirectory="" arguments="">
<envs />
</makefile>
<method v="2">
<option name="RunConfigurationTask" enabled="true" run_configuration_name="Clean" run_configuration_type="MAKEFILE_TARGET_RUN_CONFIGURATION" />
</method>
</configuration>
</component>

8
.idea/runConfigurations/Clean.xml generated Normal file
View file

@ -0,0 +1,8 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Clean" type="MAKEFILE_TARGET_RUN_CONFIGURATION" factoryName="Makefile">
<makefile filename="$PROJECT_DIR$/Makefile" target="clean" workingDirectory="" arguments="">
<envs />
</makefile>
<method v="2" />
</configuration>
</component>

View file

@ -5,15 +5,115 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [2.0.7] - 2025-01-13
This update introduces a minor fix
### Fixed
- Fixed FileLogging issue by setting the write permission to 0666 for the log file if it doesn't exist.
## [2.0.6] - 2025-01-10
This update introduces a minor change
### Changed
- File logging is disabled for web environments due to instability in file locking, until a better solution is found.
## [2.0.5] - 2025-01-09
This update introduces a minor bug fix
### Fixed
- Refactor file locking to return status and handle failure.
## [2.0.4] - 2024-12-04
This update introduces a minor bug fix
## [2.0.3] - 2024-11-05
This update introduces a minor bug fix
## [2.0.2] - 2024-10-30
This update introduces minor improvements
### Changed
- Refactored exception handling in FileLogging where it will always attempt to print the exception no matter
the log level for as long as the log level isn't silent
- Implement enhanced error and exception handling
## [2.0.1] - 2024-10-29
This update introduces a critical bug fix where Console logging was enabled in web environments
## [2.0.0] - 2024-10-29
This update introduces some additional features & bug fixes
### Added
- Added File Logging support
- Added LogHandlerInterface & refactored Application settings to allow for use of custom logging handlers
- Added Logging type for logging type handling
- Added a Logger object for creating a logging instance for Applications
### Changed
- All abstract classes are now enum classes
- Refactored Console Logging to use the new LogHandlerInterface
### Fixed
- Set default log level to 'info' in Utilities. instead of using `null` due to deprecation error
### Removed
- Removed Unused BRIGHT_COLORS constant from ConsoleColors
- Removed Unused Options object
- Removed Unused RuntimeOptions object
- Removed unused Console class
## [1.1.1] - 2024-10-13
Update build system
## [1.1.0] - 2023-10-12
Updated loglib to work with ncc 2.+.
### Changed
- Various code improvements and optimizations
- Removed unused code
## [1.0.2] - 2023-07-06
### Changed
* Changed the Timestamp format to display micro time instead of a date format
* Timestamp Formats can now display in red or yellow to indicate performance impacts between log entries
### Fixed
* Fixed mistake in `\LogLib\Classes > Console > outException()` where the function attempts to print out a previous
exception by calling `getPrevious()` as an array instead of a function call.
## [1.0.1] - 2023-02-10 ## [1.0.1] - 2023-02-10
### Added ### Added
* Added PSR compatible LoggerInterface implementation (\LogLib\Psr)
* Added PSR compatible LoggerInterface implementation (\LogLib\Psr) * Added new option `--log-level` to set the log level (Can also be applied via the environment variable `LOG_LEVEL`)
* Added new option `--log-level` to set the log level (Can also be applied via the environment variable `LOG_LEVEL`)
## [1.0.0] - 2023-01-29 ## [1.0.0] - 2023-01-29
### Added ### Added
* First Release * First Release

24
LICENSE
View file

@ -1,18 +1,14 @@
Copyright 2022-2023 Nosial All Rights Reserved. Copyright 2022-2023 Nosial All Rights Reserved.
Permission is hereby granted, free of charge, to any person obtaining Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
a copy of this software and associated documentation files (the "Software"), documentation files (the "Software"), to deal in the Software without restriction, including without limitation
to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
the rights to use, copy, modify, merge, publish, distribute, sublicense, permit persons to whom the Software is furnished to do so, subject to the following conditions:
and/or sell copies of the Software, and to permit persons to whom the Software
is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all The above copyright notice and this permission notice shall be included in all copies or substantial portions of
copies or substantial portions of the Software. the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View file

@ -1,8 +1,33 @@
# Variables
DEFAULT_CONFIGURATION ?= release
LOG_LEVEL = debug
# Default Target
all: release release-compressed debug-compressed release-executable release-compressed-executable debug-compressed-executable
# Build Steps
release: release:
ncc build --config="release" ncc build --config=release --log-level $(LOG_LEVEL)
release-compressed:
ncc build --config=release-compressed --log-level $(LOG_LEVEL)
debug-compressed:
ncc build --config=debug-compressed --log-level $(LOG_LEVEL)
release-executable:
ncc build --config=release-executable --log-level $(LOG_LEVEL)
release-compressed-executable:
ncc build --config=release-compressed-executable --log-level $(LOG_LEVEL)
debug-compressed-executable:
ncc build --config=debug-compressed-executable --log-level $(LOG_LEVEL)
install:
ncc package install --package="build/release/net.nosial.loglib.ncc" --skip-dependencies --reinstall -y
uninstall: install: release
ncc package uninstall -y --package="net.nosial.loglib" ncc package install --package=build/release/net.nosial.loglib.ncc --skip-dependencies --build-source --reinstall -y --log-level $(LOG_LEVEL)
test: release
[ -f phpunit.xml ] || { echo "phpunit.xml not found"; exit 1; }
phpunit
clean:
rm -rf build
.PHONY: all install test clean release release-compressed debug-compressed release-executable release-compressed-executable debug-compressed-executable

View file

@ -1,7 +1,6 @@
# LogLib # LogLib
A logging library for PHP/ncc, this was quickly thrown together A logging library for PHP/ncc, this was quickly thrown together to provide a simple logging interface and to test out
to provide a simple logging interface and to test out
NCC's capabilities for PHP. NCC's capabilities for PHP.
## Table of Contents ## Table of Contents
@ -25,20 +24,24 @@ The library can be installed using ncc:
ncc install -p "nosial/libs.log=latest@n64" ncc install -p "nosial/libs.log=latest@n64"
``` ```
or by adding the following to your project.json file under A static version of the library can be installed using ncc, the `--skip-dependencies` flag is option but prevents
the `build.dependencies` section: additional dependencies from being installed.
```bash
ncc install -p "nosial/libs.log=latest@n64" --static --skip-dependencies
```
or by adding the following to your project.json file under the `build.dependencies` section:
```json ```json
{ {
"name": "net.nosial.loglib", "name": "net.nosial.loglib",
"version": "latest", "version": "latest",
"source_type": "remote",
"source": "nosial/libs.log=latest@n64" "source": "nosial/libs.log=latest@n64"
} }
``` ```
If you don't have the n64 source configured you can add it If you don't have the n64 source configured you can add it by running the following command:
by running the following command:
```bash ```bash
ncc source add --name n64 --type gitlab --host git.n64.cc ncc source add --name n64 --type gitlab --host git.n64.cc
@ -61,8 +64,7 @@ make release
## Usage ## Usage
The usage of this library is very simple, there are The usage of this library is very simple, there are multiple error levels that can be used to log messages
multiple error levels that can be used to log messages
```php ```php
<?php <?php
@ -70,19 +72,17 @@ multiple error levels that can be used to log messages
require 'ncc'; require 'ncc';
import('net.nosial.loglib'); import('net.nosial.loglib');
\LogLib\Log::debug('This is a debug message'); \LogLib\Log::debug('com.example.lib', 'This is a debug message');
\LogLib\Log::verbose('This is a verbose message'); \LogLib\Log::verbose('com.example.lib', 'This is a verbose message');
\LogLib\Log::info('This is an info message'); \LogLib\Log::info('com.example.lib', 'This is an info message');
\LogLib\Log::warning('This is a warning message'); \LogLib\Log::warning('com.example.lib', 'This is a warning message');
\LogLib\Log::error('This is an error message'); \LogLib\Log::error('com.example.lib', 'This is an error message');
\LogLib\Log::fatal('This is a fatal message'); \LogLib\Log::fatal('com.example.lib', 'This is a fatal message');
``` ```
To display the log messages, you can run your program To display the log messages, you can run your program with the `--log-level` argument, this will display all messages
with the `--log-level` argument, this will display all with a level equal to or higher than the one specified.
messages with a level equal to or higher than the one
specified.
```bash ```bash
myprogram --log-level info myprogram --log-level info
@ -100,9 +100,8 @@ The log level can be set to one of the following:
The default log level is `info`. The default log level is `info`.
> Note: Log messages are only displayed if the program > Note: Log messages are only displayed if the program is run from the command line, if you are running the program
is run from the command line, if you are running the > from a web server, the log messages will be shown
program from a web server, the log messages will be shown
## Changelog ## Changelog

3
bootstrap.php Normal file
View file

@ -0,0 +1,3 @@
<?php
require 'ncc';
import('net.nosial.loglib');

15
phpdoc.dist.xml Normal file
View file

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8" ?>
<phpdocumentor configVersion="3" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="https://www.phpdoc.org" xsi:noNamespaceSchemaLocation="https://raw.githubusercontent.com/phpDocumentor/phpDocumentor/master/data/xsd/phpdoc.xsd">
<paths>
<output>build/docs</output>
<cache>build/cache</cache>
</paths>
<version number="latest">
<api>
<source dsn=".">
<path>src/LogLib</path>
</source>
<default-package-name>LogLib</default-package-name>
</api>
</version>
</phpdocumentor>

11
phpunit.xml Normal file
View file

@ -0,0 +1,11 @@
<phpunit bootstrap="bootstrap.php">
<testsuites>
<testsuite name="LogLib Test Suite">
<directory>tests</directory>
</testsuite>
</testsuites>
<php>
<ini name="error_reporting" value="-1"/>
<server name="KERNEL_DIR" value="app/"/>
</php>
</phpunit>

View file

@ -13,15 +13,14 @@
"host": "git.n64.cc", "host": "git.n64.cc",
"ssl": true "ssl": true
} }
}, }
"options": []
}, },
"assembly": { "assembly": {
"name": "LogLib", "name": "LogLib",
"package": "net.nosial.loglib", "package": "net.nosial.loglib",
"company": "Nosial", "company": "Nosial",
"copyright": "Copyright (c) 2022-2023 Nosial", "copyright": "Copyright (c) 2022-2023 Nosial",
"version": "1.0.0", "version": "2.0.7",
"uuid": "de1deca6-7b65-11ed-a8b0-a172264634d8" "uuid": "de1deca6-7b65-11ed-a8b0-a172264634d8"
}, },
"build": { "build": {
@ -31,14 +30,57 @@
{ {
"name": "net.nosial.optslib", "name": "net.nosial.optslib",
"version": "latest", "version": "latest",
"source_type": "remote",
"source": "nosial/libs.opts=latest@n64" "source": "nosial/libs.opts=latest@n64"
} }
], ],
"configurations": [ "configurations": [
{ {
"name": "release", "name": "release",
"output_path": "build/release" "build_type": "ncc",
"output": "build/release/%ASSEMBLY.PACKAGE%.ncc"
},
{
"name": "release-compressed",
"build_type": "ncc",
"output": "build/release/%ASSEMBLY.PACKAGE%.gz.ncc",
"options": {
"compression": "high"
}
},
{
"name": "debug-compressed",
"build_type": "ncc",
"output": "build/debug/%ASSEMBLY.PACKAGE%.gz.ncc",
"options": {
"compression": "high"
},
"define_constants": {
"DEBUG": "1"
}
},
{
"name": "release-executable",
"build_type": "executable",
"output": "build/release/release_executable_gz",
"options": {
"ncc_configuration": "release"
}
},
{
"name": "release-compressed-executable",
"build_type": "executable",
"output": "build/release/release_compressed_executable",
"options": {
"ncc_configuration": "release-compressed"
}
},
{
"name": "debug-compressed-executable",
"build_type": "executable",
"output": "build/debug/debug_compressed_executable",
"options": {
"ncc_configuration": "debug-compressed"
}
} }
] ]
} }

View file

@ -1,10 +0,0 @@
<?php
namespace LogLib\Abstracts;
abstract class CallType
{
const MethodCall = '->';
const StaticCall = '::';
const FunctionCall = ' ';
}

View file

@ -1,54 +0,0 @@
<?php
namespace LogLib\Abstracts;
abstract class ConsoleColors
{
const Black = "0;30";
const DarkGray = "1;30";
const Blue = "0;34";
const LightBlue = "1;34";
const Green = "0;32";
const LightGreen = "1;32";
const Cyan = "0;36";
const LightCyan = "1;36";
const Red = "0;31";
const LightRed = "1;31";
const Purple = "0;35";
const LightPurple = "1;35";
const Brown = "0;33";
const Yellow = "1;33";
const LightGray = "0;37";
const White = "1;37";
const Reset = "0";
const All = [
self::Black,
self::DarkGray,
self::Blue,
self::LightBlue,
self::Green,
self::LightGreen,
self::Cyan,
self::LightCyan,
self::Red,
self::LightRed,
self::Purple,
self::LightPurple,
self::Brown,
self::Yellow,
self::LightGray,
self::White
];
/**
* A list of random usable bright colors
*/
const BrightColors = [
self::LightBlue,
self::LightGreen,
self::LightCyan,
self::LightRed,
self::LightPurple,
];
}

View file

@ -1,27 +0,0 @@
<?php
namespace LogLib\Abstracts;
abstract class LevelType
{
const Silent = 0;
const Fatal = 1;
const Error = 2;
const Warning = 3;
const Info = 4;
const Verbose = 5;
const Debug = 6;
/**
* All types.
*/
const All = [
self::Silent,
self::Fatal,
self::Error,
self::Warning,
self::Info,
self::Verbose,
self::Debug
];
}

View file

@ -0,0 +1,87 @@
<?php
namespace LogLib\Classes;
class BacktraceParser
{
/**
* Determines if the given backtrace originates from the exception handler.
*
* @param array $backtrace The backtrace array to inspect.
* @return bool Returns true if the backtrace originates from the exception handler within LogLib\Runtime class, false otherwise.
*/
public static function fromExceptionHandler(array $backtrace): bool
{
/** @var array $trace */
foreach($backtrace as $trace)
{
if(!isset($trace['function']) || $trace['function'] != 'exceptionHandler')
{
continue;
}
if(!isset($trace['class']) || $trace['class'] != 'LogLib\Runtime')
{
continue;
}
return true;
}
return false;
}
/**
* Determines if the given backtrace originates from the error handler.
*
* @param array $backtrace The backtrace array to inspect.
* @return bool Returns true if the backtrace originates from the error handler within LogLib\Runtime class, false otherwise.
*/
public static function fromErrorHandler(array $backtrace): bool
{
/** @var array $trace */
foreach($backtrace as $trace)
{
if(!isset($trace['function']) || $trace['function'] != 'errorHandler')
{
continue;
}
if(!isset($trace['class']) || $trace['class'] != 'LogLib\Runtime')
{
continue;
}
return true;
}
return false;
}
/**
* Determines if a given backtrace contains a call to the shutdownHandler method in the LogLib\Runtime class.
*
* @param array $backtrace The backtrace to be analyzed.
* @return bool True if the shutdownHandler method in the LogLib\Runtime class is found in the backtrace; otherwise, false.
*/
public static function fromShutdownHandler(array $backtrace): bool
{
/** @var array $trace */
foreach($backtrace as $trace)
{
if(!isset($trace['function']) || $trace['function'] != 'shutdownHandler')
{
continue;
}
if(!isset($trace['class']) || $trace['class'] != 'LogLib\Runtime')
{
continue;
}
return true;
}
return false;
}
}

View file

@ -1,167 +0,0 @@
<?php
/** @noinspection PhpMissingFieldTypeInspection */
namespace LogLib\Classes;
use LogLib\Abstracts\ConsoleColors;
use LogLib\Abstracts\LevelType;
use LogLib\Log;
use LogLib\Objects\Event;
use LogLib\Objects\Options;
use Throwable;
class Console
{
/**
* @var array
*/
private static $ApplicationColors = [];
/**
* Formats the application color for the console
*
* @param string $application
* @return string
*/
private static function formatAppColor(string $application): string
{
if(!Log::getRuntimeOptions()->isDisplayAnsi())
return $application;
if(!isset(self::$ApplicationColors[$application]))
{
$colors = ConsoleColors::BrightColors;
$color = $colors[array_rand($colors)];
self::$ApplicationColors[$application] = $color;
}
return self::color($application, self::$ApplicationColors[$application]);
}
/**
* Returns a color formatted string for the console
*
* @param string $text
* @param string $color
* @return string
*/
private static function color(string $text, string $color): string
{
if(!Log::getRuntimeOptions()->isDisplayAnsi())
return $text;
return "\033[" . $color . "m" . $text . "\033[0m";
}
/**
* Colorizes a event string for the console
*
* @param Event $event
* @param string $text
* @return string
*/
private static function colorize(Event $event, string $text): string
{
if(!Log::getRuntimeOptions()->isDisplayAnsi())
return Utilities::levelToString($text);
$color = null;
switch($event->Level)
{
case LevelType::Debug:
$color = ConsoleColors::LightPurple;
break;
case LevelType::Verbose:
$color = ConsoleColors::LightCyan;
break;
case LevelType::Info:
$color = ConsoleColors::White;
break;
case LevelType::Warning:
$color = ConsoleColors::Yellow;
break;
case LevelType::Fatal:
$color = ConsoleColors::Red;
break;
case LevelType::Error:
$color = ConsoleColors::LightRed;
break;
}
if($color == null)
return Utilities::levelToString($text);
return self::color(Utilities::levelToString($text), $color);
}
/**
* Regular console output for the event object
*
* @param Options $options
* @param Event $event
* @return void
*/
public static function out(Options $options, Event $event): void
{
if(!Utilities::runningInCli())
return;
if(Validate::checkLevelType(LevelType::Verbose, Log::getRuntimeOptions()->getLogLevel()))
{
$backtrace_output = Utilities::getTraceString($event, Log::getRuntimeOptions()->isDisplayAnsi());
print(sprintf(
"%s [%s] [%s] %s %s" . PHP_EOL,
$event->getTimestamp(),
self::formatAppColor($options->getApplicationName()),
self::colorize($event, $event->Level),
$backtrace_output, $event->Message
));
if($event->Exception !== null)
self::outException($event->Exception);
return;
}
print(sprintf(
"%s [%s] [%s] %s" . PHP_EOL,
$event->getTimestamp(),
self::formatAppColor($options->getApplicationName()),
self::colorize($event, $event->Level),
$event->Message
));
}
/**
* Prints out the exception details
*
* @param Throwable $exception
* @return void
*/
private static function outException(Throwable $exception): void
{
$trace_header = self::color($exception->getFile() . ':' . $exception->getLine(), ConsoleColors::Purple);
$trace_error = self::color('error: ', ConsoleColors::Red);
print($trace_header . ' ' . $trace_error . $exception->getMessage() . PHP_EOL);
print(sprintf('Error code: %s', $exception->getCode()) . PHP_EOL);
$trace = $exception->getTrace();
if(count($trace) > 1)
{
print('Stack Trace:' . PHP_EOL);
foreach($trace as $item)
{
print( ' - ' . self::color($item['file'], ConsoleColors::Red) . ':' . $item['line'] . PHP_EOL);
}
}
if($exception->getPrevious() !== null)
{
print('Previous Exception:' . PHP_EOL);
self::outException($exception['previous']);
}
}
}

View file

@ -0,0 +1,122 @@
<?php
namespace LogLib\Classes;
use RuntimeException;
/**
* Class FileLock
*
* This class provides functionalities to safely work with file locks and ensures
* that concurrent write operations do not overwrite each other.
* It offers methods to lock, unlock, and append data to a file.
*/
class FileLock
{
private $fileHandle;
private string $filePath;
private int $retryInterval; // in microseconds
private int $confirmationInterval; // in microseconds
/**
* Constructor for FileLock.
*
* @param string $filePath Path to the file.
* @param int $retryInterval Time to wait between retries (in microseconds).
* @param int $confirmationInterval Time to wait before double confirmation (in microseconds).
*/
public function __construct(string $filePath, int $retryInterval=100000, int $confirmationInterval=50000)
{
$this->filePath = $filePath;
$this->retryInterval = $retryInterval;
$this->confirmationInterval = $confirmationInterval;
// Create the file if it doesn't exist
if (!file_exists($filePath))
{
// Create the file
touch($filePath);
// Set the file permissions to 0666
chmod($filePath, 0666);
}
}
/**
* Locks the file.
*
* @throws RuntimeException if unable to open or lock the file.
*/
private function lock(): bool
{
$this->fileHandle = @fopen($this->filePath, 'a');
if ($this->fileHandle === false)
{
return false;
}
// Keep trying to acquire the lock until it succeeds
while (!flock($this->fileHandle, LOCK_EX))
{
usleep($this->retryInterval); // Wait for the specified interval before trying again
}
// Double confirmation
usleep($this->confirmationInterval); // Wait for the specified confirmation interval
if (!flock($this->fileHandle, LOCK_EX | LOCK_NB))
{
// If the lock cannot be re-acquired, release the current lock and retry
flock($this->fileHandle, LOCK_UN);
$this->lock();
}
return true;
}
/**
* Unlocks the file after performing write operations.
*/
private function unlock(): void
{
if ($this->fileHandle !== null)
{
flock($this->fileHandle, LOCK_UN); // Release the lock
fclose($this->fileHandle); // Close the file handle
$this->fileHandle = null; // Reset the file handle
}
}
/**
* Appends data to the file.
*
* @param string $data Data to append.
* @throws RuntimeException if unable to write to the file.
*/
public function append(string $data): void
{
if(!$this->lock())
{
// Do not proceed if the file cannot be locked
return;
}
if ($this->fileHandle !== false)
{
if (fwrite($this->fileHandle, $data) === false)
{
throw new RuntimeException("Unable to write to the file: " . $this->filePath);
}
}
$this->unlock();
}
/**
* Destructor to ensure the file handle is closed.
*/
public function __destruct()
{
if ($this->fileHandle)
{
fclose($this->fileHandle);
}
}
}

View file

@ -2,8 +2,8 @@
namespace LogLib\Classes; namespace LogLib\Classes;
use LogLib\Abstracts\LevelType; use LogLib\Enums\CallType;
use LogLib\Objects\Backtrace; use LogLib\Enums\LogLevel;
use LogLib\Objects\Event; use LogLib\Objects\Event;
use OptsLib\Parse; use OptsLib\Parse;
use Throwable; use Throwable;
@ -11,59 +11,50 @@
class Utilities class Utilities
{ {
/** /**
* Returns the current backtrace * Returns a backtrace of the calling code.
* *
* @param bool $full * @return array An array containing backtrace information.
* @return array
*/ */
public static function getBacktrace(bool $full=false): array public static function getBacktrace(): array
{ {
if(!function_exists('debug_backtrace')) if(!function_exists('debug_backtrace'))
return [];
$backtrace = debug_backtrace();
$results = [];
foreach($backtrace as $trace)
{ {
if(isset($trace['class'] ) && str_contains($trace['class'], 'LogLib') && !$full) return [];
continue;
$results[] = new Backtrace($trace);
} }
return $results; return debug_backtrace();
} }
/** /**
* Returns the current level type as a string * Converts a log level to its corresponding string representation.
* *
* @param int $level * @param LogLevel $level The log level to convert.
* @return string * @return string The string representation of the log level.
*/ */
public static function levelToString(int $level): string public static function levelToString(LogLevel $level): string
{ {
return match ($level) return match ($level)
{ {
LevelType::Debug => 'DBG', LogLevel::DEBUG => 'DBG',
LevelType::Verbose => 'VRB', LogLevel::VERBOSE => 'VRB',
LevelType::Info => 'INF', LogLevel::INFO => 'INF',
LevelType::Warning => 'WRN', LogLevel::WARNING => 'WRN',
LevelType::Fatal => 'CRT', LogLevel::FATAL => 'CRT',
LevelType::Error => 'ERR', LogLevel::ERROR => 'ERR',
default => 'UNK', default => 'UNK',
}; };
} }
/** /**
* A simple method to determine if the current environment is a CLI environment * Determines whether the application is currently running in the command line interface (CLI) mode.
* *
* @return bool * @return bool true if running in CLI mode, false otherwise.
*/ */
public static function runningInCli(): bool public static function runningInCli(): bool
{ {
if(function_exists('php_sapi_name')) if(function_exists('php_sapi_name'))
{ {
/** @noinspection ConstantCanBeUsedInspection */
return strtolower(php_sapi_name()) === 'cli'; return strtolower(php_sapi_name()) === 'cli';
} }
@ -76,68 +67,72 @@
} }
/** /**
* Attempts to determine the current log level from the command line arguments * Returns the log level based on the configuration.
* *
* @return int * @return LogLevel The log level. This value represents the severity or importance of the log messages.
* The returned value will be one of the constants defined in the LevelType class:
* - DEBUG (6)
* - VERBOSE (5)
* - INFO (4)
* - WARNING (3)
* - ERROR (2)
* - FATAL (1)
* - SILENT (0)
* If no log level is configured or the configured level is not recognized, the INFO level (4) will be returned by default.
*/ */
public static function getLogLevel(): int private static function parseLogLevel(string $logLevel): LogLevel
{ {
$args = Parse::getArguments(); switch(strtolower($logLevel))
$selected_level = ($args['log'] ?? $args['log-level'] ?? (getenv('LOG_LEVEL') ?: null) ?? null);
if($selected_level === null)
return LevelType::Info;
switch(strtolower($selected_level))
{ {
case LevelType::Debug: case LogLevel::DEBUG:
case 'debug': case 'debug':
case '6': case '6':
case 'dbg': case 'dbg':
return LevelType::Debug; return LogLevel::DEBUG;
case LevelType::Verbose: case LogLevel::VERBOSE:
case 'verbose': case 'verbose':
case '5': case '5':
case 'vrb': case 'vrb':
return LevelType::Verbose; return LogLevel::VERBOSE;
default: default:
case LevelType::Info: case LogLevel::INFO:
case 'info': case 'info':
case '4': case '4':
case 'inf': case 'inf':
return LevelType::Info; return LogLevel::INFO;
case LevelType::Warning: case LogLevel::WARNING:
case 'warning': case 'warning':
case '3': case '3':
case 'wrn': case 'wrn':
return LevelType::Warning; return LogLevel::WARNING;
case LevelType::Error: case LogLevel::ERROR:
case 'error': case 'error':
case '2': case '2':
case 'err': case 'err':
return LevelType::Error; return LogLevel::ERROR;
case LevelType::Fatal: case LogLevel::FATAL:
case 'fatal': case 'fatal':
case '1': case '1':
case 'crt': case 'crt':
return LevelType::Fatal; return LogLevel::FATAL;
case LevelType::Silent: case LogLevel::SILENT:
case 'silent': case 'silent':
case '0': case '0':
case 'sil': case 'sil':
return LevelType::Silent; return LogLevel::SILENT;
} }
} }
/** /**
* @return bool * Checks if ANSI escape sequences should be displayed in the output.
*
* @return bool Returns true if ANSI escape sequences should be displayed, false otherwise.
*/ */
public static function getDisplayAnsi(): bool public static function getDisplayAnsi(): bool
{ {
@ -145,74 +140,140 @@
$display_ansi = ($args['display-ansi'] ?? $args['ansi'] ?? null); $display_ansi = ($args['display-ansi'] ?? $args['ansi'] ?? null);
if($display_ansi === null) if($display_ansi === null)
{
return true; return true;
}
// Strict boolean response // Strict boolean response
return strtolower($display_ansi) === 'true' || $display_ansi === '1'; return strtolower($display_ansi) === 'true' || $display_ansi === '1';
} }
/** /**
* Returns the current active log file name, the current value can * Returns a string representation of the backtrace for the given event.
* change depending on the date/time, if it has changed; close the
* old file and open a new one.
* *
* @return string * @param Event $event The event object for which to generate the backtrace string.
* @return string|null A string representation of the backtrace for the event, or null if the event has no backtrace.
* The output format is: ClassName::methodName() or functionName() depending on the type of call.
* If $ansi is true, the output will be colored using ANSI escape codes.
* If the event has no backtrace, the constant CallType::LAMBDA_CALL will be returned.
*/ */
public static function getLogFilename(): string public static function getTraceString(Event $event, bool $ansi = true): ?string
{ {
return date('Y-m-d') . '.log'; if ($event->getBacktrace() === null || count($event->getBacktrace()) === 0)
{
return CallType::LAMBDA_CALL->value;
}
$backtrace = $event->getBacktrace()[count($event->getBacktrace()) - 1];
// Ignore \LogLib namespace
if (isset($backtrace['class']) && str_starts_with($backtrace['class'], 'LogLib'))
{
if (isset($backtrace['file']))
{
if ($ansi)
{
return "\033[1;37m" . basename($backtrace['file']) . "\033[0m";
}
return basename($backtrace['file']);
}
return self::determineCallType($event->getBacktrace())->value; // Return a placeholder value
}
if ($backtrace['function'] === '{closure}')
{
if (isset($backtrace['file']))
{
if ($ansi)
{
return "\033[1;37m" . basename($backtrace['file']) . "\033[0m" . CallType::STATIC_CALL->value . CallType::LAMBDA_CALL->value;
}
return basename($backtrace['file']) . CallType::STATIC_CALL->value . CallType::LAMBDA_CALL->value;
}
return self::determineCallType($event->getBacktrace())->value . CallType::STATIC_CALL->value . CallType::LAMBDA_CALL->value; // Adjusted to handle missing 'file'
}
if ($backtrace['function'] === 'eval')
{
if (isset($backtrace['file']))
{
if ($ansi)
{
return "\033[1;37m" . basename($backtrace['file']) . "\033[0m" . CallType::STATIC_CALL->value . CallType::EVAL_CALL->value;
}
return basename($backtrace['file']) . CallType::STATIC_CALL->value . CallType::EVAL_CALL->value;
}
return self::determineCallType($event->getBacktrace())->value . CallType::STATIC_CALL->value . CallType::EVAL_CALL->value; // Adjusted to handle missing 'file'
}
if ($ansi)
{
$function = sprintf("\033[1;37m%s\033[0m", $backtrace['function']);
}
else
{
$function = $backtrace['function'];
}
$class = null;
if (isset($backtrace["class"]))
{
if ($ansi)
{
$class = sprintf("\033[1;37m%s\033[0m", $backtrace['class']);
}
else
{
$class = $backtrace['class'];
}
}
if ($class === null)
{
return $function . CallType::FUNCTION_CALL->value;
}
$type = ($backtrace['type'] === CallType::METHOD_CALL ? CallType::METHOD_CALL : CallType::STATIC_CALL);
return "{$class}{$type->value}{$function}" . CallType::FUNCTION_CALL->value;
} }
/** /**
* Returns a random string of characters * Determines the type of call based on the provided backtrace.
* *
* @param int $length * @param array $backtrace The backtrace information of the calling code.
* @return string * @return CallType The type of call detected.
*/ */
public static function randomString(int $length = 32): string private static function determineCallType(array $backtrace): CallType
{ {
$characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; if(BacktraceParser::fromErrorHandler($backtrace))
$charactersLength = strlen($characters);
$randomString = '';
for ($i = 0; $i < $length; $i++)
{ {
$randomString .= $characters[rand(0, $charactersLength - 1)]; return CallType::ERROR_HANDLER;
} }
return $randomString;
if(BacktraceParser::fromExceptionHandler($backtrace))
{
return CallType::EXCEPTION_HANDLER;
}
if(BacktraceParser::fromShutdownHandler($backtrace))
{
return CallType::SHUTDOWN_HANDLER;
}
return CallType::UNKNOWN_FILE;
} }
/** /**
* @param Event $event * Converts an exception object to an array representation.
* @param bool $ansi
* @return string|null
*/
public static function getTraceString(Event $event, bool $ansi=false): ?string
{
if($event->getBacktrace() == null)
return 'λ';
$backtrace = $event->getBacktrace()[0];
$function = $backtrace->getFunction();
$class = $backtrace->getClass();
if($ansi)
{
$function = "\033[1;37m$function\033[0m";
$class = "\033[1;37m$class\033[0m";
}
if($class == null)
return "{$function}()";
$type = ($backtrace->getType() == '->' ? '->' : '::');
return "{$class}{$type}{$function}()";
}
/**
* Returns an array representation of a throwable exception
* *
* @param Throwable $e * @param Throwable $e The exception object to convert.
* @return array * @return array An array containing the details of the exception.
* The array includes the exception message, code, file, line, and a formatted trace.
* The trace is formatted as a string containing the file, line, class, type, and function for each call in the traceback.
*/ */
public static function exceptionToArray(Throwable $e): array public static function exceptionToArray(Throwable $e): array
{ {
@ -233,4 +294,135 @@
]; ];
} }
/**
* Retrieves the directory path for logging purposes.
*
* @return string The path to the log directory, or a temporary directory if not set.
*/
public static function getLogDirectory(): string
{
$args = Parse::getArguments();
$LOGGING_DIRECTORY = ($args['logging-directory'] ?? getenv('LOGGING_DIRECTORY') ?? null);
if($LOGGING_DIRECTORY === null)
{
return sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'logs';
}
return $LOGGING_DIRECTORY;
}
/**
* Determines if console logging is enabled based on command-line arguments,
* execution environment, or environment variables.
*
* @return bool True if console logging is enabled, false otherwise.
*/
public static function getConsoleLoggingEnabled(): bool
{
if(isset(Parse::getArguments()['log-level']))
{
return true;
}
if(self::runningInCli())
{
return true;
}
if(getenv('LOGGING_CONSOLE') === 'true' || getenv('LOGGING_CONSOLE') === '1')
{
return true;
}
return false;
}
/**
* Retrieves the current logging level for console output.
*
* @return LogLevel The determined logging level.
*/
public static function getConsoleLoggingLevel(): LogLevel
{
return self::parseLogLevel(($args['log'] ?? $args['log-level'] ?? (getenv('LOG_LEVEL') ?: 'info') ?? 'info'));
}
/**
* Determines if file logging is enabled based on various conditions.
*
* @return bool True if file logging is enabled, false otherwise.
*/
public static function getFileLoggingEnabled(): bool
{
if(isset(Parse::getArguments()['logging-directory']))
{
return true;
}
if(getenv('LOGGING_DIRECTORY') !== null)
{
return true;
}
if(!is_writable(self::getLogDirectory()))
{
return false;
}
return false;
}
/**
* Retrieves the logging level for file outputs based on environment variables or command line arguments.
*
* @return LogLevel The logging level to be used for file logging.
*/
public static function getFileLoggingLevel(): LogLevel
{
if(getenv('LOGGING_FILE_LEVEL') !== null)
{
return self::parseLogLevel(getenv('LOGGING_FILE_LEVEL'));
}
if(isset(Parse::getArguments()['logging-file-level']))
{
return self::parseLogLevel(Parse::getArguments()['logging-file-level']);
}
return LogLevel::WARNING;
}
/**
* Returns the directory path for file logging.
*
* @return string The logging directory path.
*/
public static function getFileLoggingDirectory(): string
{
$args = Parse::getArguments();
$LOGGING_DIRECTORY = ($args['logging-directory'] ?? getenv('LOGGING_DIRECTORY') ?? null);
if($LOGGING_DIRECTORY === null || $LOGGING_DIRECTORY === false || strlen($LOGGING_DIRECTORY) === 0)
{
return sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'logs';
}
return $LOGGING_DIRECTORY;
}
/**
* Sanitizes a file name by replacing spaces with hyphens and removing illegal characters.
*
* @param string $name The file name to sanitize.
* @return string The sanitized file name.
*/
public static function sanitizeFileName(string $name): string
{
// Replace spaces with hyphens
$name = str_replace(' ', '-', $name);
// Remove illegal characters and replace escapable characters with underscores
return preg_replace('/[\/:*?"<>|.]/', '_', $name);
}
} }

View file

@ -2,108 +2,78 @@
namespace LogLib\Classes; namespace LogLib\Classes;
use LogLib\Abstracts\LevelType; use LogLib\Enums\LogLevel;
class Validate class Validate
{ {
/** /**
* Validates that the level is valid * Checks if the given input level is valid for the current level.
* *
* @param string $level * @param LogLevel $input The input level to check.
* @return bool * @param LogLevel $current_level The current level to compare against.
* @return bool Returns true if the input level is valid for the current level, false otherwise.
*/ */
public static function LevelType(string $level): bool public static function checkLevelType(LogLevel $input, LogLevel $current_level): bool
{ {
return in_array($level, LevelType::All);
}
/**
* Checks if the input level matches the current level
*
* @param string $input
* @param string $current_level
* @return bool
*/
public static function checkLevelType(string $input, string $current_level): bool
{
if($input == null)
return false;
if($current_level == null)
return false;
$input = strtolower($input);
if(!Validate::LevelType($input))
return false;
$current_level = strtolower($current_level);
if(!Validate::LevelType($current_level))
return false;
switch($current_level) switch($current_level)
{ {
case LevelType::Debug: case LogLevel::DEBUG:
$levels = [ $levels = [
LevelType::Debug, LogLevel::DEBUG,
LevelType::Verbose, LogLevel::VERBOSE,
LevelType::Info, LogLevel::INFO,
LevelType::Warning, LogLevel::WARNING,
LevelType::Fatal, LogLevel::FATAL,
LevelType::Error LogLevel::ERROR
]; ];
if(in_array($input, $levels))
return true;
return false;
case LevelType::Verbose: return in_array($input, $levels, true);
case LogLevel::VERBOSE:
$levels = [ $levels = [
LevelType::Verbose, LogLevel::VERBOSE,
LevelType::Info, LogLevel::INFO,
LevelType::Warning, LogLevel::WARNING,
LevelType::Fatal, LogLevel::FATAL,
LevelType::Error LogLevel::ERROR
]; ];
if(in_array($input, $levels))
return true;
return false;
case LevelType::Info: return in_array($input, $levels, true);
case LogLevel::INFO:
$levels = [ $levels = [
LevelType::Info, LogLevel::INFO,
LevelType::Warning, LogLevel::WARNING,
LevelType::Fatal, LogLevel::FATAL,
LevelType::Error LogLevel::ERROR
]; ];
if(in_array($input, $levels))
return true;
return false;
case LevelType::Warning: return in_array($input, $levels, true);
case LogLevel::WARNING:
$levels = [ $levels = [
LevelType::Warning, LogLevel::WARNING,
LevelType::Fatal, LogLevel::FATAL,
LevelType::Error LogLevel::ERROR
]; ];
if(in_array($input, $levels))
return true;
return false;
case LevelType::Error: return in_array($input, $levels, true);
case LogLevel::ERROR:
$levels = [ $levels = [
LevelType::Fatal, LogLevel::FATAL,
LevelType::Error LogLevel::ERROR
]; ];
if(in_array($input, $levels))
return true;
return false;
case LevelType::Fatal: return in_array($input, $levels, true);
if($input == LevelType::Fatal)
return true; case LogLevel::FATAL:
return false; return $input == LogLevel::FATAL;
case LogLevel::SILENT:
return false;
}
default:
case LevelType::Silent:
return false; return false;
} }
} }
}

View file

@ -0,0 +1,69 @@
<?php
namespace LogLib\Enums;
enum CallType : string
{
/**
* Represents a method call.
*
* @var string METHOD_CALL
*/
case METHOD_CALL = '->';
/**
* Represents a static method call.
*
* @var string STATIC_CALL
*/
case STATIC_CALL = '::';
/**
* Represents a function call.
*
* @var string FUNCTION_CALL
*/
case FUNCTION_CALL = '()';
/**
* Represents a lambda function call.
*
* @var string LAMBDA_CALL
*/
case LAMBDA_CALL = 'λ';
/**
* Represents an eval() call.
*
* @var string EVAL_CALL
*/
case EVAL_CALL = 'eval()';
/**
* Represents an unknown file.
*
* @var string UNKNOWN_FILE
*/
case UNKNOWN_FILE = '?';
/**
* Represents a runtime error handler.
*
* @var string ERROR_HANDLER
*/
case ERROR_HANDLER = 'RUNTIME_ERROR';
/**
* Represents a shutdown handler event.
*
* @var string SHUTDOWN_HANDLER
*/
case SHUTDOWN_HANDLER = 'SHUTDOWN_ERROR';
/**
* Represents an exception handler for runtime exceptions.
*
* @var string EXCEPTION_HANDLER
*/
case EXCEPTION_HANDLER = 'RUNTIME_EXCEPTION';
}

View file

@ -0,0 +1,58 @@
<?php
namespace LogLib\Enums;
enum ConsoleColors : string
{
case BLUE = "0;34";
case LIGHT_BLUE = "1;34";
case GREEN = "0;32";
case LIGHT_GREEN = "1;32";
case CYAN = "0;36";
case LIGHT_CYAN = "1;36";
case RED = "0;31";
case LIGHT_RED = "1;31";
case PURPLE = "0;35";
case LIGHT_PURPLE = "1;35";
case BROWN = "0;33";
case YELLOW = "1;33";
case LIGHT_GRAY = "0;37";
case WHITE = "1;37";
case RESET = "0";
/**
* Represents an array of all possible supported color values.
*
* @var array
*/
public const ALL = [
self::BLUE,
self::LIGHT_BLUE,
self::GREEN,
self::LIGHT_GREEN,
self::CYAN,
self::LIGHT_CYAN,
self::RED,
self::LIGHT_RED,
self::PURPLE,
self::LIGHT_PURPLE,
self::BROWN,
self::YELLOW,
self::LIGHT_GRAY,
self::WHITE
];
}

View file

@ -0,0 +1,54 @@
<?php
namespace LogLib\Enums;
enum LogLevel : int
{
/**
* Silent type.
*/
case SILENT = 0;
/**
* Fatal type.
*/
case FATAL = 1;
/**
* Error type.
*/
case ERROR = 2;
/**
*
*/
case WARNING = 3;
/**
* Information type.
*/
case INFO = 4;
/**
* Verbose type.
*/
case VERBOSE = 5;
/**
* Debug type.
*/
case DEBUG = 6;
/**
* All types.
*/
public const ALL = [
self::SILENT,
self::FATAL,
self::ERROR,
self::WARNING,
self::INFO,
self::VERBOSE,
self::DEBUG
];
}

View file

@ -1,21 +0,0 @@
<?php
namespace LogLib\Exceptions;
use Exception;
use Throwable;
class ConfigurationException extends Exception
{
/**
* @param string $message
* @param int $code
* @param Throwable|null $previous
*/
public function __construct(string $message = "", int $code = 0, ?Throwable $previous = null)
{
parent::__construct($message, $code, $previous);
$this->message = $message;
$this->code = $code;
}
}

View file

@ -0,0 +1,223 @@
<?php
namespace LogLib\Handlers;
use Exception;
use LogLib\Classes\Utilities;
use LogLib\Classes\Validate;
use LogLib\Enums\ConsoleColors;
use LogLib\Enums\LogLevel;
use LogLib\Interfaces\LogHandlerInterface;
use LogLib\Objects\Application;
use LogLib\Objects\Event;
use RuntimeException;
use Throwable;
class ConsoleLogging implements LogHandlerInterface
{
private static array $application_colors = [];
private static float|int|null $last_tick_time = null;
private static ?int $largest_tick_length = null;
/**
* @inheritDoc
*/
public static function handle(Application $application, Event $event): void
{
if(!Utilities::runningInCli())
{
return;
}
// Check if the event level is enabled for console logging
if(!Validate::checkLevelType($event->getLevel(), $application->getConsoleLoggingLevel()))
{
return;
}
if(Validate::checkLevelType(LogLevel::DEBUG, $application->getConsoleLoggingLevel()))
{
$backtrace_output = Utilities::getTraceString($event);
print(sprintf("[%s] [%s] [%s] %s %s" . PHP_EOL,
self::getTimestamp(), self::formatAppColor($application->getApplicationName()), self::colorize($event),
$backtrace_output, $event->getMessage()
));
if($event->getException() !== null)
{
self::outException($event->getException());
}
return;
}
if(Validate::checkLevelType(LogLevel::VERBOSE, $application->getConsoleLoggingLevel()))
{
$backtrace_output = Utilities::getTraceString($event);
print(sprintf("[%s] [%s] %s %s" . PHP_EOL,
self::formatAppColor($application->getApplicationName()),
self::colorize($event),
$backtrace_output, $event->getMessage()
));
if($event->getException() !== null)
{
self::outException($event->getException());
}
return;
}
print(sprintf("[%s] [%s] %s" . PHP_EOL,
self::formatAppColor($application->getApplicationName()),
self::colorize($event),
$event->getMessage()
));
}
/**
* Formats the application name with a color for the console
*
* @param string $application_name The application name
* @return string The formatted application name
*/
private static function formatAppColor(string $application_name): string
{
if(!isset(self::$application_colors[$application_name]))
{
$colors = ConsoleColors::ALL;
try
{
$color = $colors[random_int(0, count($colors) - 1)];
}
catch (Exception $e)
{
throw new RuntimeException(sprintf('Unable to generate random color for application "%s"', $application_name), $e->getCode(), $e);
}
self::$application_colors[$application_name] = $color;
}
return self::color($application_name, self::$application_colors[$application_name]);
}
/**
* Applies a specified color to the given text, using ANSI escape sequences.
*
* @param string $text The text to apply the color to.
* @param ConsoleColors $color The ANSI color code to apply to the text.
* @return string The text with the specified color applied.
*/
private static function color(string $text, ConsoleColors $color): string
{
return "\033[" . $color->value . "m" . $text . "\033[0m";
}
/**
* Colorizes the log message based on the event level using ANSI escape sequences.
*
* @param Event $event The log event to colorize.
* @return string The colorized log message.
*/
private static function colorize(Event $event): string
{
$color = match($event->getLevel())
{
LogLevel::DEBUG => ConsoleColors::LIGHT_PURPLE,
LogLevel::VERBOSE => ConsoleColors::LIGHT_CYAN,
LogLevel::INFO => ConsoleColors::WHITE,
LogLevel::WARNING => ConsoleColors::YELLOW,
LogLevel::FATAL => ConsoleColors::RED,
LogLevel::ERROR => ConsoleColors::LIGHT_RED,
default => null,
};
if($color === null)
{
return Utilities::levelToString($event->getLevel());
}
return self::color(Utilities::levelToString($event->getLevel()), $color);
}
/**
* Retrieves the current timestamp as a formatted string.
*
* @return string The current timestamp.
*/
private static function getTimestamp(): string
{
$tick_time = (string)microtime(true);
if(!is_null(self::$largest_tick_length) && strlen($tick_time) > (int)self::$largest_tick_length)
{
self::$largest_tick_length = strlen($tick_time);
}
if(strlen($tick_time) < self::$largest_tick_length)
{
$tick_time = str_pad($tick_time, (strlen($tick_time) + (self::$largest_tick_length - strlen($tick_time))));
}
$fmt_tick = $tick_time;
if(self::$last_tick_time !== null)
{
$timeDiff = microtime(true) - self::$last_tick_time;
if ($timeDiff > 1.0)
{
$fmt_tick = self::color($tick_time, ConsoleColors::LIGHT_RED);
}
elseif ($timeDiff > 0.5)
{
$fmt_tick = self::color($tick_time, ConsoleColors::YELLOW);
}
}
self::$last_tick_time = $tick_time;
return $fmt_tick;
}
/**
* Prints information about the given exception, including the error message, error code,
* and stack trace.
*
* @param Throwable|null $exception The exception to print information about.
* @return void
*/
private static function outException(?Throwable $exception=null): void
{
if($exception === null)
{
return;
}
$trace_header = self::color($exception->getFile() . ':' . $exception->getLine(), ConsoleColors::PURPLE);
$trace_error = self::color('error: ', ConsoleColors::RED);
print($trace_header . ' ' . $trace_error . $exception->getMessage() . PHP_EOL);
print(sprintf('Error code: %s', $exception->getCode()) . PHP_EOL);
$trace = $exception->getTrace();
if(count($trace) > 1)
{
print('Stack Trace:' . PHP_EOL);
foreach($trace as $item)
{
if(isset($item['file']) && isset($item['line']))
{
print( ' - ' . self::color($item['file'], ConsoleColors::RED) . ':' . $item['line'] . PHP_EOL);
}
}
}
if($exception->getPrevious() !== null)
{
print('Previous Exception:' . PHP_EOL);
self::outException($exception->getPrevious());
}
}
}

View file

@ -0,0 +1,151 @@
<?php
namespace LogLib\Handlers;
use LogLib\Classes\FileLock;
use LogLib\Classes\Utilities;
use LogLib\Classes\Validate;
use LogLib\Enums\LogLevel;
use LogLib\Interfaces\LogHandlerInterface;
use LogLib\Objects\Application;
use LogLib\Objects\Event;
use RuntimeException;
use Throwable;
class FileLogging implements LogHandlerInterface
{
private static array $application_logs = [];
/**
* @inheritDoc
*/
public static function handle(Application $application, Event $event): void
{
if(!Validate::checkLevelType($event->getLevel(), $application->getFileLoggingLevel()))
{
return;
}
if(Validate::checkLevelType(LogLevel::DEBUG, $application->getConsoleLoggingLevel()))
{
$backtrace_output = Utilities::getTraceString($event, false);
$output = sprintf("[%s] [%s] [%s] %s %s" . PHP_EOL,
self::getTimestamp(), $application->getApplicationName(), $event->getLevel()->name, $backtrace_output, $event->getMessage()
);
}
else if(Validate::checkLevelType(LogLevel::VERBOSE, $application->getConsoleLoggingLevel()))
{
$backtrace_output = Utilities::getTraceString($event, false);
$output = sprintf("[%s] [%s] [%s] %s %s" . PHP_EOL, self::getTimestamp(), $application->getApplicationName(), $event->getLevel()->name, $backtrace_output, $event->getMessage());
}
else
{
$output = sprintf("[%s] [%s] [%s] %s" . PHP_EOL, self::getTimestamp(), $application->getApplicationName(), $event->getLevel()->name, $event->getMessage());
}
if($event->getException() !== null)
{
$output .= self::outException($event->getException());
}
self::getLogger($application)->append($output);
}
/**
* Retrieves the logger instance associated with the given application.
* If the logger does not exist, it initializes a new one and stores it.
*
* @param Application $application The application for which the logger is to be retrieved.
* @return FileLock The logger instance associated with the specified application.
*/
private static function getLogger(Application $application): FileLock
{
if(!isset(self::$application_logs[$application->getApplicationName()]))
{
self::$application_logs[$application->getApplicationName()] = new FileLock(self::getLogFile($application));
}
return self::$application_logs[$application->getApplicationName()];
}
/**
* Retrieves the log file path for the specified application.
*
* @param Application $application The application instance for which the log file is to be retrieved.
* @return string The full path of the log file.
*/
private static function getLogFile(Application $application): string
{
$logging_directory = $application->getFileLoggingDirectory();
if(!file_exists($logging_directory))
{
if(!mkdir($logging_directory))
{
throw new RuntimeException(sprintf("Cannot write to %s due to insufficient permissions", $logging_directory));
}
}
$logging_file = $logging_directory . DIRECTORY_SEPARATOR . Utilities::sanitizeFileName($application->getApplicationName()) . '-' . date('Y-m-d') . '.log';
if(!file_exists($logging_file) && !@touch($logging_file))
{
throw new RuntimeException(sprintf("Cannot write to %s due to insufficient permissions", $logging_file));
}
return $logging_file;
}
/**
* Retrieves the current timestamp formatted as "yd/m/y H:i".
*
* @return string The formatted current timestamp.
*/
private static function getTimestamp(): string
{
return date('yd/m/y H:i');
}
/**
* Generates a detailed string representation of a given Throwable object, including its message, code,
* file, line of occurrence, stack trace, and any previous exceptions.
*
* @param Throwable|null $exception The throwable object to process. If null, an empty string is returned.
* @return string A detailed string representation of the throwable object.
*/
private static function outException(?Throwable $exception=null): string
{
if($exception === null)
{
return '';
}
$output = '';
$trace_header = $exception->getFile() . ':' . $exception->getLine();
$trace_error = 'error: ';
$output .= $trace_header . ' ' . $trace_error . $exception->getMessage() . PHP_EOL;
$output .= sprintf('Error code: %s', $exception->getCode() . PHP_EOL);
$trace = $exception->getTrace();
if(count($trace) > 1)
{
$output .= 'Stack Trace:' . PHP_EOL;
foreach($trace as $item)
{
if(isset($item['file']) && isset($item['line']))
{
$output .= ' - ' . $item['file'] . ':' . $item['line'] . PHP_EOL;
}
}
}
if($exception->getPrevious() !== null)
{
$output .= 'Previous Exception:' . PHP_EOL;
$output .= self::outException($exception->getPrevious());
}
return $output;
}
}

View file

@ -0,0 +1,20 @@
<?php
namespace LogLib\Interfaces;
use LogLib\Exceptions\LoggingException;
use LogLib\Objects\Application;
use LogLib\Objects\Event;
interface LogHandlerInterface
{
/**
* Outputs the event details based on the given options.
*
* @param Application $application The options used to configure the output
* @param Event $event The event to be output
* @return void
* @throws LoggingException If an error occurs while handling the event
*/
public static function handle(Application $application, Event $event): void;
}

View file

@ -4,40 +4,48 @@
namespace LogLib; namespace LogLib;
use ErrorException;
use Exception;
use InvalidArgumentException; use InvalidArgumentException;
use LogLib\Abstracts\LevelType;
use LogLib\Classes\Console;
use LogLib\Classes\Utilities; use LogLib\Classes\Utilities;
use LogLib\Classes\Validate; use LogLib\Enums\LogLevel;
use LogLib\Objects\Application;
use LogLib\Objects\Event; use LogLib\Objects\Event;
use LogLib\Objects\Options;
use LogLib\Objects\RuntimeOptions;
use Throwable; use Throwable;
class Log class Log
{ {
/** /**
* @var Options[] * @var Application[]|null
*/ */
private static $Applications; private static $applications;
/**
* @var RuntimeOptions
*/
private static $RuntimeOptions;
/** /**
* Registers a new application logger * Registers a new application logger
* *
* @param Options $options The options for the application * @param Application $application The options for the application
* @param bool $overwrite
* @return bool * @return bool
*/ */
public static function register(Options $options): bool public static function register(Application $application, bool $overwrite=false): bool
{ {
if(self::isRegistered($options->getApplicationName())) if(self::isRegistered($application->getApplicationName()))
return false; {
if($overwrite)
{
self::$applications[$application->getApplicationName()] = $application;
return true;
}
self::$Applications[$options->getApplicationName()] = $options; return false;
}
if(self::$applications === null)
{
self::$applications = [];
}
self::$applications[$application->getApplicationName()] = $application;
return true; return true;
} }
@ -49,8 +57,15 @@
*/ */
public static function unregister(string $application): void public static function unregister(string $application): void
{ {
if(isset(self::$Applications[$application])) if(self::$applications === null)
unset(self::$Applications[$application]); {
return;
}
if(isset(self::$applications[$application]))
{
unset(self::$applications[$application]);
}
} }
/** /**
@ -61,64 +76,65 @@
*/ */
private static function isRegistered(string $application): bool private static function isRegistered(string $application): bool
{ {
return isset(self::$Applications[$application]); return isset(self::$applications[$application]);
} }
/** /**
* @param string $application * Retrieves the application options. If the application is not registered, it optionally creates and registers a new one.
* @return Options *
* @param string $application The name of the application.
* @param bool $create (Optional) Whether to create the application if it is not registered. Default is true.
* @return Application The options for the specified application.
*/ */
public static function getApplication(string $application): Options public static function getApplication(string $application, bool $create=true): Application
{ {
if(!self::isRegistered($application)) if(!self::isRegistered($application))
{
if(!$create)
{
throw new InvalidArgumentException("The application '$application' is not registered"); throw new InvalidArgumentException("The application '$application' is not registered");
}
return self::$Applications[$application]; self::register(new Application($application));
}
return self::$applications[$application];
} }
/** /**
* @param string $application_name The name of the application * Logs a message with a specified application name, level, optional message, and optional throwable.
* @return Options The options for the application *
*/ * @param string|null $application_name The name of the application
public static function getOptions(string $application_name): Options * @param LogLevel $level The level type of the log (default is LevelType::INFO)
{ * @param string|null $message The message of the log event
if(!self::isRegistered($application_name))
{
self::register(new Options($application_name));
}
return self::$Applications[$application_name];
}
/**
* @param string $application_name The name of the application
* @param string $level The level of the event
* @param string|null $message The message of the event
* @param Throwable|null $throwable The exception that was thrown, if any * @param Throwable|null $throwable The exception that was thrown, if any
* @return void * @return void
*/ */
private static function log(string $application_name, string $level=LevelType::Info, ?string $message=null, ?Throwable $throwable=null): void private static function log(?string $application_name, LogLevel $level=LogLevel::INFO, ?string $message=null, ?Throwable $throwable=null): void
{ {
$application = self::getOptions($application_name); $application = self::getApplication($application_name);
if(!Validate::checkLevelType($level, self::getRuntimeOptions()->getLogLevel())) if($message === null)
return; {
if($message == null)
throw new InvalidArgumentException('Message cannot be null'); throw new InvalidArgumentException('Message cannot be null');
if($level == null || !Validate::levelType($level)) }
throw new InvalidArgumentException('Invalid logging level');
$event = new Event(); $event = new Event($message, $level, $throwable);
$event->Level = $level;
$event->Message = $message;
$event->Exception = $throwable;
if($event->getBacktrace() == null) if($event->getBacktrace() === null)
{
$event->setBacktrace(Utilities::getBacktrace()); $event->setBacktrace(Utilities::getBacktrace());
}
if(self::getRuntimeOptions()->isConsoleOutput()) if($application->isConsoleLoggingEnabled())
Console::out($application, $event); {
$application->getConsoleLoggingHandler()::handle($application, $event);
}
if($application->isFileLoggingEnabled())
{
$application->getFileLoggingHandler()::handle($application, $event);
}
} }
/** /**
@ -128,7 +144,7 @@
*/ */
public static function info(string $application_name, string $message): void public static function info(string $application_name, string $message): void
{ {
self::log($application_name, LevelType::Info, $message); self::log($application_name, LogLevel::INFO, $message);
} }
/** /**
@ -138,7 +154,7 @@
*/ */
public static function verbose(string $application_name, string $message): void public static function verbose(string $application_name, string $message): void
{ {
self::log($application_name, LevelType::Verbose, $message); self::log($application_name, LogLevel::VERBOSE, $message);
} }
/** /**
@ -148,74 +164,65 @@
*/ */
public static function debug(string $application_name, string $message): void public static function debug(string $application_name, string $message): void
{ {
self::log($application_name, LevelType::Debug, $message); self::log($application_name, LogLevel::DEBUG, $message);
} }
/** /**
* @param string $application_name The name of the application * Logs a warning message.
* @param string $message The message of the event *
* @param Throwable|null $throwable The exception that was thrown, if any * @param string $application_name The name of the application.
* @param string $message The warning message to log.
* @param Throwable|null $throwable (Optional) The throwable object associated with the warning.
* @return void * @return void
*/ */
public static function warning(string $application_name, string $message, ?Throwable $throwable=null): void public static function warning(string $application_name, string $message, ?Throwable $throwable=null): void
{ {
self::log($application_name, LevelType::Warning, $message, $throwable); self::log($application_name, LogLevel::WARNING, $message, $throwable);
} }
/** /**
* @param string $application_name The name of the application * Logs an error message.
* @param string $message The message of the event *
* @param Throwable|null $throwable The exception that was thrown, if any * @param string $application_name The name of the application.
* @param string $message The error message.
* @param Throwable|null $throwable The optional throwable object associated with the error.
* @return void * @return void
*/ */
public static function error(string $application_name, string $message, ?Throwable $throwable=null): void public static function error(string $application_name, string $message, ?Throwable $throwable=null): void
{ {
self::log($application_name, LevelType::Error, $message, $throwable); self::log($application_name, LogLevel::ERROR, $message, $throwable);
} }
/** /**
* @param string $application_name The name of the application * Logs a fatal message.
* @param string $message The message of the event *
* @param Throwable|null $throwable The exception that was thrown, if any * @param string $application_name The name of the application.
* @param string $message The fatal message to log.
* @param Throwable|null $throwable (Optional) The throwable object associated with the fatal message.
* @return void * @return void
*/ */
public static function fatal(string $application_name, string $message, ?Throwable $throwable=null): void public static function fatal(string $application_name, string $message, ?Throwable $throwable=null): void
{ {
self::log($application_name, LevelType::Fatal, $message, $throwable); self::log($application_name, LogLevel::FATAL, $message, $throwable);
} }
/** /**
* Registers LogLib as a exception handler * Registers an exception handler that logs any uncaught exceptions as errors.
* *
* @return void * @return void
*/ */
public static function registerExceptionHandler(): void public static function registerExceptionHandler(): void
{ {
set_exception_handler(function(Throwable $throwable) { Runtime::registerExceptionHandler();
self::error('Runtime', $throwable->getMessage(), $throwable);
});
} }
/** /**
* Unregisters all applications * Unregisters the currently registered exception handler.
* *
* @return void * @return void
*/ */
public static function unregisterExceptionHandler(): void public static function unregisterExceptionHandler(): void
{ {
set_exception_handler(null); Runtime::unregisterExceptionHandler();
} }
/**
* @return RuntimeOptions
*/
public static function getRuntimeOptions(): RuntimeOptions
{
if(self::$RuntimeOptions == null)
{
self::$RuntimeOptions = new RuntimeOptions();
}
return self::$RuntimeOptions;
}
} }

86
src/LogLib/Logger.php Normal file
View file

@ -0,0 +1,86 @@
<?php
namespace LogLib;
use LogLib\Objects\Application;
use Throwable;
class Logger extends Application
{
/**
* @inheritDoc
*/
public function __construct(string $applicationName)
{
parent::__construct($applicationName);
Log::register($this, true);
}
/**
* Logs an informational message.
*
* @param string $message The message to log.
* @return void
*/
public function info(string $message): void
{
Log::info($this->getApplicationName(), $message);
}
/**
* Logs a verbose message with the application name.
*
* @param string $message The message to be logged.
* @return void
*/
public function verbose(string $message): void
{
Log::verbose($this->getApplicationName(), $message);
}
/**
* Logs a debug message.
*
* @param string $message The debug message to log.
* @return void
*/
public function debug(string $message): void
{
Log::debug($this->getApplicationName(), $message);
}
/**
* Logs a warning message with the application name.
*
* @param string $message The warning message to log.
* @return void
*/
public function warning(string $message): void
{
Log::warning($this->getApplicationName(), $message);
}
/**
* Logs an error message with an optional throwable instance.
*
* @param string $message The error message to be logged.
* @param Throwable|null $throwable An optional throwable instance to be logged along with the error message.
* @return void
*/
public function error(string $message, ?Throwable $throwable=null): void
{
Log::error($this->getApplicationName(), $message, $throwable);
}
/**
* Logs a fatal error message along with an optional throwable.
*
* @param string $message The fatal error message to log.
* @param Throwable|null $throwable Optional throwable associated with the fatal error.
* @return void
*/
public function fatal(string $message, ?Throwable $throwable=null): void
{
Log::fatal($this->getApplicationName(), $message, $throwable);
}
}

View file

@ -0,0 +1,248 @@
<?php
namespace LogLib\Objects;
use InvalidArgumentException;
use LogLib\Classes\Utilities;
use LogLib\Enums\LogLevel;
use LogLib\Handlers\ConsoleLogging;
use LogLib\Handlers\FileLogging;
use LogLib\Interfaces\LogHandlerInterface;
class Application
{
private string $applicationName;
private bool $handleExceptions;
private bool $consoleLoggingEnabled;
private string $consoleLoggingHandler;
private LogLevel $consoleLoggingLevel;
private bool $fileLoggingEnabled;
private string $fileLoggingHandler;
private LogLevel $fileLoggingLevel;
private string $fileLoggingDirectory;
/**
* Constructor for initializing the application logging and exception handling.
*
* @param string $applicationName The name of the application.
* @return void
*/
public function __construct(string $applicationName)
{
$this->applicationName = $applicationName;
$this->handleExceptions = true;
$this->consoleLoggingEnabled = Utilities::getConsoleLoggingEnabled();
$this->consoleLoggingHandler = ConsoleLogging::class;
$this->consoleLoggingLevel = Utilities::getConsoleLoggingLevel();
$this->fileLoggingEnabled = Utilities::getFileLoggingEnabled();
$this->fileLoggingHandler = FileLogging::class;
$this->fileLoggingLevel = Utilities::getFileLoggingLevel();
$this->fileLoggingDirectory = Utilities::getFileLoggingDirectory();
}
/**
*
* @return string
*/
public function getApplicationName(): string
{
return $this->applicationName;
}
/**
* Checks if exceptions are being handled.
*
* @return bool
*/
public function isHandleExceptions(): bool
{
return $this->handleExceptions;
}
/**
* Sets the flag to handle exceptions.
*
* @param bool $handleExceptions Flag indicating whether to handle exceptions.
* @return void
*/
public function setHandleExceptions(bool $handleExceptions): void
{
$this->handleExceptions = $handleExceptions;
}
/**
* Checks if console logging is enabled.
*
* @return bool True if console logging is enabled, false otherwise.
*/
public function isConsoleLoggingEnabled(): bool
{
return $this->consoleLoggingEnabled;
}
/**
*
* @param bool $consoleLoggingEnabled Indicates whether console logging is enabled.
* @return void
*/
public function setConsoleLoggingEnabled(bool $consoleLoggingEnabled): void
{
$this->consoleLoggingEnabled = $consoleLoggingEnabled;
}
/**
* Gets the current console logging handler.
*
* @return LogHandlerInterface|string The console logging handler currently set.
*/
public function getConsoleLoggingHandler(): LogHandlerInterface|string
{
return new $this->consoleLoggingHandler;
}
/**
* Sets the console logging handler.
*
* @param LogHandlerInterface|string $consoleLoggingHandler The console logging handler to set.
* @return void
*/
public function setConsoleLoggingHandler(LogHandlerInterface|string $consoleLoggingHandler): void
{
if($consoleLoggingHandler instanceof LogHandlerInterface)
{
$this->consoleLoggingHandler = get_class($consoleLoggingHandler);
return;
}
if(!class_exists($consoleLoggingHandler))
{
throw new InvalidArgumentException("The class $consoleLoggingHandler does not exist.");
}
if(!in_array(LogHandlerInterface::class, class_implements($consoleLoggingHandler)))
{
throw new InvalidArgumentException("The class $consoleLoggingHandler does not implement LogHandlerInterface.");
}
$this->consoleLoggingHandler = $consoleLoggingHandler;
}
/**
* Gets the current console logging level.
*
* @return LogLevel The current logging level for the console.
*/
public function getConsoleLoggingLevel(): LogLevel
{
return $this->consoleLoggingLevel;
}
/**
* Sets the logging level for console output.
*
* @param LogLevel $consoleLoggingLevel The logging level to set for console output.
* @return void
*/
public function setConsoleLoggingLevel(LogLevel $consoleLoggingLevel): void
{
$this->consoleLoggingLevel = $consoleLoggingLevel;
}
/**
* Checks if file logging is enabled.
*
* @return bool Returns true if file logging is enabled, false otherwise.
*/
public function isFileLoggingEnabled(): bool
{
return $this->fileLoggingEnabled;
}
/**
* Enables or disables file logging.
*
* @param bool $fileLoggingEnabled True to enable file logging, false to disable.
* @return void
*/
public function setFileLoggingEnabled(bool $fileLoggingEnabled): void
{
$this->fileLoggingEnabled = $fileLoggingEnabled;
}
/**
* @return LogHandlerInterface|string
*/
public function getFileLoggingHandler(): LogHandlerInterface|string
{
return $this->fileLoggingHandler;
}
/**
* Sets the file logging handler.
*
* @param string $fileLoggingHandler The file logging handler to be set.
* @return void
*/
public function setFileLoggingHandler(string $fileLoggingHandler): void
{
if($fileLoggingHandler instanceof LogHandlerInterface)
{
$this->consoleLoggingHandler = get_class($fileLoggingHandler);
return;
}
if(!class_exists($fileLoggingHandler))
{
throw new InvalidArgumentException("The class $fileLoggingHandler does not exist.");
}
if(!in_array(LogHandlerInterface::class, class_implements($fileLoggingHandler)))
{
throw new InvalidArgumentException("The class $fileLoggingHandler does not implement LogHandlerInterface.");
}
$this->consoleLoggingHandler = $fileLoggingHandler;
}
/**
* Retrieves the logging level for file outputs.
*
* @return LogLevel The current file logging level.
*/
public function getFileLoggingLevel(): LogLevel
{
return $this->fileLoggingLevel;
}
/**
* Sets the logging level for file-based logging.
*
* @param LogLevel $fileLoggingLevel The logging level to set for file logging.
* @return void
*/
public function setFileLoggingLevel(LogLevel $fileLoggingLevel): void
{
$this->fileLoggingLevel = $fileLoggingLevel;
}
/**
* Get the directory path for file logging.
*
* @return string The directory path where log files are stored.
*/
public function getFileLoggingDirectory(): string
{
return $this->fileLoggingDirectory;
}
/**
* Sets the directory for file logging.
*
* @param string $fileLoggingDirectory The path to the directory where log files will be stored.
* @return void
*/
public function setFileLoggingDirectory(string $fileLoggingDirectory): void
{
$this->fileLoggingDirectory = $fileLoggingDirectory;
}
}

View file

@ -1,174 +0,0 @@
<?php
/** @noinspection PhpMissingFieldTypeInspection */
namespace LogLib\Objects;
class Backtrace
{
/**
* The function name of the backtrace
*
* @var string|null
* @property_name function
*/
private $Function;
/**
* The line number of the backtrace
*
* @var int|null
* @property_name line
*/
private $Line;
/**
* The file name of the backtrace
*
* @var string|null
* @property_name file
*/
private $File;
/**
* The class name, if any, of the backtrace
*
* @var string|null
* @property_name class
*/
private $Class;
/**
* The current call type. If a method call, "->" is returned.
* If a static method call, "::" is returned. If a function call,
* nothing is returned.
*
* @see CallType
* @var string|null
* @property_name type
*/
private $Type;
/**
* If inside a function, this lists the functions arguments. If inside
* an included file, this lists the included file name(s).
*
* @var array|null
* @property_name args
*/
private $Args;
/**
* Public Constructor
*
* @param array|null $backtrace
*/
public function __construct(?array $backtrace=null)
{
if($backtrace === null)
return;
$this->Function = $backtrace['function'] ?? null;
$this->Line = $backtrace['line'] ?? null;
$this->File = $backtrace['file'] ?? null;
$this->Class = $backtrace['class'] ?? null;
$this->Type = $backtrace['type'] ?? null;
$this->Args = $backtrace['args'] ?? null;
}
/**
* @return string|null
*/
public function getFunction(): ?string
{
return $this->Function;
}
/**
* @param string|null $Function
*/
public function setFunction(?string $Function): void
{
$this->Function = $Function;
}
/**
* @return int|null
*/
public function getLine(): ?int
{
return $this->Line;
}
/**
* @param int|null $Line
*/
public function setLine(?int $Line): void
{
$this->Line = $Line;
}
/**
* @return string|null
*/
public function getFile(): ?string
{
return $this->File;
}
/**
* @param string|null $File
*/
public function setFile(?string $File): void
{
$this->File = $File;
}
/**
* @return string|null
*/
public function getClass(): ?string
{
return $this->Class;
}
/**
* @param string|null $Class
*/
public function setClass(?string $Class): void
{
$this->Class = $Class;
}
/**
* @return string|null
*/
public function getType(): ?string
{
return $this->Type;
}
/**
* @param string|null $Type
*/
public function setType(?string $Type): void
{
$this->Type = $Type;
}
/**
* @return array|null
*/
public function getArgs(): ?array
{
return $this->Args;
}
/**
* @param array|null $Args
*/
public function setArgs(?array $Args): void
{
$this->Args = $Args;
}
}

View file

@ -4,54 +4,77 @@
namespace LogLib\Objects; namespace LogLib\Objects;
use LogLib\Abstracts\LevelType;
use LogLib\Classes\Utilities; use LogLib\Classes\Utilities;
use LogLib\Enums\LogLevel;
use Throwable; use Throwable;
class Event class Event
{ {
/** /**
* The level of the event * @var LogLevel
*
* @see LevelType
* @var string
* @property_name level
*/ */
public $Level; private $level;
/** /**
* The Unix Timestamp of when the event was created * @var array|null
*
* @var string
*/ */
private $Timestamp; private $backtrace;
/** /**
* An array of backtraces, if any, that were created when the event was created
*
* @var Backtrace[]|null
*/
private $Backtrace;
/**
* The exception that was thrown, if any
*
* @var Throwable|null * @var Throwable|null
*/ */
public $Exception; private $exception;
/** /**
* The message of the event
*
* @var string * @var string
*/ */
public $Message; private $message;
/** /**
* Public Constructor * Event constructor.
*
* @param string $message
* @param LogLevel $level
* @param Throwable|null $exception
* @param array|null $backtrace
*/ */
public function __construct() public function __construct(string $message, LogLevel $level, ?Throwable $exception=null, ?array $backtrace=null)
{ {
$this->Timestamp = date('c'); $this->message = $message;
$this->level = $level;
$this->exception = $exception;
$this->backtrace = $backtrace;
}
/**
* Returns the level of the event
*
* @return LogLevel
* @see LogLevel
*/
public function getLevel(): LogLevel
{
return $this->level;
}
/**
* Returns the message of the event
*
* @return string
*/
public function getMessage(): string
{
return $this->message;
}
/**
* Optional. Returns the exception to the event
*
* @return Throwable|null
*/
public function getException(): ?Throwable
{
return $this->exception;
} }
/** /**
@ -62,31 +85,26 @@
*/ */
public function setException(Throwable $e): void public function setException(Throwable $e): void
{ {
$this->Exception = Utilities::exceptionToArray($e); $this->exception = Utilities::exceptionToArray($e);
}
/**
* @return string
*/
public function getTimestamp(): string
{
return $this->Timestamp;
} }
/** /**
* Returns the backtrace of the event
*
* @return array|null * @return array|null
*/ */
public function getBacktrace(): ?array public function getBacktrace(): ?array
{ {
return $this->Backtrace; return $this->backtrace;
} }
/** /**
* @param array|null $Backtrace * Sets the backtrace of the event
*
* @param array|null $backtrace
*/ */
public function setBacktrace(?array $Backtrace): void public function setBacktrace(?array $backtrace): void
{ {
$this->Backtrace = $Backtrace; $this->backtrace = $backtrace;
} }
} }

View file

@ -1,36 +0,0 @@
<?php
/** @noinspection PhpMissingFieldTypeInspection */
namespace LogLib\Objects;
class Options
{
/**
* The name of the application
*
* @var string
* @property_name application_name
*/
private $ApplicationName;
/**
* Options constructor.
*/
public function __construct(string $application_name)
{
$this->ApplicationName = $application_name;
}
/**
* Returns the name of the Application
*
* @return string
*/
public function getApplicationName(): string
{
return $this->ApplicationName;
}
}

View file

@ -1,119 +0,0 @@
<?php
/** @noinspection PhpMissingFieldTypeInspection */
namespace LogLib\Objects;
use InvalidArgumentException;
use LogLib\Abstracts\LevelType;
use LogLib\Classes\Utilities;
class RuntimeOptions
{
/**
* Indicates if the console output is enabled
*
* @var bool
* @property_name console_output
*/
private $ConsoleOutput;
/**
* Indicates if ANSI colors should be used in the console output
*
* @var bool
* @property_name display_ansi
*/
private $DisplayAnsi;
/**
* Indicates if LogLib should handle uncaught exceptions
*
* @var bool
* @property_name handle_exceptions
*/
private $HandleExceptions;
/**
* The current log level
*
* @var int
* @see LevelType
*/
private $LogLevel;
/**
* Public Constructor
*/
public function __construct()
{
$this->ConsoleOutput = Utilities::runningInCli();
$this->DisplayAnsi = Utilities::getDisplayAnsi();
$this->HandleExceptions = true;
$this->LogLevel = Utilities::getLogLevel();
}
/**
* @return bool
*/
public function isConsoleOutput(): bool
{
return $this->ConsoleOutput;
}
/**
* @param bool $ConsoleOutput
*/
public function setConsoleOutput(bool $ConsoleOutput): void
{
$this->ConsoleOutput = $ConsoleOutput;
}
/**
* @return bool
*/
public function isDisplayAnsi(): bool
{
return $this->DisplayAnsi;
}
/**
* @param bool $DisplayAnsi
*/
public function setDisplayAnsi(bool $DisplayAnsi): void
{
$this->DisplayAnsi = $DisplayAnsi;
}
/**
* @return bool
*/
public function isHandleExceptions(): bool
{
return $this->HandleExceptions;
}
/**
* @param bool $HandleExceptions
*/
public function setHandleExceptions(bool $HandleExceptions): void
{
$this->HandleExceptions = $HandleExceptions;
}
/**
* @return int
*/
public function getLogLevel(): int
{
return $this->LogLevel;
}
/**
* @param int $LogLevel
*/
public function setLogLevel(int $LogLevel): void
{
$this->LogLevel = $LogLevel;
}
}

124
src/LogLib/Runtime.php Normal file
View file

@ -0,0 +1,124 @@
<?php
namespace LogLib;
use ErrorException;
use Exception;
use Throwable;
class Runtime
{
/**
* Registers an exception handler that logs any uncaught exceptions as errors.
*
* @return void
*/
public static function registerExceptionHandler(): void
{
set_exception_handler([__CLASS__, 'exceptionHandler']);
set_error_handler([__CLASS__, 'errorHandler']);
register_shutdown_function([__CLASS__, 'shutdownHandler']);
}
/**
* Handles uncaught exceptions by logging them with a fatal error level.
*
* @param Throwable $throwable The exception or error that was thrown.
* @return void
*/
public static function exceptionHandler(Throwable $throwable): void
{
try
{
Log::Fatal('Runtime', $throwable->getMessage(), $throwable);
}
catch(Exception)
{
return;
}
}
/**
* Handles PHP errors by converting them to exceptions and logging appropriately.
*
* @param int $errno The level of the error raised.
* @param string $errstr The error message.
* @param string $errfile The filename that the error was raised in.
* @param int $errline The line number the error was raised at.
* @return bool True to prevent PHP's internal error handler from being invoked.
*/
public static function errorHandler(int $errno, string $errstr, string $errfile = '', int $errline = 0): bool
{
try
{
// Convert error to exception for consistent handling
$exception = new ErrorException($errstr, 0, $errno, $errfile, $errline);
// Handle different error types
switch ($errno)
{
case E_ERROR:
case E_PARSE:
case E_CORE_ERROR:
case E_COMPILE_ERROR:
case E_USER_ERROR:
Log::error('Runtime', $errstr, $exception);
break;
case E_USER_DEPRECATED:
case E_DEPRECATED:
case E_USER_NOTICE:
case E_NOTICE:
case E_USER_WARNING:
case E_WARNING:
default:
Log::warning('Runtime', $errstr, $exception);
break;
}
}
catch(Exception)
{
return false;
}
// Return true to prevent PHP's internal error handler
return true;
}
/**
* Handles script shutdown by checking for any fatal errors and logging them.
*
* This method is designed to be registered with the `register_shutdown_function`,
* and it inspects the last error that occurred using `error_get_last`. If a fatal
* error is detected, it logs the error details.
*
* @return void
*/
public static function shutdownHandler(): void
{
$error = error_get_last();
if ($error !== null && in_array($error['type'], [E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR, E_USER_ERROR]))
{
try
{
$exception = new ErrorException($error['message'], 0, $error['type'], $error['file'], $error['line']);
Log::error('Fatal Error', $error['message'], $exception);
}
catch(Exception)
{
return;
}
}
}
/**
* Unregisters the currently registered exception handler.
*
* @return void
*/
public static function unregisterExceptionHandler(): void
{
set_exception_handler(null);
}
}

74
tests/LogLib/LogTest.php Normal file
View file

@ -0,0 +1,74 @@
<?php
namespace LogLib;
use PHPUnit\Framework\TestCase;
class LogTest extends TestCase
{
public function testDebug()
{
}
public function testInfo()
{
}
public function testGetRuntimeOptions()
{
}
public function testGetOptions()
{
}
public function testGetApplication()
{
}
public function testUnregisterExceptionHandler()
{
}
public function testUnregister()
{
}
public function testWarning()
{
}
public function testError()
{
}
public function testFatal()
{
}
public function testRegisterExceptionHandler()
{
}
public function testVerbose()
{
}
public function testRegister()
{
}
}

View file

@ -1,10 +1,9 @@
<?php <?php
use LogLib\Abstracts\LevelType; use LogLib\Log;
use LogLib\Log; use LogLib\Objects\Options;
use LogLib\Objects\Options;
require('ncc'); require('ncc');
import('net.nosial.loglib', 'latest'); import('net.nosial.loglib', 'latest');
$options = new Options('net.nosial.optslib'); $options = new Options('net.nosial.optslib');

View file

@ -1,6 +1,5 @@
<?php <?php
use LogLib\Abstracts\LevelType;
use LogLib\Log; use LogLib\Log;
use LogLib\Objects\Options; use LogLib\Objects\Options;
@ -16,3 +15,60 @@
Log::warning('net.nosial.optslib', 'This is a warning message'); Log::warning('net.nosial.optslib', 'This is a warning message');
Log::error('net.nosial.optslib', 'This is an error message'); Log::error('net.nosial.optslib', 'This is an error message');
Log::fatal('net.nosial.optslib', 'This is a fatal message'); Log::fatal('net.nosial.optslib', 'This is a fatal message');
class test
{
public function testLogging(): void
{
Log::debug('net.nosial.optslib', 'This is a debug message');
Log::verbose('net.nosial.optslib', 'This is a verbose message');
Log::info('net.nosial.optslib', 'This is an info message');
Log::warning('net.nosial.optslib', 'This is a warning message');
Log::error('net.nosial.optslib', 'This is an error message');
Log::fatal('net.nosial.optslib', 'This is a fatal message');
}
}
$test = new test();
$test->testLogging();
eval('\LogLib\Log::debug(\'net.nosial.optslib\', \'This is a debug message\');');
eval('\LogLib\Log::verbose(\'net.nosial.optslib\', \'This is a verbose message\');');
$callable = static function()
{
Log::info('net.nosial.optslib', 'This is an info message');
Log::warning('net.nosial.optslib', 'This is a warning message');
Log::error('net.nosial.optslib', 'This is an error message');
Log::fatal('net.nosial.optslib', 'This is a fatal message');
};
$callable();
class test2
{
public function testEval(): void
{
eval('\LogLib\Log::debug(\'net.nosial.optslib\', \'This is a debug message\');');
eval('\LogLib\Log::verbose(\'net.nosial.optslib\', \'This is a verbose message\');');
}
public function testCallable(): void
{
$b = static function()
{
Log::info('net.nosial.optslib', 'This is an info message');
Log::warning('net.nosial.optslib', 'This is a warning message');
Log::error('net.nosial.optslib', 'This is an error message');
Log::fatal('net.nosial.optslib', 'This is a fatal message');
};
$b();
}
}
$test2 = new test2();
$test2->testEval();
$test2->testCallable();