92_SingleFileLog.pm 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367
  1. ##############################################
  2. # $Id: 92_SingleFileLog.pm 14888 2017-08-13 12:07:12Z rudolfkoenig $
  3. package main;
  4. use strict;
  5. use warnings;
  6. use IO::File;
  7. use Blocking;
  8. #####################################
  9. sub
  10. SingleFileLog_Initialize($)
  11. {
  12. my ($hash) = @_;
  13. $hash->{DefFn} = "SingleFileLog_Define";
  14. $hash->{NotifyFn} = "SingleFileLog_Log";
  15. $hash->{AttrFn} = "SingleFileLog_Attr";
  16. no warnings 'qw';
  17. my @attrList = qw(
  18. addStateEvent:1,0
  19. disable:1,0
  20. disabledForIntervals
  21. dosLineEnding:1,0
  22. numberFormat
  23. readySuffix
  24. syncAfterWrite:1,0
  25. template:textField-long
  26. writeInBackground:1,0
  27. );
  28. use warnings 'qw';
  29. $hash->{AttrList} = join(" ", @attrList);
  30. }
  31. #####################################
  32. sub
  33. SingleFileLog_Define($@)
  34. {
  35. my ($hash, $def) = @_;
  36. my @a = split("[ \t][ \t]*", $def);
  37. my $fh;
  38. return "wrong syntax: define <name> SingleFileLog filename regexp"
  39. if(int(@a) != 4);
  40. return "Bad regexp: starting with *" if($a[3] =~ m/^\*/);
  41. eval { "Hallo" =~ m/^$a[3]$/ };
  42. return "Bad regexp: $@" if($@);
  43. $hash->{FH} = $fh;
  44. $hash->{REGEXP} = $a[3];
  45. $hash->{LOGFILE} =
  46. ($a[2] =~ m/^{.*}$/ ? AnalyzePerlCommand(undef, $a[2]) : $a[2]);
  47. $hash->{STATE} = "active";
  48. readingsSingleUpdate($hash, "filecount", 0, 0);
  49. notifyRegexpChanged($hash, $a[3]);
  50. return undef;
  51. }
  52. #####################################
  53. sub
  54. SingleFileLog_Log($$)
  55. {
  56. # Log is my entry, Dev is the entry of the changed device
  57. my ($log, $dev) = @_;
  58. return if($log->{READONLY});
  59. my $ln = $log->{NAME};
  60. return if(IsDisabled($ln));
  61. my $events = deviceEvents($dev, AttrVal($ln, "addStateEvent", 0));
  62. return if(!$events);
  63. my $n = $dev->{NAME};
  64. my $re = $log->{REGEXP};
  65. my $max = int(@{$events});
  66. my $tn = $dev->{NTFY_TRIGGERTIME};
  67. my $ct = $dev->{CHANGETIME};
  68. for (my $i = 0; $i < $max; $i++) {
  69. my $s = $events->[$i];
  70. $s = "" if(!defined($s));
  71. my $t = (($ct && $ct->[$i]) ? $ct->[$i] : $tn);
  72. if($n =~ m/^$re$/ || "$n:$s" =~ m/^$re$/ || "$t:$n:$s" =~ m/^$re$/) {
  73. my $fc = ReadingsVal($ln,"filecount",0)+1;
  74. readingsSingleUpdate($log, "filecount", $fc, 0);
  75. my %arg = (log=>$log, dev=>$dev, evt=>$s);
  76. if(AttrVal($ln, "writeInBackground",0)) {
  77. BlockingCall("SingleFileLog_Write", \%arg);
  78. } else {
  79. SingleFileLog_Write(\%arg);
  80. }
  81. }
  82. }
  83. return "";
  84. }
  85. ###################################
  86. sub
  87. SingleFileLog_Write($)
  88. {
  89. my ($ptr) = @_;
  90. my ($log, $dev, $EVENT) = ($ptr->{log}, $ptr->{dev}, $ptr->{evt});
  91. my $NAME = $dev->{NAME};
  92. my $ln = $log->{NAME};
  93. my ($seconds, $microseconds) = gettimeofday();
  94. my @time = localtime($seconds);
  95. my $f = $log->{LOGFILE};
  96. my $fc = ReadingsVal($ln,"filecount",0);
  97. $f =~ s/%Q/$fc/g;
  98. $f = ResolveDateWildcards($f, @time);
  99. Log3 $ln, 4, "$ln: Writing $f";
  100. my $time = $dev->{NTFY_TRIGGERTIME};
  101. my $time14 = sprintf("%04d%02d%02d%02d%02d%02d",
  102. $time[5]+1900,$time[4]+1,$time[3],$time[2],$time[1],$time[0]);
  103. my $time16 = $time14.sprintf("%02d", $microseconds/100000);
  104. my ($decl,$idx) = ("",0);
  105. my $nf = AttrVal($ln, "numberFormat", "%1.6E");
  106. foreach my $part (split(" ", $EVENT)) {
  107. $decl .= "my \$EVTPART$idx='$part';";
  108. $decl .= "my \$EVTNUM$idx='".sprintf($nf,$part)."';"
  109. if(looks_like_number($part));
  110. $idx++;
  111. }
  112. my $template = AttrVal($ln, "template", '$time $NAME $EVENT\n');
  113. $template = "\"$template\"" if($template !~ m/^{.*}$/);
  114. my $data = eval "$decl $template";
  115. if($@) {
  116. Log3 $ln, 1, "$ln: error evaluating template: $@";
  117. return;
  118. }
  119. $data =~ s/\n/\r\n/mg if(AttrVal($ln, "dosLineEnding", 0));
  120. my $fh = new IO::File ">>$f.tmp";
  121. if(!defined($fh)) {
  122. Log3 $ln, 1, "$ln: Can't open $f.tmp: $!";
  123. return;
  124. }
  125. print $fh $data;
  126. $fh->sync if($^O ne 'MSWin32' && AttrVal($ln, "syncAfterWrite", 0));
  127. close($fh);
  128. my $sfx = AttrVal($ln, "readySuffix", ".rdy");
  129. $sfx = "" if(lc($sfx) eq "none");
  130. if($sfx ne ".tmp") {
  131. Log3 $ln, 1, "Cannot rename $f.tmp to $f$sfx" if(!rename "$f.tmp","$f$sfx");
  132. }
  133. }
  134. ###################################
  135. sub
  136. SingleFileLog_Attr(@)
  137. {
  138. my @a = @_;
  139. my $do = 0;
  140. if($a[0] eq "set" && $a[2] eq "disable") {
  141. $do = (!defined($a[3]) || $a[3]) ? 1 : 2;
  142. }
  143. $do = 2 if($a[0] eq "del" && (!$a[2] || $a[2] eq "disable"));
  144. return if(!$do);
  145. $defs{$a[1]}{STATE} = ($do == 1 ? "disabled" : "active");
  146. return undef;
  147. }
  148. 1;
  149. =pod
  150. =item helper
  151. =item summary write single events to a separate file each, using templates
  152. =item summary_DE schreibt einzelne Events in separate Dateien via templates
  153. =begin html
  154. <a name="SingleFileLog"></a>
  155. <h3>SingleFileLog</h3>
  156. <ul>
  157. <br>
  158. <a name="SingleFileLogdefine"></a>
  159. <b>Define</b>
  160. <ul>
  161. <code>define &lt;name&gt; SingleFileLog &lt;filename&gt; &lt;regexp&gt;
  162. </code>
  163. <br><br>
  164. For each event or devicename:event matching the &lt;regexp&gt; create a
  165. new file &lt;filename&gt;<br>
  166. <code>&lt;filename&gt;</code> may contain %-wildcards of the
  167. POSIX strftime function of the underlying OS (see your strftime manual),
  168. additionally %Q is replaced with a sequential number unique to the
  169. SingleFileLog device. The file content is based on the template attribute,
  170. see below.<br>
  171. If the filename is enclosed in {} then it is evaluated as a perl expression,
  172. which can be used to use a global path different from %L.
  173. </ul>
  174. <br>
  175. <a name="SingleFileLogset"></a>
  176. <b>Set</b> <ul>N/A</ul><br>
  177. <a name="SingleFileLogget"></a>
  178. <b>Get</b> <ul>N/A</ul><br>
  179. <a name="SingleFileLogattr"></a>
  180. <b>Attributes</b>
  181. <ul>
  182. <li><a href="#addStateEvent">addStateEvent</a></li><br>
  183. <li><a href="#disable">disable</a></li>
  184. <li><a href="#disabledForIntervals">disabledForIntervals</a></li><br>
  185. <li><a href="#dosLineEnding">dosLineEnding</a><br>
  186. Create the file with the line-terminator used on Windows systems (\r\n).
  187. </li><br>
  188. <li><a name="#numberFormat">numberFormat</a><br>
  189. If a word in the event looks like a number, then it is reformatted using
  190. the numberFormat, and $EVTNUMx is set (analogue to $EVTPARx). Default is
  191. %1.6E, see the printf manual for details.
  192. </li><br>
  193. <li><a name="#readySuffy">readySuffix</a><br>
  194. The file specified in the definition will be created with the suffix .tmp
  195. and after the file is closed, will be renamed to the value of this
  196. attribute. Default is .rdy, specify none to remove the suffix completely.
  197. </li><br>
  198. <li><a name="#syncAfterWrite">syncAfterWrite</a><br>
  199. Force the operating system to write the contents to the disc if set to 1.
  200. Note: this can slow down the writing, and may shorten the life of SSDs.
  201. Defalt is 0 (off)
  202. </li><br>
  203. <li><a name="#template">template</a><br>
  204. This attribute specifies the content of the file. Following variables
  205. are replaced before writing the file:
  206. <ul>
  207. <li>$EVENT - the complete event</li>
  208. <li>$EVTPART0 $EVTPART1 ... - the event broken into single words</li>
  209. <li>$EVTNUM0 $EVTNUM1 ... - reformatted as numbers, see numberFormat
  210. above</li>
  211. <li>$NAME - the name of the device generating the event</li>
  212. <li>$time - the current time, formatted as YYYY-MM-DD HH:MM:SS</li>
  213. <li>$time14 - the current time, formatted as YYYYMMDDHHMMSS</li>
  214. <li>$time16 - the current time, formatted as YYYYMMDDHHMMSSCC,
  215. where CC is the hundredth second</li>
  216. </ul>
  217. If the template is enclosed in {} than it will be evaluated as a perl
  218. expression, and its result is written to the file.<br>
  219. Default is $time $NAME $EVENT\n
  220. </li><br>
  221. <li><a name="#writeInBackground">writeInBackground</a><br>
  222. if set (to 1), the writing will occur in a background process to avoid
  223. blocking FHEM. Default is 0.
  224. </li><br>
  225. </ul>
  226. <br>
  227. </ul>
  228. =end html
  229. =begin html_DE
  230. <a name="SingleFileLog"></a>
  231. <h3>SingleFileLog</h3>
  232. <ul>
  233. <br>
  234. <a name="SingleFileLogdefine"></a>
  235. <b>Define</b>
  236. <ul>
  237. <code>define &lt;name&gt; SingleFileLog &lt;filename&gt; &lt;regexp&gt;
  238. </code>
  239. <br><br>
  240. F&uuml; jedes Event oder Ger&auml;tename:Event, worauf &lt;regexp&gt;
  241. zutrifft, wird eine separate Datei angelegt, der Inhalt wird von dem
  242. template Attribut gesteuert (s.u.).
  243. <code>&lt;filename&gt;</code> kann %-Wildcards der POSIX strftime-Funktion
  244. des darunterliegenden OS enthalten (siehe auch man strftime).
  245. Zus&auml;tzlich wird %Q durch eine fortlaufende Zahl ersetzt.<br>
  246. Falls filename in {} eingeschlossen ist, dann wird sie als perl-Ausdruck
  247. ausgewertet, was erm&ouml;glicht einen vom %L abweichenden globalem Pfad zu
  248. definieren.
  249. </ul>
  250. <br>
  251. <a name="SingleFileLogset"></a>
  252. <b>Set</b> <ul>N/A</ul><br>
  253. <a name="SingleFileLogget"></a>
  254. <b>Get</b> <ul>N/A</ul><br>
  255. <a name="SingleFileLogattr"></a>
  256. <b>Attribute</b>
  257. <ul>
  258. <li><a href="#addStateEvent">addStateEvent</a></li><br>
  259. <li><a href="#disable">disable</a></li>
  260. <li><a href="#disabledForIntervals">disabledForIntervals</a></li><br>
  261. <li><a href="#dosLineEnding">dosLineEnding</a><br>
  262. Erzeugt die Datei mit der auf Windows Systemen ueblichen Zeilenenden
  263. (\r\n)
  264. </li><br>
  265. <li><a name="#numberFormat">numberFormat</a><br>
  266. Falls ein Wort im Event wie eine Zahl aussieht, dann wird es wie in
  267. numberFormat angegeben, umformatiert, und als $EVTNUMx (analog zu
  268. $EVTPARTx) zur Verf&uuml;gung gestellt. Voreinstellung ist %1.6E, siehe
  269. die printf Formatbeschreibung f&uuml;r Details.
  270. </li><br>
  271. <li><a name="#readySuffy">readySuffix</a><br>
  272. Die in der Definition spezifizierte Datei wird mit dem Suffix .tmp
  273. angelegt, und nachdem sie fertig ist, mit readySuffix umbenannt.
  274. Die Voreinstellung ist .rdy; um kein Suffix zu erzeugen, muss man none
  275. spezifizieren.
  276. </li><br>
  277. <li><a name="#syncAfterWrite">syncAfterWrite</a><br>
  278. Zwingt das Betriebssystem die Datei sofort auf die Platte zu schreiben.
  279. Achtung: das kann die Geschwindigkeit beeintr&auml;chtigen, und bei SSDs
  280. zu einem fr&uuml;heren Ausfall f&uuml;hren. Die Voreinstellung ist 0
  281. (aus).
  282. </li><br>
  283. <li><a name="#template">template</a><br>
  284. Damit wird der Inhalt der geschriebenen Datei spezifiziert. Folgende
  285. Variablen werden vor dem Schreiben der Datei ersetzt:
  286. <ul>
  287. <li>$EVENT - das vollst&auml;ndige Event.</li>
  288. <li>$EVTPART0 $EVTPART1 ... - die einzelnen W&ouml;rter des Events.</li>
  289. <li>$EVTNUM0 $EVTNUM1 ... - umformatiert als Zahl, siehe numberFormat
  290. weiter oben.</li>
  291. <li>$NAME - der Name des Ger&auml;tes, das das Event generiert.</li>
  292. <li>$time - die aktuelle Zeit, formatiert als YYYY-MM-DD HH:MM:SS</li>
  293. <li>$time14 - die aktuelle Zeit, formatiert als YYYYMMDDHHMMSS</li>
  294. <li>$time16 - die aktuelle Zeit, formatiert als YYYYMMDDHHMMSSCC,
  295. wobei CC die hundertstel Sekunde ist</li>
  296. </ul>
  297. Falls template in {} eingeschlossen ist, dann wird er als perl-Ausdruck
  298. ausgefuehrt, und das Ergebnis wird in die Datei geschrieben.<br>
  299. Die Voreinstellung ist $time $NAME $EVENT\n
  300. </li><br>
  301. <li><a name="#writeInBackground">writeInBackground</a><br>
  302. falls gesetzt (auf 1), dann erfolgt das Schreiben in einem
  303. Hintergrundprozess, um FHEM nicht zu blockieren. Die Voreinstellung ist 0
  304. (aus).
  305. </li><br>
  306. </ul>
  307. <br>
  308. </ul>
  309. =end html_DE
  310. =cut