Skip to content

Fix NewRelic browser monitoring - #22

Merged
jantman merged 3 commits into
mainfrom
fix-newrelic-browser-monitoring
Mar 4, 2026
Merged

Fix NewRelic browser monitoring#22
jantman merged 3 commits into
mainfrom
fix-newrelic-browser-monitoring

Conversation

@jantman

@jantman jantman commented Mar 4, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Fix NewRelic browser monitoring JS never being injected into pages due to calls to the removed get_browser_timing_footer() API
  • The NewRelic Python agent now returns the complete browser script from get_browser_timing_header() alone; the footer function no longer exists
  • Remove {{ newrelic_footer }} from all Jinja2 templates and simplify the injection logic in app.py
  • Bump version to 0.3.5

Test plan

  • Rebuild and deploy Docker image
  • Verify no get_browser_timing_footer errors in container logs
  • View page source on /admin/login and /display/<name> to confirm NewRelic browser JS is present in <head>
  • Verify browser monitoring data appears in NewRelic UI

🤖 Generated with Claude Code

…g_footer() call

The NewRelic Python agent removed the `get_browser_timing_footer()` API;
`get_browser_timing_header()` now returns the complete browser monitoring
script. Every request was hitting the generic exception handler in both
`newrelic_browser_timing()` and `_get_newrelic_browser_scripts()`, logging
"module 'newrelic.agent' has no attribute 'get_browser_timing_footer'" and
returning empty strings, which meant no browser JS was ever injected.

Changes:
- Remove all calls to `newrelic.agent.get_browser_timing_footer()` in app.py
- Simplify `_get_newrelic_browser_scripts()` to `_get_newrelic_browser_script()`
  returning a single string instead of a tuple
- Simplify `_inject_newrelic_into_html()` to only inject the header script
- Remove `newrelic_footer` from context processor return values
- Remove `{{ newrelic_footer }}` from all three Jinja2 templates:
  base.html, display.html, display/slideshow.html
- Bump version to 0.3.5

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings March 4, 2026 01:09
@github-actions

github-actions Bot commented Mar 4, 2026

Copy link
Copy Markdown

Coverage

Coverage Report
FileStmtsMissCoverMissing
__init__.py100100% 
app.py1383177%35, 75, 193, 235–236, 238–239, 241–244, 256–258, 261, 263, 273, 275, 278–279, 282–285, 289–292, 295–296, 299
database_utils.py17013023%30–36, 41–47, 57, 59–61, 64–65, 67–69, 72–74, 77, 79, 88, 90–93, 95, 116, 118, 121–123, 126, 129, 131–132, 134–135, 137–139, 142–146, 159, 161–164, 167, 177, 186–187, 190, 219, 247, 250, 283–284, 286–287, 289–292, 301, 303, 308, 310, 315, 318–319, 324, 326, 333, 336–337, 339–340, 347–348, 356, 361, 384–387, 390, 393–396, 399–401, 404–406, 408–409, 411–413, 426–429, 431–433, 436, 439, 442, 445–447, 449–450, 452–454
db_resilience.py122397%186, 256, 280
exceptions.py1100100% 
health.py1121685%81–83, 86–87, 98, 119–120, 134, 147, 158–160, 170–172
ical_parser.py113694%85, 120, 124, 127, 134, 197
ical_service.py1701889%80, 82–85, 87, 127–128, 130, 233, 342, 368–369, 375, 382, 454, 456, 460
logging_config.py81396%110, 112, 190
metrics.py236697%426–427, 471–472, 482–483
middleware.py36683%77, 95–96, 109–110, 117
migration_utils.py73730%8–9, 11–12, 15, 18–19, 26–32, 34–35, 46–48, 50–55, 57–58, 68–70, 72–77, 79–80, 90–96, 98–99, 105–108, 110–111, 117–120, 122–123, 133–138, 141, 144, 151, 153–154, 156, 161–162
sse.py1675865%45, 47–48, 50–51, 53–54, 57–59, 61–62, 65–66, 68, 118, 120–122, 204, 210–211, 213–214, 218, 225, 227–228, 230, 232, 234, 309, 322, 324, 332, 335–336, 338, 341, 345–346, 348, 352–353, 355, 358–359, 361, 365, 369–370, 372, 383, 395–398, 449
storage.py3094685%87–89, 155–156, 162, 167, 258–260, 488–490, 511, 525, 572–575, 579, 603–605, 616–618, 639–641, 681–684, 712–713, 715–721, 723, 770–771, 798
storage_resilience.py1635367%68–69, 203–204, 211, 213–219, 266–270, 303–305, 334, 347–348, 352–358, 362–364, 382, 384–389, 391–401, 403, 405
validation.py1381787%221, 223, 229, 263, 265–266, 268–269, 337–342, 348, 376, 413
api
   __init__.py10280%27, 38
   helpers.py772074%170, 179, 222, 230, 235, 243, 248, 256, 261, 270, 275, 283–286, 293, 301–304
   v1.py152964457%89, 177–179, 219, 225, 231, 240, 262, 290–292, 304, 341, 345, 361–364, 384, 388, 391, 411–412, 415, 425, 429, 433, 552–554, 557, 571, 575, 579, 583, 648, 651–652, 657–658, 661–662, 664–665, 668–669, 672, 685, 696, 719–722, 732, 736, 740, 759–762, 772, 776, 780, 784, 788, 809, 816–818, 835–840, 858, 862, 881–885, 908, 923–925, 940, 947–949, 963, 972–974, 991, 1002–1004, 1014, 1018, 1023, 1028, 1033, 1053–1054, 1070–1073, 1083, 1087, 1091–1093, 1103, 1107, 1111, 1127–1128, 1132, 1145, 1202–1205, 1212–1215, 1217–1219, 1221–1223, 1226–1227, 1230–1233, 1236–1237, 1240, 1248–1249, 1251, 1254, 1265, 1270, 1273, 1277–1281, 1284, 1294, 1298, 1309–1312, 1324–1327, 1329–1331, 1333–1334, 1336–1337, 1339, 1342, 1344–1347, 1354–1357, 1359–1361, 1363–1364, 1366–1368, 1370, 1373, 1375–1378, 1395, 1423–1425, 1432–1434, 1436, 1438–1440, 1459, 1461–1464, 1467, 1470–1475, 1478, 1481, 1483, 1485–1489, 1501–1504, 1506, 1509, 1511, 1513–1515, 1522–1525, 1527–1529, 1532–1533, 1536, 1539–1540, 1543, 1556–1557, 1559, 1562, 1566–1576, 1583–1586, 1588–1590, 1592–1593, 1595, 1597–1599, 1606–1609, 1611–1613, 1615–1616, 1618–1620, 1623, 1625, 1632–1634, 1636–1649, 1651–1652, 1654, 1657, 1659–1669, 1676–1679, 1681–1683, 1685–1686, 1688–1690, 1692, 1695, 1697–1700, 1711–1714, 1716–1718, 1720–1722, 1724–1725, 1727–1728, 1731–1733, 1735, 1738, 1740–1745, 1748, 1755–1758, 1760–1762, 1764–1765, 1767–1770, 1772–1774, 1776–1777, 1780–1782, 1784–1788, 1791, 1793–1794, 1797, 1799–1800, 1802–1803, 1805, 1807, 1811, 1818, 1822–1825, 1867, 1947–1949, 1952–1953, 1961, 1967, 1975, 1984, 2005, 2009, 2039–2041, 2043–2045, 2053, 2063, 2067, 2106–2108, 2110–2112, 2120, 2147, 2155, 2199–2201, 2203–2205, 2207–2209, 2213, 2240, 2260, 2270, 2288–2290, 2292–2294, 2302, 2340–2342, 2350, 2365, 2373, 2397, 2405, 2417–2419, 2429, 2433, 2437, 2442, 2447–2448, 2453, 2469, 2481–2483, 2525, 2533, 2560–2562, 2572, 2583–2584, 2592–2594, 2619, 2621, 2624, 2628, 2630, 2632–2637, 2643, 2645–2647, 2650, 2655–2656, 2658, 2662, 2664–2666, 2682, 2684, 2686, 2690, 2696–2697, 2699–2705, 2708–2709, 2712–2720, 2723–2731, 2733–2734, 2737–2738, 2741–2742, 2745–2751, 2755, 2757, 2759–2760, 2763, 2779, 2833–2835, 2842–2843, 2845–2847, 2849–2851, 2854, 2863, 2865, 2869, 2879–2881, 2904, 2933–2935, 3055–3056, 3063–3064, 3066–3068, 3070–3072, 3074, 3077, 3080, 3083–3085, 3087–3094, 3096–3097, 3099–3104, 3106, 3109–3110, 3112–3113, 3126, 3129–3130, 3134, 3136–3137, 3149–3150, 3154, 3158, 3169–3171, 3174
auth
   __init__.py40100% 
   decorators.py481275%54, 106, 122, 132–134, 136, 148–149, 151, 153–154
   views.py831878%73, 75–77, 120–121, 162–163, 165–166, 172, 240–241, 244–245, 254, 258, 267
cli
   __init__.py10100% 
   init_db.py856721%41–46, 48–49, 52–55, 57–58, 61–62, 65–66, 68, 73, 75–79, 81–82, 85–92, 94, 96, 100, 102–103, 141–142, 145, 148–151, 153–157, 159, 162, 171–173, 176–177, 179–183, 185, 187–188
   main.py351557%17, 28, 30–32, 41, 43–44, 53–54, 56, 58–59, 61, 66
config
   __init__.py580100% 
dashboard
   __init__.py20100% 
   views.py561769%88–91, 151, 153–157, 159, 161, 163, 165, 177, 188–189
display
   __init__.py20100% 
   views.py1514768%31, 33, 38, 53, 171–172, 187, 195, 230–231, 239, 273, 275–276, 278, 280–281, 283, 285, 306–308, 311–312, 314, 325, 333–334, 343, 354, 356–357, 364, 366, 368, 375, 388, 391, 410, 413, 415–416, 423, 425, 427, 433, 444
main
   __init__.py17170%10, 12, 14, 17, 20–21, 30, 33–35, 42, 45, 52, 55–56, 63, 69
models
   __init__.py4794391%126, 141, 263, 405, 407, 414, 431, 559, 584, 592, 594, 601, 609, 617, 725, 792, 799, 827, 885, 890, 924–927, 930, 1096, 1112, 1115, 1117, 1165–1166, 1168–1173, 1179, 1181, 1200, 1203, 1210, 1213
slideshow
   __init__.py503432%43, 46, 52–54, 56–58, 61–64, 66, 73–77, 80–82, 84, 90–91, 97, 103, 105, 107–112, 114
static
   __init__.py00100% 
templates
   __init__.py00100% 
utils
   __init__.py523532%43–46, 49–52, 65, 67–69, 71–72, 75–79, 92–97, 110–111, 121, 135–137, 140, 143–144, 146
TOTAL4887143770% 

Tests Skipped Failures Errors Time
669 1 💤 0 ❌ 0 🔥 55.254s ⏱️

The NewRelic agent's get_browser_timing_header() is typed as returning Any,
which causes a mypy no-any-return error when used in a function declared to
return str. Wrapping with str() satisfies the type checker.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Fixes New Relic browser monitoring injection after the Python agent removed get_browser_timing_footer(), ensuring the browser script is injected via get_browser_timing_header() only and cleaning up template usage accordingly.

Changes:

  • Remove New Relic “footer” injection from Jinja2 templates and simplify the Flask context processor to only provide newrelic_header.
  • Update the React admin index.html injection helper to inject a single New Relic browser script into <head>.
  • Bump application version to 0.3.5.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
pyproject.toml Bumps project version to 0.3.5.
kiosk_show_replacement/app.py Removes footer usage; updates context processor and HTML injection to use header-only script.
kiosk_show_replacement/templates/base.html Removes {{ newrelic_footer }} from the base template.
kiosk_show_replacement/templates/display.html Removes {{ newrelic_footer }} from the display template.
kiosk_show_replacement/templates/display/slideshow.html Removes {{ newrelic_footer }} from the slideshow display template.
kiosk_show_replacement/__init__.py Bumps package __version__ to 0.3.5.

Comment thread kiosk_show_replacement/app.py
Add TestNewRelicBrowserTiming test class covering:
- Context processor injects browser script when newrelic agent is available
- Context processor returns empty markup when newrelic is not installed
- Context processor handles exceptions from get_browser_timing_header gracefully
- Admin SPA HTML injection works when built frontend exists

Tests use sys.modules patching to mock the newrelic.agent module without
requiring the newrelic package to be installed in the test environment.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@jantman
jantman merged commit 11ae634 into main Mar 4, 2026
7 checks passed
@jantman
jantman deleted the fix-newrelic-browser-monitoring branch March 4, 2026 01:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants