Skip to content

Commit 5c6e650

Browse files
committed
added interceptor to write task name and id to MDC logging context
1 parent cddbcc2 commit 5c6e650

File tree

10 files changed

+171
-54
lines changed

10 files changed

+171
-54
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# Changelog
22

3+
## v1.5.5
4+
5+
- Added MdcTriggerInterceptor
6+
37
## v1.5.4 - (2025-01-14)
48

59
- adjusted trigger cols that the UI does not break

core/src/main/java/org/sterl/spring/persistent_tasks/api/TriggerKey.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,16 @@
1010
import lombok.Builder;
1111
import lombok.Data;
1212
import lombok.NoArgsConstructor;
13+
import lombok.ToString;
1314

1415
/**
1516
* Unique key of a trigger during it's execution. But it after that the same key
1617
* can be added if needed. Ensures that only one trigger with the same key
1718
* is currently scheduled for execution.
1819
*/
1920
@Data
20-
@Builder(toBuilder = true)
21+
@ToString
22+
@Builder
2123
@NoArgsConstructor
2224
@AllArgsConstructor
2325
public class TriggerKey implements Serializable {
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package org.sterl.spring.persistent_tasks.exception;
2+
3+
import lombok.Getter;
4+
5+
public class SpringPersistentTaskException extends RuntimeException {
6+
private static final long serialVersionUID = 1L;
7+
@Getter
8+
protected final Object state;
9+
10+
public SpringPersistentTaskException(String message, Object state, Throwable cause) {
11+
super(message, cause);
12+
this.state = state;
13+
}
14+
15+
public SpringPersistentTaskException(String message, Object state) {
16+
super(message);
17+
this.state = state;
18+
}
19+
}

core/src/main/java/org/sterl/spring/persistent_tasks/trigger/component/EditTriggerComponent.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import org.sterl.spring.persistent_tasks.trigger.event.TriggerAddedEvent;
1919
import org.sterl.spring.persistent_tasks.trigger.event.TriggerCanceledEvent;
2020
import org.sterl.spring.persistent_tasks.trigger.event.TriggerFailedEvent;
21+
import org.sterl.spring.persistent_tasks.trigger.event.TriggerRunningEvent;
2122
import org.sterl.spring.persistent_tasks.trigger.event.TriggerSuccessEvent;
2223
import org.sterl.spring.persistent_tasks.trigger.model.TriggerEntity;
2324
import org.sterl.spring.persistent_tasks.trigger.repository.TriggerRepository;
@@ -40,9 +41,9 @@ public Optional<TriggerEntity> completeTaskWithSuccess(TriggerKey key, Serializa
4041

4142
result.ifPresent(t -> {
4243
t.complete(null);
44+
log.debug("{} set to status={}", key, t.getData().getStatus());
4345
publisher.publishEvent(new TriggerSuccessEvent(
4446
t.getId(), t.copyData(), state));
45-
log.debug("Setting {} to status={}", key, t.getData().getStatus());
4647
triggerRepository.delete(t);
4748
});
4849
return result;
@@ -146,4 +147,10 @@ public int markTriggersAsRunning(Collection<TriggerKey> keys, String runOn) {
146147
return triggerRepository.markTriggersAsRunning(keys, runOn,
147148
OffsetDateTime.now(), TriggerStatus.RUNNING);
148149
}
150+
151+
public void triggerIsNowRunning(TriggerEntity trigger, Serializable state) {
152+
if (!trigger.isRunning()) trigger.runOn(trigger.getRunningOn());
153+
publisher.publishEvent(new TriggerRunningEvent(
154+
trigger.getId(), trigger.copyData(), state, trigger.getRunningOn()));
155+
}
149156
}
Lines changed: 14 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,14 @@
11
package org.sterl.spring.persistent_tasks.trigger.component;
22

3-
import java.io.Serializable;
43
import java.time.OffsetDateTime;
54
import java.util.Optional;
65

7-
import org.springframework.context.ApplicationEventPublisher;
86
import org.springframework.lang.Nullable;
97
import org.springframework.stereotype.Component;
108
import org.springframework.transaction.annotation.Propagation;
119
import org.springframework.transaction.annotation.Transactional;
12-
import org.springframework.transaction.support.TransactionTemplate;
13-
import org.sterl.spring.persistent_tasks.api.PersistentTask;
1410
import org.sterl.spring.persistent_tasks.task.TaskService;
15-
import org.sterl.spring.persistent_tasks.trigger.event.TriggerRunningEvent;
11+
import org.sterl.spring.persistent_tasks.trigger.model.RunTaskWithStateCommand;
1612
import org.sterl.spring.persistent_tasks.trigger.model.TriggerEntity;
1713

1814
import lombok.RequiredArgsConstructor;
@@ -25,7 +21,6 @@ public class RunTriggerComponent {
2521

2622
private final TaskService taskService;
2723
private final EditTriggerComponent editTrigger;
28-
private final ApplicationEventPublisher eventPublisher;
2924
private final StateSerializer serializer = new StateSerializer();
3025

3126
/**
@@ -36,79 +31,50 @@ public Optional<TriggerEntity> execute(TriggerEntity trigger) {
3631
if (trigger == null) {
3732
return Optional.empty();
3833
}
34+
3935
final var taskAndState = getTastAndState(trigger);
4036
// something went really wrong this trigger is crap
4137
if (taskAndState == null) return Optional.of(trigger);
4238

4339
try {
44-
return taskAndState.call();
40+
return taskAndState.execute(editTrigger);
4541
} catch (Exception e) {
4642
return failTaskAndState(taskAndState, e);
4743
}
4844
}
4945

5046
@Nullable
51-
private TaskAndState getTastAndState(TriggerEntity trigger) {
47+
private RunTaskWithStateCommand getTastAndState(TriggerEntity trigger) {
5248
try {
53-
var task = taskService.assertIsKnown(trigger.newTaskId());
54-
var trx = taskService.getTransactionTemplate(task);
55-
var state = serializer.deserialize(trigger.getData().getState());
56-
return new TaskAndState(task, trx, state, trigger);
49+
final var task = taskService.assertIsKnown(trigger.newTaskId());
50+
final var trx = taskService.getTransactionTemplate(task);
51+
final var state = serializer.deserialize(trigger.getData().getState());
52+
return new RunTaskWithStateCommand(task, trx, state, trigger);
5753
} catch (Exception e) {
58-
// this trigger is somehow crap, no retry and done.
59-
failTaskAndState(new TaskAndState(null, Optional.empty(), null, trigger), e);
54+
failTaskAndState(new RunTaskWithStateCommand(null, Optional.empty(), null, trigger), e);
6055
return null;
6156
}
6257
}
6358

64-
private Optional<TriggerEntity> failTaskAndState(TaskAndState taskAndState, Exception e) {
59+
private Optional<TriggerEntity> failTaskAndState(RunTaskWithStateCommand runTaskWithStateCommand, Exception e) {
6560

66-
var trigger = taskAndState.trigger;
67-
var task = taskAndState.persistentTask;
61+
var trigger = runTaskWithStateCommand.trigger();
62+
var task = runTaskWithStateCommand.task();
6863
Optional<TriggerEntity> result;
6964

7065
if (task != null
7166
&& task.retryStrategy().shouldRetry(trigger.getData().getExecutionCount(), e)) {
7267

7368
final OffsetDateTime retryAt = task.retryStrategy().retryAt(trigger.getData().getExecutionCount(), e);
7469

75-
result = editTrigger.failTrigger(trigger.getKey(), taskAndState.state, e, retryAt);
70+
result = editTrigger.failTrigger(trigger.getKey(), runTaskWithStateCommand.state(), e, retryAt);
7671

7772
} else {
7873
log.error("{} failed, no more retries! {}", trigger.getKey(),
7974
e == null ? "No exception given." : e.getMessage(), e);
8075

81-
result = editTrigger.failTrigger(trigger.getKey(), taskAndState.state, e, null);
76+
result = editTrigger.failTrigger(trigger.getKey(), runTaskWithStateCommand.state(), e, null);
8277
}
8378
return result;
8479
}
85-
86-
@RequiredArgsConstructor
87-
class TaskAndState {
88-
final PersistentTask<Serializable> persistentTask;
89-
final Optional<TransactionTemplate> trx;
90-
final Serializable state;
91-
final TriggerEntity trigger;
92-
93-
Optional<TriggerEntity> call() {
94-
if (trx.isPresent()) {
95-
return trx.get().execute(t -> runTask());
96-
} else {
97-
return runTask();
98-
}
99-
}
100-
101-
private Optional<TriggerEntity> runTask() {
102-
if (!trigger.isRunning()) trigger.runOn(trigger.getRunningOn());
103-
eventPublisher.publishEvent(new TriggerRunningEvent(
104-
trigger.getId(), trigger.copyData(), state, trigger.getRunningOn()));
105-
106-
persistentTask.accept(state);
107-
108-
var result = editTrigger.completeTaskWithSuccess(trigger.getKey(), state);
109-
editTrigger.deleteTrigger(trigger);
110-
111-
return result;
112-
}
113-
}
11480
}

core/src/main/java/org/sterl/spring/persistent_tasks/trigger/component/StateSerializer.java

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,27 @@
77
import java.io.ObjectOutputStream;
88
import java.io.Serializable;
99

10+
import org.sterl.spring.persistent_tasks.exception.SpringPersistentTaskException;
11+
1012
import lombok.extern.slf4j.Slf4j;
1113

1214
@Slf4j
1315
public class StateSerializer {
16+
public static class DeSerializationFailedException extends SpringPersistentTaskException {
17+
private static final long serialVersionUID = 1L;
18+
19+
public DeSerializationFailedException(byte[] bytes, Exception e) {
20+
super("Failed to deserialize state of length " + bytes.length, bytes, e);
21+
}
22+
}
23+
24+
public static class SerializationFailedException extends SpringPersistentTaskException {
25+
private static final long serialVersionUID = 1L;
26+
27+
public SerializationFailedException(Serializable obj, Exception e) {
28+
super("Failed to serialize state " + obj.getClass(), obj, e);
29+
}
30+
}
1431

1532
public byte[] serialize(final Serializable obj) {
1633
if (obj == null) {
@@ -23,7 +40,7 @@ public byte[] serialize(final Serializable obj) {
2340
out.writeObject(obj);
2441
return bos.toByteArray();
2542
} catch (Exception ex) {
26-
throw new RuntimeException(ex);
43+
throw new SerializationFailedException(obj, ex);
2744
}
2845
}
2946

@@ -36,7 +53,7 @@ public Serializable deserialize(byte[] bytes) {
3653
try (ObjectInput in = new ObjectInputStream(bis)) {
3754
return (Serializable)in.readObject();
3855
} catch (Exception ex) {
39-
throw new RuntimeException("Failed to deserialize state of length " + bytes.length, ex);
56+
throw new DeSerializationFailedException(bytes, ex);
4057
}
4158
}
4259

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package org.sterl.spring.persistent_tasks.trigger.interceptor;
2+
3+
import org.slf4j.MDC;
4+
import org.springframework.context.event.EventListener;
5+
import org.springframework.stereotype.Component;
6+
import org.sterl.spring.persistent_tasks.trigger.event.TriggerFailedEvent;
7+
import org.sterl.spring.persistent_tasks.trigger.event.TriggerRunningEvent;
8+
import org.sterl.spring.persistent_tasks.trigger.event.TriggerSuccessEvent;
9+
10+
/**
11+
* Adds task name and id to the {@link MDC} context.
12+
*/
13+
@Component
14+
public class MdcTriggerInterceptor {
15+
16+
public static final String TASK_NAME = "taskName";
17+
public static final String TASK_ID = "taskId";
18+
19+
@EventListener
20+
public void beforeRun(TriggerRunningEvent data) {
21+
MDC.put(TASK_NAME, data.key().getTaskName());
22+
MDC.put(TASK_ID, data.key().getId());
23+
}
24+
@EventListener
25+
public void onFailed(TriggerFailedEvent data) {
26+
MDC.remove(TASK_NAME);
27+
MDC.remove(TASK_ID);
28+
}
29+
@EventListener
30+
public void onSuccess(TriggerSuccessEvent data) {
31+
MDC.remove(TASK_NAME);
32+
MDC.remove(TASK_ID);
33+
}
34+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package org.sterl.spring.persistent_tasks.trigger.model;
2+
3+
import java.io.Serializable;
4+
import java.util.Optional;
5+
6+
import org.springframework.transaction.support.TransactionTemplate;
7+
import org.sterl.spring.persistent_tasks.api.PersistentTask;
8+
import org.sterl.spring.persistent_tasks.shared.model.HasTriggerData;
9+
import org.sterl.spring.persistent_tasks.shared.model.TriggerData;
10+
import org.sterl.spring.persistent_tasks.trigger.component.EditTriggerComponent;
11+
12+
public record RunTaskWithStateCommand (
13+
PersistentTask<Serializable> task,
14+
Optional<TransactionTemplate> trx,
15+
Serializable state,
16+
TriggerEntity trigger) implements HasTriggerData {
17+
18+
public Optional<TriggerEntity> execute(EditTriggerComponent editTrigger) {
19+
if (trx.isPresent()) {
20+
return trx.get().execute(t -> runTask(editTrigger));
21+
} else {
22+
return runTask(editTrigger);
23+
}
24+
}
25+
26+
private Optional<TriggerEntity> runTask(EditTriggerComponent editTrigger) {
27+
editTrigger.triggerIsNowRunning(trigger, state);
28+
29+
task.accept(state);
30+
31+
var result = editTrigger.completeTaskWithSuccess(trigger.getKey(), state);
32+
editTrigger.deleteTrigger(trigger);
33+
34+
return result;
35+
}
36+
37+
@Override
38+
public TriggerData getData() {
39+
return trigger.getData();
40+
}
41+
}

core/src/test/java/org/sterl/spring/persistent_tasks/AbstractSpringTest.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
import org.springframework.context.annotation.Configuration;
1717
import org.springframework.context.annotation.Primary;
1818
import org.springframework.stereotype.Component;
19+
import org.springframework.test.context.event.RecordApplicationEvents;
1920
import org.springframework.transaction.PlatformTransactionManager;
2021
import org.springframework.transaction.support.TransactionTemplate;
2122
import org.sterl.spring.persistent_tasks.api.PersistentTask;
@@ -40,6 +41,7 @@
4041

4142
// @ActiveProfiles("mssql") // postgres mssql mariadb mysql
4243
@SpringBootTest(classes = SampleApp.class, webEnvironment = WebEnvironment.RANDOM_PORT)
44+
@RecordApplicationEvents
4345
public class AbstractSpringTest {
4446

4547
@Autowired

0 commit comments

Comments
 (0)