檢視原始碼 disk_log (核心 v10.2)
disk_log
是一個基於磁碟的項目記錄器,可以有效率地將項目記錄到檔案中。
支援三種類型的日誌
halt logs (停止日誌) - 將項目附加到單個檔案,其大小可以由
disk_log
模組限制。wrap logs (環繞日誌) - 使用一系列大小有限的環繞日誌檔案。當一個環繞日誌檔案被填滿時,後續項目會被記錄到序列中的下一個檔案,當最後一個檔案被填滿時,又從第一個檔案開始。
rotate logs (輪替日誌) - 使用一系列大小有限的輪替日誌檔案。當一個日誌檔案被填滿時,它會被輪替然後壓縮。只有一個作用中的日誌檔案和最多配置數量的壓縮日誌檔案。只支援外部格式化的日誌。它遵循與處理器 logger_std_h (用於 Logger) 相同的命名慣例。有關命名慣例的更多詳細資訊,請查看
open/1
的 file 參數。它遵循與 Linux 的 logrotate 和 BSD 的 newsyslog 的壓縮檔案相同的命名慣例。
為了效率起見,項目總是作為二進制資料寫入檔案。
支援兩種日誌檔案格式
internal format (內部格式) - 支援自動修復未正確關閉的日誌檔案,並可以使用此模組中定義的一組函數,有效率地讀取 區塊 中的記錄項目。這是讀取內部格式化日誌的唯一方法。記錄到內部格式化日誌的項目不得佔用超過 4 GB 的磁碟空間 (大小必須適合 4 個位元組)。
external format (外部格式) - 讓使用者讀取和解釋記錄的資料。
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
的非同步版本。
log_terms/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}
。
同步將項目清單附加到內部格式化的日誌。
對於環繞日誌 (wrap logs),它會強制磁碟日誌開始寫入下一個日誌檔案。例如,它可以與 change_size/2
一起使用,以減少磁碟日誌分配的磁碟空間量。
開啟一個新的 disk_log 檔案以進行讀取或寫入。
返回目前節點上 disk log 程序的 pid 所對應的日誌名稱,如果指定的 pid 不是 disk log 程序,則返回 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)。新的檔案最大數量和目前檔案之間的檔案(即檔案 7 和 8)會在下次檔案 6 已滿時移除。
對於輪換日誌,如果減少檔案的最大數量,則會立即刪除多餘的檔案。
如果減少檔案的大小,則變更會立即影響目前的日誌。在下次使用它們之前,它不會變更已滿的日誌檔案的大小。
如果減少日誌大小,例如為了節省空間,可以使用函式 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 KB 的區塊來最大限度地減少磁碟 I/O。
第一次呼叫 chunk()
時,必須提供初始的延續,即原子 start
。
當呼叫 chunk/3
時,N
控制每個區塊從日誌讀取的最大項目數。infinity
表示會讀取 64 KB 區塊中包含的所有項目。如果傳回的項目少於 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
與環繞式日誌一起使用時,傳回的 continuation 可能在下次呼叫 chunk/3
時無效。這是因為日誌可能會環繞並刪除 continuation 指向的檔案。為了防止這種情況,可以在搜尋期間封鎖日誌。
-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
傳回的 continuation 作為參數,並在環繞式日誌中向前(或向後)移動 Step
個檔案。傳回的 continuation 指向新目前檔案中的第一個日誌項目。
如果指定原子 start
作為 continuation,則會選擇環繞式日誌的第一個檔案作為新的目前檔案。
如果環繞式日誌未滿,因為所有檔案尚未被使用,如果嘗試超出日誌範圍,則會傳回 {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}
- 對於停止日誌,File
是檔案名稱,對於環繞式日誌,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
模式開啟的停止日誌傳回
{full, Full}
-Full
是true
或false
,取決於停止日誌是否已滿。
以下配對會針對以 read_write
模式開啟的環繞式日誌傳回
{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().
對於環繞日誌 (wrap logs),它會強制磁碟日誌開始寫入下一個日誌檔案。例如,它可以與 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}
- 指定用於記錄項目的檔案名稱。如果省略此值,且日誌名稱為原子或字串,則停止日誌的檔案名稱預設為lists:concat([Log, ".LOG"])
。對於環繞式日誌,這是檔案的基本名稱。環繞式日誌中的每個檔案都稱為
<FileName>.N
,其中N
是整數。每個環繞式日誌也有兩個檔案,分別稱為<FileName>.idx
和<FileName>.siz
。對於輪換日誌,這是活動日誌檔案的名稱。壓縮檔案的名稱為
<FileName>.N.gz
,其中N
是整數,而<FileName>.0.gz
是最新的壓縮日誌檔案。所有壓縮檔案都會在每次輪換時重新命名,以便最新的檔案具有最小的索引。N 的最大值是MaxNoFiles
的值減 1。{linkto, LinkTo}
如果LinkTo
是 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
個位元組,並在使用MaxNoFiles
個檔案後,從第一個 wrap 日誌檔重新開始。無論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
必須是一個 term,否則為iodata/0
。預設值為none
,表示不會先在檔案中寫入標頭。{head_func, {M,F,A}}
- 指定每次開啟新日誌檔時要呼叫的函數。呼叫M:F(A)
應傳回{ok, Head}
。項目Head
會先寫入每個檔案中。如果格式為internal
,則Head
必須是一個 term,否則為iodata/0
。{mode, Mode}
- 指定日誌是否要以唯讀或讀寫模式開啟。預設值為read_write
。{quiet, Boolean}
- 指定是否將關於日誌檔的可復原錯誤的訊息傳送到error_logger
。預設值為false
。
open/1
如果日誌檔成功開啟,則傳回 {ok, Log}
。如果檔案成功修復,則傳回元組 {repaired, Log, {recovered, Rec}, {badbytes, Bad}}
,其中 Rec
是在檔案中找到的完整 Erlang term 數,而 Bad
是檔案中非 Erlang term 的位元組數。
當以讀寫模式開啟磁碟日誌時,會檢查是否有任何現有的日誌檔。如果沒有,則會建立一個新的空日誌,否則會在最後記錄項目之後的位置開啟現有檔案,並從該處開始記錄項目。如果格式為 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}
。
返回目前節點上 disk log 程序的 pid 所對應的日誌名稱,如果指定的 pid 不是 disk log 程序,則返回 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().
解除日誌的封鎖。日誌只能由封鎖它的程序解除封鎖。