1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
|
<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE erlref SYSTEM "erlref.dtd">
<erlref>
<header>
<copyright>
<year>1997</year>
<year>2016</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
The Initial Developer of the Original Code is Ericsson AB.
</legalnotice>
<title>disk_log</title>
<prepared>Claes Wikström</prepared>
<responsible>Claes Wikström</responsible>
<docno></docno>
<approved>nobody</approved>
<checked>no</checked>
<date>1999-10-10</date>
<rev>D</rev>
<file>disk_log.sgml</file>
</header>
<module>disk_log</module>
<modulesummary>A disk based term logging facility</modulesummary>
<description>
<p><c>disk_log</c> is a disk based term logger which makes
it possible to efficiently log items on files.
Two types of logs are supported,
<em>halt logs</em> and <em>wrap logs</em>. A halt log
appends items to a single file, the size of which may or may
not be limited by the disk log module, whereas a wrap log utilizes
a sequence of wrap log files of limited size. As a wrap log file
has been filled up, further items are logged onto to the next
file in the sequence, starting all over with the first file when
the last file has been filled up. For the sake of efficiency,
items are always written to files as binaries.
</p>
<p>Two formats of the log files are supported, the <em>internal format</em> and the <em>external format</em>. The internal
format supports automatic repair of log files that have not been
properly closed, and makes it possible to efficiently read
logged items in <em>chunks</em> using a set of functions defined
in this module. In fact, this is the only way to read internally
formatted logs. The external format leaves it up to the user to
read the logged deep byte lists. The disk log module cannot
repair externally formatted logs. An item logged to an
internally formatted log must not occupy more than 4 GB of disk
space (the size must fit in 4 bytes).
</p>
<p>For each open disk log there is one process that handles requests
made to the disk log; the disk log process is created when <c>open/1</c>
is called, provided there exists no process handling the disk log.
A process that opens a disk log can either be an <em>owner</em>
or an anonymous <em>user</em> of the disk log. Each owner is
linked to the disk log
process, and the disk log is closed by the owner should the
owner terminate. Owners can subscribe to <em>notifications</em>,
messages of the form <c>{disk_log, Node, Log, Info}</c> that are sent
from the disk log process when certain events occur, see
the commands below and in particular the <c>open/1</c> option
<seealso marker="#notify">notify</seealso>.
There can be several owners of a log, but a process cannot own a
log more than once. One and the same process may, however,
open the log
as a user more than once. For a disk log process to properly close
its file and terminate, it must be closed by its owners and once by
some non-owner process for each time the log was used anonymously;
the users are counted, and there must not be any users left when the
disk log process terminates.
</p>
<p>Items can be logged <em>synchronously</em> by using the functions
<c>log/2</c>, <c>blog/2</c>, <c>log_terms/2</c> and
<c>blog_terms/2</c>. For each of these functions, the caller is put
on hold until the items have been logged (but not necessarily
written, use <c>sync/1</c> to ensure that). By adding an <c>a</c>
to each of the mentioned function names we get functions that log
items <em>asynchronously</em>. Asynchronous functions do not wait for
the disk log process to actually write the items to the file, but
return the control to the caller more or less immediately.
</p>
<p>When using the internal format for logs, the functions
<c>log/2</c>, <c>log_terms/2</c>, <c>alog/2</c>, and
<c>alog_terms/2</c> should be used. These functions log one or
more Erlang terms. By prefixing each of the functions with
a <c>b</c> (for "binary") we get the corresponding <c>blog</c>
functions for the external format. These functions log one or
more deep lists of bytes or, alternatively, binaries of deep lists
of bytes.
For example, to log the string <c>"hello"</c> in ASCII format, we
can use <c>disk_log:blog(Log, "hello")</c>, or
<c>disk_log:blog(Log, list_to_binary("hello"))</c>. The two
alternatives are equally efficient. The <c>blog</c> functions
can be used for internally formatted logs as well, but in
this case they must be called with binaries constructed with
calls to <c>term_to_binary/1</c>. There is no check to ensure
this, it is entirely the responsibility of the caller. If these
functions are called with binaries that do not correspond to
Erlang terms, the <c>chunk/2,3</c> and automatic repair
functions will fail. The corresponding terms (not the binaries)
will be returned when <c>chunk/2,3</c> is called.
</p>
<p>A collection of open disk logs with the same name running on
different nodes is said to be a <em>a distributed disk log</em>
if requests made to any one of the logs are automatically made to
the other logs as well. The members of such a collection will be
called individual distributed disk logs, or just distributed
disk logs if there is no risk of confusion. There is no order
between the members of such a collection. For instance, logged
terms are not necessarily written onto the node where the
request was made before written onto the other nodes. One could
note here that there are a few functions that do not make
requests to all members of distributed disk logs, namely
<c>info</c>, <c>chunk</c>, <c>bchunk</c>, <c>chunk_step</c> and
<c>lclose</c>. An open disk log that is not a distributed disk
log is said to be a <em>local disk log</em>. A local disk log is
accessible only from the node where the disk log process runs,
whereas a distributed disk log is accessible from all nodes in
the Erlang system, with exception for those nodes where a local
disk log with the same name as the distributed disk log exists.
All processes on nodes that have access to a local or
distributed disk log can log items or otherwise change, inspect
or close the log.
</p>
<p>It is not guaranteed that all log files of a distributed disk log
contain the same log items; there is no attempt made to synchronize
the contents of the files. However, as long as at least one of
the involved nodes is alive at each time, all items will be logged.
When logging items to a distributed log, or otherwise trying to
change the log, the replies from individual logs are
ignored. If all nodes are down, the disk log functions
reply with a <c>nonode</c> error.
</p>
<note>
<p>In some applications it may not be acceptable that
replies from individual logs are ignored. An alternative in such
situations is to use several local disk logs instead of one
distributed disk log, and implement the distribution without use
of the disk log module.</p>
</note>
<p>Errors are reported differently for asynchronous log attempts
and other uses of the disk log module. When used synchronously
the disk log module replies with an error message, but when called
asynchronously, the disk log module does not know where to send
the error message. Instead owners subscribing to notifications will
receive an <c>error_status</c> message.
</p>
<p>The disk log module itself does not report errors to the
<c>error_logger</c> module; it is up to the caller to decide
whether the error logger should be employed or not. The function
<c>format_error/1</c> can be used to produce readable messages
from error replies. Information events are however sent to the
error logger in two situations, namely when a log is repaired,
or when a file is missing while reading chunks.
</p>
<p>The error message <c>no_such_log</c> means that the given
disk log is not currently open. Nothing is said about
whether the disk log files exist or not.
</p>
<note>
<p>If an attempt to reopen or truncate a log fails (see
<c>reopen</c> and <c>truncate</c>) the disk log process
immediately terminates. Before the process terminates links to
to owners and blocking processes (see <c>block</c>) are removed.
The effect is that the links work in one direction only; any
process using a disk log has to check for the error message
<c>no_such_log</c> if some other process might truncate or
reopen the log simultaneously.</p>
</note>
</description>
<datatypes>
<datatype>
<name name="log"/>
</datatype>
<datatype>
<name name="dlog_size"/>
</datatype>
<datatype>
<name name="dlog_format"/>
</datatype>
<datatype>
<name name="dlog_head_opt"/>
</datatype>
<datatype>
<name name="dlog_byte"/>
</datatype>
<datatype>
<name name="dlog_mode"/>
</datatype>
<datatype>
<name name="dlog_type"/>
</datatype>
<datatype>
<name name="continuation"/>
<desc><p>Chunk continuation returned by
<c>chunk/2,3</c>, <c>bchunk/2,3</c>, or <c>chunk_step/3</c>.</p>
</desc>
</datatype>
<datatype>
<name name="bytes"/>
</datatype>
<datatype>
<name name="invalid_header"/>
</datatype>
<datatype>
<name name="file_error"/>
</datatype>
</datatypes>
<funcs>
<func>
<name name="accessible_logs" arity="0"/>
<fsummary>Return the accessible disk logs on the current node.</fsummary>
<desc>
<p>The <c>accessible_logs/0</c> function returns
the names of the disk logs accessible on the current node.
The first list contains local disk logs, and the
second list contains distributed disk logs.
</p>
</desc>
</func>
<func>
<name name="alog" arity="2"/>
<name name="balog" arity="2"/>
<fsummary>Asynchronously log an item onto a disk log.</fsummary>
<type variable="Log"/>
<type variable="Term" name_i="1"/>
<type variable="Bytes"/>
<type name="notify_ret"/>
<desc>
<p>The <c>alog/2</c> and <c>balog/2</c> functions asynchronously
append an item to a disk log. The function <c>alog/2</c> is
used for internally formatted logs, and the function <c>balog/2</c>
for externally formatted logs. <c>balog/2</c> can be used
for internally formatted logs as well provided the binary was
constructed with a call to <c>term_to_binary/1</c>.
</p>
<p>The owners that subscribe to notifications will receive the
message <c>read_only</c>, <c>blocked_log</c>
or <c>format_external</c> in case the item cannot be written
on the log, and possibly one of the messages <c>wrap</c>,
<c>full</c> and <c>error_status</c> if an item was written
on the log. The message <c>error_status</c> is sent if there
is something wrong with the header function or a file error
occurred.
</p>
</desc>
</func>
<func>
<name name="alog_terms" arity="2"/>
<name name="balog_terms" arity="2"/>
<fsummary>Asynchronously log several items onto a disk log.</fsummary>
<type variable="Log"/>
<type variable="TermList" name_i="1"/>
<type variable="ByteList"/>
<type name="notify_ret"/>
<desc>
<p>The <c>alog_terms/2</c> and <c>balog_terms/2</c> functions
asynchronously append a list of items to a disk log.
The function <c>alog_terms/2</c> is used for internally
formatted logs, and the function <c>balog_terms/2</c>
for externally formatted logs. <c>balog_terms/2</c> can be used
for internally formatted logs as well provided the binaries were
constructed with calls to <c>term_to_binary/1</c>.
</p>
<p>The owners that subscribe to notifications will receive the
message <c>read_only</c>, <c>blocked_log</c>
or <c>format_external</c> in case the items cannot be written
on the log, and possibly one or more of the messages <c>wrap</c>,
<c>full</c> and <c>error_status</c> if items were written
on the log. The message <c>error_status</c> is sent if there
is something wrong with the header function or a file error
occurred.
</p>
</desc>
</func>
<func>
<name name="block" arity="1"/>
<name name="block" arity="2"/>
<fsummary>Block a disk log.</fsummary>
<type name="block_error_rsn"/>
<desc>
<p>With a call to <c>block/1,2</c> a process can block a log.
If the blocking process is not an owner of the log, a temporary
link is created between the disk log process and the blocking
process. The link is used to ensure that the disk log is
unblocked should the blocking process terminate without
first closing or unblocking the log.
</p>
<p>Any process can probe a blocked log with <c>info/1</c> or
close it with <c>close/1</c>. The blocking process can also
use the functions <c>chunk/2,3</c>, <c>bchunk/2,3</c>,
<c>chunk_step/3</c>, and <c>unblock/1</c> without being
affected by the block. Any other attempt than those hitherto
mentioned to update or read a blocked log suspends the
calling process until the log is unblocked or returns an
error message <c>{blocked_log, <anno>Log</anno>}</c>, depending on
whether the value of <c><anno>QueueLogRecords</anno></c> is <c>true</c>
or <c>false</c>. The default value of <c><anno>QueueLogRecords</anno></c>
is <c>true</c>, which is used by <c>block/1</c>.
</p>
</desc>
</func>
<func>
<name name="change_header" arity="2"/>
<fsummary>Change the head or head_func option for an owner of a disk log.</fsummary>
<desc>
<p>The <c>change_header/2</c> function changes the value of
the <c>head</c> or <c>head_func</c> option of a disk log.</p>
</desc>
</func>
<func>
<name name="change_notify" arity="3"/>
<fsummary>Change the notify option for an owner of a disk log.</fsummary>
<desc>
<p>The <c>change_notify/3</c> function changes the value of the
<c>notify</c> option for an owner of a disk log. </p>
</desc>
</func>
<func>
<name name="change_size" arity="2"/>
<fsummary>Change the size of an open disk log.</fsummary>
<desc>
<p>The <c>change_size/2</c> function changes the size of an open log.
For a halt log it is always possible to increase the size,
but it is not possible to decrease the size to something less than
the current size of the file.
</p>
<p>For a wrap log it is always possible to increase both the
size and number of files, as long as the number of files does not
exceed 65000. If the maximum number of files is decreased, the
change will not be valid until the current file is full and the
log wraps to the next file.
The redundant files will be removed next time the log wraps around,
i.e. starts to log to file number 1.
</p>
<p>As an example, assume that the old maximum number of files
is 10 and that the new maximum number of files is 6. If
the current file number is not greater than the new maximum number
of files, the files 7 to 10 will be removed when file number 6
is full and the log starts to write to file number 1 again.
Otherwise the files greater than the current
file will be removed when the current file is full (e.g. if
the current file is 8, the files 9 and 10); the files between
new maximum number of files and the current
file (i.e. files 7 and 8) will be removed next time file number 6
is full.
</p>
<p>If the size of the files is decreased the change will immediately
affect the current log. It will not of course change the
size of log files already full until next time they are used.
</p>
<p>If the log size is decreased for instance to save space,
the function <c>inc_wrap_file/1</c> can be used to force the log
to wrap.
</p>
</desc>
</func>
<func>
<name name="chunk" arity="2"/>
<name name="chunk" arity="3"/>
<name name="bchunk" arity="2"/>
<name name="bchunk" arity="3"/>
<fsummary>Read a chunk of items written to a disk log.</fsummary>
<type variable="Log"/>
<type variable="Continuation"/>
<type variable="N"/>
<type name="chunk_ret"/>
<type name="bchunk_ret"/>
<type name="chunk_error_rsn"/>
<desc>
<p>The <c>chunk/2,3</c> and <c>bchunk/2,3</c> functions make
it possible to efficiently read the terms which have been
appended to an internally formatted log. It minimizes disk
I/O by reading 64 kilobyte chunks from the file. The
<c>bchunk/2,3</c> functions return the binaries read from
the file; they do not call <c>binary_to_term</c>. Otherwise
the work just like <c>chunk/2,3</c>.
</p>
<p>The first time <c>chunk</c> (or <c>bchunk</c>) is called,
an initial continuation, the atom <c>start</c>, must be
provided. If there is a disk log process running on the
current node, terms are read from that log, otherwise an
individual distributed log on some other node is chosen, if
such a log exists.
</p>
<p>When <c>chunk/3</c> is called, <c><anno>N</anno></c> controls the
maximum number of terms that are read from the log in each
chunk. Default is <c>infinity</c>, which means that all the
terms contained in the 64 kilobyte chunk are read. If less than
<c><anno>N</anno></c> terms are returned, this does not necessarily mean
that the end of the file has been reached.
</p>
<p>The <c>chunk</c> function returns a tuple
<c>{<anno>Continuation2</anno>, <anno>Terms</anno>}</c>, where <c><anno>Terms</anno></c> is a list
of terms found in the log. <c><anno>Continuation2</anno></c> is yet
another continuation which must be passed on to any
subsequent calls to <c>chunk</c>. With a series of calls to
<c>chunk</c> it is possible to extract all terms from a log.
</p>
<p>The <c>chunk</c> function returns a tuple
<c>{<anno>Continuation2</anno>, <anno>Terms</anno>, <anno>Badbytes</anno>}</c> if the log is opened
in read-only mode and the read chunk is corrupt. <c><anno>Badbytes</anno></c>
is the number of bytes in the file which were found not to be
Erlang terms in the chunk. Note also that the log is not repaired.
When trying to read chunks from a log opened in read-write mode,
the tuple <c>{corrupt_log_file, <anno>FileName</anno>}</c> is returned if the
read chunk is corrupt.
</p>
<p><c>chunk</c> returns <c>eof</c> when the end of the log is
reached, or <c>{error, <anno>Reason</anno>}</c> if an error occurs. Should
a wrap log file be missing, a message is output on the error log.
</p>
<p>When <c>chunk/2,3</c> is used with wrap logs, the returned
continuation may or may not be valid in the next call to
<c>chunk</c>. This is because the log may wrap and delete
the file into which the continuation points. To make sure
this does not happen, the log can be blocked during the
search.
</p>
</desc>
</func>
<func>
<name name="chunk_info" arity="1"/>
<fsummary>Return information about a chunk continuation of a disk log.</fsummary>
<desc>
<p>The <c>chunk_info/1</c> function returns the following pair
describing the chunk continuation returned by
<c>chunk/2,3</c>, <c>bchunk/2,3</c>, or <c>chunk_step/3</c>:
</p>
<list type="bulleted">
<item>
<p><c>{node, <anno>Node</anno>}</c>. Terms are read from
the disk log running on <c><anno>Node</anno></c>.</p>
</item>
</list>
</desc>
</func>
<func>
<name name="chunk_step" arity="3"/>
<fsummary>Step forward or backward among the wrap log files of a disk log.</fsummary>
<desc>
<p>The function <c>chunk_step</c> can be used in conjunction
with <c>chunk/2,3</c> and <c>bchunk/2,3</c> to search
through an internally formatted wrap log. It takes as
argument a continuation as returned by <c>chunk/2,3</c>,
<c>bchunk/2,3</c>, or <c>chunk_step/3</c>, and steps forward
(or backward) <c><anno>Step</anno></c> files in the wrap log. The
continuation returned points to the first log item in the
new current file.
</p>
<p>If the atom <c>start</c> is given as continuation, a disk log
to read terms from is chosen. A local or distributed disk log
on the current node is preferred to an
individual distributed log on some other node.
</p>
<p>If the wrap log is not full because all files have not been
used yet, <c>{error, end_of_log}</c> is returned if trying to
step outside the log.
</p>
</desc>
</func>
<func>
<name name="close" arity="1"/>
<fsummary>Close a disk log.</fsummary>
<type name="close_error_rsn"/>
<desc>
<p><marker id="close_1"></marker>The function <c>close/1</c> closes a
local or distributed disk log properly. An internally
formatted log must be closed before the Erlang system is
stopped, otherwise the log is regarded as unclosed and the
automatic repair procedure will be activated next time the
log is opened.
</p>
<p>The disk log process in not terminated as long as there are
owners or users of the log. It should be stressed that each
and every owner must close the log, possibly by terminating,
and that any other process - not only the processes that have
opened the log anonymously - can decrement the <c>users</c>
counter by closing the log.
Attempts to close a log by a process that is
not an owner are simply ignored if there are no users.
</p>
<p>If the log is blocked by the closing process, the log is also
unblocked.
</p>
</desc>
</func>
<func>
<name name="format_error" arity="1"/>
<fsummary>Return an English description of a disk log error reply.</fsummary>
<desc>
<p>Given the error returned by any function in this module,
the function <c>format_error</c> returns a descriptive string
of the error in English. For file errors, the function
<c>format_error/1</c> in the <c>file</c> module is called.</p>
</desc>
</func>
<func>
<name name="inc_wrap_file" arity="1"/>
<fsummary>Change to the next wrap log file of a disk log.</fsummary>
<type name="inc_wrap_error_rsn"/>
<type name="invalid_header"/>
<desc>
<p>The <c>inc_wrap_file/1</c> function forces the internally formatted
disk log to start logging to the
next log file. It can be used, for instance, in conjunction with
<c>change_size/2</c> to reduce the amount of disk space allocated
by the disk log.
</p>
<p>The owners that subscribe to notifications will normally
receive a <c>wrap</c> message, but in case of
an error with a reason tag of <c>invalid_header</c> or
<c>file_error</c> an <c>error_status</c> message will be sent.</p>
</desc>
</func>
<func>
<name name="info" arity="1"/>
<fsummary>Return information about a disk log.</fsummary>
<type name="dlog_info"/>
<desc>
<p>The <c>info/1</c> function returns a list of <c>{Tag, Value}</c>
pairs describing the log. If there is a disk log process running
on the current node, that log is used as source of information,
otherwise an individual distributed log on
some other node is chosen, if such a log exists.
</p>
<p>The following pairs are returned for all logs:
</p>
<list type="bulleted">
<item>
<p><c>{name, <anno>Log</anno>}</c>, where <c><anno>Log</anno></c> is the name of
the log as given by the <c>open/1</c> option <c>name</c>.</p>
</item>
<item>
<p><c>{file, <anno>File</anno>}</c>. For halt logs <c><anno>File</anno></c> is the
filename, and for wrap logs <c><anno>File</anno></c> is the base name.</p>
</item>
<item>
<p><c>{type, <anno>Type</anno>}</c>, where <c><anno>Type</anno></c> is the type of
the log as given by the <c>open/1</c> option <c>type</c>.</p>
</item>
<item>
<p><c>{format, <anno>Format</anno>}</c>, where <c><anno>Format</anno></c> is the format
of the log as given by the <c>open/1</c> option <c>format</c>.</p>
</item>
<item>
<p><c>{size, <anno>Size</anno>}</c>, where <c><anno>Size</anno></c> is the size
of the log as given by the <c>open/1</c> option <c>size</c>,
or the size set by <c>change_size/2</c>. The value set by
<c>change_size/2</c> is reflected immediately.</p>
</item>
<item>
<p><c>{mode, <anno>Mode</anno>}</c>, where <c><anno>Mode</anno></c> is the mode
of the log as given by the <c>open/1</c> option <c>mode</c>.</p>
</item>
<item>
<p><c>{owners, [{pid(), <anno>Notify</anno>}]}</c> where <c><anno>Notify</anno></c>
is the value set by the <c>open/1</c> option <c>notify</c>
or the function <c>change_notify/3</c> for the owners of
the log.</p>
</item>
<item>
<p><c>{users, <anno>Users</anno>}</c> where <c><anno>Users</anno></c> is the number
of anonymous users of the log, see the <c>open/1</c> option
<seealso marker="#linkto">linkto</seealso>.</p>
</item>
<item>
<p><c>{status, <anno>Status</anno>}</c>, where <c><anno>Status</anno></c> is <c>ok</c>
or <c>{blocked, <anno>QueueLogRecords</anno>}</c> as set by the functions
<c>block/1,2</c> and <c>unblock/1</c>.</p>
</item>
<item>
<p><c>{node, <anno>Node</anno>}</c>. The information returned by the
current invocation of the <c>info/1</c> function has been
gathered from the disk log process running on <c><anno>Node</anno></c>.</p>
</item>
<item>
<p><c>{distributed, <anno>Dist</anno>}</c>. If the log is local on
the current node, then <c><anno>Dist</anno></c> has the value <c>local</c>,
otherwise all nodes where the log is distributed
are returned as a list.</p>
</item>
</list>
<p>The following pairs are returned for all logs opened in
<c>read_write</c> mode:
</p>
<list type="bulleted">
<item>
<p><c>{head, <anno>Head</anno>}</c>. Depending of the value of
the <c>open/1</c> options <c>head</c> and <c>head_func</c>
or set by the function <c>change_header/2</c>, the value
of <c><anno>Head</anno></c> is <c>none</c> (default),
<c>{head, H}</c> (<c>head</c> option) or <c>{M,F,A}</c>
(<c>head_func</c> option).</p>
</item>
<item>
<p><c>{no_written_items, <anno>NoWrittenItems</anno>}</c>, where
<c><anno>NoWrittenItems</anno></c> is the number of items
written to the log since the disk log process was created.</p>
</item>
</list>
<p>The following pair is returned for halt logs opened in
<c>read_write</c> mode:
</p>
<list type="bulleted">
<item>
<p><c>{full, <anno>Full</anno>}</c>, where <c><anno>Full</anno></c> is <c>true</c> or
<c>false</c> depending on whether the halt log is full or not.</p>
</item>
</list>
<p>The following pairs are returned for wrap logs opened in
<c>read_write</c> mode:
</p>
<list type="bulleted">
<item>
<p><c>{no_current_bytes, integer() >= 0}</c> is the number
of bytes written to the current wrap log file.</p>
</item>
<item>
<p><c>{no_current_items, integer() >= 0}</c> is the number
of items written to the current wrap log file, header
inclusive.</p>
</item>
<item>
<p><c>{no_items, integer() >= 0}</c> is the total number
of items in all wrap log files.</p>
</item>
<item>
<p><c>{current_file, integer()}</c> is the ordinal for
the current wrap log file in the range <c>1..MaxNoFiles</c>,
where <c>MaxNoFiles</c> is given by the <c>open/1</c> option
<c>size</c> or set by <c>change_size/2</c>.</p>
</item>
<item>
<p><c>{no_overflows, {<anno>SinceLogWasOpened</anno>, <anno>SinceLastInfo</anno>}}</c>,
where <c><anno>SinceLogWasOpened</anno></c> (<c><anno>SinceLastInfo</anno></c>) is
the number of times a wrap log file has been filled up and a
new one opened or <c>inc_wrap_file/1</c> has been called since
the disk log was last opened (<c>info/1</c>
was last called). The first time <c>info/2</c> is called
after a log was (re)opened or truncated, the two values
are equal.</p>
</item>
</list>
<p>Note that the <c>chunk/2,3</c>, <c>bchunk/2,3</c>, and
<c>chunk_step/3</c> functions do not affect any value
returned by <c>info/1</c>.
</p>
</desc>
</func>
<func>
<name name="lclose" arity="1"/>
<name name="lclose" arity="2"/>
<fsummary>Close a disk log on one node.</fsummary>
<type name="lclose_error_rsn"/>
<desc>
<p>The function <c>lclose/1</c> closes a local log or an
individual distributed log on the current node.
The function <c>lclose/2</c> closes an individual
distributed log on the specified node if the node
is not the current one.
<c>lclose(<anno>Log</anno>)</c> is equivalent to
<c>lclose(<anno>Log</anno>, node())</c>.
See also <seealso marker="#close_1">close/1</seealso>.
</p>
<p>If there is no log with the given name
on the specified node, <c>no_such_log</c> is returned.
</p>
</desc>
</func>
<func>
<name name="log" arity="2"/>
<name name="blog" arity="2"/>
<fsummary>Log an item onto a disk log.</fsummary>
<type variable="Log"/>
<type variable="Term" name_i="1"/>
<type variable="Bytes"/>
<type name="log_error_rsn"/>
<desc>
<p>The <c>log/2</c> and <c>blog/2</c> functions synchronously
append a term to a disk log. They return <c>ok</c> or
<c>{error, <anno>Reason</anno>}</c> when the term has been written to
disk. If the log is distributed, <c>ok</c> is always
returned, unless all nodes are down. Terms are written by
means of the ordinary <c>write()</c> function of the
operating system. Hence, there is no guarantee that the term
has actually been written to the disk, it might linger in
the operating system kernel for a while. To make sure the
item is actually written to disk, the <c>sync/1</c> function
must be called.
</p>
<p>The <c>log/2</c> function is used for internally formatted logs,
and <c>blog/2</c> for externally formatted logs.
<c>blog/2</c> can be used
for internally formatted logs as well provided the binary was
constructed with a call to <c>term_to_binary/1</c>.
</p>
<p>The owners that subscribe to notifications will be notified
of an error with an <c>error_status</c> message if the error
reason tag is <c>invalid_header</c> or <c>file_error</c>.
</p>
</desc>
</func>
<func>
<name name="log_terms" arity="2"/>
<name name="blog_terms" arity="2"/>
<fsummary>Log several items onto a disk log.</fsummary>
<type variable="Log"/>
<type variable="TermList" name_i="1"/>
<type variable="BytesList"/>
<type name="log_error_rsn"/>
<desc>
<p>The <c>log_terms/2</c> and <c>blog_terms/2</c> functions
synchronously append a list of items to the log. The benefit
of using these functions rather than the <c>log/2</c> and
<c>blog/2</c> functions is that of efficiency: the given
list is split into as large sublists as possible (limited by
the size of wrap log files), and each sublist is logged as
one single item, which reduces the overhead.
</p>
<p>The <c>log_terms/2</c> function is used for internally formatted
logs, and <c>blog_terms/2</c> for externally formatted logs.
<c>blog_terms/2</c> can be used
for internally formatted logs as well provided the binaries were
constructed with calls to <c>term_to_binary/1</c>.
</p>
<p>The owners that subscribe to notifications will be notified
of an error with an <c>error_status</c> message if the error
reason tag is <c>invalid_header</c> or <c>file_error</c>.
</p>
</desc>
</func>
<func>
<name name="open" arity="1"/>
<fsummary>Open a disk log file.</fsummary>
<type name="dlog_options"/>
<type name="dlog_option"/>
<type name="open_ret"/>
<type name="ret"/>
<type name="dist_open_ret"/>
<type name="dist_error_rsn"/>
<type name="open_error_rsn"/>
<type name="dlog_optattr"/>
<type name="dlog_size"/>
<desc>
<p>The <c><anno>ArgL</anno></c> parameter is a list of options which have
the following meanings:</p>
<list type="bulleted">
<item>
<p><c>{name, <anno>Log</anno>}</c> specifies the name of the log.
This is the name which must be passed on as a parameter in
all subsequent logging operations. A name must always
be supplied.
</p>
</item>
<item>
<p><c>{file, <anno>FileName</anno>}</c> specifies the name of the
file which will be used for logged terms. If this value is
omitted and the name of the log is either an atom or a string,
the file name will default to <c>lists:concat([<anno>Log</anno>, ".LOG"])</c> for halt logs. For wrap logs, this will be
the base name of the files. Each file in a wrap log
will be called <c><![CDATA[<base_name>.N]]></c>, where <c>N</c> is an
integer. Each wrap log will also have two files called
<c><![CDATA[<base_name>.idx]]></c> and <c><![CDATA[<base_name>.siz]]></c>.
</p>
</item>
<item>
<p><c>{linkto, <anno>LinkTo</anno>}</c>. <marker id="linkto"></marker>
If
<c><anno>LinkTo</anno></c> is a pid, that pid becomes an owner of the
log. If <c><anno>LinkTo</anno></c> is <c>none</c> the log records
that it is used anonymously by some process by
incrementing the <c>users</c> counter. By default, the
process which calls <c>open/1</c> owns the log.
</p>
</item>
<item>
<p><c>{repair, <anno>Repair</anno>}</c>. If <c><anno>Repair</anno></c> is <c>true</c>,
the current log file will be repaired, if needed. As the
restoration is initiated, a message is output on the error log.
If <c>false</c> is given,
no automatic repair will be attempted. Instead, the
tuple <c>{error, {need_repair, <anno>Log</anno>}}</c> is returned if an
attempt is made to open a corrupt log file.
If <c>truncate</c> is given, the log file will
be truncated, creating an empty log. Default is
<c>true</c>, which has no effect on logs opened in
read-only mode.
</p>
</item>
<item>
<p><c>{type, <anno>Type</anno>}</c> is the type of the log. Default
is <c>halt</c>.
</p>
</item>
<item>
<p><c>{format, <anno>Format</anno>}</c> specifies the format of the
disk log. Default is <c>internal</c>.
</p>
</item>
<item>
<p><c>{size, <anno>Size</anno>}</c> specifies the size of the log.
When a halt log has reached its maximum size, all attempts to
log more items are rejected. The default size is
<c>infinity</c>, which for halt implies that there is no
maximum size. For wrap logs, the <c><anno>Size</anno></c> parameter
may be either a pair
<c>{<anno>MaxNoBytes</anno>, <anno>MaxNoFiles</anno>}</c> or <c>infinity</c>. In the
latter case, if the files of an already existing wrap log
with the same name can be found, the size is read
from the existing wrap log, otherwise an error is returned.
Wrap logs write at most <c><anno>MaxNoBytes</anno></c> bytes on each file
and use <c><anno>MaxNoFiles</anno></c> files before starting all over with
the first wrap log file. Regardless of <c><anno>MaxNoBytes</anno></c>,
at least the header (if there is one) and one
item is written on each wrap log file before
wrapping to the next file.
When opening an existing wrap log, it is not
necessary to supply a value for the option <c>Size</c>, but any
supplied value must equal the current size of the log, otherwise
the tuple <c>{error, {size_mismatch, <anno>CurrentSize</anno>, <anno>NewSize</anno>}}</c>
is returned.
</p>
</item>
<item>
<p><c>{distributed, <anno>Nodes</anno>}</c>. This option can be used for
adding members to a distributed disk log. The
default value is <c>[]</c>, which means that
the log is local on the current node.
</p>
</item>
<item>
<marker id="notify"></marker>
<p><c>{notify, bool()}</c>. If <c>true</c>, the owners of the
log are notified when certain events occur in the log.
Default is <c>false</c>. The owners are sent one of the
following messages when an event occurs:
</p>
<list type="bulleted">
<item>
<p><c>{disk_log, Node, Log, {wrap, NoLostItems}}</c> is sent when a wrap log has
filled up one of its files and a new file is
opened. <c>NoLostItems</c> is the number of
previously logged items that have been lost when
truncating existing files.
</p>
</item>
<item>
<p><c>{disk_log, Node, Log, {truncated, NoLostItems}}</c> is sent when a log has been
truncated or reopened. For halt logs <c>NoLostItems</c>
is the number of items written on the log since the
disk log process was created. For wrap logs
<c>NoLostItems</c> is the number of items on all
wrap log files.
</p>
</item>
<item>
<p><c>{disk_log, Node, Log, {read_only, Items}}</c>
is sent when an asynchronous log attempt is made to
a log file opened in read-only mode.
<c>Items</c> is the items from the log attempt.
</p>
</item>
<item>
<p><c>{disk_log, Node, Log, {blocked_log, Items}}</c>
is sent when an asynchronous log attempt is made to
a blocked log that does not queue log attempts.
<c>Items</c> is the items from the log attempt.
</p>
</item>
<item>
<p><c>{disk_log, Node, Log, {format_external, Items}}</c>
is sent when <c>alog/2</c> or <c>alog_terms/2</c> is
used for internally formatted logs. <c>Items</c> is the
items from the log attempt.
</p>
</item>
<item>
<p><c>{disk_log, Node, Log, full}</c> is sent when
an attempt to log items to a wrap log would write more
bytes than the limit set by the <c>size</c> option.
</p>
</item>
<item>
<p><c>{disk_log, Node, Log, {error_status, Status}}</c>
is sent when the error status changes. The error status
is defined by the outcome of the last attempt to log
items to a the log or to truncate the log or the last
use of <c>sync/1</c>, <c>inc_wrap_file/1</c> or
<c>change_size/2</c>. <c>Status</c> is one of <c>ok</c> and
<c>{error, Error}</c>, the former being the initial value.
</p>
</item>
</list>
</item>
<item>
<p><c>{head, <anno>Head</anno>}</c> specifies a header to be
written first on the log file. If the log is a wrap
log, the item <c><anno>Head</anno></c> is written first in each new file.
<c><anno>Head</anno></c> should be a term if the format is
<c>internal</c>, and a deep list of bytes (or a binary)
otherwise. Default is <c>none</c>, which means that
no header is written first on the file.
</p>
</item>
<item>
<p><c>{head_func, {M,F,A}}</c> specifies a function
to be called each time a new log file is opened.
The call <c>M:F(A)</c> is assumed to return <c>{ok, Head}</c>.
The item <c>Head</c> is written first in each file.
<c>Head</c> should be a term if the format is
<c>internal</c>, and a deep list of bytes (or a binary)
otherwise.
</p>
</item>
<item>
<p><c>{mode, <anno>Mode</anno>}</c> specifies if the log is to be
opened in read-only or read-write mode. It defaults to
<c>read_write</c>.
</p>
</item>
</list>
<p>The <c>open/1</c> function returns <c>{ok, <anno>Log</anno>}</c> if the
log file was successfully opened. If the file was
successfully repaired, the tuple <c>{repaired, <anno>Log</anno>, {recovered, <anno>Rec</anno>}, {badbytes, <anno>Bad</anno>}}</c> is returned, where
<c><anno>Rec</anno></c> is the number of whole Erlang terms found in the
file and <c><anno>Bad</anno></c> is the number of bytes in the file which
were non-Erlang terms. If the <c>distributed</c> parameter
was given, <c>open/1</c> returns a list of
successful replies and a list of erroneous replies. Each
reply is tagged with the node name.
</p>
<p>When a disk log is opened in read-write mode, any existing
log file is checked for. If there is none a new empty
log is created, otherwise the existing file is opened at the
position after the last logged item, and the logging of items
will commence from there. If the format is <c>internal</c>
and the existing file is not recognized as an internally
formatted log, a tuple <c>{error, {not_a_log_file, <anno>FileName</anno>}}</c>
is returned.
</p>
<p>The <c>open/1</c> function cannot be used for changing the
values of options of an already open log; when there are prior
owners or users of a log, all option values except <c>name</c>,
<c>linkto</c> and <c>notify</c> are just checked against
the values that have been supplied before as option values
to <c>open/1</c>, <c>change_header/2</c>, <c>change_notify/3</c>
or <c>change_size/2</c>. As a consequence,
none of the options except <c>name</c> is mandatory. If some
given value differs from the current value, a tuple
<c>{error, {arg_mismatch, <anno>OptionName</anno>, <anno>CurrentValue</anno>, <anno>Value</anno>}}</c>
is returned. Caution: an owner's attempt to open a log
as owner once again is acknowledged with the return value
<c>{ok, <anno>Log</anno>}</c>, but the state of the disk log is not
affected in any way.
</p>
<p>If a log with a given name is local on some node,
and one tries to open the log distributed on the same node,
then the tuple <c>{error, {node_already_open, <anno>Log</anno>}}</c> is
returned. The same tuple is returned if the log is distributed on
some node, and one tries to open the log locally on the same node.
Opening individual distributed disk logs for the first time
adds those logs to a (possibly empty) distributed disk log.
The option values supplied are used
on all nodes mentioned by the <c>distributed</c> option.
Individual distributed logs know nothing
about each other's option values, so each node can be
given unique option values by creating a distributed
log with several calls to <c>open/1</c>.
</p>
<p>It is possible to open a log file more than once by giving
different values to the option <c>name</c> or by using the
same file when distributing a log on different nodes.
It is up to the user of the <c>disk_log</c>
module to ensure that no more than one
disk log process has write access to any file, or the
the file may be corrupted.
</p>
<p>If an attempt to open a log file for the first time fails,
the disk log process terminates with the EXIT message
<c>{{failed,Reason},[{disk_log,open,1}]}</c>.
The function returns <c>{error, Reason}</c> for all other errors.
</p>
</desc>
</func>
<func>
<name name="pid2name" arity="1"/>
<fsummary>Return the name of the disk log handled by a pid.</fsummary>
<desc>
<p>The <c>pid2name/1</c> function returns the name of the log
given the pid of a disk log process on the current node, or
<c>undefined</c> if the given pid is not a disk log process.
</p>
<p>This function is meant to be used for debugging only.
</p>
</desc>
</func>
<func>
<name name="reopen" arity="2"/>
<name name="reopen" arity="3"/>
<name name="breopen" arity="3"/>
<fsummary>Reopen a disk log and save the old log.</fsummary>
<type variable="Log"/>
<type variable="File" name_i="1"/>
<type variable="Head" name_i="2"/>
<type variable="BHead"/>
<type name="reopen_error_rsn"/>
<desc>
<p>The <c>reopen</c> functions first rename the log file
to <c><anno>File</anno></c> and then re-create a new log file.
In case of a wrap log, <c><anno>File</anno></c> is used as the base name
of the renamed files.
By default the header given to <c>open/1</c> is written first in
the newly opened log file, but if the <c><anno>Head</anno></c> or the
<c><anno>BHead</anno></c> argument is given, this item is used instead.
The header argument is used once only; next time a wrap log file
is opened, the header given to <c>open/1</c> is used.
</p>
<p>The <c>reopen/2,3</c> functions are used for internally formatted
logs, and <c>breopen/3</c> for externally formatted logs.
</p>
<p>The owners that subscribe to notifications will receive
a <c>truncate</c> message.
</p>
<p>Upon failure to reopen the log, the disk log process terminates
with the EXIT message <c>{{failed,Error},[{disk_log,Fun,Arity}]}</c>,
and other processes that have requests queued receive the message
<c>{disk_log, Node, {error, disk_log_stopped}}</c>.
</p>
</desc>
</func>
<func>
<name name="sync" arity="1"/>
<fsummary>Flush the contents of a disk log to the disk.</fsummary>
<type name="sync_error_rsn"/>
<desc>
<p>The <c>sync/1</c> function ensures that the contents of the
log are actually written to the disk.
This is usually a rather expensive operation.
</p>
</desc>
</func>
<func>
<name name="truncate" arity="1"/>
<name name="truncate" arity="2"/>
<name name="btruncate" arity="2"/>
<fsummary>Truncate a disk log.</fsummary>
<type variable="Log"/>
<type variable="Head" name_i="2"/>
<type variable="BHead"/>
<type name="trunc_error_rsn"/>
<desc>
<p>The <c>truncate</c> functions remove all items from a disk log.
If the <c><anno>Head</anno></c> or the <c><anno>BHead</anno></c> argument is
given, this item is written first in the newly truncated
log, otherwise the header given to <c>open/1</c> is used.
The header argument is only used once; next time a wrap log file
is opened, the header given to <c>open/1</c> is used.
</p>
<p>The <c>truncate/1,2</c> functions are used for internally
formatted logs, and <c>btruncate/2</c> for externally formatted
logs.
</p>
<p>The owners that subscribe to notifications will receive
a <c>truncate</c> message.
</p>
<p>If the attempt to truncate the log fails, the disk log process
terminates with the EXIT message
<c>{{failed,Reason},[{disk_log,Fun,Arity}]}</c>, and
other processes that have requests queued receive the message
<c>{disk_log, Node, {error, disk_log_stopped}}</c>.
</p>
</desc>
</func>
<func>
<name name="unblock" arity="1"/>
<fsummary>Unblock a disk log.</fsummary>
<type name="unblock_error_rsn"/>
<desc>
<p>The <c>unblock/1</c> function unblocks a log.
A log can only be unblocked by the blocking process.
</p>
</desc>
</func>
</funcs>
<section>
<title>See Also</title>
<p><seealso marker="file">file(3)</seealso>,
<seealso marker="pg2">pg2(3)</seealso>,
<seealso marker="wrap_log_reader">wrap_log_reader(3)</seealso></p>
</section>
</erlref>
|