%% Licensed to the Apache Software Foundation (ASF) under one %% or more contributor license agreements. See the NOTICE file %% distributed with this work for additional information %% regarding copyright ownership. The ASF licenses this file %% to you 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. %% %% @doc %% Formatter wrapper for systemd's journal level information. %% %% Journald uses log-level format similar to Linux's `printk' logging function. %% This simply adds special prefix to the message formatted by the formatter %% specified via `parent' option. %% %% == Automatic registration == %% %% This formatter will be automatically registered for all handlers that use %% `logger_std_h' with `type' set to one of `standard_io' or `standard_error' if %% the respective FDs point to journal that is pointed by `JOURNAL_STREAM' which %% is autometically set by systemd if one of the options `StandardOutput' or %% `StandardError' is set to `journal'. So if you are using default logger %% handler then all you need to do is to set %% %% ``` %% StandardOutput=journal %% ''' %% %% In your `systemd.service(8)' file, and this library will handle the rest. %% %% It is important that this formatter will still work poorly with multiline %% logs, if you are using such, then check out {@link systemd_journal_h. %% `systemd_journal_h'} handler (which SHOULD NOT be used with this %% formatter). %% %% To disable this behaviour set `auto_formatter' option for `systemd' to `false'. %% %% == Example == %% %% ``` %% logger:add_handler(journal_stdout, %% logger_std_h, %% #{formatter => {systemd_kmsg_formatter, #{}}). %% ''' %% %% == Options == %% %%
%%
`parent'
%%
"Parent" formatter that will be used as a "main" formatter. %% %% Defaults to `logger_formatter'.
%%
%% %% Rest of the options will be passed to `parent' with this option removed. %% %% @since 0.3.0 %% @end -module(systemd_kmsg_formatter). -include("systemd.hrl"). -include_lib("kernel/include/file.hrl"). -export([check_config/1, format/2]). -export([auto_install/0]). %% @hidden -spec check_config(logger:formatter_config()) -> ok | {error, term()}. check_config(Config0) -> case maps:take(parent, Config0) of {Formatter, Config} -> case erlang:function_exported(Formatter, check_config, 1) of true -> Formatter:check_config(Config); _ -> ok end; error -> logger_formatter:check_config(Config0) end. %% @hidden -spec format(logger:log_event(), logger:formatter_config()) -> unicode:chardata(). format(#{level := Level}=LogEvent, Config0) -> {Formatter, Config} = case maps:take(parent, Config0) of {FMod, Conf} -> {FMod, Conf}; error -> {logger_formatter, Config0} end, Priority = format_level(Level), Message0 = Formatter:format(LogEvent, Config), Message = string:replace(Message0, "\n", [$\n, Priority], all), [Priority | Message]. format_level(emergency) -> ?SD_EMERG; format_level(alert) -> ?SD_ALERT; format_level(critical) -> ?SD_CRIT; format_level(error) -> ?SD_ERR; format_level(warning) -> ?SD_WARNING; format_level(notice) -> ?SD_NOTICE; format_level(info) -> ?SD_INFO; format_level(debug) -> ?SD_DEBUG. %% @hidden Automatically install kmsg formatter for all handlers that use %% logger_std_h and points to journal stream auto_install() -> case get_journal_stream() of {Dev, Inode} -> [auto_install(Dev, Inode, Config) || Config <- logger:get_handler_config()]; _ -> ok end, ok. auto_install(Dev, Inode, #{id := Id, module := logger_std_h, config := #{type := Type}, formatter := {FMod, FConf}}) when FMod =/= ?MODULE -> case file_info(Type) of {ok, #file_info{major_device=Dev, inode=Inode}} -> logger:update_handler_config( Id, #{formatter => {?MODULE, FConf#{parent => FMod}}}); _ -> ok end; auto_install(_Dev, _Inode, _Config) -> ok. get_journal_stream() -> Env = os:getenv("JOURNAL_STREAM", ""), case string:split(Env, ":") of [DevStr, InodeStr | _] -> try {list_to_integer(DevStr), list_to_integer(InodeStr)} catch error:badarg:_ -> error end; _ -> error end. file_info(standard_io) -> file:read_file_info("/dev/stdout"); file_info(standard_error) -> file:read_file_info("/dev/stderr"); file_info(_) -> {error, unknown_device}.