Repository: kestra-io/kestra (java)

Issue:
Remove the log ID display to save space in the Logs view
### Feature description

Side-by-side comparison of before and after

(the more compact view on the left is preferrable)

![image](https://github.com/kestra-io/kestra/assets/86264395/e2c927ea-a0f3-4bae-9305-1cde43ae7db2)


Pull request description:
feat(*): remove logId and delete log by query
Fixes #3026

Patch:
diff --git a/core/src/main/java/io/kestra/core/models/executions/LogEntry.java b/core/src/main/java/io/kestra/core/models/executions/LogEntry.java
index 4131b54be..64e803a0f 100644
--- a/core/src/main/java/io/kestra/core/models/executions/LogEntry.java
+++ b/core/src/main/java/io/kestra/core/models/executions/LogEntry.java
@@ -1,13 +1,11 @@
 package io.kestra.core.models.executions;
 
-import com.fasterxml.jackson.annotation.JsonIgnore;
 import com.fasterxml.jackson.annotation.JsonInclude;
 import io.kestra.core.models.DeletedInterface;
 import io.kestra.core.models.TenantInterface;
 import io.kestra.core.models.flows.Flow;
 import io.kestra.core.models.triggers.AbstractTrigger;
 import io.kestra.core.models.triggers.TriggerContext;
-import io.kestra.core.utils.IdUtils;
 import io.micronaut.core.annotation.Nullable;
 import io.swagger.v3.oas.annotations.Hidden;
 import lombok.Builder;
@@ -24,9 +22,6 @@ import java.util.stream.Stream;
 @Value
 @Builder(toBuilder = true)
 public class LogEntry implements DeletedInterface, TenantInterface {
-    @NotNull
-    String id;
-
     @Hidden
     @Pattern(regexp = "^[a-z0-9][a-z0-9_-]*")
     String tenantId;
@@ -78,7 +73,6 @@ public class LogEntry implements DeletedInterface, TenantInterface {
 
     public static LogEntry of(Execution execution) {
         return LogEntry.builder()
-            .id(IdUtils.create())
             .tenantId(execution.getTenantId())
             .namespace(execution.getNamespace())
             .flowId(execution.getFlowId())
@@ -88,7 +82,6 @@ public class LogEntry implements DeletedInterface, TenantInterface {
 
     public static LogEntry of(TaskRun taskRun) {
         return LogEntry.builder()
-            .id(IdUtils.create())
             .tenantId(taskRun.getTenantId())
             .namespace(taskRun.getNamespace())
             .flowId(taskRun.getFlowId())
@@ -101,7 +94,6 @@ public class LogEntry implements DeletedInterface, TenantInterface {
 
     public static LogEntry of(Flow flow, AbstractTrigger abstractTrigger) {
         return LogEntry.builder()
-            .id(IdUtils.create())
             .tenantId(flow.getTenantId())
             .namespace(flow.getNamespace())
             .flowId(flow.getId())
@@ -111,7 +103,6 @@ public class LogEntry implements DeletedInterface, TenantInterface {
 
     public static LogEntry of(TriggerContext triggerContext, AbstractTrigger abstractTrigger) {
         return LogEntry.builder()
-            .id(IdUtils.create())
             .tenantId(triggerContext.getTenantId())
             .namespace(triggerContext.getNamespace())
             .flowId(triggerContext.getFlowId())
diff --git a/core/src/main/java/io/kestra/core/queues/QueueService.java b/core/src/main/java/io/kestra/core/queues/QueueService.java
index 281365c5c..dc44d6fc4 100644
--- a/core/src/main/java/io/kestra/core/queues/QueueService.java
+++ b/core/src/main/java/io/kestra/core/queues/QueueService.java
@@ -24,7 +24,7 @@ public class QueueService {
         } else if (object.getClass() == WorkerTaskResult.class) {
             return ((WorkerTaskResult) object).getTaskRun().getId();
         } else if (object.getClass() == LogEntry.class) {
-            return ((LogEntry) object).getId();
+            return null;
         } else if (object.getClass() == Flow.class) {
             return ((Flow) object).uid();
         } else if (object.getClass() == Template.class) {
diff --git a/core/src/main/java/io/kestra/core/repositories/LogRepositoryInterface.java b/core/src/main/java/io/kestra/core/repositories/LogRepositoryInterface.java
index 3b144d8cf..085de2cb3 100644
--- a/core/src/main/java/io/kestra/core/repositories/LogRepositoryInterface.java
+++ b/core/src/main/java/io/kestra/core/repositories/LogRepositoryInterface.java
@@ -42,5 +42,5 @@ public interface LogRepositoryInterface extends SaveRepositoryInterface<LogEntry
 
     Integer purge(Execution execution);
 
-    void delete(LogEntry logEntry);
+    void deleteByQuery(String tenantId, String executionId, String taskId, String taskRunId, Level minLevel, Integer attempt);
 }
diff --git a/core/src/main/java/io/kestra/core/runners/RunContextLogger.java b/core/src/main/java/io/kestra/core/runners/RunContextLogger.java
index 3947d108e..5f219e274 100644
--- a/core/src/main/java/io/kestra/core/runners/RunContextLogger.java
+++ b/core/src/main/java/io/kestra/core/runners/RunContextLogger.java
@@ -66,7 +66,6 @@ public class RunContextLogger {
         long i = 0;
         for (String s : split) {
             result.add(LogEntry.builder()
-                .id(IdUtils.create())
                 .namespace(logEntry.getNamespace())
                 .tenantId(logEntry.getTenantId())
                 .flowId(logEntry.getFlowId())
diff --git a/core/src/main/java/io/kestra/core/tasks/flows/EachSequential.java b/core/src/main/java/io/kestra/core/tasks/flows/EachSequential.java
index 897fe52b8..de0dbb765 100644
--- a/core/src/main/java/io/kestra/core/tasks/flows/EachSequential.java
+++ b/core/src/main/java/io/kestra/core/tasks/flows/EachSequential.java
@@ -47,25 +47,33 @@ import java.util.Optional;
     examples = {
         @Example(
             full = true,
-            code = {
-                "id: each-sequential",
-                "namespace: io.kestra.tests",
-                "",
-                "tasks:",
-                "  - id: each-sequential",
-                "    type: io.kestra.core.tasks.flows.EachSequential",
-                "    value: '[\"value 1\", \"value 2\", \"value 3\"]'",
-                "    tasks:",
-                "      - id: each-value",
-                "        type: io.kestra.core.tasks.debugs.Return",
-                "        format: \"{{ task.id }} with current value '{{ taskrun.value }}'\"",
-            }
+            title = "The taskrun.value from the `each_sequential` task is available only to immediate child tasks such as the `before_if` and the `if` tasks. To access the taskrun value in child tasks of the `if` task (such as in the `after_if` task), you need to use the syntax `{{ parent.taskrun.value }}` as this allows you to access the taskrun value of the parent task `each_sequential`.",
+            code = """
+                id: loop_example
+                namespace: dev
+
+                tasks:
+                  - id: each_sequential
+                    type: io.kestra.core.tasks.flows.EachSequential
+                    value: ["value 1", "value 2", "value 3"]
+                    tasks:
+                      - id: before_if
+                        type: io.kestra.core.tasks.debugs.Return
+                        format: 'Before if {{ taskrun.value }}'
+                      - id: if
+                        type: io.kestra.core.tasks.flows.If
+                        condition: '{{ taskrun.value == "value 2" }}'
+                        then:
+                          - id: after_if
+                            type: io.kestra.core.tasks.debugs.Return
+                            format: 'After if {{ parent.taskrun.value }}'"""
         ),
         @Example(
             full = true,
+            title = "This task shows that the value can be a bullet-style list. The task iterates over the list of values and executes the `each-value` child task for each value.",
             code = {
-                "id: each-sequential",
-                "namespace: io.kestra.tests",
+                "id: each_sequential",
+                "namespace: dev",
                 "",
                 "tasks:",
                 "  - id: each-sequential",
@@ -77,9 +85,9 @@ import java.util.Optional;
                 "    tasks:",
                 "      - id: each-value",
                 "        type: io.kestra.core.tasks.debugs.Return",
-                "        format: \"{{ task.id }} with current value '{{ taskrun.value }}'\"",
+                "        format: \"{{ task.id }} with value '{{ taskrun.value }}'\"",
             }
-        ),
+        ),        
     }
 )
 public class EachSequential extends Sequential implements FlowableTask<VoidOutput> {
diff --git a/jdbc/src/main/java/io/kestra/jdbc/repository/AbstractJdbcLogRepository.java b/jdbc/src/main/java/io/kestra/jdbc/repository/AbstractJdbcLogRepository.java
index 2966b93e2..c03de4077 100644
--- a/jdbc/src/main/java/io/kestra/jdbc/repository/AbstractJdbcLogRepository.java
+++ b/jdbc/src/main/java/io/kestra/jdbc/repository/AbstractJdbcLogRepository.java
@@ -188,8 +188,35 @@ public abstract class AbstractJdbcLogRepository extends AbstractJdbcRepository i
     }
 
     @Override
-    public void delete(LogEntry logEntry) {
-        this.jdbcRepository.delete(logEntry);
+    public void deleteByQuery(String tenantId, String executionId, String taskId, String taskRunId, Level minLevel, Integer attempt) {
+        this.jdbcRepository
+            .getDslContextWrapper()
+            .transaction(configuration -> {
+                DSLContext context = DSL.using(configuration);
+
+                var delete = context
+                    .delete(this.jdbcRepository.getTable())
+                    .where(this.defaultFilter(tenantId))
+                    .and(field("execution_id").eq(executionId));
+
+                if (taskId != null) {
+                    delete.and(field("task_id").eq(taskId));
+                }
+
+                if (taskRunId != null) {
+                    delete.and(field("taskrun_id").eq(taskRunId));
+                }
+
+                if (minLevel != null) {
+                    delete.and(minLevel(minLevel));
+                }
+
+                if (attempt != null) {
+                    delete.and(field("attempt_number").eq(attempt));
+                }
+
+                delete.execute();
+            });
     }
 
     private ArrayListTotal<LogEntry> query(String tenantId, Condition condition, Level minLevel, Pageable pageable) {
diff --git a/repository-memory/src/main/java/io/kestra/repository/memory/MemoryLogRepository.java b/repository-memory/src/main/java/io/kestra/repository/memory/MemoryLogRepository.java
index fc4223951..52aa9646d 100644
--- a/repository-memory/src/main/java/io/kestra/repository/memory/MemoryLogRepository.java
+++ b/repository-memory/src/main/java/io/kestra/repository/memory/MemoryLogRepository.java
@@ -94,7 +94,7 @@ public class MemoryLogRepository implements LogRepositoryInterface {
     }
 
     @Override
-    public void delete(LogEntry logEntry) {
+    public void deleteByQuery(String tenantId, String executionId, String taskId, String taskRunId, Level minLevel, Integer attempt) {
         throw new UnsupportedOperationException();
     }
 }
diff --git a/webserver/src/main/java/io/kestra/webserver/controllers/LogController.java b/webserver/src/main/java/io/kestra/webserver/controllers/LogController.java
index 8ac03fc20..6a7b08b88 100644
--- a/webserver/src/main/java/io/kestra/webserver/controllers/LogController.java
+++ b/webserver/src/main/java/io/kestra/webserver/controllers/LogController.java
@@ -169,19 +169,6 @@ public class LogController {
         @Parameter(description = "The task id") @Nullable @QueryValue String taskId,
         @Parameter(description = "The attempt number") @Nullable @QueryValue Integer attempt
     ) {
-        List<LogEntry> logEntries;
-        if (taskId != null) {
-            logEntries = logRepository.findByExecutionIdAndTaskId(tenantService.resolveTenant(), executionId, taskId, minLevel);
-        } else if (taskRunId != null) {
-            if (attempt != null) {
-                logEntries = logRepository.findByExecutionIdAndTaskRunIdAndAttempt(tenantService.resolveTenant(), executionId, taskRunId, minLevel, attempt);
-            } else {
-                logEntries = logRepository.findByExecutionIdAndTaskRunId(tenantService.resolveTenant(), executionId, taskRunId, minLevel);
-            }
-        } else {
-            logEntries = logRepository.findByExecutionId(tenantService.resolveTenant(), executionId, minLevel);
-        }
-
-        logEntries.forEach(log -> logRepository.delete(log));
+        logRepository.deleteByQuery(tenantService.resolveTenant(), executionId, taskId, taskRunId, minLevel, attempt);
     }
 }


Test patch:
diff --git a/core/src/test/java/io/kestra/core/repositories/AbstractLogRepositoryTest.java b/core/src/test/java/io/kestra/core/repositories/AbstractLogRepositoryTest.java
index de8f22705..ab8d7f887 100644
--- a/core/src/test/java/io/kestra/core/repositories/AbstractLogRepositoryTest.java
+++ b/core/src/test/java/io/kestra/core/repositories/AbstractLogRepositoryTest.java
@@ -22,7 +22,6 @@ public abstract class AbstractLogRepositoryTest {
 
     private static LogEntry.LogEntryBuilder logEntry(Level level) {
         return LogEntry.builder()
-            .id(IdUtils.create())
             .flowId(IdUtils.create())
             .namespace("io.kestra.unittest")
             .taskId("taskId")
@@ -89,13 +88,13 @@ public abstract class AbstractLogRepositoryTest {
         builder.executionId(executionId);
 
         for (int i = 0; i < 80; i++) {
-            logRepository.save(builder.id(IdUtils.create()).build());
+            logRepository.save(builder.build());
         }
 
         builder = logEntry(Level.INFO).executionId(executionId).taskId("taskId2").taskRunId("taskRunId2");
         LogEntry logEntry2 = logRepository.save(builder.build());
         for (int i = 0; i < 20; i++) {
-            logRepository.save(builder.id(IdUtils.create()).build());
+            logRepository.save(builder.build());
         }
 
         ArrayListTotal<LogEntry> find = logRepository.findByExecutionId(null, executionId, null, Pageable.from(1, 50));
@@ -133,7 +132,7 @@ public abstract class AbstractLogRepositoryTest {
         LogEntry log1 = logEntry(Level.INFO).build();
         logRepository.save(log1);
 
-        logRepository.delete(log1);
+        logRepository.deleteByQuery(null, log1.getExecutionId(), null, null, null, null);
 
         ArrayListTotal<LogEntry> find = logRepository.findByExecutionId(null, log1.getExecutionId(), null, Pageable.from(1, 50));
         assertThat(find.size(), is(0));
diff --git a/webserver/src/test/java/io/kestra/webserver/controllers/LogControllerTest.java b/webserver/src/test/java/io/kestra/webserver/controllers/LogControllerTest.java
index a74618157..74e2c8f52 100644
--- a/webserver/src/test/java/io/kestra/webserver/controllers/LogControllerTest.java
+++ b/webserver/src/test/java/io/kestra/webserver/controllers/LogControllerTest.java
@@ -62,7 +62,7 @@ class LogControllerTest extends JdbcH2ControllerTest {
     @Test
     void findByExecution() {
         LogEntry log1 = logEntry(Level.INFO);
-        LogEntry log2 = log1.toBuilder().message("another message").id(IdUtils.create()).build();
+        LogEntry log2 = log1.toBuilder().message("another message").build();
         LogEntry log3 = logEntry(Level.DEBUG);
         logRepository.save(log1);
         logRepository.save(log2);
@@ -80,7 +80,7 @@ class LogControllerTest extends JdbcH2ControllerTest {
     @Test
     void download() {
         LogEntry log1 = logEntry(Level.INFO);
-        LogEntry log2 = log1.toBuilder().message("another message").id(IdUtils.create()).build();
+        LogEntry log2 = log1.toBuilder().message("another message").build();
         LogEntry log3 = logEntry(Level.DEBUG);
         logRepository.save(log1);
         logRepository.save(log2);
@@ -97,7 +97,7 @@ class LogControllerTest extends JdbcH2ControllerTest {
     @Test
     void delete() {
         LogEntry log1 = logEntry(Level.INFO);
-        LogEntry log2 = log1.toBuilder().message("another message").id(IdUtils.create()).build();
+        LogEntry log2 = log1.toBuilder().message("another message").build();
         LogEntry log3 = logEntry(Level.DEBUG);
         logRepository.save(log1);
         logRepository.save(log2);
@@ -117,7 +117,6 @@ class LogControllerTest extends JdbcH2ControllerTest {
 
     private static LogEntry logEntry(Level level) {
         return LogEntry.builder()
-            .id(IdUtils.create())
             .flowId(IdUtils.create())
             .namespace("io.kestra.unittest")
             .taskId("taskId")