# Συλλέξτε σφάλματα με το ErrorBuilder, καταγράψτε τα με το ERROR() και κρατήστε ίχνος ελέγχου με το AUDIT()

> Απαντήστε με πολλά σφάλματα ταυτόχρονα με το ErrorBuilder, την απάντηση SUCCESS(), πώς το ERROR() καταγράφει τις αποτυχίες και πώς το AUDIT() προσθέτει σε ένα αρχείο καταγραφής ποιος έκανε τι.

Framework: total5
Product: Total.js 5 (total5@0.0.19)
Language: el
Content-SHA256: 087f86aebb7a02a898dffd2626a777606274e8eaebf946f227f863e6ef1367fa
Snapshot: c0700e321579725c5cf4f5ace1ad7d2866d1b4f93c24a63c0b41b6b7998e976c
Sources: total5-installed@total5@0.0.19
Review: not-recorded

- Το **`ErrorBuilder`** συλλέγει σφάλματα. Το `push(message, [path], [index])` προσθέτει ένα (το `path` είναι το πεδίο στο οποίο ανήκει), το `push(404)` προσθέτει το κείμενο ενός κωδικού κατάστασης και ορίζει την κατάσταση της απάντησης, το `length` τα μετράει, το `output()` τα επιστρέφει ως πίνακα `{ error, path, index }` και το `toString()` ενώνει τα μηνύματα. Το `$.invalid(builder)` απαντά με αυτόν τον πίνακα και την κατάσταση του builder (400 εκτός αν προστέθηκε κωδικός κατάστασης).
- Το **`SUCCESS(value)`** επιστρέφει το αντικείμενο μιας επιτυχημένης λειτουργίας: `{ success: true, value: value }`.
- Το **`ERROR(name)`** επιστρέφει ένα callback για APIs με `function(err)`: όταν λάβει σφάλμα το καταγράφει με το όνομα `name`, και δεν κάνει τίποτα αν το σφάλμα είναι κενό. Το `ERROR()` είναι το ίδιο callback χωρίς όνομα. Ένα καταγεγραμμένο σφάλμα εκτυπώνεται στην κονσόλα, μετριέται στο `F.stats.error` και τα δέκα τελευταία κρατούνται στο `F.errors` ως `{ error, name, date }`.
- Το **`AUDIT($, message, type)`** προσθέτει μια γραμμή JSON στο `logs/audit.log` με το ποιος και πού (`ip`, `ua`, `url` και ο χρήστης όταν υπάρχει), το `message`, το `type`, τα `params`, `query` και `model` του αιτήματος στο `data`, το όνομα της εφαρμογής στο `app` και την ώρα στο `dtcreated`. Το `AUDIT('name', $, message, type)` γράφει αντί γι' αυτό στο `logs/name.log`.

```js controllers/reports.js
exports.install = function() {
	ROUTE('GET /builder/', builder);
	ROUTE('GET /builder404/', builder404);
	ROUTE('GET /success/', success);
	ROUTE('GET /error/', error);
	ROUTE('GET /audit/', audit);
	ROUTE('GET /audit-read/{name}/', auditread);
};

function builder($) {
	const errors = new ErrorBuilder();
	errors.push('Name is required', 'name');
	errors.push('Age is too low', 'age', 2);
	$.invalid(errors);
}

function builder404($) {
	const errors = new ErrorBuilder();
	errors.push(404);
	$.invalid(errors);
}

function success($) {
	$.json(SUCCESS('saved'));
}

function error($) {
	const before = F.stats.error;
	ERROR('mypart')('Something broke');
	ERROR('mypart')(null);
	ERROR()('Without a name');
	$.json({ counted: F.stats.error - before, last: F.errors.slice(-2).map(n => ({ error: n.error, name: n.name || null, date: n.date instanceof Date || typeof n.date === 'string' })) });
}

function audit($) {
	AUDIT($, 'Opened the report', 'info');
	AUDIT('security', $, 'Checked the access');
	$.json({ ok: true });
}

function auditread($) {
	setTimeout(function() {
		const lines = require('fs').readFileSync(PATH.logs($.params.name + '.log'), 'utf8').trim().split('\n');
		$.json(lines.map(n => JSON.parse(n)));
	}, 300);
}
```
