diff --git a/src/Controller/ApiBaseController.php b/src/Controller/ApiBaseController.php
index f14817729cc89ae9a2cf960c33dd6d22976e8869..aa31272cccbdfc404ddb652e2219382088e711c8 100644
--- a/src/Controller/ApiBaseController.php
+++ b/src/Controller/ApiBaseController.php
@@ -320,7 +320,9 @@ class ApiBaseController extends AbstractController
      */
     public function mergeEntries(Entry $targetEntry, Entry $sourceEntry)
     {
-        $targetEntry->setComments($targetEntry->getComments() . "\n\nCommentaires ajoutés suite à une fusion:\n" . $sourceEntry->getComments());
+        if ($sourceEntry->getComments() && $sourceEntry->getComments() != $targetEntry->getComments()) {
+            $targetEntry->setCommentsBy($targetEntry->getComments() . "\n\nCommentaires ajoutés suite à une fusion:\n" . $sourceEntry->getComments(), $this->getUser());
+        }
 
         $sourceAttributes = $sourceEntry->getAttributes();
         $targetAttributes = $targetEntry->getAttributes();
diff --git a/src/Controller/ApiEntryController.php b/src/Controller/ApiEntryController.php
index 22fb564871f819eaaebf6386f76f33eb0c6abfa8..00c659395712736e801293f6a655386dfc2d6817 100644
--- a/src/Controller/ApiEntryController.php
+++ b/src/Controller/ApiEntryController.php
@@ -185,7 +185,7 @@ class ApiEntryController extends ApiBaseController
     }
 
     /**
-     * Copie une sélection d'entrée vers des lexiques cibles. Si force=true, on crée  l'entrée même si le mot n'est pas trouvé dans le wiktionnaire
+     * Copie une sélection d'entrée vers des lexiques cibles. Si force=true, on crée l'entrée même si le mot n'est pas trouvé dans le wiktionnaire
      *
      * @Route("/copy", name="api_copy_entries", methods={"POST"})
      *
@@ -270,7 +270,7 @@ class ApiEntryController extends ApiBaseController
                     switch ($data['merge']) {
                         case Lexicon::MERGE_OVERWRITE: // On remplace l'entrée cible si elle existe
                             $targetEntry->setAttributes($entry->getAttributes());
-                            $targetEntry->setComments($entry->getComments());
+                            $targetEntry->setCommentsBy($entry->getComments(), $this->getUser());
                             $overwritten[] = $targetEntry->getId();
                             break;
                         case Lexicon::MERGE_IGNORE: // On ne fait rien si l'entrée cible existe
@@ -394,8 +394,8 @@ class ApiEntryController extends ApiBaseController
             return $this->createJsonResponse(401, ['error' => $message]);
         } else {
             $entry->setAttributes($data['attributes']);
-            if ($data['comment'] ?? null) {
-                $entry->setComments($entry->getComments() . "\n" . $data['comment']);
+            if ($data['comment'] ?? null && $data['comment'] != $entry->getComments()) {
+                $entry->setCommentsBy($entry->getComments() . "\n" . $data['comment'], $this->getUser());
             }
             $this->doctrine->getManager()->flush();
         }
diff --git a/src/Controller/ApiTraceController.php b/src/Controller/ApiTraceController.php
new file mode 100644
index 0000000000000000000000000000000000000000..7e23ec5fc6eef85104ceb2e2cc9568ec71af5c9e
--- /dev/null
+++ b/src/Controller/ApiTraceController.php
@@ -0,0 +1,75 @@
+<?php
+
+namespace App\Controller;
+
+use App\Entity\Entry;
+use App\Entity\Graphy;
+use App\Entity\Headword;
+use App\Entity\Label;
+use App\Entity\Lexicon;
+use App\Entity\Trace;
+use App\Manager\WiktionaryManager;
+use App\Repository\LabelRepository;
+use Doctrine\Persistence\ManagerRegistry;
+use Sensio\Bundle\FrameworkExtraBundle\Configuration\IsGranted;
+use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
+use Symfony\Component\HttpFoundation\JsonResponse;
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\HttpFoundation\Response;
+use Symfony\Component\Routing\Annotation\Route;
+use Nelmio\ApiDocBundle\Annotation\Security;
+use OpenApi\Annotations as OA;
+use Nelmio\ApiDocBundle\Annotation\Model;
+use Symfony\Component\Serializer\SerializerInterface;
+use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
+
+/**
+ * @Route("/api/trace")
+ */
+class ApiTraceController extends ApiBaseController
+{
+    /**
+     *
+     * @Route("/create", name="api_trace_create", methods={"POST"})
+     *
+     * @OA\Response(response=200, description="success", @OA\JsonContent(type="string"))
+     * @OA\Response(response=401, description="error", @OA\JsonContent(type="string"))
+     * @OA\Response(response=403, description="error", @OA\JsonContent(type="string"))
+     * @OA\Response(response=500, description="error", @OA\JsonContent(type="string"))
+     * @OA\RequestBody(
+     *     required=true,
+     *     @OA\JsonContent(
+     *       required={"origin", "action"},
+     *       @OA\Property(property="origin", type="string", example="Prisms"),
+     *       @OA\Property(property="action", type="string", example="partie annulée"),
+     *       @OA\Property(property="additional_info", type="object"),
+     *     )
+     * )
+     * @OA\Tag(name="Traces")
+     * @Security(name="OAuth2")
+     */
+    public function createTrace(Request $request): Response
+    {
+        $data = json_decode($request->getContent(), true);
+        if (!$data) {
+            return $this->createJsonResponse(401, ['error' => sprintf("Json non valide")]);
+        }
+        if ($missingFields = $this->getMissingFields($data, ['origin', 'action'])) {
+            return $this->createJsonResponse(401, ['error' => sprintf("Veuillez fournir une valeur pour: %s", implode(', ', $missingFields))]);
+        }
+
+        $trace = new Trace();
+        $trace->setCreatedBy($this->getUser());
+        $trace->setAction($data['action']);
+        $trace->setOrigin($data['origin']);
+        if ($data['additional_info'] ?? null) {
+            $trace->setAdditionalInfo($data['additional_info']);
+        }
+        $this->doctrine->getManager()->persist($trace);
+        $this->doctrine->getManager()->flush();
+
+        $this->success[] = sprintf("Trace id %s créée.", $trace->getId());
+
+        return $this->createJsonResponse(200);
+    }
+}
diff --git a/src/Entity/Entry.php b/src/Entity/Entry.php
index dcd073ae7a80b52be445343e6633d1adb7ea7934..945fcf529d821e547293ca8adfd4b2975614bc9c 100644
--- a/src/Entity/Entry.php
+++ b/src/Entity/Entry.php
@@ -3,6 +3,8 @@
 namespace App\Entity;
 
 use App\Repository\EntryRepository;
+use Doctrine\Common\Collections\ArrayCollection;
+use Doctrine\Common\Collections\Collection;
 use Doctrine\ORM\Mapping as ORM;
 use Symfony\Component\Serializer\Annotation\Groups;
 use OpenApi\Annotations as OA;
@@ -19,6 +21,8 @@ use OpenApi\Annotations as OA;
  */
 class Entry
 {
+    use LoggableTrait;
+
     /**
      * @ORM\Id
      * @ORM\GeneratedValue
@@ -39,6 +43,12 @@ class Entry
      */
     private $comments;
 
+    /**
+     * @Groups({"entry:read"})
+     * @ORM\Column(type="text", nullable=true)
+     */
+    private $discussion;
+
     /**
      * @Groups({"entry:read"})
      * @ORM\Column(type="string", length=100)
@@ -69,8 +79,14 @@ class Entry
      */
     private $lexicon;
 
+    /**
+     * @ORM\OneToMany(targetEntity=Log::class, mappedBy="lexicon", cascade={"remove", "persist"})
+     */
+    private $logs;
+
     public function __construct()
     {
+        $this->logs = new ArrayCollection();
     }
 
     public function __toString()
@@ -124,7 +140,7 @@ class Entry
         return $this->comments;
     }
 
-    public function setComments(?string $comments): self
+    private function setComments(?string $comments): self
     {
         $this->comments = $comments;
 
@@ -190,4 +206,46 @@ class Entry
 
         return $this;
     }
+
+    public function getDiscussion(): ?string
+    {
+        return $this->discussion;
+    }
+
+    private function setDiscussion(?string $discussion): self
+    {
+        $this->discussion = $discussion;
+
+        return $this;
+    }
+
+    /**
+     * @return Collection<int, Log>
+     */
+    public function getLogs(): Collection
+    {
+        return $this->logs;
+    }
+
+    public function addLog(Log $log): self
+    {
+        if (!$this->logs->contains($log)) {
+            $this->logs[] = $log;
+            $log->setLexicon($this);
+        }
+
+        return $this;
+    }
+
+    public function removeLog(Log $log): self
+    {
+        if ($this->logs->removeElement($log)) {
+            // set the owning side to null (unless already changed)
+            if ($log->getLexicon() === $this) {
+                $log->setLexicon(null);
+            }
+        }
+
+        return $this;
+    }
 }
diff --git a/src/Entity/Headword.php b/src/Entity/Headword.php
index 5fff1a73f13b5589d32f5eafde7436078dc98636..0a901efec6da879c4863bbca8e6d6a77d5a438b1 100644
--- a/src/Entity/Headword.php
+++ b/src/Entity/Headword.php
@@ -2,7 +2,6 @@
 
 namespace App\Entity;
 
-use ApiPlatform\Core\Annotation\ApiResource;
 use App\Repository\HeadwordRepository;
 use Doctrine\Common\Collections\ArrayCollection;
 use Doctrine\Common\Collections\Collection;
diff --git a/src/Entity/Lexicon.php b/src/Entity/Lexicon.php
index 80fe18529edec312e6cc44382bba6c2612e8b575..8d4b5e8b13784d66486ce51eb3d08c88153776c4 100644
--- a/src/Entity/Lexicon.php
+++ b/src/Entity/Lexicon.php
@@ -2,7 +2,6 @@
 
 namespace App\Entity;
 
-use ApiPlatform\Core\Annotation\ApiResource;
 use App\Repository\LexiconRepository;
 use Doctrine\Common\Collections\ArrayCollection;
 use Doctrine\Common\Collections\Collection;
@@ -15,6 +14,9 @@ use Symfony\Component\Serializer\Annotation\Groups;
  */
 class Lexicon
 {
+    use LoggableTrait;
+
+
     const TYPE_USER       = 'User';
     const TYPE_GROUP      = 'Group';
     const TYPE_ZERO       = 'Zero';
@@ -57,6 +59,18 @@ class Lexicon
      */
     private $category;
 
+    /**
+     * @Groups({"lexicon:read"})
+     * @ORM\Column(type="text", nullable=true)
+     */
+    private $comments;
+
+    /**
+     * @Groups({"lexicon:read"})
+     * @ORM\Column(type="text", nullable=true)
+     */
+    private $discussion;
+
     /**
      * @ORM\OneToOne(targetEntity=User::class, mappedBy="lexicon")
      * @Groups({"lexicon:read", "lexicon:write"})
@@ -79,6 +93,11 @@ class Lexicon
      */
     private $graphyLists;
 
+    /**
+     * @ORM\OneToMany(targetEntity=Log::class, mappedBy="lexicon", cascade={"remove", "persist"})
+     */
+    private $logs;
+
     public function __toString()
     {
         switch ($this->getCategory()) {
@@ -93,6 +112,7 @@ class Lexicon
     {
         $this->entries = new ArrayCollection();
         $this->graphyLists = new ArrayCollection();
+        $this->logs = new ArrayCollection();
     }
 
     public function isZero()
@@ -259,4 +279,58 @@ class Lexicon
 
         return $this;
     }
+
+    public function getComments(): ?string
+    {
+        return $this->comments;
+    }
+
+    private function setComments(?string $comments): self
+    {
+        $this->comments = $comments;
+
+        return $this;
+    }
+
+    public function getDiscussion(): ?string
+    {
+        return $this->discussion;
+    }
+
+    private function setDiscussion(?string $discussion): self
+    {
+        $this->discussion = $discussion;
+
+        return $this;
+    }
+
+    /**
+     * @return Collection<int, Log>
+     */
+    public function getLogs(): Collection
+    {
+        return $this->logs;
+    }
+
+    public function addLog(Log $log): self
+    {
+        if (!$this->logs->contains($log)) {
+            $this->logs[] = $log;
+            $log->setLexicon($this);
+        }
+
+        return $this;
+    }
+
+    public function removeLog(Log $log): self
+    {
+        if ($this->logs->removeElement($log)) {
+            // set the owning side to null (unless already changed)
+            if ($log->getLexicon() === $this) {
+                $log->setLexicon(null);
+            }
+        }
+
+        return $this;
+    }
 }
diff --git a/src/Entity/Log.php b/src/Entity/Log.php
new file mode 100644
index 0000000000000000000000000000000000000000..32cc1590ed4ec59cf876df6a2cd85c0136527e43
--- /dev/null
+++ b/src/Entity/Log.php
@@ -0,0 +1,151 @@
+<?php
+
+namespace App\Entity;
+
+use App\Repository\LexiconRepository;
+use Doctrine\Common\Collections\ArrayCollection;
+use Doctrine\Common\Collections\Collection;
+use Doctrine\ORM\Mapping as ORM;
+use Symfony\Component\Serializer\Annotation\Groups;
+
+/**
+ * @ORM\Entity(repositoryClass=LexiconRepository::class)
+ * @ORM\HasLifecycleCallbacks
+ */
+class Log
+{
+    const CATEGORY_UPDATE = 'Update';
+
+    /**
+     * @ORM\Id
+     * @ORM\GeneratedValue
+     * @ORM\Column(type="integer")
+     */
+    private $id;
+
+    /**
+     * @Groups({"log:read"})
+     * @ORM\Column(type="datetime_immutable")
+     */
+    private $createdAt;
+
+    /**
+     * @ORM\ManyToOne(targetEntity=User::class, inversedBy="logs")
+     * @Groups({"log:read"})
+     */
+    private $createdBy;
+
+    /**
+     * @ORM\Column(type="string", length=80)
+     * @Groups({"log:read"})
+     */
+    private $category;
+
+    /**
+     * @Groups({"log:read"})
+     * @ORM\Column(type="text", nullable=true)
+     */
+    private $content;
+
+    /**
+     * @ORM\ManyToOne(targetEntity=Lexicon::class, inversedBy="logs")
+     */
+    private $lexicon;
+
+    /**
+     * @ORM\ManyToOne(targetEntity=Entry::class, inversedBy="logs")
+     */
+    private $entry;
+
+    public function __toString()
+    {
+        return $this->getCategory() . ' du ' . $this->getCreatedAt()->format('d/m/Y à h:i:s');
+    }
+
+    public function __construct()
+    {
+        $this->createdAt = new \DateTimeImmutable();
+    }
+
+    public function isUpdate()
+    {
+        return $this->getCategory() === self::CATEGORY_UPDATE;
+    }
+
+    public function getId(): ?int
+    {
+        return $this->id;
+    }
+
+    public function getCreatedAt(): ?\DateTimeImmutable
+    {
+        return $this->createdAt;
+    }
+
+    public function setCreatedAt(\DateTimeImmutable $createdAt): self
+    {
+        $this->createdAt = $createdAt;
+
+        return $this;
+    }
+
+    public function getContent(): ?string
+    {
+        return $this->content;
+    }
+
+    public function setContent(?string $content): self
+    {
+        $this->content = $content;
+
+        return $this;
+    }
+
+    public function getCreatedBy(): ?User
+    {
+        return $this->createdBy;
+    }
+
+    public function setCreatedBy(?User $createdBy): self
+    {
+        $this->createdBy = $createdBy;
+
+        return $this;
+    }
+
+    public function getLexicon(): ?Lexicon
+    {
+        return $this->lexicon;
+    }
+
+    public function setLexicon(?Lexicon $lexicon): self
+    {
+        $this->lexicon = $lexicon;
+
+        return $this;
+    }
+
+    public function getEntry(): ?Entry
+    {
+        return $this->entry;
+    }
+
+    public function setEntry(?Entry $entry): self
+    {
+        $this->entry = $entry;
+
+        return $this;
+    }
+
+    public function getCategory(): ?string
+    {
+        return $this->category;
+    }
+
+    public function setCategory(string $category): self
+    {
+        $this->category = $category;
+
+        return $this;
+    }
+}
diff --git a/src/Entity/LoggableTrait.php b/src/Entity/LoggableTrait.php
new file mode 100644
index 0000000000000000000000000000000000000000..cfa426704b6ad9f7e115bf9403bbe3bb8f9574d4
--- /dev/null
+++ b/src/Entity/LoggableTrait.php
@@ -0,0 +1,28 @@
+<?php
+
+
+namespace App\Entity;
+
+
+trait LoggableTrait
+{
+    public function setCommentsBy($comments, User $user)
+    {
+        $log = new Log();
+        $log->setCreatedBy($user);
+        $log->setContent($this->getComments());
+        $this->setComments($comments);
+
+        return $this;
+    }
+
+    public function setDiscussionBy($discussions, User $user)
+    {
+        $log = new Log();
+        $log->setCreatedBy($user);
+        $log->setContent($this->getDiscussions());
+        $this->setDiscussions($discussions);
+
+        return $this;
+    }
+}
\ No newline at end of file
diff --git a/src/Entity/Trace.php b/src/Entity/Trace.php
new file mode 100644
index 0000000000000000000000000000000000000000..e866f9d36a98b1c53e0dff1fc445ed1385df4a00
--- /dev/null
+++ b/src/Entity/Trace.php
@@ -0,0 +1,130 @@
+<?php
+
+namespace App\Entity;
+
+use App\Repository\LexiconRepository;
+use Doctrine\Common\Collections\ArrayCollection;
+use Doctrine\Common\Collections\Collection;
+use Doctrine\ORM\Mapping as ORM;
+use Symfony\Component\Serializer\Annotation\Groups;
+use OpenApi\Annotations as OA;
+
+/**
+ * @ORM\Entity(repositoryClass=LexiconRepository::class)
+ * @ORM\HasLifecycleCallbacks
+ */
+class Trace
+{
+    /**
+     * @ORM\Id
+     * @ORM\GeneratedValue
+     * @ORM\Column(type="integer")
+     */
+    private $id;
+
+    /**
+     * @Groups({"trace:read"})
+     * @ORM\Column(type="datetime_immutable")
+     */
+    private $createdAt;
+
+    /**
+     * @ORM\ManyToOne(targetEntity=User::class, inversedBy="traces")
+     * @Groups({"trace:read"})
+     */
+    private $createdBy;
+
+    /**
+     * @Groups({"trace:read"})
+     * @ORM\Column(type="string", nullable=true)
+     */
+    private $origin;
+
+    /**
+     * @Groups({"trace:read"})
+     * @ORM\Column(type="string", nullable=true)
+     */
+    private $action;
+
+    /**
+     * @ORM\Column(type="json", nullable=true)
+     * @Groups({"trace:read"})
+     * @OA\Property(type="json")
+     */
+    private $additionalInfo = [];
+
+    public function __toString()
+    {
+        return $this->getAction() . ' par ' . $this->getCreatedBy() . ' depuis ' . $this->getOrigin() . ' le ' . $this->getCreatedAt()->format('d/m/Y à h:i:s');
+    }
+
+    public function __construct()
+    {
+        $this->createdAt = new \DateTimeImmutable();
+    }
+
+    public function getId(): ?int
+    {
+        return $this->id;
+    }
+
+    public function getCreatedAt(): ?\DateTimeImmutable
+    {
+        return $this->createdAt;
+    }
+
+    public function setCreatedAt(\DateTimeImmutable $createdAt): self
+    {
+        $this->createdAt = $createdAt;
+
+        return $this;
+    }
+
+    public function getOrigin(): ?string
+    {
+        return $this->origin;
+    }
+
+    public function setOrigin(?string $origin): self
+    {
+        $this->origin = $origin;
+
+        return $this;
+    }
+
+    public function getAction(): ?string
+    {
+        return $this->action;
+    }
+
+    public function setAction(?string $action): self
+    {
+        $this->action = $action;
+
+        return $this;
+    }
+
+    public function getAdditionalInfo(): ?array
+    {
+        return $this->additionalInfo;
+    }
+
+    public function setAdditionalInfo(?array $additionalInfo): self
+    {
+        $this->additionalInfo = $additionalInfo;
+
+        return $this;
+    }
+
+    public function getCreatedBy(): ?User
+    {
+        return $this->createdBy;
+    }
+
+    public function setCreatedBy(?User $createdBy): self
+    {
+        $this->createdBy = $createdBy;
+
+        return $this;
+    }
+}
diff --git a/src/Entity/User.php b/src/Entity/User.php
index e5c9d03cde3b160bf8439198077d3ca0bcd1ff05..063a7e8a99d5d8bfa551e328c8a294e4a8aaabcf 100644
--- a/src/Entity/User.php
+++ b/src/Entity/User.php
@@ -197,6 +197,16 @@ class User implements UserInterface, PasswordAuthenticatedUserInterface
      */
     private $groupMemberships;
 
+    /**
+     * @ORM\OneToMany(targetEntity=Log::class, mappedBy="createdBy", cascade={"remove"})
+     */
+    private $logs;
+
+    /**
+     * @ORM\OneToMany(targetEntity=Trace::class, mappedBy="createdBy", cascade={"remove"})
+     */
+    private $traces;
+
     public function __toString()
     {
         return $this->getPseudo();
@@ -217,6 +227,7 @@ class User implements UserInterface, PasswordAuthenticatedUserInterface
         $this->groupMemberships = new ArrayCollection();
         $this->labels = new ArrayCollection();
         $this->createdGraphyLists = new ArrayCollection();
+        $this->logs = new ArrayCollection();
     }
 
     /**
@@ -773,4 +784,34 @@ class User implements UserInterface, PasswordAuthenticatedUserInterface
 
         return $this;
     }
+
+    /**
+     * @return Collection<int, Log>
+     */
+    public function getLogs(): Collection
+    {
+        return $this->logs;
+    }
+
+    public function addLog(Log $log): self
+    {
+        if (!$this->logs->contains($log)) {
+            $this->logs[] = $log;
+            $log->setCreatedBy($this);
+        }
+
+        return $this;
+    }
+
+    public function removeLog(Log $log): self
+    {
+        if ($this->logs->removeElement($log)) {
+            // set the owning side to null (unless already changed)
+            if ($log->getCreatedBy() === $this) {
+                $log->setCreatedBy(null);
+            }
+        }
+
+        return $this;
+    }
 }