<h2> Structured and hierarchical logging with <kbd>:token</kbd>-parametric API </h2>

 * <small> State: ***experimental*** </small>
 * <small> Category: logging </small>
 * <small> Features: journaling, structured, hierarchical </small>
 * <small> Backend: SQLite, <del>JSON</del>, <del>fluentd</del> </small>
 * <small> Signature: hybrid, parametric </small>

**logStruck <kbd>`ł`</kbd>** implements a logging API and SQLite/JSON storage backend.

 * It's purpose is storing *structured* log data, and it tries to retain log event hierarchies.
 * Implements a hybrid and terse function interface.
 * And accepts plain string messages, Ruby-style `:token` categorizers and placeholders, and expects array structures.
 * Implicitly captures and maps PHP errors, unhandled exceptions, and most importantly *`assert()`ions*.

Unlike other PHP logging frameworks it's not a primarily text/line-oriented message store.


## Quick example

Basic invocations are 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 go into the `context` array.

<style>
 table.dbstruct {
   width: 65%;
   margin-left: 5%;
 }
 table.dbstruct tr:nth-child(2n) {
   background: #efefef;
 }
</style>
<table class=dbstruct>
<tr><td> <kbd>i</kbd>  </td>  <td>PRIM</td>  <td rowspan=3> `i` is the primary index,<br> `g` the event group,<br> and `p` the parent reference. <img width=514 height=93 alt="log
<tr><td> <kbd>g</kbd>  </td>  <td>INT</td>            </tr>
<tr><td> <kbd>p</kbd>  </td>  <td>INT</td>            </tr>
<tr><td> <kbd>timestamp</kbd>   </td>  <td>REAL</td> <td>Timestamp with
<tr><td> <td>TEXT</td> <td>ISO DateTime
<tr><td> <kbd>host</kbd>   </td>  <td>TEXT</td>
<tr><td> <kbd>pri</kbd>    </td>  <td>INT</td> <td>Priority number
<tr><td> <td>TEXT</td> <td>Priority string
<tr><td> <kbd>source</kbd> </td>  <td>TEXT</td>
<tr><td> <kbd>errno</kbd>  </td>  <td>INT</td>
<tr><td> <kbd>app</kbd>    </td>  <td>TEXT</td>
<tr><td> <td>TEXT</td> <td>Application structure / module / part /
<tr><td> <kbd>file</kbd>   </td>  <td>TEXT</td>
<tr><td> <kbd>line</kbd>   </td>  <td>INT</td>
<tr><td> <td>TEXT</td> <td>Meta data from source
<tr><td> <td>TEXT</td> <td>Primary log event message
<tr><td> <kbd>doc</kbd>    </td>  <td>TEXT</td> <td>Extra documentation / long message /
<tr><td> <td>JSON</td> <td>Array of
<tr><td> <kbd>code</kbd>   </td>  <td>TEXT</td> <td>Extracted code context (3
<tr><td> <kbd>vars</kbd>   </td>  <td>JSON</td> <td>Main $vars[]
<tr><td> <td>JSON</td> <td>Additional / user-defined
<table>


## Flexible parametric API

The chief invocation method is <kbd>ł()</kbd>. On the outset it's a procedural function, thus available globally. Behind the scenes it keeps a primary logger group. Alternatively it can be invoked via <kbd>ł::<em style=color:#630>option</em>_<em

For plain old log messages it's as simple to use as:

     ł("A thing happened.");

Usually you also want to convey a <u>priority</u> however:

     ł("Warnful warning", ':warn');

The <kbd>:token</kbd> attributes are the most important concept in logStruck. They simulate Ruby-style symbols. In PHP they need to be enquoted as strings however.

Besides priority levels, an *important* use is classifying an <u>application section</u>. Anything that isn't a reserved keyword :token will simply be assumed to refer an application module:

     ł(':auth', "Authorization error", ':notice');

Make up memorable designators to categorize your log messages to your application structure.

An obvious benefit of the :token syntax is that it allows <u>freely ordered</u> logging parameter lists.
These are all equivalent:

     ł(':warn', ':db', "Database error");
     ł(':db', ':warn', "Database error");
     ł(':warn', "Database error", ':db');

You'd probably want to settle on one of these. But it occasionally helps readability to be flexible, in particular when passing functional :tokens or arrays/lists.


#### Array data

As mentioned, this logger API isn't meant for just string data. You often want to convey <u>context data</u>, and additional attributes. It's often as simple as just attaching an array to the parameter list:

     ł(':debug', "Front controller state", $RequestVars);

Now those would end up in the `context` database field. To retain them in the `vars` column, you have two options:

     ł(':debug', "Front controller state", ['vars' => $RequestVars]);

Or the more fancy <kbd>:vars</kbd> array designator:

     ł(':debug', "Front controller state", ':vars', ⃕$RequestVars);

You might also use this for other fields like `:message` or `:code` or `:doc`, as long as the following parameter is an array.


#### Data mapping with `"field: value"`

Plain strings usually end up in the `message` field. But the structured logging database has more <u>fields</u>, each with specific purposes. You can easily populate them with the key:value syntax.

     ł("File reading error", "errno: EACCESS");

Here `errno` is also an integer field, which will be converted afterwards.

A more interesting field to take care of is the <u>`doc`</u> column.

     ł("Cache directory locked", "doc: ?wiki=SetCachePerm");

Nowadays logs are often consumed by machines rather than humans. For some projects you may wish to augment the often coarse and technical messsage summary with a human-readable description for non-coders. (This is usually an afterthought, if at all implemented or manageable in log processors/viewers).

While you could use the `doc:` field for a long prosaic documentation, this would of course stuff the database too much. Instead prefer hyperlinks, or references. A `"?tktid=12345"` or `"See setup.txt on chmod cache"` are helpful minimums too.

All of *primary fields* can alternatively and also be set using the key:value scheme:

     ł('section: auth', "Auth warning");
     ł('source: sys', "Exec failure", "errno: $retval", [$cmd]);
     ł("message: Log message");
     ł("Regex failed", "code: $rx", ':error', ':iniparser');

And fields that aren't primary log event columns/fields, will end up in the `context` database array.

     ł("Special needs logging", "var1: $var1", "method: $callback");

This is basically equivalent to using a `["key"=>"value", ...]` list. Again, prefer what's more readable in whatever context.


#### Injectors

Now if there wasn't enough flexibility already, <kbd>:tokens</kbd> can also refer to data source functions.

You can augment log events with a <kbd>:backtrace</kbd> most of the time:

     ł(':warn', "How did we get here?", ':backtrace');

The placeholder token will be substituted by an array, before being pushed to the log store.

Likewise you can interpolate some common vars:

     ł(':debug', "Front Controller startup", ':server');

Or extract pretty much all available meta data:

     ł(':debug', "Debug by logging", ':backtrace', ':file', ':code', ':version');

The built-in error / exception / assert handlers do this automatically for example, to different degrees.


#### Hybrid <kbd>ł::<em style=color:#630>option</em>_<em

The :token scheme is pretty neat, but certainly not to everyones liking, and sometimes less readable than plain boring method calls. Therefore the `ł` function and `ł` class go hand in hand. Instead of listing tokens as arguments, you can just compact them into a virutal method name:

     ł::debug_auth("Authorization failed", $UserObject);

You can even freely mix in injector callbacks and one array designator there:

     ł::warn_db_backtrace_file_vars("DB error", $stmt);

Again, you're the programmer. Make sound choices on a case-by-case basis. Don't be clingy with same-old same-old.


## All the <kbd>:tokens</kbd>!

You're free to choose any `:section` name for grouping log events. Well, as long as they're not matching one of the reserved token keywords:


<table class=dbstruct style="width:75%">

<tr><th colspan=3>Priority
<td>`7`</td> <td>Low-level debug
<td>`6`</td> <td>Process flow infos
<tr><td><kbd>:notice</kbd>, <td>`5`</td> <td>Lowest priority language
<tr><td><kbd>:warning</kbd>, <td>`4`</td>
<tr><td><kbd>:error</kbd>, <td>`3`</td> <td>PHP or system
<tr><td><kbd>:critical</kbd>, <td>`2`</td> <td>This can't be
<tr><td><kbd>:alert</kbd>, <td>`1`</td> <td>Turn on the bat
<tr><td><kbd>:emergency</kbd>, <td>`0`</td> <td>Someone call the

<tr><th colspan=3>Source /
<td></td> <td>Application origin, normal/manual log
<td></td> <td>System-level events and errno
<td></td> <td>Language errors, warnings, notices,
<td></td> <td>Langauge/runtime
<td></td> <td>`Assert()`

<tr><th colspan=3>Field
<tr><td colspan=3>Any database column / primary field name can be represented as `:token`. It's pretty much only useful to use <kbd>:vars</kbd> however to map the following array

<tr><th colspan=3>Injector
<td></td> <td>Populates
<td></td> <td>Inserts $_SERVER array into
<td></td> <td>Uncovers `file` and `line` from
<td></td> <td>Reads out meta data (file/scm version, and section) from script
<td></td> <td>Inserts 3 lines of `code`
<td></td> <td>Tries to deduce log event hierarchy from prior calls, sections, and backtraces. (Not yet

</table>


### Notes / Rationale

 * So, this is either genius, or completely bonkers.
 * The function name <kbd>ł</kbd> isn't completely settled on. (Maybe a bit too much novelty strive.)
 * Extensibility of the database scheme is not planned for. (But easily done.)