Audit Extension provides a system-wide controller that can be added into any of your or 3rd party models. Once Added audit will keep track of all record changes. Changes will be stored into a separate model and will be sufficient to answer the following questions at any later time.
Here is a simple feature comparison between Agile Audit and some other audit implementations
Agile Audit | Laravel | ||
---|---|---|---|
Supports logs? | Any table, any fields, any persistence | SQL, specific fields | |
Clear old records | no | yes | |
Exclude fields | yes | yes | |
Cutsom actions | yes | yes | |
Requested/Reactive | yes | no | |
Multi-model logs | yes | no | |
How do add into model | add(), relies on hooks | trait | |
User message | yes | yes | |
Whitelist | yes | no | |
Undo event | yes | no | |
Replay event | yes | no | |
Admin UI | yes | no | |
Apply action to others | yes | no | |
Convent to PHPUnit | yes | no |
Notice: please report to us if you notice any inaccuracy. The table above depicts anly basic functionality.
Majority of enerprise application will require some sort of audit logging. This extensions covers a wide range of logging requirements.
When compared to traditional audit-log implementations, Agile Audit Extension offers a significant number of advantages that goes beyond logging.
Any model in your system regardless of your database choice can be audited. You can enlist specific models by adding controller manually or perform a system-wide audit.
use \atk4\audit;
class User extends \atk4\data\Model {
public $table = 'user';
function init() {
parent::init();
$this->addField('name');
$this->addField('email');
$this->addField('password');
// The following lines enable full audit:
$this->add(new audit\Controller(
new audit\model\AuditLog()
));
}
}
You have a choice. You may use model\AuditLog
that will automatically store information about the enviroment. Alternatively you can supply your own model, which you can extend from model\AuditLog
.
More often you would want all of your models to be automatically audited. The next snippet demonstrates how to implement numerous things:
Here is the relevant usage code. I start by defining my own Audit model:
class Audit extends \atk4\audit\model\Audit
{
public $no_audit = true;
public $exclude_field_values = ['password'];
public $exclude_field_types = ['encrypted'];
function getExtraData()
{
$extra = [];
// If we have App context, record currently logged user
if (isset($this->app->user)) {
$extra['user'] = $this->app->user;
}
return $extra;
}
}
To continue I need to add a handler into persistence which will automatically attach itself to all initialized models:
$audit = new \atk4\audit\Controller(new Audit());
$db->addHook('afterAdd', function($owner, $element) {
if ($element instanceof \atk4\data\Model) {
if (isset($element->no_audit) && $element->no_audit) {
// Whitelisting this model, won't audit
break;
}
$element->add($audit); // re-using same object to save resources
}
});
You can refer to full class documentation if you want to further extend Audit behaviours.
Agile Data incorporates rich volume of logic that allow you to make a lot of decision across the system when even a smallest change is requested. For example assuming you have the following structure:
addFields(['total_net', 'total_vat', 'total_gross'], ['type' => 'money']);
hasMany('Line')
addField('qty', ['type => 'int'])
addField('vat_rate', ['type' => 'float'])
addFields(['price', 'vat', 'net', 'gross'], ['type' => 'money']);
Your afterSave
hooks will automatically recalculate and update Invoice
whenever you change the Line
. Additionally, changing qty
will trigger change in vat
, net
and gross
.
Looking at he following code:
$m = new Invoice($db);
$m->load(1);
$m['qty']++;
$m->save();
Only a single field falls into "requested" change, which is qty
. The original value and a new value will be stored in JSON:
{"qty": [5, 6]}
However due to hooks, many other values have also been updated. For the Line the "reactive" changes are:
{"net": [50, 60], "vat": [11.5, 13.8], "gross": [51.5, 63.8]}
Then you have some "reactive" changes for the Invoice
model too:
{"total_net": [100, 110], "total_vat": [23.0, 25.3], "total_gross": [123.0, 135.3]}
The default way for Audit Log is to store only "reactive" changes, however you can enable storing of both "requested" and "reactivte":
$audit = new \atk4\audit\Controller([
new Audit(),
['requested_log' => true, 'reactive_log' => true, 'link' => true]
);
The option for nested
will also associate changes inside Invoice
with the requested changes of Line
. If you need to customise settings on per-model basis, you should create individual controllers.
Audit Extension uses array persistence to prepare values for storage inside JSON. If you need to tweak how values are stored exactly, you shourd refer to documentation on typecasting.
System is storing using business-domain field names. If "net" has an actual field of "sql_net", then audit will store "net". Additionally
Agile Audit Extension perform a strict type auditing which can be quite useful for automation. Certain actions can be "undone" or "replayed" (Redo).
Those action can be performed on the Audit
model after you load the record:
$audit->load(20);
$audit->replay();
Assuming that audit log with ID=20 corresponds to the qty
modifications as I was explaining above, the replay will perform the following:
Line
qty
qty
Line
and Invoice
Similarly you can also call undo()
, which will:
new
/ old
values for original Audit event and all related onesBoth Undo
and Replay
functionality can bypass the verification steps or can actually enforce Reactive
changes to be used. Those modes are less safe but if that's what you want you can try it.
Finally, Replay feature can also override id
of the original model. In this scenario changes will be re-applied to a different record.
Undo can be offered to a user as an option. Because implementation of Undo
especially in transaction-supporting database is pretty safe, you can execute multiple Undo
actions effectively allowing you to walk between revisions of your persistence.
$audit = new Audit($db);
$audit->addCondition('user_id', $this->app->user->id);
$audit->addCondition('date', '>', $unroll_to_date);
$audit->setOrder('id desc');
$audit->each('undo');
Applying Replay
on the range of entries makes a pretty effective multi-record update technique.
$invoices = $client->ref('Invoice'); // references multiple invoices
$invoices->add($audit);
// record action
$invoices->loadAny();
$invoices->save( $new_data );
$audit->last_action->applyOnOthers($invoices);
Finally, replay can be used in creating unit tests. If you have enabled Audit Log for your application and have already performed some actions, you can generate PHPUnit
-compatible code through Admin Audit Page.
Audit Extension comes with Agile UI based page that contains a handy management console where you can browse all the recent events, convert them into unit-tests, undo or re-apply some of those. Additionally selecting an event will also show you all the "Reactive" actions that have been done.
Audit Extension is currently in Beta. You need to contact us if you wish to get early access.