Update of "log"
Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.
Structured and hierarchical logging with :token-parametric API
- State: experimental
- Category: logging
- Features: journaling, structured, hierarchical
- Backend: SQLite, JSON,
- Signature: hybrid, parametric
ł implements a logging API and SQLite/JSON storage backend.
- Its purpose is storing structured log data and retaining log event hierarchies.
- Implements a hybrid and terse function interface.
- Accepts plain string messages, Ruby-style
:tokencategorizers and placeholders, and array data.
- Implicitly captures and maps PHP errors, unhandled exceptions, and most importantly
Unlike other PHP logging frameworks it's not primarily a text/line-oriented message dump.
Invocations can be along the lines of:
ł(':warn', ':wikiauth', "User doesn't have permission", $pageObj, ':vars', $_SESSION);
All the fun is in the
:token literals, and passing arrays or objects.
Database scheme, primary fields
All columns in the database scheme are primary fields. Any extra data/values end up in the
|timestamp||REAL||Timestamp with microseconds.|
|timestr||TEXT||ISO DateTime string. In GMT/UTC of course.|
|pri||INT||Priority number (0…7).|
|prio||TEXT||Priority string (emerg…info)|
|source||TEXT||log | sys | lang | excpt | assert|
|section||TEXT||Application structure / module / part / section.|
|version||TEXT||Meta data from source code.|
|message||TEXT||Primary log event message string.|
|doc||TEXT||Extra documentation / long message / href.|
|backtrace||JSON||Array of |
|code||TEXT||Extracted code context (3 lines).|
|vars||JSON||Main $vars array.|
|context||JSON||Additional / user-defined fields.|
|:debug||`7`||Low-level debug events.|
|:info||`6`||Process flow infos etc.|
|:notice, :note||`5`||Lowest priority language notices.|
|:error, :err||`3`||PHP or system error.|
|:critical, :crit||`2`||This can't be good.|
|:alert, :alrt||`1`||Turn on the bat light.|
|:emergency, :emerg||`0`||Someone call the president.|
|Source / generator|
|:log||Application origin, normal/manual log calls.|
|:sys||System-level events and errno codes.|
|:lang||Language errors, warnings, notices, etc.|
|Any database column / primary field name can be represented as `:token`. It's pretty much only useful to use :vars however to map the following array parameter.|
|Besides the prio levels, there are a few more shortened aliases to common fields. Among them :documentation for :doc, :priority for :prio, :help for :doc, :msg for :message, :language for lang, :exc for :exception, :app as :log generator source alias, :trace and :stack as aliases for :backtrace, and :env for :server|
|:server||Inserts $_SERVER array into `context`.|
|:file||Uncovers `file` and `line` from backtrace.|
|:version||Reads out meta data (file/scm version, and section) from script comments.|
|:code||Inserts 3 lines of `code` context.|
|:p||Tries to deduce log event hierarchy from prior calls, sections, and backtraces. (Not yet implemented.)|
:token name can be used freely to classify and group your application flow. They'll be used as section names.
You obviously need a readily available
log.db SQLite store. Best keep it
DOCUMENT_ROOT-relative, so it's easy to declare on instantiation:
ł::$db = "$_SERVER[DOCUMENT_ROOT]/config/log.db"; ł::$app = "YourAppID";
You can of course manually load the library. Most autoloaders would already load it implicitly because of the class reference. (Even PSR-x ones, and they'd even be accidentially correct for once with case-sensitive Unicode lookups here).
While you ought to use
:section names for logging calls, you can also override/update the default throughout your application flow with:
ł::$section = "forum";
Or likewise adapt properties of the global logger group
$logger instance in
ł() takes a list of default :token options and injectors. You should adapt it directly to enable further features.
$logger = new ł(":log", ':backtrace', ':file', ':version', ':p');
For instance would engage full event population for all/manual logging calls.
Yes, you could actually run multiple logger groups, or pass around the
$loggerhandler. Don't do it. (Kind of works, but wasn't intended to.)
The alternative branches and
store()implementations are meant to be patched in. It makes zero sense to DI / runtime-bind them. You're only going to use one approach in practice, so don't complicate instantiation.
Take care with leaking information through logs. It's tempting to include a backtrace for all calls. But in particular authorization-sensitive variable states may only be useful for concrete debugging tasks, not in all log events. (That's in important consideration for any logging scheme; but moreso for logStruck due to its much simpler API and utilization.)
Notes / Rationale
So, this is all either genius, or completely bonkers.
logStruck is decidedly different to most other logging libraries in PHP. It doesn't follow PSR-3 (lowest common denominator) and historic line/text-oriented logging. In particular it tries to avoid parsing+reformatting backends or making structured/JSON logging an afterthought.
Entirely intended as userland runtime. Mostly suitable for wee projects. It's primary use case is application-level debugging and auditability.
The function name ł isn't completely settled on. (Maybe a bit too much novelty strive.)
Extensibility of the database scheme is easily done, but not planned for.
Alternative logging backends are best implemented in branches.
- (In the time and age of GitHub forkeritis anyway.)
- It doesn't seem senseful to impose a configuration-centric instantiation.
- However making
$ł->dbjust a Callable would be trivial.
- The API and JSON-logging design is specifically meant to avoid MonoLog-style message formatting / parsing / filter chains. Events are structured from the start, shouldn't be downconverted to suit textdump interfaces.
Inspired by structlog, cabin, journald, graylog, PEAR log even, and with logstash/fluentd in mind.
The fancy ':token' signature is used in place of named params and constant literals in PHP.
Currently just inserts one-dimensional events. The API mapping is too crude still for spatial message/section/prio collections.
Log events are only associated to a primary group event as of now. The
:pfilter will allow to regroup events automatically from context information.
- It'll scan the backtrace for matching prior code paths, and obviously the used :sections to relate events to another.
Alternatively control it manually:
$p = ł("first"); ł("second", "p:$p");
A logger call returns the new event id. And
p:is just the field that keeps the handle.
The alternative JSON file-append store just keeps event-local ids/groups=1/parent ids. This needs an insertion transaction or trigger for reconverting into a SQLite store. (Rather simple.)
To mix application and server-level logging an injector for Apaches
UNIQUE_IDmight make sense. Still would require log post-processing to turn it into a sequential event id. Or alternatively stash all entries with a UUID.
Investigate whether logstash (supposedly works with multiline/mutate), fluentd or graylog2 make suitable targets. Neither seems to provide incremental log ids on submission. Each requires post processing on pushed events, or incremental json/sqlite imports.
Most obiously this implementation hinges an a proper GUI. It's kinda pointless to make another ETL toy, if it wasn't for the absence of hierarchical support in existing log viewers. While a web viewer would suffice, only an actual desktop tool would benefit from the intended JSON → SQLite storage.