I'm assuming that your real goal is to make any missing characters obvious, so probably the best way to do that is to replace any missing characters with a bright red box, so that they're impossible to miss:
\RequirePackage{luacode}
\begin{luacode*}
-- Constants
local width = tex.sp("10pt")
local height = tex.sp("10pt")
local message = string.formatters["Missing glyph %c (%U) on page %d"]
local pdf_start = "q 1 0 0 rg"
local pdf_stop = "Q"
-- Make luaotfload find our "missing_fallback" font
luatexbase.add_to_callback("luaotfload.resolve_font", function(specification)
if specification.name:match("^missing_fallback") then
specification.forced = "missing_fallback"
specification.size = width
return "missing_fallback", "", true
end
end, "missing_fallback")
-- Define the "missing_fallback" font
function fonts.readers.missing_fallback(specification)
local missing = {
width = width,
height = height,
depth = 0,
commands = {
{ "pdf", "direct", pdf_start },
{ "lua", function(font_id, char_code)
tex.error(message(char_code, char_code, tex.count[0]))
end },
{ "rule", height, width },
{ "pdf", "direct", pdf_stop },
},
}
local unicode_max = 0x10FFFF
local characters = lua.newtable(unicode_max, 0)
for i = 0, unicode_max do
characters[i] = missing
end
return {
name = "missing_fallback",
parameters = {},
properties = {},
size = specification.size,
characters = characters,
}
end
-- Make sure that the missing fallback font is used for all loaded fonts
luaotfload.add_fallback("missing_fallback", { "my:missing_fallback" })
luaotfload.features.global.fallback = "missing_fallback"
\end{luacode*}
\documentclass{article}
\pagestyle{empty}
\begin{document}
\section{Missing ☹ Glyphs}
Hello, world! 🦆 \emph{also works here π}
\end{document}

This is LuaHBTeX, Version 1.22.0 (TeX Live 2025)
restricted system commands enabled.
(./luatex-missing-glyph.tex
LaTeX2e <2024-11-01> patch level 2
L3 programming layer <2025-01-18>
(/usr/local/texlive/2025/texmf-dist/tex/lualatex/luacode/luacode.sty (/usr/local/texlive/2025/texmf-dist/tex/generic/iftex/ifluatex.sty (/usr/local/texlive/2025/texmf-dist/tex/generic/iftex/iftex.sty)) (/usr/local/texlive/2025/texmf-dist/tex/luatex/luatexbase/luatexbase.sty (/usr/local/texlive/2025/texmf-dist/tex/luatex/ctablestack/ctablestack.sty))) (/usr/local/texlive/2025/texmf-dist/tex/latex/base/article.cls
Document Class: article 2024/06/29 v1.4n Standard LaTeX document class
(/usr/local/texlive/2025/texmf-dist/tex/latex/base/size10.clo)) (/usr/local/texlive/2025/texmf-dist/tex/latex/l3backend/l3backend-luatex.def) (./luatex-missing-glyph.aux)
[1{/usr/local/texlive/2025/texmf-var/fonts/map/pdftex/updmap/pdftex.map}
! Missing glyph ☹ (U+02639) on page 1.
<argument> ...hipout/lastpage}\@kernel@after@shipout@lastpage }\bool_gset_true:N \g__shipout_lastpage_handled_bool }}\hook_use:n {shipout}\__shipout_finalize_box: \cs_set_eq:NN \protect \exp_not:N \tex_shipout:D \box_use:N \l_shipout_box
\__shipout_dr...
l.61 \end{document}
! Missing glyph 🦆 (U+1F986) on page 1.
<argument> ...hipout/lastpage}\@kernel@after@shipout@lastpage }\bool_gset_true:N \g__shipout_lastpage_handled_bool }}\hook_use:n {shipout}\__shipout_finalize_box: \cs_set_eq:NN \protect \exp_not:N \tex_shipout:D \box_use:N \l_shipout_box
\__shipout_dr...
l.61 \end{document}
! Missing glyph π (U+003C0) on page 1.
<argument> ...hipout/lastpage}\@kernel@after@shipout@lastpage }\bool_gset_true:N \g__shipout_lastpage_handled_bool }}\hook_use:n {shipout}\__shipout_finalize_box: \cs_set_eq:NN \protect \exp_not:N \tex_shipout:D \box_use:N \l_shipout_box
\__shipout_dr...
l.61 \end{document}
] (./luatex-missing-glyph.aux))
(see the transcript file for additional information)
411 words of node memory still in use:
3 hlist, 1 vlist, 1 rule, 2 glue, 3 kern, 1 glyph, 4 attribute, 49 glue_spec, 4 attribute_list, 1 write nodes
avail lists: 2:34,3:10,4:1,5:25,6:4,7:140,9:20,11:2
</usr/local/texlive/2025/texmf-dist/fonts/opentype/public/lm/lmroman10-italic.otf></usr/local/texlive/2025/texmf-dist/fonts/opentype/public/lm/lmroman10-regular.otf></usr/local/texlive/2025/texmf-dist/fonts/opentype/public/lm/lmroman12-bold.otf>
Output written on luatex-missing-glyph.pdf (1 page, 8443 bytes).
Transcript written on luatex-missing-glyph.log.
wrapup_runcallback if you want to parse the log file, but LuaTeX actually provides aglyph_not_foundcallback that is specifically designed to do what you're asking for. I'll write a full answer in a couple hours.