Skip to content

Commit

Permalink
sync-service: only include telemetry dependencies and processes in ap…
Browse files Browse the repository at this point in the history
…plication target build (#2379)

configure telemetry dependencies to only be included when building the
`application` target and do compile-time checks to exclude certain
modules when compiling without telemetry

uses both checks against Mix.target() and checks for the presence of
dependent modules to make compilation and runtime robust when running as
docker application, local dev server and when used as a dependency
  • Loading branch information
magnetised authored Feb 26, 2025
1 parent 0a95da1 commit 5da8f25
Show file tree
Hide file tree
Showing 12 changed files with 1,074 additions and 927 deletions.
5 changes: 5 additions & 0 deletions .changeset/few-trains-kick.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@core/sync-service": patch
---

Only include telemetry in docker build
38 changes: 24 additions & 14 deletions packages/sync-service/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,21 @@ ARG DEBIAN_VERSION=bookworm-20241223-slim

ARG BUILDER_IMAGE="hexpm/elixir:${ELIXIR_VERSION}-erlang-${OTP_VERSION}-debian-${DEBIAN_VERSION}"
ARG RUNNER_IMAGE="debian:${DEBIAN_VERSION}"
ARG MIX_TARGET=application

FROM ${BUILDER_IMAGE} AS builder
LABEL maintainer="[email protected]"

RUN apt-get update -y && \
apt-get install -y build-essential git curl && \
apt-get clean && \
rm -f /var/lib/apt/lists/*_*
apt-get install -y build-essential git curl && \
apt-get clean && \
rm -f /var/lib/apt/lists/*_*

RUN mix local.hex --force && mix local.rebar --force

ENV MIX_ENV=prod
ARG MIX_ENV=prod
ARG ELECTRIC_VERSION
ARG MIX_TARGET

WORKDIR /app

Expand All @@ -30,38 +33,45 @@ COPY lib /app/lib/
COPY package.json /app/
COPY config/*.exs /app/config/

ARG ELECTRIC_VERSION

RUN mix compile
RUN mix sentry.package_source_code
RUN mix release

RUN ls -l /app/_build

FROM ${RUNNER_IMAGE} AS runner_setup

RUN apt-get update -y && \
apt-get install -y libstdc++6 openssl libncurses5 locales ca-certificates curl && \
apt-get clean && \
rm -f /var/lib/apt/lists/*_*
apt-get install -y libstdc++6 openssl libncurses5 locales ca-certificates curl && \
apt-get clean && \
rm -f /var/lib/apt/lists/*_*

# Set the locale
RUN sed -i '/en_US.UTF-8/s/^# //g' /etc/locale.gen && locale-gen

ENV LANG=en_US.UTF-8
ENV LANGUAGE=en_US:en
ENV LC_ALL=en_US.UTF-8
ENV LANG=en_US.UTF-8 \
LANGUAGE=en_US:en \
LC_ALL=en_US.UTF-8 \
MIX_ENV=prod \
MIX_TARGET=application

WORKDIR "/app"

RUN chown nobody /app

FROM runner_setup AS runner

ARG MIX_TARGET
ARG RELEASE_NAME=electric
## Vaxine configuration via environment variables
COPY --from=builder /app/_build/prod/rel/${RELEASE_NAME} ./
RUN mv /app/bin/${RELEASE_NAME} /app/bin/entrypoint


COPY --from=builder "/app/_build/${MIX_TARGET}_prod/rel/${RELEASE_NAME}" ./

RUN mv /app/bin/${RELEASE_NAME} /app/bin/entrypoint

HEALTHCHECK --start-period=10s CMD curl --fail http://localhost:${ELECTRIC_PORT-3000}/v1/health || exit 1

ENTRYPOINT ["/app/bin/entrypoint"]

CMD ["start"]
12 changes: 10 additions & 2 deletions packages/sync-service/lib/electric/plug/router.ex
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
defmodule Electric.Plug.Router do
use Plug.Router, copy_opts_to_assign: :config
use Sentry.PlugCapture
use Electric.Telemetry

with_telemetry Sentry.PlugCapture do
use Sentry.PlugCapture
end

alias Electric.Plug.Utils.CORSHeaderPlug
alias Electric.Plug.Utils.PassAssignToOptsPlug
Expand All @@ -16,7 +20,11 @@ defmodule Electric.Plug.Router do
plug Electric.Plug.TraceContextPlug
plug Plug.Telemetry, event_prefix: [:electric, :routing]
plug Plug.Logger
plug Sentry.PlugContext

with_telemetry Sentry.PlugCapture do
plug Sentry.PlugContext
end

plug :put_cors_headers
plug :dispatch

Expand Down
7 changes: 6 additions & 1 deletion packages/sync-service/lib/electric/plug/utility_router.ex
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
defmodule Electric.Plug.UtilityRouter do
use Plug.Router
use Electric.Telemetry

plug :match
plug :dispatch

get "/metrics", do: resp(conn, 200, TelemetryMetricsPrometheus.Core.scrape())
with_telemetry TelemetryMetricsPrometheus.Core do
get "/metrics", do: resp(conn, 200, TelemetryMetricsPrometheus.Core.scrape())
else
get "/metrics", do: resp(conn, 200, "[]")
end
end
40 changes: 40 additions & 0 deletions packages/sync-service/lib/electric/telemetry.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
defmodule Electric.Telemetry do
require Logger

@enabled Mix.env() == :test || Mix.target() == Electric.MixProject.telemetry_target()
@log_level Application.compile_env(:electric, [Electric.Telemetry, :log_level], false)

defmacro __using__(_opts) do
quote do
import Electric.Telemetry
end
end

# uses the availability of the given dependencies to optionally compile
# the provided block when MIX_TARGET is `:application`.
defmacro with_telemetry(dependencies, do: block, else: else_block) do
include_with_telemetry(__CALLER__, __ENV__, dependencies, block, else_block)
end

defmacro with_telemetry(dependencies, do: block) do
include_with_telemetry(__CALLER__, __ENV__, dependencies, block, nil)
end

defp include_with_telemetry(caller, env, dependencies, block, else_block) do
modules = List.wrap(dependencies) |> Enum.map(&Macro.expand(&1, env))
available? = Enum.all?(modules, &Code.ensure_loaded?/1)

if @enabled && available? do
if @log_level,
do:
Logger.log(
@log_level,
"Enabling telemetry in #{caller.module || Path.relative_to(caller.file, Path.expand("..", __DIR__))}"
)

quote(do: unquote(block))
else
if else_block, do: quote(do: unquote(else_block))
end
end
end
Loading

0 comments on commit 5da8f25

Please sign in to comment.