Update of "log"
Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Artifact ID: | 6f70a4b48f2329534aa6cb57503aab196bccbb7a |
---|---|
Page Name: | log |
Date: | 2015-01-11 07:23:38 |
Original User: | mario |
Mimetype: | text/x-markdown |
Parent: | 7a1846629b10f2c4dbf02633cb13dc933952fb6f (diff) |
Next | 77fac470f6161a858450b2889ac543ba5fbb6724 |
Structured and hierarchical logging with :token-parametric API
- State: experimental
- Category: logging
- Features: journaling, structured, hierarchical
- Backend: SQLite, JSON,
fluentd,logstash - Signature: hybrid, parametric
logStruck Ε
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
:token
categorizers and placeholders, and array data. - Implicitly captures and maps PHP errors, unhandled exceptions, and most importantly
assert()
ions.
Unlike other PHP logging frameworks it's not primarily a text/line-oriented message dump.
Quick example
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 context
array.
<style> table.dbstruct { width: 75%; margin-left: 3%; } table.dbstruct td { vertical-align: top; } table.dbstruct tr:nth-child(2n) { background: #efefef; } </style>
i | PRIM | Where i is the primary index, g the event group, and p the parent reference. Which allows displaying event group hierarchies. |
g | INT | |
p | INT | |
timestamp | REAL | Timestamp with microseconds. |
timestr | TEXT | ISO DateTime string. In GMT/UTC of course. |
host | TEXT | Hostname. |
pri | INT | Priority number (0β¦7). |
prio | TEXT | Priority string (emergβ¦info) |
source | TEXT | log | sys | lang | excpt | assert |
errno | INT | 0β¦32767 |
app | TEXT | AppName.php |
section | TEXT | Application structure / module / part / section. |
file | TEXT | path/file.php |
line | INT | 125 |
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 :backtrace |
code | TEXT | Extracted code context (3 lines). |
vars | JSON | Main $vars[] array. |
context | JSON | Additional / user-defined fields. |
Priority levels | ||
---|---|---|
:debug | 7 | Low-level debug events. |
:info | 6 | Process flow infos etc. |
:notice, :note | 5 | Lowest priority language notices. |
:warning, :warn | 4 | Warnings. |
: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. | |
:exception | Langauge/runtime exceptions. | |
:assert | Assert() warnings. | |
Field names | ||
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. | ||
Aliases | ||
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 | ||
Injector calls | ||
:backtrace | Populates backtrace. | |
: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.) |
Any other :token
name can be used freely to classify and group your application flow. They'll be used as section names.
Setup
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 Ε()->section=..
.
Default injectors
The $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.
Avoid complexity
Yes, you could actually run multiple logger groups, or pass around the
$logger
handler. 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
$Ε->db
just 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.
ToDo
Log events are only associated to a primary group event as of now. The
:p
filter 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_ID
might 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.