Skip to content
This repository has been archived by the owner on Aug 13, 2020. It is now read-only.

Commit

Permalink
Create service for reporting event errors
Browse files Browse the repository at this point in the history
  • Loading branch information
amckenzie committed Dec 11, 2019
1 parent e134671 commit 282ea69
Show file tree
Hide file tree
Showing 9 changed files with 339 additions and 9 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ on [Keep a CHANGELOG](http://keepachangelog.com/). This project adheres to
## [Unreleased]
- Update framework-api to 4.2.0
- Add table in system database 'event_error_log' for storing errors with events that failed to process
- New SystemErrorService for reporting system errors

## [6.4.0] - 2019-11-12
### Changed
Expand Down
53 changes: 53 additions & 0 deletions framework-system/framework-system-errors/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>framework-system</artifactId>
<groupId>uk.gov.justice.services</groupId>
<version>6.5.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

<artifactId>framework-system-errors</artifactId>

<dependencies>
<dependency>
<groupId>javax</groupId>
<artifactId>javaee-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>uk.gov.justice.services</groupId>
<artifactId>framework-system-persistence</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>uk.gov.justice.framework-api</groupId>
<artifactId>framework-api-system-errors</artifactId>
<version>${framework-api.version}</version>
</dependency>
<dependency>
<groupId>uk.gov.justice.framework-api</groupId>
<artifactId>framework-api-core</artifactId>
</dependency>
<dependency>
<groupId>uk.gov.justice.services</groupId>
<artifactId>framework-utilities</artifactId>
<version>${project.version}</version>
</dependency>

<!-- Test dependencies -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package uk.gov.justice.services.framework.system.errors;

import static java.lang.String.format;
import static javax.transaction.Transactional.TxType.NOT_SUPPORTED;

import uk.gov.justice.services.common.util.UtcClock;
import uk.gov.justice.services.framework.system.errors.SystemErrorService;
import uk.gov.justice.services.framework.utilities.exceptions.StackTraceProvider;
import uk.gov.justice.services.messaging.JsonEnvelope;
import uk.gov.justice.services.messaging.Metadata;
import uk.gov.justice.services.system.domain.EventError;
import uk.gov.justice.services.system.persistence.EventErrorLogRepository;

import java.time.Clock;
import java.util.Optional;
import java.util.UUID;

import javax.inject.Inject;
import javax.transaction.Transactional;

public class DefaultSystemErrorService implements SystemErrorService {

private static final Long MISSING_EVENT_NUMBER = -1L;
private static final String NO_COMMENT = "";

@Inject
private EventErrorLogRepository eventErrorLogRepository;

@Inject
private StackTraceProvider stackTraceProvider;

@Inject
private UtcClock clock;

@Override
@Transactional(NOT_SUPPORTED)
public void reportError(
final String messageId,
final String componentName,
final JsonEnvelope jsonEnvelope,
final Throwable exception) {

final Metadata metadata = jsonEnvelope.metadata();
final UUID eventId = metadata.id();
final Optional<Long> eventNumber = metadata.eventNumber();

final EventError eventError = new EventError(
eventId,
eventNumber.orElse(MISSING_EVENT_NUMBER),
componentName,
messageId,
metadata.asJsonObject().toString(),
jsonEnvelope.payload().toString(),
exception.getMessage(),
stackTraceProvider.getStackTrace(exception),
clock.now(),
getComment(eventNumber)
);

eventErrorLogRepository.save(eventError);
}

private String getComment(final Optional<Long> eventNumberOptional) {

if (eventNumberOptional.isPresent()) {
return NO_COMMENT;
}

return format("Event number is missing from event. Setting to %d instead", MISSING_EVENT_NUMBER);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
package uk.gov.justice.services.framework.system.errors;

import static java.util.Optional.empty;
import static java.util.Optional.of;
import static java.util.UUID.randomUUID;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.nullValue;
import static org.junit.Assert.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import uk.gov.justice.services.common.util.UtcClock;
import uk.gov.justice.services.framework.utilities.exceptions.StackTraceProvider;
import uk.gov.justice.services.messaging.JsonEnvelope;
import uk.gov.justice.services.messaging.Metadata;
import uk.gov.justice.services.system.domain.EventError;
import uk.gov.justice.services.system.persistence.EventErrorLogRepository;

import java.time.ZonedDateTime;
import java.util.UUID;

import javax.json.JsonObject;
import javax.json.JsonValue;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;

@RunWith(MockitoJUnitRunner.class)
public class DefaultSystemErrorServiceTest {

private static final String AN_EMPTY_STRING = "";

@Mock
private EventErrorLogRepository eventErrorLogRepository;

@Mock
private StackTraceProvider stackTraceProvider;

@Mock
private UtcClock clock;

@InjectMocks
private DefaultSystemErrorService defaultSystemErrorService;

@Captor
private ArgumentCaptor<EventError> eventErrorCaptor;

@Test
public void shouldCreateEventErrorAndPersist() throws Exception {

final String messageId = "message id";
final String componentName = "EVENT_LISTENER";
final String errorMessage = "Help help we're all going to die";
final Throwable exception = new NullPointerException(errorMessage);
final Long eventNumber = 239874L;
final UUID eventId = randomUUID();
final String metadataJson = "{metadata: stuff}";
final String payloadJson = "{payload: stuff}";
final String stackTrace = "the stacktrace";
final ZonedDateTime erroredAt = new UtcClock().now();

final JsonEnvelope jsonEnvelope = mock(JsonEnvelope.class);
final Metadata metadata = mock(Metadata.class);
final JsonObject metadataJsonObject = mock(JsonObject.class);
final JsonValue payload = mock(JsonValue.class);

when(jsonEnvelope.metadata()).thenReturn(metadata);
when(metadata.id()).thenReturn(eventId);
when(metadata.eventNumber()).thenReturn(of(eventNumber));

when(metadata.asJsonObject()).thenReturn(metadataJsonObject);
when(jsonEnvelope.payload()).thenReturn(payload);

when(metadataJsonObject.toString()).thenReturn(metadataJson);
when(payload.toString()).thenReturn(payloadJson);
when(stackTraceProvider.getStackTrace(exception)).thenReturn(stackTrace);

when(clock.now()).thenReturn(erroredAt);

defaultSystemErrorService.reportError(
messageId,
componentName,
jsonEnvelope,
exception
);

verify(eventErrorLogRepository).save(eventErrorCaptor.capture());

final EventError eventError = eventErrorCaptor.getValue();

assertThat(eventError.getMessageId(), is(messageId));
assertThat(eventError.getComponent(), is(componentName));
assertThat(eventError.getErrorMessage(), is(errorMessage));
assertThat(eventError.getEventNumber(), is(eventNumber));
assertThat(eventError.getEventId(), is(eventId));
assertThat(eventError.getMetadata(), is(metadataJson));
assertThat(eventError.getPayload(), is(payloadJson));
assertThat(eventError.getStacktrace(), is(stackTrace));
assertThat(eventError.getErroredAt(), is(erroredAt));
assertThat(eventError.getComments(), is(AN_EMPTY_STRING));
}

@Test
public void shouldHandleMissingEventNumber() throws Exception {

final String messageId = "message id";
final String componentName = "EVENT_LISTENER";
final String errorMessage = "Help help we're all going to die";
final Throwable exception = new NullPointerException(errorMessage);
final UUID eventId = randomUUID();
final String metadataJson = "{metadata: stuff}";
final String payloadJson = "{payload: stuff}";
final String stackTrace = "the stacktrace";
final ZonedDateTime erroredAt = new UtcClock().now();

final JsonEnvelope jsonEnvelope = mock(JsonEnvelope.class);
final Metadata metadata = mock(Metadata.class);
final JsonObject metadataJsonObject = mock(JsonObject.class);
final JsonValue payload = mock(JsonValue.class);

when(jsonEnvelope.metadata()).thenReturn(metadata);
when(metadata.id()).thenReturn(eventId);
when(metadata.eventNumber()).thenReturn(empty());

when(metadata.asJsonObject()).thenReturn(metadataJsonObject);
when(jsonEnvelope.payload()).thenReturn(payload);

when(metadataJsonObject.toString()).thenReturn(metadataJson);
when(payload.toString()).thenReturn(payloadJson);
when(stackTraceProvider.getStackTrace(exception)).thenReturn(stackTrace);

when(clock.now()).thenReturn(erroredAt);

defaultSystemErrorService.reportError(
messageId,
componentName,
jsonEnvelope,
exception
);

verify(eventErrorLogRepository).save(eventErrorCaptor.capture());

final EventError eventError = eventErrorCaptor.getValue();

assertThat(eventError.getMessageId(), is(messageId));
assertThat(eventError.getComponent(), is(componentName));
assertThat(eventError.getErrorMessage(), is(errorMessage));
assertThat(eventError.getEventNumber(), is(-1L));
assertThat(eventError.getEventId(), is(eventId));
assertThat(eventError.getMetadata(), is(metadataJson));
assertThat(eventError.getPayload(), is(payloadJson));
assertThat(eventError.getStacktrace(), is(stackTrace));
assertThat(eventError.getErroredAt(), is(erroredAt));
assertThat(eventError.getComments(), is("Event number is missing from event. Setting to -1 instead"));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<databaseChangeLog xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog
http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.3.xsd">

<changeSet
id="framework-system-005"
author="TechPod"
logicalFilePath="005-add-comment-column-to-event-error-log-table.xml">

<addColumn tableName="event_error_log">
<column name="comments" type="TEXT">
<constraints nullable="true"/>
</column>
</addColumn>

<rollback>
<dropColumn tableName="event_error_log" columnName="comments"/>
</rollback>
</changeSet>
</databaseChangeLog>
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ public class EventError {
private final String errorMessage;
private final String stacktrace;
private final ZonedDateTime erroredAt;
private final String comments;

public EventError(
final UUID eventId,
Expand All @@ -25,7 +26,8 @@ public EventError(
final String payload,
final String errorMessage,
final String stacktrace,
final ZonedDateTime erroredAt) {
final ZonedDateTime erroredAt,
final String comments) {
this.eventId = eventId;
this.eventNumber = eventNumber;
this.component = component;
Expand All @@ -35,6 +37,7 @@ public EventError(
this.errorMessage = errorMessage;
this.stacktrace = stacktrace;
this.erroredAt = erroredAt;
this.comments = comments;
}

public UUID getEventId() {
Expand Down Expand Up @@ -73,6 +76,10 @@ public ZonedDateTime getErroredAt() {
return erroredAt;
}

public String getComments() {
return comments;
}

@Override
public boolean equals(final Object o) {
if (this == o) return true;
Expand All @@ -86,12 +93,13 @@ public boolean equals(final Object o) {
Objects.equals(payload, that.payload) &&
Objects.equals(errorMessage, that.errorMessage) &&
Objects.equals(stacktrace, that.stacktrace) &&
Objects.equals(erroredAt, that.erroredAt);
Objects.equals(erroredAt, that.erroredAt) &&
Objects.equals(comments, that.comments);
}

@Override
public int hashCode() {
return Objects.hash(eventId, eventNumber, component, messageId, metadata, payload, errorMessage, stacktrace, erroredAt);
return Objects.hash(eventId, eventNumber, component, messageId, metadata, payload, errorMessage, stacktrace, erroredAt, comments);
}

@Override
Expand All @@ -106,6 +114,7 @@ public String toString() {
", errorMessage='" + errorMessage + '\'' +
", stacktrace='" + stacktrace + '\'' +
", erroredAt=" + erroredAt +
", comments='" + comments + '\'' +
'}';
}
}
Loading

0 comments on commit 282ea69

Please sign in to comment.