Skip to main content

src/barrel_docdb_app.erl

%%%-------------------------------------------------------------------
%%% @doc barrel_docdb application module
%%%
%%% Starts and stops the barrel_docdb application.
%%% @end
%%%-------------------------------------------------------------------
-module(barrel_docdb_app).

-behaviour(application).

%% Application callbacks
-export([start/2, stop/1]).

%%====================================================================
%% Application callbacks
%%====================================================================

%% @doc Start the barrel_docdb application
-spec start(application:start_type(), term()) -> {ok, pid()} | {error, term()}.
start(_StartType, _StartArgs) ->
    %% Install instrument logger filter for trace context enrichment
    ok = instrument_logger:install(),
    %% Configure observability exporters
    ok = configure_tracing(),
    ok = configure_metrics(),
    ok = configure_logging(),
    logger:info("Starting barrel_docdb application"),
    barrel_docdb_sup:start_link().

%% @doc Stop the barrel_docdb application
-spec stop(term()) -> ok.
stop(_State) ->
    logger:info("Stopping barrel_docdb application"),
    %% Shutdown all exporters
    _ = instrument_exporter:shutdown(),
    _ = instrument_metrics_exporter:shutdown(),
    _ = instrument_log_exporter:shutdown(),
    %% Uninstall instrument logger filter
    instrument_logger:uninstall(),
    ok.

%%====================================================================
%% Internal functions
%%====================================================================

%% @doc Configure tracing based on application environment.
%%
%% Configuration options:
%% - {tracing, enabled} - Enable/disable tracing (default: true)
%% - {tracing, exporter} - console | otlp | {module, Config} (default: none)
%% - {tracing, otlp_endpoint} - OTLP endpoint URL (for otlp exporter)
%%
%% Example sys.config:
%% ```
%% {barrel_docdb, [
%%     {tracing, [
%%         {enabled, true},
%%         {exporter, console}  %% or otlp or none
%%     ]}
%% ]}
%% '''
-spec configure_tracing() -> ok.
configure_tracing() ->
    TracingConfig = application:get_env(barrel_docdb, tracing, []),
    Enabled = proplists:get_value(enabled, TracingConfig, true),
    case Enabled of
        true ->
            configure_exporter(TracingConfig);
        false ->
            %% Disable tracing globally for zero overhead
            instrument_config:set_tracing_enabled(false),
            logger:info("Tracing disabled globally"),
            ok
    end.

%% @doc Configure the span exporter based on configuration.
configure_exporter(TracingConfig) ->
    Exporter = proplists:get_value(exporter, TracingConfig, none),
    case Exporter of
        none ->
            ok;
        console ->
            Format = proplists:get_value(console_format, TracingConfig, text),
            _ = instrument_exporter:register(instrument_exporter_console:new(#{
                format => Format
            })),
            logger:info("Tracing enabled with console exporter"),
            ok;
        otlp ->
            Endpoint = proplists:get_value(otlp_endpoint, TracingConfig, "http://localhost:4318"),
            _ = instrument_exporter:register(instrument_exporter_otlp:new(#{
                endpoint => Endpoint
            })),
            logger:info("Tracing enabled with OTLP exporter, endpoint: ~s", [Endpoint]),
            ok;
        {Module, Config} when is_atom(Module), is_map(Config) ->
            _ = instrument_exporter:register(#{module => Module, config => Config}),
            logger:info("Tracing enabled with custom exporter: ~p", [Module]),
            ok;
        _ ->
            logger:warning("Unknown tracing exporter configuration: ~p", [Exporter]),
            ok
    end.

%% @doc Configure metrics exporter based on application environment.
%%
%% Configuration options:
%% - {metrics, exporter} - none | console | otlp (default: none)
%% - {metrics, otlp_endpoint} - OTLP endpoint URL
%% - {metrics, otlp_headers} - Additional HTTP headers
%% - {metrics, otlp_compression} - none | gzip
%% - {metrics, export_interval} - Export interval in ms (default: 60000)
-spec configure_metrics() -> ok.
configure_metrics() ->
    MetricsConfig = application:get_env(barrel_docdb, metrics, []),
    Exporter = proplists:get_value(exporter, MetricsConfig, none),
    case Exporter of
        none ->
            ok;
        console ->
            _ = instrument_metrics_exporter:register(
                instrument_metrics_exporter_console:new(#{})),
            logger:info("Metrics exporter enabled: console"),
            ok;
        otlp ->
            Endpoint = proplists:get_value(otlp_endpoint, MetricsConfig, "http://localhost:4318"),
            Headers = proplists:get_value(otlp_headers, MetricsConfig, #{}),
            Compression = proplists:get_value(otlp_compression, MetricsConfig, none),
            _ = instrument_metrics_exporter:register(
                instrument_metrics_exporter_otlp:new(#{
                    endpoint => Endpoint,
                    headers => Headers,
                    compression => Compression
                })),
            logger:info("Metrics exporter enabled: OTLP (~s)", [Endpoint]),
            ok;
        {Module, Config} when is_atom(Module), is_map(Config) ->
            _ = instrument_metrics_exporter:register(#{module => Module, config => Config}),
            logger:info("Metrics exporter enabled: ~p", [Module]),
            ok;
        _ ->
            logger:warning("Unknown metrics exporter configuration: ~p", [Exporter]),
            ok
    end.

%% @doc Configure logging exporter based on application environment.
%%
%% Configuration options:
%% - {logging, exporter} - none | console | file | otlp (default: none)
%% - {logging, file_path} - Log file path (for file exporter)
%% - {logging, file_format} - text | json (default: text)
%% - {logging, file_max_size} - Max file size in bytes (default: 10MB, 0 = unlimited)
%% - {logging, file_max_files} - Number of rotated files (default: 5)
%% - {logging, file_compress} - Compress rotated files (default: false)
%% - {logging, otlp_endpoint} - OTLP endpoint URL
%% - {logging, otlp_headers} - Additional HTTP headers
%% - {logging, otlp_compression} - none | gzip
-spec configure_logging() -> ok.
configure_logging() ->
    LoggingConfig = application:get_env(barrel_docdb, logging, []),
    Exporter = proplists:get_value(exporter, LoggingConfig, none),
    case Exporter of
        none ->
            ok;
        console ->
            _ = instrument_log_exporter:register(
                instrument_log_exporter_console:new(#{})),
            logger:info("Log exporter enabled: console"),
            ok;
        file ->
            Path = proplists:get_value(file_path, LoggingConfig, "/var/log/barrel.log"),
            Format = proplists:get_value(file_format, LoggingConfig, text),
            MaxSize = proplists:get_value(file_max_size, LoggingConfig, 10485760),
            MaxFiles = proplists:get_value(file_max_files, LoggingConfig, 5),
            Compress = proplists:get_value(file_compress, LoggingConfig, false),
            _ = instrument_log_exporter:register(
                instrument_log_exporter_file:new(#{
                    path => Path,
                    format => Format,
                    max_size => MaxSize,
                    max_files => MaxFiles,
                    compress => Compress
                })),
            logger:info("Log exporter enabled: file (~s)", [Path]),
            ok;
        otlp ->
            Endpoint = proplists:get_value(otlp_endpoint, LoggingConfig, "http://localhost:4318"),
            Headers = proplists:get_value(otlp_headers, LoggingConfig, #{}),
            Compression = proplists:get_value(otlp_compression, LoggingConfig, none),
            _ = instrument_log_exporter:register(
                instrument_log_exporter_otlp:new(#{
                    endpoint => Endpoint,
                    headers => Headers,
                    compression => Compression
                })),
            logger:info("Log exporter enabled: OTLP (~s)", [Endpoint]),
            ok;
        {Module, Config} when is_atom(Module), is_map(Config) ->
            _ = instrument_log_exporter:register(#{module => Module, config => Config}),
            logger:info("Log exporter enabled: ~p", [Module]),
            ok;
        _ ->
            logger:warning("Unknown logging exporter configuration: ~p", [Exporter]),
            ok
    end.