Skip to content

Commit

Permalink
Add ability to customize product name, version & banner
Browse files Browse the repository at this point in the history
To override the product name (defaulting to "RabbitMQ"):
* set the `$RABBITMQ_PRODUCT_NAME` environment variable, or
* set the `rabbit` application `product_name` variable.

To override the product version:
* set the `$RABBITMQ_PRODUCT_VERSION` environment variable, or
* set the `rabbit` application `product_version` variable.

To add content to the banner (both the copy logged and the one printed
to stdout), indicate the filename which contains it, à la `/etc/motd`
using:
* the `$RABBITMQ_MOTD_FILE` environment variable, or
* the `rabbit` application `motd_file` variable.

The default motd file is `/etc/rabbitmq/motd` on Unix and
`%APPDATA%\RabbitMQ\motd.txt` on Windows.

Here is an example of the printed banner with name, version & motd
configured:

      ##  ##      WeatherMQ 1.2.3
      ##  ##
      ##########  Copyright (c) 2007-2020 Pivotal Software, Inc.
      ######  ##
      ##########  Licensed under the MPL 1.1. Website: https://rabbitmq.com

      This is an example of a RabbitMQ message of the day.

      The message is written in Paris, France.    \  /
      It is partly cloudy outside, with a       _ /"".-.
      temperature of 12°C. Wind is around         \_(   ).
      30-40 km/h, from south-west.                /(___(__)

      Doc guides: https://rabbitmq.com/documentation.html
      Support:    https://rabbitmq.com/contact.html
      Tutorials:  https://rabbitmq.com/getstarted.html
      Monitoring: https://rabbitmq.com/monitoring.html

      Logs: /tmp/rabbitmq-test-instances/rabbit/log/rabbit@cassini.log
            /tmp/rabbitmq-test-instances/rabbit/log/rabbit@cassini_upgrade.log

      Config file(s): /tmp/rabbitmq-test-instances/test.config

      Starting broker... completed with 0 plugins.

New APIS are available to query those product informations and use them
in e.g. plugins such as the management API/UI:
* rabbit:product_info/0
* rabbit:product_name/0
* rabbit:product_version/0
* rabbit:motd_file/0
* rabbit:motd/0

[#170054940]
  • Loading branch information
dumbbell committed Feb 20, 2020
1 parent a5f8ac4 commit 8d5cf5e
Show file tree
Hide file tree
Showing 3 changed files with 196 additions and 12 deletions.
11 changes: 11 additions & 0 deletions docs/rabbitmq.conf.example
Original file line number Diff line number Diff line change
Expand Up @@ -520,6 +520,17 @@
##
# proxy_protocol = false

## Overriden product name and version.
## They are set to "RabbitMQ" and the release version by default.
# product.name = RabbitMQ
# product.version = 1.2.3

## "Message of the day" file.
## Its content is used to expand the logged and printed banners.
## Default to /etc/rabbitmq/motd on Unix, %APPDATA%\RabbitMQ\motd.txt
## on Windows.
# motd_file = /etc/rabbitmq/motd

## ----------------------------------------------------------------------------
## Advanced Erlang Networking/Clustering Options.
##
Expand Down
18 changes: 18 additions & 0 deletions priv/schema/rabbit.schema
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
% vim:ft=erlang:
% ==============================
% Rabbit app section
% ==============================
Expand Down Expand Up @@ -1110,6 +1111,23 @@ end}.
{validators, ["non_zero_positive_integer"]}
]}.

%% Product name & version overrides.

{mapping, "product.name", "rabbit.product_name", [
{datatype, string}
]}.
{mapping, "product.version", "rabbit.product_version", [
{datatype, string}
]}.

%% Message of the day file.
%% The content of that file is added to the banners, both logged and
%% printed.

{mapping, "motd_file", "rabbit.motd_file", [
{datatype, string}
]}.

% ==========================
% Lager section
% ==========================
Expand Down
179 changes: 167 additions & 12 deletions src/rabbit.erl
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,11 @@

-export([start/2, stop/1, prep_stop/1]).
-export([start_apps/1, start_apps/2, stop_apps/1]).
-export([product_info/0,
product_name/0,
product_version/0,
motd_file/0,
motd/0]).
-export([log_locations/0, config_files/0]). %% for testing and mgmt-agent
-export([is_booted/1, is_booted/0, is_booting/1, is_booting/0]).

Expand Down Expand Up @@ -435,10 +440,12 @@ stop() ->
ok ->
case rabbit_boot_state:get() of
ready ->
rabbit_log:info("RabbitMQ is asked to stop..."),
Product = product_name(),
rabbit_log:info("~s is asked to stop...", [Product]),
do_stop(),
rabbit_log:info(
"Successfully stopped RabbitMQ and its dependencies"),
"Successfully stopped ~s and its dependencies",
[Product]),
ok;
stopped ->
ok
Expand All @@ -462,7 +469,9 @@ stop_and_halt() ->
try
stop()
catch Type:Reason ->
rabbit_log:error("Error trying to stop RabbitMQ: ~p:~p", [Type, Reason]),
rabbit_log:error(
"Error trying to stop ~s: ~p:~p",
[product_name(), Type, Reason]),
error({Type, Reason})
after
%% Enclose all the logging in the try block.
Expand Down Expand Up @@ -518,9 +527,9 @@ stop_apps([]) ->
ok;
stop_apps(Apps) ->
rabbit_log:info(
lists:flatten(["Stopping RabbitMQ applications and their dependencies in the following order:~n",
lists:flatten(["Stopping ~s applications and their dependencies in the following order:~n",
[" ~p~n" || _ <- Apps]]),
lists:reverse(Apps)),
[product_name() | lists:reverse(Apps)]),
ok = app_utils:stop_applications(
Apps, handle_app_error(error_during_shutdown)),
case lists:member(rabbit, Apps) of
Expand Down Expand Up @@ -663,7 +672,7 @@ maybe_print_boot_progress(true, IterationsLeft) ->
{memory, any()}].

status() ->
{ok, Version} = application:get_key(rabbit, vsn),
Version = product_version(),
S1 = [{pid, list_to_integer(os:getpid())},
%% The timeout value used is twice that of gen_server:call/2.
{running_applications, rabbit_misc:which_applications()},
Expand Down Expand Up @@ -816,9 +825,10 @@ start(normal, []) ->
try
run_prelaunch_second_phase(),

rabbit_log:info("~n Starting RabbitMQ ~s on Erlang ~s~n ~s~n ~s~n",
[rabbit_misc:version(), rabbit_misc:otp_release(),
rabbit_log:info("~n Starting ~s ~s on Erlang ~s~n ~s~n ~s~n",
[product_name(), product_version(), rabbit_misc:otp_release(),
?COPYRIGHT_MESSAGE, ?INFORMATION_MESSAGE]),
log_motd(),
{ok, SupPid} = rabbit_sup:start_link(),

%% Compatibility with older RabbitMQ versions + required by
Expand Down Expand Up @@ -904,7 +914,7 @@ do_run_postlaunch_phase() ->
end
end, Plugins),

rabbit_log_prelaunch:debug("Marking RabbitMQ as running"),
rabbit_log_prelaunch:debug("Marking ~s as running", [product_name()]),
rabbit_boot_state:set(ready),

ok = rabbit_lager:broker_is_started(),
Expand Down Expand Up @@ -1058,8 +1068,8 @@ log_broker_started(Plugins) ->
"~n " ?BG32_START " " ?C_END " ~s").

print_banner() ->
{ok, Product} = application:get_key(description),
{ok, Version} = application:get_key(vsn),
Product = product_name(),
Version = product_version(),
LineListFormatter = fun (Placeholder, [_ | Tail] = LL) ->
LF = lists:flatten([Placeholder || _ <- lists:seq(1, length(Tail))]),
{LF, LL};
Expand All @@ -1082,8 +1092,21 @@ print_banner() ->
%% padded list lines
{LogFmt, LogLocations} = LineListFormatter("~n ~ts", log_locations()),
{CfgFmt, CfgLocations} = LineListFormatter("~n ~ts", config_locations()),
{MOTDFormat, MOTDArgs} = case motd() of
undefined ->
{"", []};
MOTD ->
Lines = string:split(MOTD, "\n", all),
Padded = [case Line of
<<>> -> "\n";
_ -> [" ", Line, "\n"]
end
|| Line <- Lines],
{"~n~ts", [Padded]}
end,
io:format(Logo ++
"~n"
"~n" ++
MOTDFormat ++
"~n Doc guides: https://rabbitmq.com/documentation.html"
"~n Support: https://rabbitmq.com/contact.html"
"~n Tutorials: https://rabbitmq.com/getstarted.html"
Expand All @@ -1093,9 +1116,24 @@ print_banner() ->
"~n Config file(s): ~ts" ++ CfgFmt ++ "~n"
"~n Starting broker...",
[Product, Version, ?COPYRIGHT_MESSAGE, ?INFORMATION_MESSAGE] ++
MOTDArgs ++
LogLocations ++
CfgLocations).

log_motd() ->
case motd() of
undefined ->
ok;
MOTD ->
Lines = string:split(MOTD, "\n", all),
Padded = [case Line of
<<>> -> "\n";
_ -> [" ", Line, "\n"]
end
|| Line <- Lines],
rabbit_log:info("~n~ts", [string:trim(Padded, trailing, [$\r, $\n])])
end.

log_banner() ->
{FirstLog, OtherLogs} = case log_locations() of
[Head | Tail] ->
Expand Down Expand Up @@ -1235,6 +1273,123 @@ validate_msg_store_io_batch_size_and_credit_disc_bound(CreditDiscBound,
end
end.

-spec product_name() -> string().

product_name() ->
#{name := ProductName} = product_info(),
ProductName.

-spec product_version() -> string().

product_version() ->
#{version := ProductVersion} = product_info(),
ProductVersion.

-spec product_info() -> #{name := string(),
version := string()}.

product_info() ->
PTKey = {?MODULE, product},
try
%% The value is cached the first time to avoid calling the
%% application master many times just for that.
persistent_term:get(PTKey)
catch
error:badarg ->
{NameFromEnv, VersionFromEnv} =
case rabbit_env:get_context() of
#{product_name := NFE,
product_version := VFE} -> {NFE, VFE};
_ -> {undefined, undefined}
end,

Info =
if
NameFromEnv =/= undefined andalso
VersionFromEnv =/= undefined ->
#{name => NameFromEnv,
version => VersionFromEnv};
true ->
Name = case NameFromEnv of
undefined ->
string_from_app_env(
product_name,
base_product_name());
_ ->
NameFromEnv
end,
Version = case VersionFromEnv of
undefined ->
string_from_app_env(
product_version,
base_product_version());
_ ->
VersionFromEnv
end,
#{name => Name,
version => Version}
end,
persistent_term:put(PTKey, Info),
Info
end.

string_from_app_env(Key, Default) ->
case application:get_env(rabbit, Key) of
{ok, Val} ->
case io_lib:deep_char_list(Val) of
true ->
case lists:flatten(Val) of
"" -> Default;
String -> String
end;
false ->
Default
end;
undefined ->
Default
end.

base_product_name() ->
%% This function assumes the `rabbit` application was loaded in
%% product_info().
{ok, Product} = application:get_key(rabbit, description),
Product.

base_product_version() ->
%% This function assumes the `rabbit` application was loaded in
%% product_info().
rabbit_misc:version().

motd_file() ->
%% Precendence is:
%% 1. The environment variable;
%% 2. The `motd_file` configuration parameter;
%% 3. The default value.
Context = rabbit_env:get_context(),
case Context of
#{motd_file := File,
var_origins := #{motd_file := environment}}
when File =/= undefined ->
File;
_ ->
Default = case Context of
#{motd_file := File} -> File;
_ -> undefined
end,
string_from_app_env(motd_file, Default)
end.

motd() ->
case motd_file() of
undefined ->
undefined;
File ->
case file:read_file(File) of
{ok, MOTD} -> string:trim(MOTD, trailing, [$\r,$\n]);
{error, _} -> undefined
end
end.

home_dir() ->
case init:get_argument(home) of
{ok, [[Home]]} -> Home;
Expand Down

0 comments on commit 8d5cf5e

Please sign in to comment.