A disk-based term logging facility.
disk_log
is a disk-based term logger that enables
efficient logging of items on files.
Two types of logs are supported:
Appends items to a single file, which size can
be limited by the disk_log
module.
Uses a sequence of wrap log files of limited size. As a wrap log file is filled up, further items are logged on to the next file in the sequence, starting all over with the first file when the last file is filled up.
For efficiency reasons, items are always written to files as binaries.
Two formats of the log files are supported:
Supports automatic repair of log files that are not properly closed and enables efficient reading of logged items in chunks using a set of functions defined in this module. This is the only way to read internally 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).
Leaves it up to the user to read and interpret the logged data.
The disk_log
module cannot repair externally formatted logs.
For each open disk log, one process handles requests
made to the disk log. This process is created when
open/1
is called, provided there exists no process handling the disk log.
A process that opens a disk log can be an owner
or an anonymous user of the disk log. Each owner is
linked to the disk log process, and an owner can close the disk log
either explicitly (by calling close/1
or lclose/1,2
)
or by terminating.
Owners can subscribe to notifications,
messages of the form {disk_log, Node, Log, Info}
, which are sent
from the disk log process when certain events occur, see
the functions and in particular the open/1
option
notify
.
A log can have many owners, but a process cannot own a
log more than once. However, the same process can open the log
as a user more than once.
For a disk log process to close its file properly 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.
Items can be logged synchronously by using functions
log/2
,
blog/2
,
log_terms/2
, and
blog_terms/2
.
For each of these functions, the caller is put
on hold until the items are logged (but not necessarily
written, use sync/1
to ensure that). By adding an a
to each of the mentioned function names, we get functions that log
items asynchronously. Asynchronous functions do not wait for
the disk log process to write the items to the file, but
return the control to the caller more or less immediately.
When using the internal format for logs, use functions
log/2
,
log_terms/2
,
alog/2
, and
alog_terms/2
.
These functions log one or more Erlang terms.
By prefixing each of the functions with a b
(for "binary"),
we get the corresponding blog()
functions for the external format.
These functions log one or more chunks of bytes.
For example, to log the string "hello"
in ASCII format, you
can use disk_log:blog(Log, "hello")
, or
disk_log:blog(Log, list_to_binary("hello"))
. The two
alternatives are equally efficient.
The blog()
functions can also be used for internally formatted
logs, but in this case they must be called with binaries constructed
with calls to
term_to_binary/1
.
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
chunk/2,3
and automatic repair
functions fail. The corresponding terms (not the binaries)
are returned when chunk/2,3
is called.
A collection of open disk logs with the same name running on
different nodes is said to be a distributed disk log
if requests made to any of the logs are automatically made to
the other logs as well. The members of such a collection are
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 example, logged
terms are not necessarily written to the node where the
request was made before written to the other nodes. However,
a few functions do not make requests to all
members of distributed disk logs, namely
info/1
,
chunk/2,3
,
bchunk/2,3
,
chunk_step/3
, and
lclose/1,2
.
An open disk log that is not a distributed disk log is said to be a local disk log. A local disk log is only accessible from the node where the disk log process runs, whereas a distributed disk log is accessible from all nodes in the Erlang system, except 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.
It is not guaranteed that all log files of a distributed disk log
contain the same log items. No attempt is 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 are 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 nonode
error.
Note!
In some applications, it can be unacceptable that
replies from individual logs are ignored. An alternative in such
situations is to use many local disk logs instead of one
distributed disk log, and implement the distribution without use
of the disk_log
module.
Errors are reported differently for asynchronous log attempts
and other uses of the disk_log
module. When used synchronously,
this module replies with an error message, but when called
asynchronously, this module does not know where to send
the error message. Instead, owners subscribing to notifications
receive an error_status
message.
The disk_log
module does not report errors to the
error_logger
module. It is up to the caller to decide
whether to employ the error logger. Function
format_error/1
can be used to produce readable messages from error replies.
However, information events are sent to the error logger in two
situations, namely when a log is repaired, or when a file is missing
while reading chunks.
Error message no_such_log
means that the specified
disk log is not open. Nothing is said about whether the disk log
files exist or not.
Note!
If an attempt to reopen or truncate a log fails (see
reopen/2,3
and
truncate/1,2
)
the disk log process terminates immediately. Before the process
terminates, links to owners and blocking processes (see
block/1,2
) are removed.
The effect is that the links work in one direction only. Any
process using a disk log must check for error message
no_such_log
if some other process truncates or
reopens the log simultaneously.
Types
log() = term()
dlog_size() =
infinity |
integer() >= 1 |
{MaxNoBytes :: integer() >= 1, MaxNoFiles :: integer() >= 1}
dlog_format() = external | internal
dlog_head_opt() = none | term() | iodata()
dlog_mode() = read_only | read_write
dlog_type() = halt | wrap
continuation()
Chunk continuation returned by
chunk/2,3
, bchunk/2,3
, or chunk_step/3
.
invalid_header() = term()
file_error() = term()
Functions
accessible_logs() -> {[LocalLog], [DistributedLog]}
LocalLog = DistributedLog = log()
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.
alog(Log, Term) -> notify_ret()
Log = log()
Term = term()
balog(Log, Bytes) -> notify_ret()
Log = log()
Bytes = iodata()
notify_ret() = ok | {error, no_such_log}
Asynchronously append an item to a disk log. alog/2
is
used for internally formatted logs and balog/2
for externally formatted logs. balog/2
can also be used
for internally formatted logs if the binary is
constructed with a call to
term_to_binary/1
.
Owners subscribing to notifications receive
message read_only
, blocked_log
,
or format_external
if the item cannot be written
on the log, and possibly one of the messages wrap
,
full
, or error_status
if an item is written
on the log. Message error_status
is sent if
something is wrong with the header function or if a file error
occurs.
alog_terms(Log, TermList) -> notify_ret()
Log = log()
TermList = [term()]
balog_terms(Log, ByteList) -> notify_ret()
Log = log()
ByteList = [iodata()]
notify_ret() = ok | {error, no_such_log}
Asynchronously append a list of items to a disk log.
alog_terms/2
is used for internally
formatted logs and balog_terms/2
for externally formatted logs. balog_terms/2
can also be used
for internally formatted logs if the binaries are
constructed with calls to
term_to_binary/1
.
Owners subscribing to notifications receive
message read_only
, blocked_log
,
or format_external
if the items cannot be written
on the log, and possibly one or more of the messages wrap
,
full
, and error_status
if items are written
on the log. Message error_status
is sent if
something is wrong with the header function or if a file error
occurs.
block(Log) -> ok | {error, block_error_rsn()}
Log = log()
block(Log, QueueLogRecords) -> ok | {error, block_error_rsn()}
Log = log()
QueueLogRecords = boolean()
block_error_rsn() = no_such_log | nonode | {blocked_log, log()}
With a call to block/1,2
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 ensures that the disk log is
unblocked if the blocking process terminates without
first closing or unblocking the log.
Any process can probe a blocked log with info/1
or
close it with close/1
. The blocking process can also
use functions chunk/2,3
, bchunk/2,3
,
chunk_step/3
, and unblock/1
without being
affected by the block. Any other attempt than those
mentioned so far to update or read a blocked log suspends the
calling process until the log is unblocked or returns
error message {blocked_log,
, depending on
whether the value of
is true
or false
.
defaults to
true
, which is used by block/1
.
change_header(Log, Header) -> ok | {error, Reason}
Log = log()
Header =
{head, dlog_head_opt()} |
{head_func, MFA :: {atom(), atom(), list()}}Reason =
no_such_log | nonode |
{read_only_mode, Log} |
{blocked_log, Log} |
{badarg, head}
Changes the value of option head
or head_func
for an owner of a disk log.
change_notify(Log, Owner, Notify) -> ok | {error, Reason}
Log = log()
Owner = pid()
Notify = boolean()
Reason =
no_such_log | nonode |
{blocked_log, Log} |
{badarg, notify} |
{not_owner, Owner}
Changes the value of option notify
for an owner of a disk log.
change_size(Log, Size) -> ok | {error, Reason}
Log = log()
Size = dlog_size()
Reason =
no_such_log | nonode |
{read_only_mode, Log} |
{blocked_log, Log} |
{new_size_too_small, Log, CurrentSize :: integer() >= 1} |
{badarg, size} |
{file_error, file:filename(), file_error()}
Changes the size of an open log. For a halt log, the size can always be increased, but it cannot be decreased to something less than the current file size.
For a wrap log, both the size and the number of files can always be increased, as long as the number of files does not exceed 65000. If the maximum number of files is decreased, the change is not valid until the current file is full and the log wraps to the next file. The redundant files are removed the next time the log wraps around, that is, starts to log to file number 1.
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, files 7-10 are removed when file 6 is full and the log starts to write to file number 1 again. Otherwise, the files greater than the current file are removed when the current file is full (for example, if the current file is 8, files 9 and 10 are removed). The files between the new maximum number of files and the current file (that is, files 7 and 8) are removed the next time file 6 is full.
If the size of the files is decreased, the change immediately affects the current log. It does not change the size of log files already full until the next time they are used.
If the log size is decreased, for example, to save space,
function
inc_wrap_file/1
can be used to force the log to wrap.
chunk(Log, Continuation) -> chunk_ret()
Log = log()
Continuation = start | continuation()
chunk(Log, Continuation, N) -> chunk_ret()
Log = log()
Continuation = start | continuation()
N = integer() >= 1 | infinity
bchunk(Log, Continuation) -> bchunk_ret()
Log = log()
Continuation = start | continuation()
bchunk(Log, Continuation, N) -> bchunk_ret()
Log = log()
Continuation = start | continuation()
N = integer() >= 1 | infinity
chunk_ret() =
{Continuation2 :: continuation(), Terms :: [term()]} |
{Continuation2 :: continuation(),
Terms :: [term()],
Badbytes :: integer() >= 0} |
eof |
{error, Reason :: chunk_error_rsn()}
bchunk_ret() =
{Continuation2 :: continuation(), Binaries :: [binary()]} |
{Continuation2 :: continuation(),
Binaries :: [binary()],
Badbytes :: integer() >= 0} |
eof |
{error, Reason :: chunk_error_rsn()}
chunk_error_rsn() =
no_such_log |
{format_external, log()} |
{blocked_log, log()} |
{badarg, continuation} |
{not_internal_wrap, log()} |
{corrupt_log_file, FileName :: file:filename()} |
{file_error, file:filename(), file_error()}
Efficiently reads the terms that are appended
to an internally formatted log. It minimizes disk
I/O by reading 64 kilobyte chunks from the file. Functions
bchunk/2,3
return the binaries read from
the file, they do not call binary_to_term()
. Apart from that,
they work just like chunk/2,3
.
The first time chunk()
(or bchunk()
) is called,
an initial continuation, the atom start
, must be
provided. If a disk log process is 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.
When chunk/3
is called,
controls the
maximum number of terms that are read from the log in each
chunk. Defaults to infinity
, which means that all the
terms contained in the 64 kilobyte chunk are read. If less than
terms are returned, this does not necessarily mean
that the end of the file is reached.
chunk()
returns a tuple
{
, where
is a list
of terms found in the log.
is yet
another continuation, which must be passed on to any
subsequent calls to chunk()
. With a series of calls to
chunk()
, all terms from a log can be extracted.
chunk()
returns a tuple
{
if the log is opened in read-only mode and the read chunk is corrupt.
is the number of bytes in the file found not to be
Erlang terms in the chunk. Notice that the log is not repaired.
When trying to read chunks from a log opened in read-write mode,
tuple {corrupt_log_file,
is returned if the
read chunk is corrupt.
chunk()
returns eof
when the end of the log is
reached, or {error,
if an error occurs. If
a wrap log file is missing, a message is output on the error log.
When chunk/2,3
is used with wrap logs, the returned
continuation might not be valid in the next call to
chunk()
. This is because the log can wrap and delete
the file into which the continuation points. To prevent this,
the log can be blocked during the search.
chunk_info(Continuation) -> InfoList | {error, Reason}
Continuation = continuation()
InfoList = [{node, Node :: node()}, ...]
Reason = {no_continuation, Continuation}
Returns the pair {node,
,
describing the chunk continuation returned by
chunk/2,3
, bchunk/2,3
, or chunk_step/3
.
Terms are read from the disk log running on
.
chunk_step(Log, Continuation, Step) ->
{ok, any()} | {error, Reason}
Log = log()
Continuation = start | continuation()
Step = integer()
Reason =
no_such_log | end_of_log |
{format_external, Log} |
{blocked_log, Log} |
{badarg, continuation} |
{file_error, file:filename(), file_error()}
Can be used with chunk/2,3
and bchunk/2,3
to search through an internally formatted wrap log. It takes as
argument a continuation as returned by chunk/2,3
,
bchunk/2,3
, or chunk_step/3
, and steps forward
(or backward)
files in the wrap log. The
continuation returned, points to the first log item in the
new current file.
If atom start
is specified 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.
If the wrap log is not full because all files are not yet
used, {error, end_of_log}
is returned if trying to
step outside the log.
close(Log) -> ok | {error, close_error_rsn()}
Log = log()
close_error_rsn() =
no_such_log | nonode |
{file_error, file:filename(), file_error()}
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 is activated next time the log is opened.
The disk log process is not terminated as long as there are
owners or users of the log. All owners must close the log,
possibly by terminating. Also, any other process, not only the processes
that have opened the log anonymously, can decrement the users
counter by closing the log.
Attempts to close a log by a process that is
not an owner are ignored if there are no users.
If the log is blocked by the closing process, the log is also unblocked.
format_error(Error) -> io_lib:chars()
Error = term()
Given the error returned by any function in this module,
this function returns a descriptive string
of the error in English. For file errors, function
format_error/1
in module
file
is called.
inc_wrap_file(Log) -> ok | {error, inc_wrap_error_rsn()}
Log = log()
inc_wrap_error_rsn() =
no_such_log | nonode |
{read_only_mode, log()} |
{blocked_log, log()} |
{halt_log, log()} |
{invalid_header, invalid_header()} |
{file_error, file:filename(), file_error()}
invalid_header() = term()
Forces the internally formatted disk log to start logging to the
next log file. It can be used, for example, with
change_size/2
to reduce the amount of disk space allocated
by the disk log.
Owners subscribing to notifications normally
receive a wrap
message, but if
an error occurs with a reason tag of invalid_header
or
file_error
, an error_status
message is sent.
info(Log) -> InfoList | {error, no_such_log}
Log = log()
InfoList = [dlog_info()]
dlog_info() =
{name, Log :: log()} |
{file, File :: file:filename()} |
{type, Type :: dlog_type()} |
{format, Format :: dlog_format()} |
{size, Size :: dlog_size()} |
{mode, Mode :: dlog_mode()} |
{owners, [{pid(), Notify :: boolean()}]} |
{users, Users :: integer() >= 0} |
{status,
Status :: ok | {blocked, QueueLogRecords :: boolean()}} |
{node, Node :: node()} |
{distributed, Dist :: local | [node()]} |
{head,
Head ::
none | {head, term()} | (MFA :: {atom(), atom(), list()})} |
{no_written_items, NoWrittenItems :: integer() >= 0} |
{full, Full :: boolean} |
{no_current_bytes, integer() >= 0} |
{no_current_items, integer() >= 0} |
{no_items, integer() >= 0} |
{current_file, integer() >= 1} |
{no_overflows,
{SinceLogWasOpened :: integer() >= 0,
SinceLastInfo :: integer() >= 0}}
Returns a list of {Tag, Value}
pairs describing the log.
If a disk log process is 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.
The following pairs are returned for all logs:
{name, Log }
is the log name
as specified by the open/1
option name
.
{file, File }
For halt logs
is the
filename, and for wrap logs
is the base name.
{type, Type }
is the log type
as specified by the open/1
option type
.
{format, Format }
is the log format
as specified by the open/1
option format
.
{size, Size }
is the log size
as specified by the open/1
option size
,
or the size set by change_size/2
. The value set by
change_size/2
is reflected immediately.
{mode, Mode }
is the log mode
as specified by the open/1
option mode
.
{owners, [{pid(), Notify }]}
is the value set by the open/1
option notify
or function change_notify/3
for the owners of
the log.
{users, Users }
is the number
of anonymous users of the log, see the open/1
option
linkto
.
{status, Status }
is ok
or {blocked,
as set by functions
block/1,2
and unblock/1
.
{node, Node }
The information returned by the
current invocation of function info/1
is
gathered from the disk log process running on
.
{distributed, Dist }
If the log is local on
the current node,
has the value local
,
otherwise all nodes where the log is distributed
are returned as a list.
The following pairs are returned for all logs opened in
read_write
mode:
{head, Head }
Depending on the value of
the open/1
options head
and head_func
,
or set by function change_header/2
, the value
of
is none
(default),
{head, H}
(head
option), or {M,F,A}
(head_func
option).
{no_written_items, NoWrittenItems }
is the number of items
written to the log since the disk log process was created.
The following pair is returned for halt logs opened in
read_write
mode:
{full, Full }
is true
or
false
depending on whether the halt log is full or not.
The following pairs are returned for wrap logs opened in
read_write
mode:
{no_current_bytes, integer() >= 0}
The number of bytes written to the current wrap log file.
{no_current_items, integer() >= 0}
The number of items written to the current wrap log file, header inclusive.
{no_items, integer() >= 0}
The total number of items in all wrap log files.
{current_file, integer()}
The ordinal for
the current wrap log file in the range 1..MaxNoFiles
,
where MaxNoFiles
is specified by the open/1
option
size
or set by change_size/2
.
{no_overflows, {SinceLogWasOpened , SinceLastInfo }}
(
)
is the number of times a wrap log file has been filled up and a
new one is opened or inc_wrap_file/1
has been called since
the disk log was last opened (info/1
was last called). The first time info/2
is called
after a log was (re)opened or truncated, the two values
are equal.
Notice that functions chunk/2,3
, bchunk/2,3
, and
chunk_step/3
do not affect any value
returned by info/1
.
lclose(Log) -> ok | {error, lclose_error_rsn()}
Log = log()
lclose(Log, Node) -> ok | {error, lclose_error_rsn()}
Log = log()
Node = node()
lclose_error_rsn() =
no_such_log | {file_error, file:filename(), file_error()}
lclose/1
closes a local log or an individual distributed
log on the current node.
lclose/2
closes an individual distributed log on the
specified node if the node is not the current one.
lclose(
is equivalent to
lclose(
.
See also close/1
.
If no log with the specified name exist on the specified node,
no_such_log
is returned.
log(Log, Term) -> ok | {error, Reason :: log_error_rsn()}
Log = log()
Term = term()
blog(Log, Bytes) -> ok | {error, Reason :: log_error_rsn()}
Log = log()
Bytes = iodata()
log_error_rsn() =
no_such_log | nonode |
{read_only_mode, log()} |
{format_external, log()} |
{blocked_log, log()} |
{full, log()} |
{invalid_header, invalid_header()} |
{file_error, file:filename(), file_error()}
Synchronously
appends a term to a disk log. Returns ok
or
{error,
when the term is written to
disk. If the log is distributed, ok
is returned,
unless all nodes are down. Terms are written by
the ordinary write()
function of the
operating system. Hence, it is not guaranteed that the term
is written to disk, it can linger in
the operating system kernel for a while. To ensure that the
item is written to disk, function
sync/1
must be called.
log/2
is used for internally formatted logs,
and blog/2
for externally formatted logs.
blog/2
can also be used
for internally formatted logs if the binary is
constructed with a call to
term_to_binary/1
.
Owners subscribing to notifications are notified
of an error with an error_status
message if the error
reason tag is invalid_header
or file_error
.
log_terms(Log, TermList) ->
ok | {error, Resaon :: log_error_rsn()}
Log = log()
TermList = [term()]
blog_terms(Log, BytesList) ->
ok | {error, Reason :: log_error_rsn()}
Log = log()
BytesList = [iodata()]
log_error_rsn() =
no_such_log | nonode |
{read_only_mode, log()} |
{format_external, log()} |
{blocked_log, log()} |
{full, log()} |
{invalid_header, invalid_header()} |
{file_error, file:filename(), file_error()}
Synchronously appends a list of items to the log. It is more
efficient to use these functions instead of functions log/2
and blog/2
. The specified 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.
log_terms/2
is used for internally formatted
logs, and blog_terms/2
for externally formatted logs.
blog_terms/2
can also be used
for internally formatted logs if the binaries are
constructed with calls to
term_to_binary/1
.
Owners subscribing to notifications are notified
of an error with an error_status
message if the error
reason tag is invalid_header
or file_error
.
open(ArgL) -> open_ret() | dist_open_ret()
ArgL = dlog_options()
dlog_options() = [dlog_option()]
dlog_option() =
{name, Log :: log()} |
{file, FileName :: file:filename()} |
{linkto, LinkTo :: none | pid()} |
{repair, Repair :: true | false | truncate} |
{type, Type :: dlog_type()} |
{format, Format :: dlog_format()} |
{size, Size :: dlog_size()} |
{distributed, Nodes :: [node()]} |
{notify, boolean()} |
{head, Head :: dlog_head_opt()} |
{head_func, MFA :: {atom(), atom(), list()}} |
{quiet, boolean()} |
{mode, Mode :: dlog_mode()}
open_ret() = ret() | {error, open_error_rsn()}
ret() =
{ok, Log :: log()} |
{repaired,
Log :: log(),
{recovered, Rec :: integer() >= 0},
{badbytes, Bad :: integer() >= 0}}
dist_open_ret() =
{[{node(), ret()}], [{node(), {error, dist_error_rsn()}}]}
dist_error_rsn() = nodedown | open_error_rsn()
open_error_rsn() =
no_such_log |
{badarg, term()} |
{size_mismatch,
CurrentSize :: dlog_size(),
NewSize :: dlog_size()} |
{arg_mismatch,
OptionName :: dlog_optattr(),
CurrentValue :: term(),
Value :: term()} |
{name_already_open, Log :: log()} |
{open_read_write, Log :: log()} |
{open_read_only, Log :: log()} |
{need_repair, Log :: log()} |
{not_a_log_file, FileName :: file:filename()} |
{invalid_index_file, FileName :: file:filename()} |
{invalid_header, invalid_header()} |
{file_error, file:filename(), file_error()} |
{node_already_open, Log :: log()}
dlog_optattr() =
name | file | linkto | repair | type | format | size |
distributed | notify | head | head_func | mode
dlog_size() =
infinity |
integer() >= 1 |
{MaxNoBytes :: integer() >= 1, MaxNoFiles :: integer() >= 1}
Parameter
is a list of the following
options:
{name, Log }
Specifies the log name. This name must be passed on as a parameter in all subsequent logging operations. A name must always be supplied.
{file, FileName }
Specifies the name of the
file to be used for logged terms. If this value is
omitted and the log name is an atom or a string,
the filename defaults to lists:concat([
for halt logs.
For wrap logs, this is the base name of the files. Each file in
a wrap log is called <base_name>.N
, where N
is an integer. Each wrap log also has two files called
<base_name>.idx
and <base_name>.siz
.
{linkto, LinkTo }
If
is a pid, it becomes an owner of the
log. If
is none
, the log records
that it is used anonymously by some process by
incrementing the users
counter. By default, the
process that calls open/1
owns the log.
{repair, Repair }
If
is true
,
the current log file is repaired, if needed. As the
restoration is initiated, a message is output on the error log.
If false
is specified,
no automatic repair is attempted. Instead, the
tuple {error, {need_repair,
is returned if an
attempt is made to open a corrupt log file.
If truncate
is specified, the log file becomes
truncated, creating an empty log. Defaults to
true
, which has no effect on logs opened in
read-only mode.
{type, Type }
The log type. Defaults to halt
.
{format, Format }
Disk log format. Defaults to internal
.
{size, Size }
Log size.
When a halt log has reached its maximum size, all attempts to
log more items are rejected. Defaults to
infinity
, which for halt implies that there is no
maximum size.
For wrap logs, parameter
can be a pair
{
or
infinity
.
In the latter case, if the files of an 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
bytes on each file and use
files before starting all over with the first wrap log
file. Regardless of
,
at least the header (if there is one) and one
item are 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 option Size
, but any
supplied value must equal the current log size, otherwise
the tuple {error, {size_mismatch,
is returned.
{distributed, Nodes }
This option can be used for
adding members to a distributed disk log.
Defaults to []
, which means that
the log is local on the current node.
{notify, boolean()}
If true
, the log owners
are notified when certain log events occur.
Defaults to false
. The owners are sent one of the
following messages when an event occurs:
{disk_log, Node, Log, {wrap, NoLostItems}}
Sent when a wrap log has
filled up one of its files and a new file is
opened. NoLostItems
is the number of
previously logged items that were lost when
truncating existing files.
{disk_log, Node, Log, {truncated, NoLostItems}}
Sent when a log is
truncated or reopened. For halt logs NoLostItems
is the number of items written on the log since the
disk log process was created. For wrap logs
NoLostItems
is the number of items on all
wrap log files.
{disk_log, Node, Log, {read_only, Items}}
Sent when an asynchronous log attempt is made to
a log file opened in read-only mode.
Items
is the items from the log attempt.
{disk_log, Node, Log, {blocked_log, Items}}
Sent when an asynchronous log attempt is made to
a blocked log that does not queue log attempts.
Items
is the items from the log attempt.
{disk_log, Node, Log, {format_external, Items}}
Sent when function alog/2
or alog_terms/2
is
used for internally formatted logs. Items
is the
items from the log attempt.
{disk_log, Node, Log, full}
Sent when
an attempt to log items to a wrap log would write more
bytes than the limit set by option size
.
{disk_log, Node, Log, {error_status, Status}}
Sent when the error status changes. The error status
is defined by the outcome of the last attempt to log
items to the log, or to truncate the log, or the last
use of function sync/1
, inc_wrap_file/1
, or
change_size/2
. Status
is either ok
or
{error, Error}
, the former is the initial value.
{head, Head }
Specifies a header to be
written first on the log file. If the log is a wrap
log, the item
is written first in each new file.
is to be a term if the format is
internal
, otherwise a sequence of bytes.
Defaults to none
, which means that
no header is written first on the file.
{head_func, {M,F,A}}
Specifies a function
to be called each time a new log file is opened.
The call M:F(A)
is assumed to return {ok, Head}
.
The item Head
is written first in each file.
Head
is to be a term if the format is
internal
, otherwise a sequence of bytes.
{mode, Mode }
Specifies if the log is to be
opened in read-only or read-write mode. Defaults to
read_write
.
{quiet, Boolean}
Specifies if messages will be sent to
error_logger
on recoverable errors with
the log files. Defaults to false
.
open/1
returns {ok,
if the
log file is successfully opened. If the file is
successfully repaired, the tuple {repaired,
is returned, where
is the number of
whole Erlang terms found in the file and
is the number of bytes in the file that
are non-Erlang terms. If the parameter distributed
is specified, open/1
returns a list of
successful replies and a list of erroneous replies. Each
reply is tagged with the node name.
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
starts from there. If the format is internal
and the existing file is not recognized as an internally
formatted log, a tuple
{error, {not_a_log_file,
is returned.
open/1
cannot be used for changing the
values of options of an open log. When there are prior
owners or users of a log, all option values except name
,
linkto
, and notify
are only checked against
the values supplied before as option values
to function open/1
, change_header/2
, change_notify/3
,
or change_size/2
. Thus,
none of the options except name
is mandatory. If some
specified value differs from the current value, a tuple
{error, {arg_mismatch,
is returned.
Note!
If an owner attempts to open a log
as owner once again, it is acknowledged with the return value
{ok,
, but the state of the disk log is not
affected.
If a log with a specified name is local on some node,
and one tries to open the log distributed on the same node,
the tuple {error, {node_already_open,
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 supplied option values are used
on all nodes mentioned by option distributed
.
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 many calls to open/1
.
A log file can be opened more than once by giving
different values to option name
or by using the
same file when distributing a log on different nodes.
It is up to the user of module disk_log
to ensure that not more than one disk log process has write
access to any file, otherwise the file can be corrupted.
If an attempt to open a log file for the first time fails,
the disk log process terminates with the EXIT message
{{failed,Reason},[{disk_log,open,1}]}
.
The function returns {error, Reason}
for all other errors.
pid2name(Pid) -> {ok, Log} | undefined
Pid = pid()
Log = log()
Returns the log name
given the pid of a disk log process on the current node, or
undefined
if the specified pid is not a disk log process.
This function is meant to be used for debugging only.
reopen(Log, File) -> ok | {error, reopen_error_rsn()}
Log = log()
File = file:filename()
reopen(Log, File, Head) -> ok | {error, reopen_error_rsn()}
Log = log()
File = file:filename()
Head = term()
breopen(Log, File, BHead) -> ok | {error, reopen_error_rsn()}
Log = log()
File = file:filename()
BHead = iodata()
reopen_error_rsn() =
no_such_log | nonode |
{read_only_mode, log()} |
{blocked_log, log()} |
{same_file_name, log()} |
{invalid_index_file, file:filename()} |
{invalid_header, invalid_header()} |
{file_error, file:filename(), file_error()}
Renames the log file
to
and then recreates a new log file.
If a wrap log exists,
is used as the base name
of the renamed files.
By default the header given to open/1
is written first in
the newly opened log file, but if argument
or
is specified, this item is used instead.
The header argument is used only once. Next time a wrap log file
is opened, the header given to open/1
is used.
reopen/2,3
are used for internally formatted
logs, and breopen/3
for externally formatted logs.
Owners subscribing to notifications receive
a truncate
message.
Upon failure to reopen the log, the disk log process terminates
with the EXIT message {{failed,Error},[{disk_log,Fun,Arity}]}
.
Other processes having requests queued receive the message
{disk_log, Node, {error, disk_log_stopped}}
.
sync(Log) -> ok | {error, sync_error_rsn()}
Log = log()
sync_error_rsn() =
no_such_log | nonode |
{read_only_mode, log()} |
{blocked_log, log()} |
{file_error, file:filename(), file_error()}
Ensures that the contents of the log are written to the disk. This is usually a rather expensive operation.
truncate(Log) -> ok | {error, trunc_error_rsn()}
Log = log()
truncate(Log, Head) -> ok | {error, trunc_error_rsn()}
Log = log()
Head = term()
btruncate(Log, BHead) -> ok | {error, trunc_error_rsn()}
Log = log()
BHead = iodata()
trunc_error_rsn() =
no_such_log | nonode |
{read_only_mode, log()} |
{blocked_log, log()} |
{invalid_header, invalid_header()} |
{file_error, file:filename(), file_error()}
Removes all items from a disk log.
If argument
or
is
specified, this item is written first in the newly truncated
log, otherwise the header given to open/1
is used.
The header argument is used only once. Next time a wrap log file
is opened, the header given to open/1
is used.
truncate/1,2
are used for internally
formatted logs, and btruncate/2
for externally formatted
logs.
Owners subscribing to notifications receive
a truncate
message.
If the attempt to truncate the log fails, the disk log process
terminates with the EXIT message
{{failed,Reason},[{disk_log,Fun,Arity}]}
.
Other processes having requests queued receive the message
{disk_log, Node, {error, disk_log_stopped}}
.
unblock(Log) -> ok | {error, unblock_error_rsn()}
Log = log()
Unblocks a log. A log can only be unblocked by the blocking process.