How to Monitor Makeup with Vigilmon
Makeup is the syntax highlighter powering ExDoc. Where ExDoc transforms your @doc annotations into HTML, Makeup transforms code blocks inside those annotations into color-coded, token-classified output with full support for Elixir, Erlang, HTML, CSS, JavaScript, and more. Every code example on hexdocs.pm is highlighted by Makeup.
Makeup works through a lexer system: each language has a dedicated lexer package (makeup_elixir, makeup_erlang, makeup_js, etc.) that tokenizes source code before a formatter applies HTML or terminal color codes. When a lexer is missing, Makeup falls back to plain text — silently. Your docs ship without syntax highlighting and nobody notices until a user reports that code examples look wrong.
Vigilmon heartbeat monitors ensure your documentation pipeline — including syntax highlighting — stays functional.
Why Monitor Makeup?
Makeup failures are invisible in CI by default:
- Missing lexers — a new language added to doc examples without adding the corresponding
makeup_*lexer dep causes that language to render as plain text - Version conflicts — ExDoc and Makeup use a shared lexer registry; version mismatches cause some languages to fall back silently
- Broken formatter output — custom formatters or themes that produce malformed HTML pass ExDoc's build step but break rendering in browsers
- Lexer registration gaps —
Makeup.Registrynot updated after adding a new lexer package causes lookup failures at render time - Performance regression — large code blocks in docs tokenized by a slow or quadratic lexer path cause doc generation to time out in CI
Key Metrics to Track
| Metric | What it tells you | |--------|-------------------| | Doc build CI pass rate | Whether ExDoc + Makeup runs to completion | | Highlighted language count | Whether all expected lexers are registered | | Doc generation duration | Regression indicator for lexer performance issues | | Plain-text fallback count | Number of code blocks rendered without syntax highlighting | | Time since last successful build | Whether docs pipeline was silently broken |
Step 1: Add Makeup and Its Lexers
# mix.exs
defp deps do
[
{:ex_doc, "~> 0.34", only: :dev, runtime: false},
# Makeup is a transitive dependency of ex_doc, but pin it for control
{:makeup, "~> 1.2", only: :dev, runtime: false},
# Add lexers for languages you use in code examples
{:makeup_elixir, "~> 0.16", only: :dev, runtime: false},
{:makeup_erlang, "~> 1.0", only: :dev, runtime: false},
{:makeup_js, "~> 0.5", only: :dev, runtime: false},
{:makeup_html, "~> 0.1", only: :dev, runtime: false},
# rest of your deps
]
end
mix deps.get
Verify the lexer registry after installing:
# In iex -S mix
Makeup.Registry.all_supported_languages()
# => ["elixir", "erlang", "javascript", "html", ...]
Step 2: Verify Lexer Registration at Build Time
Add a pre-doc build check that verifies all expected languages are registered:
# lib/mix/tasks/check_makeup_lexers.ex
defmodule Mix.Tasks.CheckMakeupLexers do
use Mix.Task
@shortdoc "Verify Makeup has lexers registered for all languages used in docs"
@required_languages ~w[elixir erlang javascript html css]
def run(_args) do
available = Makeup.Registry.all_supported_languages()
missing = Enum.reject(@required_languages, fn lang ->
Enum.member?(available, lang)
end)
if missing == [] do
Mix.shell().info("All required Makeup lexers are registered: #{Enum.join(@required_languages, ", ")}")
else
Mix.shell().error("""
Missing Makeup lexers for: #{Enum.join(missing, ", ")}
Add the corresponding makeup_* package to your deps in mix.exs:
#{Enum.map_join(missing, "\n", fn lang -> " {:makeup_#{lang}, \"~> 1.0\", only: :dev, runtime: false}" end)}
""")
exit({:shutdown, 1})
end
end
end
Step 3: Run Makeup Verification in CI
# .github/workflows/docs.yml
name: Documentation
on:
push:
branches: [main]
pull_request:
jobs:
docs:
name: Generate Docs with Makeup
runs-on: ubuntu-latest
env:
VIGILMON_MAKEUP_HEARTBEAT_URL: ${{ secrets.VIGILMON_MAKEUP_HEARTBEAT_URL }}
steps:
- uses: actions/checkout@v4
- uses: erlef/setup-beam@v1
with:
elixir-version: '1.16'
otp-version: '26'
- name: Cache deps
uses: actions/cache@v3
with:
path: deps
key: ${{ runner.os }}-deps-${{ hashFiles('mix.lock') }}
- run: mix deps.get
- name: Verify Makeup lexers
run: mix check_makeup_lexers
- name: Generate docs
run: mix docs
- name: Verify highlighted output
run: |
# Check that syntax highlighting classes are present in generated HTML
if grep -r 'class="highlight"' doc/ | head -1 | grep -q 'highlight'; then
echo "Syntax highlighting present in generated docs"
else
echo "Warning: no highlighted code blocks found — check Makeup lexer registration"
exit 1
fi
- name: Ping Vigilmon heartbeat
if: success()
run: curl -fsS "$VIGILMON_MAKEUP_HEARTBEAT_URL"
Step 4: Create a Heartbeat Monitor in Vigilmon
- Sign in at vigilmon.online
- Click New Monitor → Heartbeat
- Name it
Makeup Doc Build — main branch - Set the expected interval to 25 hours — doc builds on every push to main
- Copy the URL and store it as
VIGILMON_MAKEUP_HEARTBEAT_URLin your CI secrets
If Makeup lexers are missing or doc generation fails, no heartbeat is sent and Vigilmon alerts you.
Step 5: Custom Makeup Formatter for Your Application
If you use Makeup in your web application (not just for ExDoc), monitor the formatter directly:
# lib/my_app/highlighter.ex
defmodule MyApp.Highlighter do
@doc """
Highlights source code in the given language.
Falls back to plain text if no lexer is available.
"""
def highlight(code, language) when is_binary(code) and is_binary(language) do
available = Makeup.Registry.all_supported_languages()
if language in available do
Makeup.highlight(code, lexer_options: [language: language])
else
# Log the fallback so you can detect it in monitoring
require Logger
Logger.warning("No Makeup lexer for language: #{language}. Rendering as plain text.")
plain_wrap(code)
end
end
defp plain_wrap(code) do
"<pre><code>#{Phoenix.HTML.html_escape(code)}</code></pre>"
end
end
Track the fallback log pattern in your observability stack:
# Alertmanager rule or Vigilmon log monitor (if using log-based monitoring)
# Alert when "No Makeup lexer for language" appears more than 5 times per hour
Step 6: Alerting
In Vigilmon, configure Notifications → New Channel:
Slack:
🔴 MISSED: Makeup Doc Build — main branch
Last successful doc build: 30 hours ago (expected every 25 hours)
Action: Check CI — possible missing Makeup lexer or ExDoc version conflict
Pagerduty for production applications using Makeup for live syntax highlighting — a broken highlighter serves malformed HTML to users.
Common Makeup Issues and Fixes
Language not highlighted (silent fallback):
# In a doc comment
@doc """
Example shell session:
```shell
mix test
"""
If `makeup_shell` isn't installed, this renders as plain text. The fix: add the lexer dep or use a supported language alias (`console`, `bash`).
**Custom lexer not in registry:**
```elixir
# After adding a custom lexer module, register it explicitly
Makeup.Registry.register_lexer(MyApp.CustomLexer, [
names: ["mylang"],
extensions: ["ml"]
])
Do this in an Application.start/2 callback or a Mix task that runs before doc generation.
HTML escaping in highlighted output:
Makeup produces safe HTML — do not double-escape. When embedding in Phoenix templates:
<%# WRONG — double-escapes the markup %>
<%= @highlighted_code %>
<%# CORRECT — mark as safe since Makeup already escaped user content %>
<%= Phoenix.HTML.raw(@highlighted_code) %>
What You've Built
| What | How |
|------|-----|
| Makeup lexer verification | Custom Mix task checking registered languages |
| CI integration | mix docs with lexer validation step |
| Syntax highlight verification | Grep check for .highlight classes in generated HTML |
| Doc build monitoring | Vigilmon heartbeat — alerts when build misses |
| Safe fallback | Logger warning when lexer unavailable in production |
| Failure alerting | Slack notification channel |
Makeup ensures code examples in your Elixir docs are readable and correctly highlighted. Vigilmon ensures the pipeline that runs Makeup never silently stops working.
Next Steps
- Add custom Makeup themes matching your brand for web application code highlighting
- Track fallback log events to discover which languages need new lexer packages
- Pin Makeup and ExDoc versions together to prevent transitive dep conflicts
- Set up a separate heartbeat for nightly full doc builds including all extras and changelogs
Get started free at vigilmon.online.