Stockage de logs datés

Voir le sujet précédent Voir le sujet suivant Aller en bas

Stockage de logs datés

Message par skypop le Mer 14 Sep - 0:19

à l'origine, je voulais juste transmettre une suggestion à arc13 pour son programme Player detector : http://forum.computercraft.fr/t394-programme-player-detector-v0-7-6

Mais quitte à présenter l'astuce, autant la mettre à disposition de tout le monde.

Dans la plupart des programmes, les fichiers "logs" sont des journaux qui permettent de garder une trace de l’exécution, ce qui a marché, ou pas, et quand, voire par qui, etc.
Le format standard est un fichier texte brut, un ligne par log. Elle commence par la date et l'heure, suivi de tout le détail nécessaire.

Le format de la date est précis et très utile, il suit cette norme : YYYY-MM-DD hh:mm:ss par exemple "2016-09-04 23:39:44"
C'est assez utile pour trier ou rechercher. Inscrit de cette manière, les dates s'ordonne de fait par ordre chronologique, du plus ancien au plus récent. Car il n'est pas très recommandé de stocker toutes les logs dans un même fichier. Plus le temps passe et plus il risque de devenir volumineux. L'écriture des logs en est potentiellement ralentie, et leur consultation est plus contraignante.

La première astuce est donc d’attribuer la date du jour dans le nom de fichier. Et comme ce format de date est pratique, les différents fichiers de logs seront de fait trié de par leur nom.
Malheureusement le World Interface (Life is peripheral) qui fournit facilement la date et l'heure IRL ne suit pas ce format. (1)
Voici donc une fonction qui traduit une date/heure du format "Sun Sep 04 23:39:44 CEST 2016" au format "2016-09-04 23:39:44"
Code:
local monthNum = {["Jan"]="01",["Feb"]="02",["Mar"]="03",["Apr"]="04",["May"]="05",["Jun"]="06",["Jul"]="07",["Aug"]="08",["Sep"]="09",["Oct"]="10",["Nov"]="11",["Dec"]="12"}
local function dateConv(str)
  local month,day,h,m,s,year = string.match(str, "%a+ (%a+) (%d+) (%d+):(%d+):(%d+) CEST (%d+)")
  return string.format("%s-%s-%s",tostring(year),monthNum[month],tostring(day)),tostring(h),string.format("%s:%s:%s",tostring(h),tostring(m),tostring(s))
end
En fait elle retourne ça en 3 variables :
- le jour : "2016-09-04"
- l'heure courante : "23" (string)
- l'heure précise : "23:39:44"

Je trouve ça plus pratique dans le contexte de génération de log files. Le jour pouvant servir de nom de fichier, la date et l'heure précise de préfixe à la ligne de log.
Dans le cas de logs conséquente, l'heure courante peut servir pour diminuer le volume des fichiers en augmentant leur nombre. C'est à dire, employer la date + heure courante comme nom de fichier.

C'est une bonne astuce de stocker les logs sur un support amovible, comme un floppy disk, si le programme qui génère les logs est une boucle infinie. Vous n'aurez pas à interrompre ce programme pour pouvoir consulter les logs. Vous pourrez également remplacer une disquette pleine par une disquette neuve, et stocker ça dans un coffre ou reformater la disquette pour vous en resservir.
Le volume d'une disquette est limité. Si c'est insuffisant, sachez que vous pouvez mettre un computer dans le Disk Drive à la place. Leur capacité de stockage est plus élevée. Il me semble qu'elle est de 900000 sur un Advanced Computer. Pour que ça marche vous devrez initialiser le computer. Posez le par terre, attribuez lui un label, créez un fichier (n'importe quoi) et supprimez ce fichier ensuite.

J'ai remarqué que d'enregistrer les logs à la volée (l'accès et l'écriture des fichiers) ralentissait de beaucoup la boucle principale de mon programme (rythmé par os.pullEvent).
J’accumule donc les logs dans une table "buffer", enregistre le tout à intervalle régulier et vide le buffer ensuite.

Code:
local logBuffer = {}
local function addLog(k,v,t)
  local _d,_,_t = dateConv(wi.getRealDate())
  logBuffer[#logBuffer+1] = string.format(
    "%s_%s|%s:%s|%s",
    _d,_t,
    v and "200" or "403", k,
    type(t)=="table" and textutils.serializeJSON(t) or tostring(t)
  )
end

J'utilise beaucoup la fonction string.format(), et je vous la recommande, car outre le fait qu'elle permet d'économiser beaucoup de guillemets et doubles points pour insérer des variables, elle effectue également une vérification du type.
Plus d'infos ici : http://www.luteus.biz/Download/LoriotPro_Doc/LUA/LUA_Training_FR/LUA_Fonction_Chaine.html#Lua_Fonction_chaine_5

Voici ma fonction qui enregistre les logs dans un fichier :
Code:
local root = "disk" -- ou peripheral.call("drive_123","getMountPath")
local function saveLog()
  local _d,_h = dateConv(wi.getRealDate())
  local path = "/"..root.."/".._d.."_".._h
  local fh = fs.open(path, fs.exists(path) and "a" or "w")
  fh.write(table.concat(logBuffer, "\n").."\n")
  fh.close()
  logBuffer={}
end

Notez cette ligne, car elle fait tout le travail :
Code:
local fh = fs.open(path, fs.exists(path) and "a" or "w")
Le second paramètre de la fonction fs.open() est variable.
Si le fichier path n'existe pas, le paramètre sera "w" (write) qui initialise le fichier et l'écrit du début.
Si le fichier path existe déjà, le paramètre sera "a" (append). Cette option fait que ce qu'on y écrira s'ajoutera à la suite du contenu déjà présent dans ce fichier.

Dans ma boucle principale, j'ai un timer spécifique, pour enregistrer les logs à intervalle régulier indépendamment de la cadence de mon  programme.
Par exemple, la boucle peut être cadencé à un tour par seconde, et les logs enregistrées toutes les 60 secondes.

Code:
local timerTick = os.startTimer(1)
local timerSaveLog = os.startTimer(60)
while true do
  local e, p1,p2,p3,p4,p5 = _pullEventRaw()
  if  e=="event au pif" then
    process() -- Processus principal
  elseif e=="timer" and p1==timerTick then
  --rythme du programme
    term.setCursorPos(1,1)
    term.clearLine()
    term.write(os.clock()
    timerTick = os.startTimer(1)
  elseif e=="timer" and p1==timerSaveLog then
  --L'heure est venue d'enregistrer les logs
    if #logBuffer>0 then
      saveLog()
    end
    timerSaveLog = os.startTimer(60)
  end
end

Réf(1) :
Date au format ISO 8601 : 2004-02-12T15:19:21+00:00


Dernière édition par skypop le Lun 3 Oct - 3:22, édité 2 fois (Raison : Erreur dans le code)
avatar
skypop

Messages : 94
Date d'inscription : 25/07/2016

Revenir en haut Aller en bas

Re: Stockage de logs datés

Message par GenialJerome le Mer 14 Sep - 13:58

Pour le formatage de la date du WorldInterface, il y a aussi mon API : [API] Format de la date du WorldInterface Wink
avatar
GenialJerome

Messages : 19
Date d'inscription : 14/04/2016
Age : 17
Localisation : Pas loin de eagle_fire

Revenir en haut Aller en bas

Re: Stockage de logs datés

Message par skypop le Jeu 15 Sep - 13:49

Désolé, j'avais l'intention de le mentionner, et puis j'ai zappé..
avatar
skypop

Messages : 94
Date d'inscription : 25/07/2016

Revenir en haut Aller en bas

Voir le sujet précédent Voir le sujet suivant Revenir en haut


 
Permission de ce forum:
Vous ne pouvez pas répondre aux sujets dans ce forum