AuditLog
1. Bevezetés
Annak érdekében, hogy a rendszerben történt események rögzítésre kerüljenek, ezért a változást eredményező metódusokat célszerű lehet auditlogolni.
Ennek segítségével a későbbiekben sokkal könnyebb hibákat feltárni, megérteni, hogy ki és mit csinált a rendszerben, továbbá akár különböző statisztikák is kinyerhetőek az auditlogból.
2. Milyen adatokat tárolunk
Adott AuditLog esemény rögzítésekor a következő adatokat tároljuk el:
- esemény neve
- sikeres volt-e az esemény
- ki hajtotta végre
- milyen típusú felhasználó
- milyen csatornán
- milyen adatokkal
- mi volt a válasz a kérésre
- mikor kezdődött az esemény végrehajtása
- mikor végződött az esemény végrehajtása
- mikor keletkezett az auditlog esemény
- mikor tároltuk el az auditlog eseményt
3. Adott metódus auditlogolásának implementálása
Nézzük meg, hogyan tudunk egy adott metódushoz auditlogolást bevezetni, a felhasználó regisztráció példáján keresztül.
A ClientRegistrationService-ben található, a regisztrációs logikát tartalmazó registration metódus, ezért ezt a metódust szeretnénk auditlogolni.
3.1 AuditLog handler létrehozása
Elősször is egy handler osztályt kell létrehozni, ami az AuditLogEventHandler interfészt implementálja.
A felhasználó regisztráció esetén ez lesz a ClientRegistrationEventHandler osztály.
Ebben az osztályban implementáljuk az auditlogolni kivánt adatokat. Továbbá, ebben az osztályban definiáljuk az adott AuditLog esemény típusát is.
AuditLog esemény típusának definiálása:
public static final String CLIENT_REGISTRATION = "CLIENT_REGISTRATION";
@Override
public String getAuditLogEventType() {
return CLIENT_REGISTRATION;
}
3.1.1 Adatok logolása
3.1.1.1 Sikeres végrehajtás esetén
A sikeres lefutás esetén auditlogolandó adatokat a getSuccessMessage metódusban tudjuk testreszabni.
3.1.1.2 Sikertelen végrehajtás esetén
A sikertelen lefutás esetén auditlogolandó adatokat a getFailureMessage metódusban tudjuk testreszabni.
3.1.2 Request/Response adatok testreszabása az AuditLog-ban
3.1.2.1 Request/Response adatok relevanciája
Előfordulhat hogy a request és/vagy a response nem tartalmaz az auditlogolás szempontjából releváns információt, így a bennük szereplő adatokat nem is szeretnénk auditlogolni.
Ez szintén a fent említett getSuccessMessage, illetve getFailureMessage metódusokban szabható testre.
Példák
Arra az esetre amikor nem akarjuk a response adatokat auditlogolni, jó példa a ResetPasswordSendEmailEventHandler.
Arra pedig amikor sem a request, sem pedig a response adatokra sincs szükségünk jó példa a ChangePasswordEventHandler.
3.1.2.2 Szenzitív adatok logolása
A regisztráció során megadott jelszót nem szeretnénk tárolni az auditlog rekordok között, ezért lehetőség van arra, hogy customizáljuk milyen mezők kerüljenek a request/response-ból perzisztálásra egy map segítségével.
private Map<String, Object> getRequestAsMap(AuditLogEventHandlerDTO auditLogEventHandlerDTO) {
ClientRegistrationRequest request = ObjectUtil.getObjectByClass(auditLogEventHandlerDTO.getMethodRequestArgs(), ClientRegistrationRequest.class);
auditLogEventHandlerDTO.getAuditLogEventMessage().setRequesterId(request.getEmail());
return Map.of("clientRegistrationRequest", getRequestParameters(request));
}
private Map<String, Object> getRequestParameters(ClientRegistrationRequest request) {
Map<String, Object> parameters = new HashMap<>();
parameters.put("name", request.getName());
parameters.put("email", request.getEmail());
return parameters;
}
Azonban lesznek olyan metódusok, ahol nincsennek szenzítiv adatok, ezért nyugodtan lerakhatjuk az egész request/response tartalmát, aminek a gyors megoldása a következő ojbectMapper segítségével.
private Map<String, Object> getRequestAsMap(AuditLogEventHandlerDTO auditLogEventHandlerDTO) {
ClientRegistrationRequest request = ObjectUtil.getObjectByClass(auditLogEventHandlerDTO.getMethodRequestArgs(), ClientRegistrationRequest.class);
auditLogEventHandlerDTO.getAuditLogEventMessage().setRequesterId(request.getEmail());
return Map.of("clientRegistrationRequest", objectMapper.convertValue(request, Map.class));
}
Fontos
Figyeljünk arra, hogy az objectMapper nem használható primitív típusokra, ezért ne használjuk olyan metódusok auditlogolása esetén, ahol a metódus bemenő paramétere csak valamilyen primitív típusú adat.
3.2 AuditLog annotáció használata a metóduson
A handler elkészülte után a következő lépés az auditlogolandó metódus felannotálása az AuditLog annotációval.
Az AuditLog annotációnál az eventType definiálása kötelező, mert ezzel azonosítjuk, hogy mi az adott AuditLog típusa és így tudjuk kötni a megfelelő handler logikát is az annotációhoz.