查看源代码 disk_log (内核 v10.2)
disk_log
是一个基于磁盘的项记录器,可以高效地将项记录到文件中。
支持三种类型的日志
停止日志 - 将项附加到单个文件中,其大小可以由
disk_log
模块限制。循环日志 - 使用一系列大小受限的循环日志文件。当一个循环日志文件被填满时,进一步的项将被记录到序列中的下一个文件,当最后一个文件被填满时,又从第一个文件重新开始。
旋转日志 - 使用一系列大小受限的旋转日志文件。当一个日志文件被填满时,它将被旋转然后压缩。有一个活动的日志文件和最多配置数量的压缩日志文件。只支持外部格式化的日志。它遵循与 Logger 的处理程序 logger_std_h 相同的命名约定。有关命名约定的更多详细信息,请查看
open/1
的 file 参数。它遵循与 Linux 的 logrotate 和 BSD 的 newsyslog 的压缩文件相同的命名约定。
出于效率的考虑,项始终以二进制形式写入文件。
支持两种日志文件格式
内部格式 - 支持自动修复未正确关闭的日志文件,并允许使用此模块中定义的一组函数高效读取 块 中记录的项。这是读取内部格式日志的唯一方法。记录到内部格式日志的项不得占用超过 4 GB 的磁盘空间(大小必须适合 4 个字节)。
外部格式 - 由用户读取和解释记录的数据。
disk_log
模块无法修复外部格式的日志。
对于每个打开的磁盘日志,一个进程处理发送给磁盘日志的请求。当调用 open/1
时,如果不存在处理磁盘日志的进程,则会创建此进程。打开磁盘日志的进程可以是磁盘日志的所有者或匿名的用户。每个所有者都链接到磁盘日志进程,并且所有者可以显式(通过调用 close/1
)或通过终止来关闭磁盘日志。
所有者可以订阅通知,即当某些事件发生时从磁盘日志进程发送的消息,形式为 {disk_log, Node, Log, Info}
,请参阅函数,特别是 open/1
选项 notify
。一个日志可以有多个所有者,但一个进程不能多次拥有一个日志。但是,同一个进程可以多次以用户身份打开日志。
要使磁盘日志进程正确关闭其文件并终止,必须由其所有者关闭,并且对于每次匿名使用日志,必须由某个非所有者进程关闭一次。对用户进行计数,当磁盘日志进程终止时,必须没有任何剩余用户。
可以使用函数 log/2
、blog/2
、log_terms/2
和 blog_terms/2
同步记录项。对于这些函数中的每一个,调用者都处于等待状态,直到项被记录(但不一定写入,请使用 sync/1
来确保)。通过在每个提到的函数名称中添加 a
,我们可以获得异步记录项的函数。异步函数不等待磁盘日志进程将项写入文件,而是或多或少立即将控制权返回给调用者。
当使用日志的内部格式时,请使用函数 log/2
、log_terms/2
、alog/2
和 alog_terms/2
。这些函数记录一个或多个 Erlang 项。通过在每个函数前面加上 b
(表示“二进制”),我们得到外部格式的相应 blog()
函数。这些函数记录一个或多个字节块。例如,要以 ASCII 格式记录字符串 "hello"
,可以使用 disk_log:blog(Log, "hello")
或 disk_log:blog(Log, list_to_binary("hello"))
。这两种方法同样有效。
blog()
函数也可以用于内部格式的日志,但在这种情况下,它们必须使用通过调用 term_to_binary/1
构建的二进制文件来调用。没有检查来确保这一点,这完全是调用者的责任。如果这些函数使用与 Erlang 项不对应的二进制文件调用,则 chunk/2,3
和自动修复函数将失败。当调用 chunk/2,3
时,将返回相应的项(而不是二进制文件)。
只能从磁盘日志进程运行的节点访问打开的磁盘日志。磁盘日志进程运行的节点上的所有进程都可以记录项或以其他方式更改、检查或关闭日志。
异步日志尝试和 disk_log
模块的其他使用情况报告错误的方式不同。当同步使用时,此模块会回复错误消息,但当异步调用时,此模块不知道将错误消息发送到哪里。相反,订阅通知的所有者会收到 error_status
消息。
disk_log
模块不会向 error_logger
模块报告错误。是否使用错误记录器取决于调用者。函数 format_error/1
可用于从错误回复生成可读消息。但是,在两种情况下,信息事件将发送到错误记录器,即当日志被修复或读取块时缺少文件时。
错误消息 no_such_log
表示指定的磁盘日志未打开。没有说明磁盘日志文件是否存在。
注意
如果重新打开或截断日志的尝试失败(请参阅
reopen/2,3
和truncate/1,2
),则磁盘日志进程会立即终止。在进程终止之前,与所有者和阻塞进程的链接(请参阅block/1,2
)将被删除。效果是链接仅在一个方向上起作用。如果某些其他进程同时截断或重新打开日志,则任何使用磁盘日志的进程都必须检查错误消息no_such_log
。
另请参阅
摘要
类型
由 chunk/2,3
、bchunk/2,3
或 chunk_step/3
返回的块延续。
函数
返回当前节点上可访问的磁盘日志的名称。
log/2
的异步版本。
blog/2
的异步版本。
等效于 chunk(Log, Continuation, N)
,除了它返回从文件中读取的二进制文件,即它不调用 binary_to_term/1
。
通过调用 block/2
,进程可以阻止日志。
等效于 log/2
,除了它用于外部格式的日志。
等效于 log_terms/2
,除了它用于外部格式的日志。
等效于 reopen
,除了它用于外部格式的日志。
等效于外部格式日志的 truncate/2
。
更改磁盘日志所有者的选项 head
或 head_func
的值。
更改磁盘日志所有者的选项 notify
的值。
更改打开日志的大小。对于停止日志,大小始终可以增加,但不能减小到小于当前文件大小的值。
高效读取附加到内部格式日志的项。
返回对 {node, Node}
,描述由 chunk/2,3
、bchunk/2,3
或 chunk_step/3
返回的块延续。
可与 chunk/2,3
和 bchunk/2,3
一起使用,以搜索内部格式的循环日志。
正确关闭磁盘日志。
给定此模块中任何函数返回的错误,此函数将返回错误的描述性字符串(英语)。
强制内部格式的磁盘日志开始记录到下一个日志文件。例如,它可以与 change_size/2
一起使用,以减少磁盘日志分配的磁盘空间量。
返回描述节点上运行的日志的 {Tag, Value}
对的列表。
同步地将一个项附加到内部格式化的磁盘日志中。当该项被写入磁盘时,返回 ok
,否则返回 {error, Reason}
。
同步地将一个项列表附加到内部格式化的日志中。
对于环绕日志,它强制磁盘日志开始记录到下一个日志文件。例如,它可以与 change_size/2
一起使用,以减少磁盘日志分配的磁盘空间量。
打开一个新的 disk_log 文件以进行读取或写入。
返回给定当前节点上磁盘日志进程的 pid 的日志名称,如果指定的 pid 不是磁盘日志进程,则返回 undefined
。
等效于 reopen(Log, File, Head)
,其中 Head
是 open/1
中指定的 Head
。
将内部格式化的日志文件重命名为 File
,然后重新创建一个新的日志文件。如果存在环绕/旋转日志,则 File
用作重命名文件的基本名称。
确保日志的内容写入磁盘。这通常是一个相当耗时的操作。
等效于 truncate(Log, Head)
,其中 Head
是 open/1
中指定的 Head
。
从内部格式化的磁盘日志中删除所有项。参数 Head
或首先写入新截断的日志中。
解除日志的阻塞。日志只能被阻塞进程解除阻塞。
类型
-type bchunk_ret() :: {Continuation2 :: continuation(), Binaries :: [binary()]} | {Continuation2 :: continuation(), Binaries :: [binary()], Badbytes :: non_neg_integer()} | eof | {error, Reason :: chunk_error_rsn()}.
-type block_error_rsn() :: no_such_log | nonode | {blocked_log, log()}.
-type 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()}.
-type chunk_ret() :: {Continuation2 :: continuation(), Terms :: [term()]} | {Continuation2 :: continuation(), Terms :: [term()], Badbytes :: non_neg_integer()} | eof | {error, Reason :: chunk_error_rsn()}.
-type close_error_rsn() :: no_such_log | nonode | {file_error, file:filename(), file_error()}.
-opaque continuation()
由 chunk/2,3
、bchunk/2,3
或 chunk_step/3
返回的块延续。
-type dlog_format() :: external | internal.
-type 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 :: non_neg_integer()} | {status, Status :: ok | {blocked, QueueLogRecords :: boolean()}} | {node, Node :: node()} | {head, Head :: none | {head, binary()} | (MFA :: {atom(), atom(), list()})} | {no_written_items, NoWrittenItems :: non_neg_integer()} | {full, Full :: boolean} | {no_current_bytes, non_neg_integer()} | {no_current_items, non_neg_integer()} | {no_items, non_neg_integer()} | {current_file, pos_integer()} | {no_overflows, {SinceLogWasOpened :: non_neg_integer(), SinceLastInfo :: non_neg_integer()}}.
-type dlog_mode() :: read_only | read_write.
-type dlog_optattr() ::
name | file | linkto | repair | type | format | size | notify | head | head_func | mode.
-type 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()} | {notify, boolean()} | {head, Head :: dlog_head_opt()} | {head_func, MFA :: {atom(), atom(), list()}} | {quiet, boolean()} | {mode, Mode :: dlog_mode()}.
-type dlog_options() :: [dlog_option()].
-type dlog_size() :: infinity | pos_integer() | {MaxNoBytes :: pos_integer(), MaxNoFiles :: pos_integer()}.
-type dlog_type() :: halt | wrap | rotate.
-type file_error() :: term().
-type inc_wrap_error_rsn() :: next_file_error_rsn().
-type invalid_header() :: term().
-type log() :: term().
-type 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()}.
-type next_file_error_rsn() :: no_such_log | nonode | {read_only_mode, log()} | {blocked_log, log()} | {halt_log, log()} | {rotate_log, log()} | {invalid_header, invalid_header()} | {file_error, file:filename(), file_error()}.
-type notify_ret() :: ok | {error, no_such_log}.
-type 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()}.
-type open_ret() :: {ok, Log :: log()} | {repaired, Log :: log(), {recovered, Rec :: non_neg_integer()}, {badbytes, Bad :: non_neg_integer()}} | {error, open_error_rsn()}.
-type 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()}.
-type sync_error_rsn() :: no_such_log | nonode | {read_only_mode, log()} | {blocked_log, log()} | {file_error, file:filename(), file_error()}.
-type trunc_error_rsn() :: no_such_log | nonode | {read_only_mode, log()} | {blocked_log, log()} | {invalid_header, invalid_header()} | {file_error, file:filename(), file_error()}.
函数
-spec all() -> [Log] when Log :: log().
返回当前节点上可访问的磁盘日志的名称。
-spec alog(Log, Term) -> notify_ret() when Log :: log(), Term :: term().
log/2
的异步版本。
订阅通知的所有者会收到消息 read_only
、blocked_log
或 format_external
,如果该项无法写入日志,并且如果该项写入日志,可能会收到消息 wrap
、full
或 error_status
之一。如果头函数出现问题或发生文件错误,则会发送消息 error_status
。
-spec alog_terms(Log, TermList) -> notify_ret() when Log :: log(), TermList :: [term()].
log_terms/2
的异步版本。
订阅通知的所有者会收到消息 read_only
、blocked_log
或 format_external
,如果这些项无法写入日志,并且如果这些项写入日志,可能会收到一个或多个消息 wrap
、full
和 error_status
。如果头函数出现问题或发生文件错误,则会发送消息 error_status
。
-spec balog(Log, Bytes) -> notify_ret() when Log :: log(), Bytes :: iodata().
blog/2
的异步版本。
订阅通知的所有者会收到消息 read_only
、blocked_log
或 format_external
,如果该项无法写入日志,并且如果该项写入日志,可能会收到消息 wrap
、full
或 error_status
之一。如果头函数出现问题或发生文件错误,则会发送消息 error_status
。
-spec balog_terms(Log, ByteList) -> notify_ret() when Log :: log(), ByteList :: [iodata()].
blog_terms/2
的异步版本。
订阅通知的所有者会收到消息 read_only
、blocked_log
或 format_external
,如果这些项无法写入日志,并且如果这些项写入日志,可能会收到一个或多个消息 wrap
、full
和 error_status
。如果头函数出现问题或发生文件错误,则会发送消息 error_status
。
-spec bchunk(Log, Continuation) -> bchunk_ret() when Log :: log(), Continuation :: start | continuation().
-spec bchunk(Log, Continuation, N) -> bchunk_ret() when Log :: log(), Continuation :: start | continuation(), N :: pos_integer() | infinity.
等效于 chunk(Log, Continuation, N)
,除了它返回从文件中读取的二进制文件,即它不调用 binary_to_term/1
。
-spec block(Log) -> ok | {error, block_error_rsn()} when Log :: log().
等效于 block(Log, true)
。
-spec block(Log, QueueLogRecords) -> ok | {error, block_error_rsn()} when Log :: log(), QueueLogRecords :: boolean().
通过调用 block/2
,进程可以阻止日志。
如果阻塞进程不是日志的所有者,则会在磁盘日志进程和阻塞进程之间创建一个临时链接。该链接确保如果阻塞进程在未首先关闭或解除阻塞日志的情况下终止,则磁盘日志会被解除阻塞。
任何进程都可以使用 info/1
探测阻塞的日志,或使用 close/1
关闭它。阻塞进程还可以使用函数 chunk/2,3
、 bchunk/2,3
、chunk_step/3
和 unblock/1
而不受阻塞的影响。除上述提到的之外的任何其他尝试更新或读取阻塞日志的操作都会暂停调用进程,直到日志被解除阻塞,或者返回错误消息 {blocked_log, Log}
,具体取决于 QueueLogRecords
的值是 true
还是 false
。
-spec blog(Log, Bytes) -> ok | {error, Reason :: log_error_rsn()} when Log :: log(), Bytes :: iodata().
等效于 log/2
,除了它用于外部格式的日志。
如果二进制文件是通过调用 term_to_binary/1
构建的,则 blog/2
也可用于内部格式化的日志。
-spec blog_terms(Log, BytesList) -> ok | {error, Reason :: log_error_rsn()} when Log :: log(), BytesList :: [iodata()].
等效于 log_terms/2
,除了它用于外部格式的日志。
如果二进制文件是通过调用 term_to_binary/1
构建的,则 blog_terms/2
也可用于内部格式化的日志。
-spec breopen(Log, File, BHead) -> ok | {error, reopen_error_rsn()} when Log :: log(), File :: file:filename(), BHead :: iodata().
等效于 reopen
,除了它用于外部格式的日志。
-spec btruncate(Log, BHead) -> ok | {error, trunc_error_rsn()} when Log :: log(), BHead :: iodata().
等效于外部格式日志的 truncate/2
。
-spec change_header(Log, Header) -> ok | {error, Reason} when 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}.
更改磁盘日志所有者的选项 head
或 head_func
的值。
-spec change_notify(Log, Owner, Notify) -> ok | {error, Reason} when Log :: log(), Owner :: pid(), Notify :: boolean(), Reason :: no_such_log | nonode | {blocked_log, Log} | {badarg, notify} | {not_owner, Owner}.
更改磁盘日志所有者的选项 notify
的值。
-spec change_size(Log, Size) -> ok | {error, Reason} when Log :: log(), Size :: dlog_size(), Reason :: no_such_log | nonode | {read_only_mode, Log} | {blocked_log, Log} | {new_size_too_small, Log, CurrentSize :: pos_integer()} | {badarg, size} | {file_error, file:filename(), file_error()}.
更改打开日志的大小。对于停止日志,大小始终可以增加,但不能减小到小于当前文件大小的值。
对于环绕或旋转日志,只要文件数量不超过 65000,就可以始终增加大小和文件数量。对于环绕日志,如果减少最大文件数量,则只有在当前文件已满且日志环绕到下一个文件时,更改才有效。多余的文件会在下次日志环绕时删除,即开始记录到文件编号 1。
例如,假设旧的最大文件数量为 10,新的最大文件数量为 6。如果当前文件编号不大于新的最大文件数量,则在文件 6 已满且日志开始写入文件编号 1 时,将删除文件 7-10。否则,当当前文件已满时,将删除大于当前文件的文件(例如,如果当前文件是 8,则删除文件 9 和 10)。下次文件 6 已满时,将删除新最大文件数量和当前文件之间的文件(即文件 7 和 8)。
对于旋转日志,如果减少了最大文件数量,则会立即删除多余的文件。
如果减少了文件的大小,则更改会立即影响当前日志。它不会更改已满的日志文件的大小,直到下次使用它们时才会更改。
如果减小了日志大小(例如,为了节省空间),可以使用函数 next_file/1
强制日志环绕。
-spec chunk(Log, Continuation) -> chunk_ret() when Log :: log(), Continuation :: start | continuation().
-spec chunk(Log, Continuation, N) -> chunk_ret() when Log :: log(), Continuation :: start | continuation(), N :: pos_integer() | infinity.
高效读取附加到内部格式日志的项。
它通过从文件中读取 64 千字节的块来最大限度地减少磁盘 I/O。
第一次调用 chunk()
时,必须提供初始延续,即原子 start
。
当调用 chunk/3
时,N
控制每次从日志中读取的最大条目数。infinity
表示读取 64 千字节块中包含的所有条目。如果返回的条目少于 N
个,这并不一定意味着已到达文件末尾。
chunk/3
返回一个元组 {Continuation2, Terms}
,其中 Terms
是日志中找到的条目列表。Continuation2
是另一个延续,必须将其传递给后续的 chunk()
调用。通过一系列 chunk()
调用,可以提取日志中的所有条目。
如果日志以只读模式打开,并且读取的块已损坏,chunk/3
将返回一个元组 {Continuation2, Terms, Badbytes}
。Badbytes
是文件中未找到 Erlang 条目的字节数。请注意,日志不会被修复。当尝试从以读写模式打开的日志中读取块时,如果读取的块已损坏,则返回元组 {corrupt_log_file, FileName}
。
当到达日志末尾时,chunk/3
返回 eof
,如果发生错误,则返回 {error, Reason}
。如果缺少环绕日志文件,则会在错误日志上输出消息。
当 chunk/2,3
与环绕日志一起使用时,返回的延续可能在下一次调用 chunk/3
时无效。这是因为日志可以环绕并删除延续指向的文件。为了防止这种情况,可以在搜索期间阻止日志。
-spec chunk_info(Continuation) -> InfoList | {error, Reason} when Continuation :: continuation(), InfoList :: [{node, Node :: node()}, ...], Reason :: {no_continuation, Continuation}.
返回对 {node, Node}
,描述由 chunk/2,3
、bchunk/2,3
或 chunk_step/3
返回的块延续。
条目从在 Node
上运行的磁盘日志中读取。
-spec chunk_step(Log, Continuation, Step) -> {ok, any()} | {error, Reason} when 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()}.
可与 chunk/2,3
和 bchunk/2,3
一起使用,以搜索内部格式的循环日志。
它以一个延续作为参数,该延续由 chunk/2,3
、bchunk/2,3
或 chunk_step/3
返回,并在环绕日志中向前(或向后)移动 Step
个文件。返回的延续指向新当前文件中的第一个日志项。
如果指定原子 start
作为延续,则选择环绕日志的第一个文件作为新的当前文件。
如果环绕日志未满,因为所有文件尚未被使用,则尝试移出日志时会返回 {error, end_of_log}
。
-spec close(Log) -> ok | {error, close_error_rsn()} when Log :: log().
正确关闭磁盘日志。
在停止 Erlang 系统之前,必须关闭内部格式化的日志。否则,该日志将被视为未关闭,并且下次打开该日志时会激活自动修复过程。
只要存在日志的所有者或用户,就不会终止磁盘日志进程。所有所有者都必须关闭日志,可能通过终止。此外,任何其他进程,而不仅仅是匿名打开日志的进程,都可以通过关闭日志来递减 users
计数器。如果没有任何用户,则忽略非所有者进程关闭日志的尝试。
如果日志被关闭进程阻止,则日志也会被解除阻止。
-spec format_error(Error) -> io_lib:chars() when Error :: term().
给定此模块中任何函数返回的错误,此函数将返回错误的描述性字符串(英语)。
对于文件错误,将调用模块 file
中的函数 format_error/1
。
-spec inc_wrap_file(Log) -> ok | {error, inc_wrap_error_rsn()} when Log :: log().
强制内部格式的磁盘日志开始记录到下一个日志文件。例如,它可以与 change_size/2
一起使用,以减少磁盘日志分配的磁盘空间量。
订阅通知的所有者通常会收到 wrap
消息,但如果发生错误,且错误原因标记为 invalid_header
或 file_error
,则会发送 error_status
消息。
返回描述节点上运行的日志的 {Tag, Value}
对的列表。
以下配对将针对所有日志返回
{name, Log}
-Log
是由open/1
选项name
指定的日志名称。{file, File}
- 对于 halt 日志,File
是文件名,对于 wrap 日志,File
是基本名称。{type, Type}
-Type
是由open/1
选项type
指定的日志类型。{format, Format}
-Format
是由open/1
选项format
指定的日志格式。{size, Size}
-Size
是由open/1
选项size
指定的日志大小,或由change_size/2
设置的大小。由change_size/2
设置的值会立即反映出来。{mode, Mode}
-Mode
是由open/1
选项mode
指定的日志模式。{owners, [{pid(), Notify}]}
-Notify
是由open/1
选项notify
或函数change_notify/3
为日志的所有者设置的值。{status, Status}
-Status
是ok
或{blocked, QueueLogRecords}
,由函数block/1,2
和unblock/1
设置。{node, Node}
- 由当前调用函数info/1
返回的信息是从在Node
上运行的磁盘日志进程收集的。
以下配对将针对所有以 read_write
模式打开的日志返回
{head, Head}
- 根据open/1
选项head
和head_func
的值,或由函数change_header/2
设置,Head
的值可以是none
(默认),{head, H}
(head
选项),或{M,F,A}
(head_func
选项)。{no_written_items, NoWrittenItems}
-NoWrittenItems
是自创建磁盘日志进程以来写入日志的条目数。
以下配对将针对以 read_write
模式打开的 halt 日志返回
{full, Full}
-Full
是true
或false
,取决于 halt 日志是否已满。
以下配对将针对以 read_write
模式打开的 wrap 日志返回
{no_current_bytes, integer() >= 0}
- 写入当前环绕日志文件的字节数。{no_current_items, integer() >= 0}
- 写入当前环绕日志文件的条目数,包括标题。{no_items, integer() >= 0}
- 所有环绕日志文件中的条目总数。{current_file, integer()}
- 当前环绕日志文件的序号,范围为1..MaxNoFiles
,其中MaxNoFiles
由open/1
选项size
指定,或由change_size/2
设置。{no_overflows, {SinceLogWasOpened, SinceLastInfo}}
-SinceLogWasOpened
(SinceLastInfo
) 是自上次打开磁盘日志以来(自上次调用info/1
以来)环绕日志文件被填满并打开新文件或调用inc_wrap_file/1
的次数。在(重新)打开或截断日志后第一次调用info/2
时,这两个值相等。
请注意,函数 chunk/2,3
、bchunk/2,3
和 chunk_step/3
不会影响 info/1
返回的任何值。
-spec log(Log, Term) -> ok | {error, Reason :: log_error_rsn()} when Log :: log(), Term :: term().
同步地将一个项附加到内部格式化的磁盘日志中。当该项被写入磁盘时,返回 ok
,否则返回 {error, Reason}
。
条目由操作系统的普通 write()
函数写入。因此,不能保证条目已写入磁盘,它可以在操作系统内核中停留一段时间。为了确保条目已写入磁盘,必须调用函数 sync/1
。
如果错误原因标记为 invalid_header
或 file_error
,订阅通知的所有者会收到 error_status
消息通知。
-spec log_terms(Log, TermList) -> ok | {error, Reason :: log_error_rsn()} when Log :: log(), TermList :: [term()].
同步地将一个项列表附加到内部格式化的日志中。
使用此函数比使用 log/2
更高效。指定的列表将拆分为尽可能大的子列表(受环绕日志文件大小的限制),并且每个子列表将作为一个单独的条目记录,从而减少开销。
如果错误原因标记为 invalid_header
或 file_error
,订阅通知的所有者会收到 error_status
消息通知。
-spec next_file(Log) -> ok | {error, next_file_error_rsn()} when Log :: log().
对于环绕日志,它强制磁盘日志开始记录到下一个日志文件。例如,它可以与 change_size/2
一起使用,以减少磁盘日志分配的磁盘空间量。
订阅通知的所有者通常会收到 wrap
消息,但如果发生错误,且错误原因标记为 invalid_header
或 file_error
,则会发送 error_status
消息。
对于轮换日志,它会强制轮换当前活动的日志文件,对其进行压缩,并打开一个新的活动文件用于日志记录。
-spec open(ArgL) -> open_ret() when ArgL :: dlog_options().
打开一个新的 disk_log 文件以进行读取或写入。
参数 ArgL
是以下选项的列表
{name, Log}
- 指定日志名称。此名称必须作为参数传递给所有后续的日志记录操作。必须始终提供名称。{file, FileName}
- 指定用于记录条目的文件名。如果省略此值,且日志名称是原子或字符串,则对于 halt 日志,文件名默认为lists:concat([Log, ".LOG"])
。对于环绕日志,这是文件的基本名称。环绕日志中的每个文件都称为
<FileName>.N
,其中N
是一个整数。每个环绕日志也有两个名为<FileName>.idx
和<FileName>.siz
的文件。对于轮转日志,这是活动日志文件的名称。压缩文件的命名格式为
<FileName>.N.gz
,其中N
是一个整数,<FileName>.0.gz
是最新的压缩日志文件。所有压缩文件在每次轮转时都会被重命名,以便最新的文件具有最小的索引。N 的最大值是MaxNoFiles
的值减 1。{linkto, LinkTo}
如果LinkTo
是一个进程 ID(pid),它将成为日志的所有者。如果LinkTo
是none
,日志会记录它被某个进程匿名使用,并通过增加users
计数器来表示。默认情况下,调用open/1
的进程拥有该日志。{repair, Repair}
- 如果Repair
是true
,当前日志文件将在需要时进行修复。当启动修复时,会在错误日志中输出一条消息。如果指定false
,则不会尝试自动修复。相反,如果尝试打开损坏的日志文件,将返回元组{error, {need_repair, Log}}
。如果指定truncate
,日志文件将被截断,无论之前的的内容如何,都会创建一个空日志。默认为true
,这对以只读模式打开的日志没有影响。{type, Type}
- 日志类型。默认为halt
。{format, Format}
- 磁盘日志格式。默认为internal
。{size, Size}
- 日志大小。当 halt 日志达到其最大大小时,所有尝试记录更多条目的操作都会被拒绝。默认为
infinity
,对于 halt 日志,这意味着没有最大大小限制。对于 wrap 和 rotate 日志,参数
Size
可以是一对{MaxNoBytes, MaxNoFiles}
。对于 wrap 日志,它也可以是infinity
。在后一种情况下,如果可以找到具有相同名称的现有 wrap 日志文件,则从现有 wrap 日志读取大小,否则将返回错误。Wrap 日志在每个文件上最多写入
MaxNoBytes
字节,并在重新开始使用第一个 wrap 日志文件之前使用MaxNoFiles
个文件。无论MaxNoBytes
的值如何,在包装到下一个文件之前,每个 wrap 日志文件上至少会写入标头(如果存在)和一个条目。第一次打开现有 wrap 日志时,即创建磁盘日志进程时,允许选项
size
的值与当前日志大小不同,并且磁盘日志的大小会根据change_size/2
进行更改。打开现有 wrap 日志时,不需要为选项
size
提供值,但是如果日志已经打开,即磁盘日志进程存在,则提供的值必须等于当前日志大小,否则将返回元组{error, {size_mismatch, CurrentSize, NewSize}}
。注意
在 Erlang/OTP 24.0 之前,第一次打开现有 wrap 日志(即创建磁盘日志进程时),选项
size
的提供值必须等于当前日志大小。Rotate 日志在活动日志文件上最多写入
MaxNoBytes
字节,并保留最新的MaxNoFiles
个压缩文件。无论MaxNoBytes
的值如何,在轮转之前,每个 rotate 日志文件上至少会写入标头(如果存在)和一个条目。当打开已打开的 halt 日志时,将忽略选项
size
。{notify, boolean()}
- 如果为true
,当某些日志事件发生时,会通知日志所有者。默认为false
。当事件发生时,所有者会收到以下消息之一{disk_log, Node, Log, {wrap, NoLostItems}}
- 当 wrap 日志填满其一个文件并打开一个新文件时发送。NoLostItems
是截断现有文件时丢失的先前记录的条目数。{disk_log, Node, Log, {truncated, NoLostItems}}
- 当日志被截断或重新打开时发送。对于 halt 日志,NoLostItems
是自创建磁盘日志进程以来写入日志的条目数。对于 wrap 日志,NoLostItems
是所有 wrap 日志文件上的条目数。{disk_log, Node, Log, {read_only, Items}}
- 当尝试对以只读模式打开的日志文件进行异步日志记录时发送。Items
是来自日志尝试的条目。{disk_log, Node, Log, {blocked_log, Items}}
- 当尝试对不排队日志尝试的阻塞日志进行异步日志记录时发送。Items
是来自日志尝试的条目。{disk_log, Node, Log, {format_external, Items}}
- 当函数alog/2
或alog_terms/2
用于内部格式化的日志时发送。Items
是来自日志尝试的条目。{disk_log, Node, Log, full}
- 当尝试将条目记录到 wrap 日志时,写入的字节数超过选项size
设置的限制时发送。{disk_log, Node, Log, {error_status, Status}}
- 当错误状态更改时发送。错误状态由上次尝试记录条目到日志、截断日志或上次使用函数sync/1
、inc_wrap_file/1
或change_size/2
的结果定义。Status
要么是ok
要么是{error, Error}
,前者是初始值。
{head, Head}
- 指定在日志文件中首先写入的标头。如果日志是 wrap 或 rotate 日志,则条目Head
会首先写入每个新文件中。如果格式是internal
,则Head
必须是一个项,否则必须是iodata/0
。默认为none
,这意味着不会首先在文件中写入标头。{head_func, {M,F,A}}
- 指定每次打开新日志文件时要调用的函数。假定调用M:F(A)
返回{ok, Head}
。条目Head
会首先写入每个文件中。如果格式是internal
,则Head
必须是一个项,否则必须是iodata/0
。{mode, Mode}
- 指定日志是以只读模式还是读写模式打开。默认为read_write
。{quiet, Boolean}
- 指定是否将有关日志文件中可恢复错误的的消息发送到error_logger
。默认为false
。
如果日志文件成功打开,open/1
返回 {ok, Log}
。如果文件成功修复,则返回元组 {repaired, Log, {recovered, Rec}, {badbytes, Bad}}
,其中 Rec
是文件中找到的完整 Erlang 项的数量,而 Bad
是文件中不是 Erlang 项的字节数。
当以读写模式打开磁盘日志时,将检查是否存在任何现有日志文件。如果没有,则会创建一个新的空日志,否则会在上次记录的条目之后的位置打开现有文件,并且从那里开始记录条目。如果格式为 internal
并且现有文件未被识别为内部格式化的日志,则返回元组 {error, {not_a_log_file, FileName}}
。
open/1
不能用于更改已打开日志的选项值。当日志有先前的所有者或用户时,除了 name
、linkto
和 notify
之外的所有选项值仅针对之前提供给函数 open/1
、change_header/2
、change_notify/3
或 change_size/2
的选项值进行检查。因此,除了 name
之外,没有哪个选项是强制性的。如果某些指定的值与当前值不同,则返回元组 {error, {arg_mismatch, OptionName, CurrentValue, Value}}
。
注意
如果所有者尝试再次以所有者身份打开日志,则会返回
{ok, Log}
进行确认,但不会影响磁盘日志的状态。
可以通过为选项 name
提供不同的值,或者在不同节点上打开日志时使用相同的文件,多次打开日志文件。模块 disk_log
的用户有责任确保不超过一个磁盘日志进程对任何文件具有写访问权限,否则文件可能会损坏。
如果第一次尝试打开日志文件失败,磁盘日志进程将终止并显示 EXIT 消息 {{failed,Reason},[{disk_log,open,1}]}
。该函数返回 {error, Reason}
以处理所有其他错误。
返回给定当前节点上磁盘日志进程的 pid 的日志名称,如果指定的 pid 不是磁盘日志进程,则返回 undefined
。
此函数仅用于调试。
-spec reopen(Log, File) -> ok | {error, reopen_error_rsn()} when Log :: log(), File :: file:filename().
等效于 reopen(Log, File, Head)
,其中 Head
是 open/1
中指定的 Head
。
-spec reopen(Log, File, Head) -> ok | {error, reopen_error_rsn()} when Log :: log(), File :: file:filename(), Head :: term().
将内部格式化的日志文件重命名为 File
,然后重新创建一个新的日志文件。如果存在环绕/旋转日志,则 File
用作重命名文件的基本名称。
在新打开的日志文件中首先写入 Head
的值。标头参数仅使用一次。下次打开 wrap/rotate 日志文件时,将使用提供给 open/1
的标头。
订阅通知的所有者会收到一条 truncate
消息。
如果重新打开日志失败,磁盘日志进程将终止并显示 EXIT 消息 {{failed,Error},[{disk_log,Fun,Arity}]}
。其他具有排队请求的进程会收到消息 {disk_log, Node, {error, disk_log_stopped}}
。
-spec sync(Log) -> ok | {error, sync_error_rsn()} when Log :: log().
确保日志的内容写入磁盘。这通常是一个相当耗时的操作。
-spec truncate(Log) -> ok | {error, trunc_error_rsn()} when Log :: log().
等效于 truncate(Log, Head)
,其中 Head
是 open/1
中指定的 Head
。
此函数可用于内部和外部格式化的日志。
-spec truncate(Log, Head) -> ok | {error, trunc_error_rsn()} when Log :: log(), Head :: term().
从内部格式化的磁盘日志中删除所有项。参数 Head
或首先写入新截断的日志中。
标头参数仅使用一次。下次打开 wrap/rotate 日志文件时,将使用提供给 open/1
的标头。
订阅通知的所有者会收到一条 truncate
消息。
如果尝试截断日志失败,磁盘日志进程将终止并显示 EXIT 消息 {{failed,Reason},[{disk_log,Fun,Arity}]}
。其他具有排队请求的进程会收到消息 {disk_log, Node, {error, disk_log_stopped}}
。
-spec unblock(Log) -> ok | {error, unblock_error_rsn()} when Log :: log().
解除日志的阻塞。日志只能被阻塞进程解除阻塞。