[{"data":1,"prerenderedAt":672},["ShallowReactive",2],{"\u002Fnext\u002Fdodock\u002Fdeveloppeurs\u002Fstockage-journaux-sqlite":3,"section:\u002Fnext\u002Fdodock\u002Fdeveloppeurs\u002Fstockage-journaux-sqlite":671},{"id":4,"title":5,"body":6,"description":655,"extension":656,"meta":657,"navigation":309,"path":667,"seo":668,"stem":669,"__hash__":670},"docs\u002F2.next\u002F3.dodock\u002F10.developpeurs\u002Fstockage-journaux-sqlite.md","Stockage des journaux dans une base SQLite",{"type":7,"value":8,"toc":641},"minimark",[9,13,17,22,111,122,126,137,143,226,230,269,323,345,349,361,377,426,438,466,471,485,503,507,513,537,541,608,612,622,626,637],[10,11,5],"h1",{"id":12},"stockage-des-journaux-dans-une-base-sqlite",[14,15,16],"p",{},"Cette page s'adresse aux développeurs. Cinq DocTypes de journalisation stockent leurs lignes dans une base SQLite dédiée du site — la base de logs — distincte de la base principale (MariaDB ou PostgreSQL). Ce sont des DocTypes virtuels : l'interface, les listes et l'ORM continuent de fonctionner normalement, et le routage vers la base de logs est transparent.",[18,19,21],"h2",{"id":20},"doctypes-concernés","DocTypes concernés",[23,24,25,38],"table",{},[26,27,28],"thead",{},[29,30,31,35],"tr",{},[32,33,34],"th",{},"DocType",[32,36,37],{},"Rôle",[39,40,41,59,72,85,98],"tbody",{},[29,42,43,56],{},[44,45,46,50,51,55],"td",{},[47,48,49],"strong",{},"Journal d'activité"," (",[52,53,54],"code",{},"Activity Log",")",[44,57,58],{},"Historique des actions : connexions, créations et modifications de documents.",[29,60,61,69],{},[44,62,63,50,66,55],{},[47,64,65],{},"Journal des Erreurs",[52,67,68],{},"Error Log",[44,70,71],{},"Erreurs et traces d'exceptions.",[29,73,74,82],{},[44,75,76,50,79,55],{},[47,77,78],{},"Journal d'accès",[52,80,81],{},"Access Log",[44,83,84],{},"Journal des accès aux documents et aux méthodes.",[29,86,87,95],{},[44,88,89,50,92,55],{},[47,90,91],{},"Historique de navigation",[52,93,94],{},"Route History",[44,96,97],{},"Historique de navigation de chaque utilisateur.",[29,99,100,108],{},[44,101,102,50,105,55],{},[47,103,104],{},"Vue de page web",[52,106,107],{},"Web Page View",[44,109,110],{},"Vues des pages du site web.",[14,112,113,114,117,118,121],{},"Chaque contrôleur hérite de ",[52,115,116],{},"SQLiteLogDocument"," (voir ci-dessous) et chaque DocType porte ",[52,119,120],{},"is_virtual: 1"," dans son JSON.",[18,123,125],{"id":124},"la-base-de-logs","La base de logs",[14,127,128,129,132,133,136],{},"La connexion à la base de logs est gérée par le module ",[52,130,131],{},"frappe.utils.logging",". Les tables y portent le nom habituel ",[52,134,135],{},"tab\u003CDocType>"," et sont créées à la demande. Les sauvegardes du site, la suppression des données personnelles et le nettoyage périodique des journaux prennent en compte cette base.",[18,138,140,141],{"id":139},"référence-de-frappeutilslogging","Référence de ",[52,142,131],{},[23,144,145,154],{},[26,146,147],{},[29,148,149,152],{},[32,150,151],{},"Fonction",[32,153,37],{},[39,155,156,170,184,206,216],{},[29,157,158,163],{},[44,159,160],{},[52,161,162],{},"get_log_db()",[44,164,165,166,169],{},"Renvoie la connexion (",[52,167,168],{},"Database",") vers la base de logs.",[29,171,172,177],{},[44,173,174],{},[52,175,176],{},"log_table(doctype)",[44,178,179,180,183],{},"Renvoie le couple ",[52,181,182],{},"(qb, table)"," : un query builder et une table liés au dialecte SQLite de la base de logs. La table est créée si elle n'existe pas encore.",[29,185,186,191],{},[44,187,188],{},[52,189,190],{},"log_cutoff(days)",[44,192,193,194,197,198,201,202,205],{},"Renvoie la date limite « il y a ",[52,195,196],{},"days"," jours », sous une forme directement utilisable dans un filtre sur ",[52,199,200],{},"creation"," (SQLite ne dispose pas de ",[52,203,204],{},"NOW()",").",[29,207,208,213],{},[44,209,210],{},[52,211,212],{},"is_log_doctype(doctype)",[44,214,215],{},"Indique si les lignes du DocType sont stockées dans la base de logs.",[29,217,218,223],{},[44,219,220],{},[52,221,222],{},"connection_for(doctype)",[44,224,225],{},"Renvoie la connexion qui contient les lignes du DocType : la base de logs pour un DocType de journalisation, la base principale sinon.",[18,227,229],{"id":228},"écrire-un-contrôleur-de-doctype-de-journalisation","Écrire un contrôleur de DocType de journalisation",[231,232,233,245,251],"ol",{},[234,235,236,237,240,241,244],"li",{},"Faites hériter le contrôleur de ",[52,238,239],{},"frappe.utils.sqlite_document.SQLiteLogDocument"," au lieu de ",[52,242,243],{},"Document",". L'insertion, la lecture et les vues de liste écrivent et lisent alors la base de logs.",[234,246,247,248,121],{},"Marquez le DocType ",[52,249,250],{},"is_virtual",[234,252,253,254,257,258,260,261,264,265,268],{},"Implémentez ",[52,255,256],{},"clear_old_logs(days)"," à l'aide de ",[52,259,162],{},", ",[52,262,263],{},"log_table()"," et ",[52,266,267],{},"log_cutoff()"," :",[270,271,276],"pre",{"className":272,"code":273,"language":274,"meta":275,"style":275},"language-python shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","@staticmethod\ndef clear_old_logs(days=30):\n    db = get_log_db()\n    qb, table = log_table(\"Mon Journal\")\n\n    db.sql(qb.from_(table).where(table.creation \u003C log_cutoff(days)).delete())\n    db.commit()\n","python","",[52,277,278,286,292,298,304,311,317],{"__ignoreMap":275},[279,280,283],"span",{"class":281,"line":282},"line",1,[279,284,285],{},"@staticmethod\n",[279,287,289],{"class":281,"line":288},2,[279,290,291],{},"def clear_old_logs(days=30):\n",[279,293,295],{"class":281,"line":294},3,[279,296,297],{},"    db = get_log_db()\n",[279,299,301],{"class":281,"line":300},4,[279,302,303],{},"    qb, table = log_table(\"Mon Journal\")\n",[279,305,307],{"class":281,"line":306},5,[279,308,310],{"emptyLinePlaceholder":309},true,"\n",[279,312,314],{"class":281,"line":313},6,[279,315,316],{},"    db.sql(qb.from_(table).where(table.creation \u003C log_cutoff(days)).delete())\n",[279,318,320],{"class":281,"line":319},7,[279,321,322],{},"    db.commit()\n",[14,324,325,326,50,329,332,333,336,337,340,341,344],{},"Les ",[47,327,328],{},"Paramètres des journaux",[52,330,331],{},"Log Settings",") délèguent le nettoyage à ",[52,334,335],{},"clear_old_logs"," pour ces DocTypes : la stratégie de copie puis renommage de ",[52,338,339],{},"clear_log_table"," ne s'applique qu'aux tables de la base principale, où un ",[52,342,343],{},"DELETE"," massif peut être coûteux.",[18,346,348],{"id":347},"requêtes-utiliser-le-bon-dialecte","Requêtes : utiliser le bon dialecte",[14,350,351,264,354,357,358,360],{},[52,352,353],{},"frappe.qb",[52,355,356],{},"QueryBuilder.run()"," sont liés à la base principale du site. Construire une requête avec ",[52,359,353],{}," pour l'exécuter sur la base de logs produirait du SQL du dialecte principal envoyé à SQLite.",[362,363,364,370],"ul",{},[234,365,366,367,369],{},"Obtenez le query builder et la table avec ",[52,368,176],{},".",[234,371,372,373,376],{},"Exécutez la requête avec ",[52,374,375],{},"get_log_db().sql(...)",", ou avec les méthodes de connexion :",[23,378,379,388],{},[26,380,381],{},[29,382,383,386],{},[32,384,385],{},"Méthode",[32,387,37],{},[39,389,390,400,416],{},[29,391,392,397],{},[44,393,394],{},[52,395,396],{},"Database.qb",[44,398,399],{},"Query builder propre au dialecte de la connexion (propriété calculée).",[29,401,402,407],{},[44,403,404],{},[52,405,406],{},"Database._run_query(query, ...)",[44,408,409,410,412,413,369],{},"Exécute un objet Query Builder sur cette connexion, comme ",[52,411,356],{},", sans être limité à ",[52,414,415],{},"frappe.local.db",[29,417,418,423],{},[44,419,420],{},[52,421,422],{},"Database._get_query(...)",[44,424,425],{},"Construit une requête de type reportview sur cette connexion.",[14,427,428,431,432,434,435,369],{},[52,429,430],{},"frappe.database.query.Engine"," accepte aussi une instance de ",[52,433,168],{}," : ",[52,436,437],{},"Engine(db=get_log_db())",[14,439,440,441,50,443,260,446,260,449,260,452,260,455,260,458,461,462,465],{},"Les helpers de ",[52,442,168],{},[52,444,445],{},"get_value",[52,447,448],{},"set_value",[52,450,451],{},"get_values",[52,453,454],{},"count",[52,456,457],{},"delete",[52,459,460],{},"bulk_insert",", etc.) utilisent le query builder de leur propre connexion : ",[52,463,464],{},"get_log_db().set_value(...)"," écrit bien dans la base de logs.",[467,468,470],"h3",{"id":469},"ne-pas-joindre-les-deux-bases","Ne pas joindre les deux bases",[14,472,473,474,50,477,480,481,484],{},"Une requête SQL ne peut pas joindre une table de la base de logs à une table de la base principale. Le rapport ",[47,475,476],{},"Connexions par type d'utilisateur",[52,478,479],{},"User Connections",") illustre la démarche retenue : compter les connexions par utilisateur et par jour dans la base de logs, puis compléter le type de chaque utilisateur (",[52,482,483],{},"User.user_type",") depuis la base principale avant d'agréger les résultats en Python.",[14,486,487,488,491,492,50,495,498,499,502],{},"Pour regrouper par jour dans la base de logs, utilisez la fonction ",[52,489,490],{},"Date"," de ",[52,493,494],{},"frappe.query_builder.functions",[52,496,497],{},"DATE()"," de SQLite) plutôt que ",[52,500,501],{},"CalendarDate",", qui suit le dialecte de la base principale.",[467,504,506],{"id":505},"index","Index",[14,508,509,512],{},[52,510,511],{},"frappe.db.add_index"," cible la base principale. Créez les index composites sur la base de logs, après avoir créé la table :",[270,514,516],{"className":272,"code":515,"language":274,"meta":275,"style":275},"def on_doctype_update():\n    log_table(\"Activity Log\")\n    db = get_log_db()\n    db.add_index(\"Activity Log\", [\"reference_doctype\", \"reference_name\"])\n",[52,517,518,523,528,532],{"__ignoreMap":275},[279,519,520],{"class":281,"line":282},[279,521,522],{},"def on_doctype_update():\n",[279,524,525],{"class":281,"line":288},[279,526,527],{},"    log_table(\"Activity Log\")\n",[279,529,530],{"class":281,"line":294},[279,531,297],{},[279,533,534],{"class":281,"line":300},[279,535,536],{},"    db.add_index(\"Activity Log\", [\"reference_doctype\", \"reference_name\"])\n",[18,538,540],{"id":539},"points-de-vigilance","Points de vigilance",[362,542,543,563,577,592],{},[234,544,545,434,548,551,552,555,556,559,560,562],{},[47,546,547],{},"Truncate",[52,549,550],{},"frappe.db.truncate(\"Error Log\")"," échoue, car aucune table ",[52,553,554],{},"tabError Log"," n'existe dans la base principale. ",[52,557,558],{},"clear_error_logs"," exécute un ",[52,561,343],{}," sans filtre sur la connexion de la base de logs.",[234,564,565,568,569,572,573,576],{},[47,566,567],{},"Mise à jour d'un champ"," : préférez ",[52,570,571],{},"connection_for(doctype).set_value(...)"," à ",[52,574,575],{},"frappe.db.set_value(...)"," lorsque le DocType peut être un DocType de journalisation. C'est le choix retenu par l'anonymisation des politiques de rétention des données, qui parcourt aussi la base de logs.",[234,578,579,434,584,587,588,591],{},[47,580,581],{},[52,582,583],{},"db_set",[52,585,586],{},"SQLiteLogDocument.db_set"," écrit dans la base de logs et valide (",[52,589,590],{},"commit",") cette connexion. Ne validez pas la transaction de la base principale à sa place.",[234,593,594,434,600,603,604,607],{},[47,595,596,597],{},"Commande ",[52,598,599],{},"clear-log-table",[52,601,602],{},"bench --site \u003Csite> clear-log-table --doctype \u003CDocType> --days \u003CN>"," ne fait pas de sauvegarde préalable pour un DocType de journalisation (",[52,605,606],{},"scheduled_backup"," n'exporte que la base principale) et nettoie directement la base de logs.",[18,609,611],{"id":610},"confidentialité-et-rétention","Confidentialité et rétention",[14,613,614,615,618,619,621],{},"La suppression des données personnelles et l'anonymisation des politiques de rétention des données couvrent aussi la base de logs, via ",[52,616,617],{},"connection_for",". Les durées de rétention saisies dans les ",[47,620,328],{}," s'appliquent aussi à ces DocTypes.",[18,623,625],{"id":624},"voir-aussi","Voir aussi",[362,627,628],{},[234,629,630],{},[631,632,636],"a",{"href":633,"rel":634},"https:\u002F\u002Fgitlab.com\u002Fdokos\u002Fdodock\u002F-\u002Fblob\u002Fdevelop\u002Ffrappe\u002Futils\u002Flogging.py",[635],"nofollow","Code du module de journalisation",[638,639,640],"style",{},"html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":275,"searchDepth":282,"depth":288,"links":642},[643,644,645,647,648,652,653,654],{"id":20,"depth":288,"text":21},{"id":124,"depth":288,"text":125},{"id":139,"depth":288,"text":646},"Référence de frappe.utils.logging",{"id":228,"depth":288,"text":229},{"id":347,"depth":288,"text":348,"children":649},[650,651],{"id":469,"depth":294,"text":470},{"id":505,"depth":294,"text":506},{"id":539,"depth":288,"text":540},{"id":610,"depth":288,"text":611},{"id":624,"depth":288,"text":625},"Découvrez la base de logs SQLite de Dodock, les DocTypes de journalisation virtuels qui y sont stockés et les API développeur pour lire, écrire et nettoyer ces journaux.","md",{"icon":658,"reviewed":309,"last_verified":659,"sources":660},"i-mdi-database","2026-11-03",[661,662,663,664,665,666],"dodock@develop:frappe\u002Futils\u002Flogging.py","dodock@develop:frappe\u002Futils\u002Fsqlite_document.py","dodock@develop:frappe\u002Fdatabase\u002Fdatabase.py","dodock@develop:frappe\u002Fcore\u002Fdoctype\u002Flog_settings\u002Flog_settings.py","dodock@develop:frappe\u002Fcore\u002Fdoctype\u002Factivity_log\u002Factivity_log.py","dodock@develop:frappe\u002Fcommands\u002Fsite.py","\u002Fnext\u002Fdodock\u002Fdeveloppeurs\u002Fstockage-journaux-sqlite",{"title":5,"description":655},"2.next\u002F3.dodock\u002F10.developpeurs\u002Fstockage-journaux-sqlite","AX5dazpdR9ZG20V-72ftVEC0wIGPo9j_mYbcctEU32g",{},1793871752045]