Kihagyás

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.

@AuditLog(eventType = ClientRegistrationEventHandler.CLIENT_REGISTRATION, requesterType = RequesterType.CLIENT)
@Transactional
public void registration(ClientRegistrationRequest registrationRequest) {
...
}