brut 0.11.0 → 0.12.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (376) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +11 -0
  3. data/Gemfile.lock +8 -8
  4. data/brut-css/package-lock.json +94 -97
  5. data/brut-css/package.json +2 -2
  6. data/brut-js/package-lock.json +3 -3
  7. data/brut-js/package.json +6 -3
  8. data/brut-js/specs/AjaxSubmit.spec.js +62 -6
  9. data/brut-js/src/AjaxSubmit.js +26 -6
  10. data/brutrb.com/forms.md +1 -0
  11. data/brutrb.com/recipes/authentication.md +1 -0
  12. data/brutrb.com/tutorials/01-intro.md +26 -2
  13. data/docs/404.html +2 -2
  14. data/docs/adrs.html +3 -3
  15. data/docs/ai.html +3 -3
  16. data/docs/api/Brut/BackEnd/SeedData.html +1 -1
  17. data/docs/api/Brut/BackEnd/Sidekiq/Middlewares/Server/FlushSpans.html +1 -1
  18. data/docs/api/Brut/BackEnd/Sidekiq/Middlewares/Server.html +1 -1
  19. data/docs/api/Brut/BackEnd/Sidekiq/Middlewares.html +1 -1
  20. data/docs/api/Brut/BackEnd/Sidekiq.html +1 -1
  21. data/docs/api/Brut/BackEnd/Validators/FormValidator.html +1 -1
  22. data/docs/api/Brut/BackEnd/Validators.html +1 -1
  23. data/docs/api/Brut/BackEnd.html +1 -1
  24. data/docs/api/Brut/CLI/App.html +1 -1
  25. data/docs/api/Brut/CLI/AppRunner.html +1 -1
  26. data/docs/api/Brut/CLI/Apps/BuildAssets/All.html +1 -1
  27. data/docs/api/Brut/CLI/Apps/BuildAssets/CSS.html +1 -1
  28. data/docs/api/Brut/CLI/Apps/BuildAssets/Images.html +1 -1
  29. data/docs/api/Brut/CLI/Apps/BuildAssets/JS.html +1 -1
  30. data/docs/api/Brut/CLI/Apps/BuildAssets.html +1 -1
  31. data/docs/api/Brut/CLI/Apps/DB/Create.html +1 -1
  32. data/docs/api/Brut/CLI/Apps/DB/Drop.html +1 -1
  33. data/docs/api/Brut/CLI/Apps/DB/Migrate.html +1 -1
  34. data/docs/api/Brut/CLI/Apps/DB/NewMigration.html +1 -1
  35. data/docs/api/Brut/CLI/Apps/DB/Rebuild.html +1 -1
  36. data/docs/api/Brut/CLI/Apps/DB/Seed.html +1 -1
  37. data/docs/api/Brut/CLI/Apps/DB/Status.html +1 -1
  38. data/docs/api/Brut/CLI/Apps/DB.html +1 -1
  39. data/docs/api/Brut/CLI/Apps/DeployBase/GitChecks.html +1 -1
  40. data/docs/api/Brut/CLI/Apps/DeployBase.html +1 -1
  41. data/docs/api/Brut/CLI/Apps/HerokuContainerBasedDeploy/Deploy.html +1 -1
  42. data/docs/api/Brut/CLI/Apps/HerokuContainerBasedDeploy.html +1 -1
  43. data/docs/api/Brut/CLI/Apps/Scaffold/Action/Route.html +1 -1
  44. data/docs/api/Brut/CLI/Apps/Scaffold/Action.html +1 -1
  45. data/docs/api/Brut/CLI/Apps/Scaffold/Component.html +1 -1
  46. data/docs/api/Brut/CLI/Apps/Scaffold/CustomElementTest.html +1 -1
  47. data/docs/api/Brut/CLI/Apps/Scaffold/DbModel.html +1 -1
  48. data/docs/api/Brut/CLI/Apps/Scaffold/E2ETest.html +1 -1
  49. data/docs/api/Brut/CLI/Apps/Scaffold/Form.html +1 -1
  50. data/docs/api/Brut/CLI/Apps/Scaffold/Page/Route.html +1 -1
  51. data/docs/api/Brut/CLI/Apps/Scaffold/Page.html +1 -1
  52. data/docs/api/Brut/CLI/Apps/Scaffold/RoutesEditor.html +1 -1
  53. data/docs/api/Brut/CLI/Apps/Scaffold/Test.html +1 -1
  54. data/docs/api/Brut/CLI/Apps/Scaffold.html +1 -1
  55. data/docs/api/Brut/CLI/Apps/Test/Audit.html +1 -1
  56. data/docs/api/Brut/CLI/Apps/Test/E2e.html +1 -1
  57. data/docs/api/Brut/CLI/Apps/Test/JS.html +1 -1
  58. data/docs/api/Brut/CLI/Apps/Test/Run.html +1 -1
  59. data/docs/api/Brut/CLI/Apps/Test.html +1 -1
  60. data/docs/api/Brut/CLI/Apps.html +1 -1
  61. data/docs/api/Brut/CLI/Command.html +1 -1
  62. data/docs/api/Brut/CLI/Error.html +1 -1
  63. data/docs/api/Brut/CLI/ExecutionResults/Result.html +1 -1
  64. data/docs/api/Brut/CLI/ExecutionResults.html +1 -1
  65. data/docs/api/Brut/CLI/Executor.html +1 -1
  66. data/docs/api/Brut/CLI/InvalidOption.html +1 -1
  67. data/docs/api/Brut/CLI/Options.html +1 -1
  68. data/docs/api/Brut/CLI/Output.html +1 -1
  69. data/docs/api/Brut/CLI/SystemExecError.html +1 -1
  70. data/docs/api/Brut/CLI.html +1 -1
  71. data/docs/api/Brut/FactoryBot.html +1 -1
  72. data/docs/api/Brut/Framework/App.html +1 -1
  73. data/docs/api/Brut/Framework/Config.html +1 -1
  74. data/docs/api/Brut/Framework/Container.html +1 -1
  75. data/docs/api/Brut/Framework/Error.html +1 -1
  76. data/docs/api/Brut/Framework/Errors/AbstractMethod.html +1 -1
  77. data/docs/api/Brut/Framework/Errors/Bug.html +1 -1
  78. data/docs/api/Brut/Framework/Errors/MissingConfiguration.html +1 -1
  79. data/docs/api/Brut/Framework/Errors/MissingParameter.html +1 -1
  80. data/docs/api/Brut/Framework/Errors/NoClassForPath.html +1 -1
  81. data/docs/api/Brut/Framework/Errors/NotFound.html +1 -1
  82. data/docs/api/Brut/Framework/Errors/NotImplemented.html +1 -1
  83. data/docs/api/Brut/Framework/Errors.html +1 -1
  84. data/docs/api/Brut/Framework/FussyTypeEnforcement.html +2 -2
  85. data/docs/api/Brut/Framework/MCP.html +1 -1
  86. data/docs/api/Brut/Framework/ProjectEnvironment.html +1 -1
  87. data/docs/api/Brut/Framework.html +1 -1
  88. data/docs/api/Brut/FrontEnd/AssetPathResolver.html +1 -1
  89. data/docs/api/Brut/FrontEnd/Component/Helpers.html +1 -1
  90. data/docs/api/Brut/FrontEnd/Component.html +1 -1
  91. data/docs/api/Brut/FrontEnd/Components/ConstraintViolations.html +1 -1
  92. data/docs/api/Brut/FrontEnd/Components/FormTag.html +1 -1
  93. data/docs/api/Brut/FrontEnd/Components/I18nTranslations.html +1 -1
  94. data/docs/api/Brut/FrontEnd/Components/Input.html +2 -2
  95. data/docs/api/Brut/FrontEnd/Components/Inputs/ButtonTag.html +447 -0
  96. data/docs/api/Brut/FrontEnd/Components/Inputs/CsrfToken.html +1 -1
  97. data/docs/api/Brut/FrontEnd/Components/Inputs/InputTag.html +14 -9
  98. data/docs/api/Brut/FrontEnd/Components/Inputs/RadioButton.html +1 -1
  99. data/docs/api/Brut/FrontEnd/Components/Inputs/SelectTagWithOptions.html +8 -9
  100. data/docs/api/Brut/FrontEnd/Components/Inputs/TextareaTag.html +10 -11
  101. data/docs/api/Brut/FrontEnd/Components/Inputs.html +2 -2
  102. data/docs/api/Brut/FrontEnd/Components/LocaleDetection.html +1 -1
  103. data/docs/api/Brut/FrontEnd/Components/PageIdentifier.html +1 -1
  104. data/docs/api/Brut/FrontEnd/Components/TimeTag.html +1 -1
  105. data/docs/api/Brut/FrontEnd/Components/Traceparent.html +1 -1
  106. data/docs/api/Brut/FrontEnd/Components.html +1 -1
  107. data/docs/api/Brut/FrontEnd/CsrfProtector.html +1 -1
  108. data/docs/api/Brut/FrontEnd/Download.html +1 -1
  109. data/docs/api/Brut/FrontEnd/Flash.html +1 -1
  110. data/docs/api/Brut/FrontEnd/Form.html +39 -39
  111. data/docs/api/Brut/FrontEnd/Forms/Button.html +331 -0
  112. data/docs/api/Brut/FrontEnd/Forms/ButtonInputDefinition.html +537 -0
  113. data/docs/api/Brut/FrontEnd/Forms/ConstraintViolation.html +1 -1
  114. data/docs/api/Brut/FrontEnd/Forms/Input/Color.html +1 -1
  115. data/docs/api/Brut/FrontEnd/Forms/Input/TimeOfDay.html +1 -1
  116. data/docs/api/Brut/FrontEnd/Forms/Input.html +6 -2
  117. data/docs/api/Brut/FrontEnd/Forms/InputDeclarations.html +111 -23
  118. data/docs/api/Brut/FrontEnd/Forms/InputDefinition.html +20 -12
  119. data/docs/api/Brut/FrontEnd/Forms/RadioButtonGroupInput.html +1 -1
  120. data/docs/api/Brut/FrontEnd/Forms/RadioButtonGroupInputDefinition.html +1 -1
  121. data/docs/api/Brut/FrontEnd/Forms/SelectInput.html +1 -1
  122. data/docs/api/Brut/FrontEnd/Forms/SelectInputDefinition.html +1 -1
  123. data/docs/api/Brut/FrontEnd/Forms/ValidityState.html +1 -1
  124. data/docs/api/Brut/FrontEnd/Forms.html +2 -2
  125. data/docs/api/Brut/FrontEnd/GenericResponse.html +1 -1
  126. data/docs/api/Brut/FrontEnd/Handler.html +1 -1
  127. data/docs/api/Brut/FrontEnd/Handlers/CspReportingHandler.html +1 -1
  128. data/docs/api/Brut/FrontEnd/Handlers/InstrumentationHandler/TraceParent.html +1 -1
  129. data/docs/api/Brut/FrontEnd/Handlers/InstrumentationHandler.html +1 -1
  130. data/docs/api/Brut/FrontEnd/Handlers/LocaleDetectionHandler.html +1 -1
  131. data/docs/api/Brut/FrontEnd/Handlers/MissingHandler/Form.html +2 -2
  132. data/docs/api/Brut/FrontEnd/Handlers/MissingHandler.html +1 -1
  133. data/docs/api/Brut/FrontEnd/Handlers.html +1 -1
  134. data/docs/api/Brut/FrontEnd/HandlingResults.html +1 -1
  135. data/docs/api/Brut/FrontEnd/HttpMethod.html +1 -1
  136. data/docs/api/Brut/FrontEnd/HttpStatus.html +1 -1
  137. data/docs/api/Brut/FrontEnd/InlineSvgLocator.html +1 -1
  138. data/docs/api/Brut/FrontEnd/Layout.html +1 -1
  139. data/docs/api/Brut/FrontEnd/Middleware.html +1 -1
  140. data/docs/api/Brut/FrontEnd/Middlewares/AnnotateBrutOwnedPaths.html +1 -1
  141. data/docs/api/Brut/FrontEnd/Middlewares/Favicon.html +1 -1
  142. data/docs/api/Brut/FrontEnd/Middlewares/OpenTelemetrySpan.html +1 -1
  143. data/docs/api/Brut/FrontEnd/Middlewares/ReloadApp.html +1 -1
  144. data/docs/api/Brut/FrontEnd/Middlewares.html +1 -1
  145. data/docs/api/Brut/FrontEnd/Page.html +1 -1
  146. data/docs/api/Brut/FrontEnd/Pages/MissingPage.html +1 -1
  147. data/docs/api/Brut/FrontEnd/Pages.html +1 -1
  148. data/docs/api/Brut/FrontEnd/RequestContext.html +1 -1
  149. data/docs/api/Brut/FrontEnd/RouteHook.html +1 -1
  150. data/docs/api/Brut/FrontEnd/RouteHooks/AgeFlash.html +1 -1
  151. data/docs/api/Brut/FrontEnd/RouteHooks/CSPNoInlineScripts.html +1 -1
  152. data/docs/api/Brut/FrontEnd/RouteHooks/CSPNoInlineStylesOrScripts/ReportOnly.html +1 -1
  153. data/docs/api/Brut/FrontEnd/RouteHooks/CSPNoInlineStylesOrScripts.html +1 -1
  154. data/docs/api/Brut/FrontEnd/RouteHooks/LocaleDetection.html +1 -1
  155. data/docs/api/Brut/FrontEnd/RouteHooks/SetupRequestContext.html +1 -1
  156. data/docs/api/Brut/FrontEnd/RouteHooks.html +1 -1
  157. data/docs/api/Brut/FrontEnd/Routing/FormHandlerRoute.html +1 -1
  158. data/docs/api/Brut/FrontEnd/Routing/FormRoute.html +1 -1
  159. data/docs/api/Brut/FrontEnd/Routing/MissingForm.html +1 -1
  160. data/docs/api/Brut/FrontEnd/Routing/MissingHandler.html +1 -1
  161. data/docs/api/Brut/FrontEnd/Routing/MissingPage.html +1 -1
  162. data/docs/api/Brut/FrontEnd/Routing/MissingPath.html +1 -1
  163. data/docs/api/Brut/FrontEnd/Routing/PageRoute.html +1 -1
  164. data/docs/api/Brut/FrontEnd/Routing/Route.html +1 -1
  165. data/docs/api/Brut/FrontEnd/Routing.html +1 -1
  166. data/docs/api/Brut/FrontEnd/Session.html +1 -1
  167. data/docs/api/Brut/FrontEnd.html +1 -1
  168. data/docs/api/Brut/I18n/BaseMethods.html +1 -1
  169. data/docs/api/Brut/I18n/ForBackEnd.html +1 -1
  170. data/docs/api/Brut/I18n/ForCLI.html +1 -1
  171. data/docs/api/Brut/I18n/ForHTML.html +1 -1
  172. data/docs/api/Brut/I18n/HTTPAcceptLanguage/AlwaysEnglish.html +1 -1
  173. data/docs/api/Brut/I18n/HTTPAcceptLanguage.html +1 -1
  174. data/docs/api/Brut/I18n.html +1 -1
  175. data/docs/api/Brut/Instrumentation/LoggerSpanExporter.html +1 -1
  176. data/docs/api/Brut/Instrumentation/Methods/ClassMethods.html +596 -0
  177. data/docs/api/Brut/Instrumentation/Methods.html +173 -0
  178. data/docs/api/Brut/Instrumentation/OpenTelemetry/NormalizedAttributes.html +7 -7
  179. data/docs/api/Brut/Instrumentation/OpenTelemetry/Span.html +7 -7
  180. data/docs/api/Brut/Instrumentation/OpenTelemetry.html +19 -17
  181. data/docs/api/Brut/Instrumentation.html +3 -1
  182. data/docs/api/Brut/RubocopConfig.html +1 -1
  183. data/docs/api/Brut/SinatraHelpers/ClassMethods.html +1 -1
  184. data/docs/api/Brut/SinatraHelpers.html +1 -1
  185. data/docs/api/Brut/SpecSupport/ClockSupport.html +1 -1
  186. data/docs/api/Brut/SpecSupport/ComponentSupport.html +1 -1
  187. data/docs/api/Brut/SpecSupport/E2ETestServer.html +1 -1
  188. data/docs/api/Brut/SpecSupport/E2eSupport.html +1 -1
  189. data/docs/api/Brut/SpecSupport/EnhancedNode.html +1 -1
  190. data/docs/api/Brut/SpecSupport/FlashSupport.html +1 -1
  191. data/docs/api/Brut/SpecSupport/GeneralSupport/ClassMethods.html +1 -1
  192. data/docs/api/Brut/SpecSupport/GeneralSupport.html +1 -1
  193. data/docs/api/Brut/SpecSupport/HandlerSupport.html +1 -1
  194. data/docs/api/Brut/SpecSupport/Matchers/BeABug.html +1 -1
  195. data/docs/api/Brut/SpecSupport/Matchers/BePageFor.html +1 -1
  196. data/docs/api/Brut/SpecSupport/Matchers/BeRoutingFor.html +1 -1
  197. data/docs/api/Brut/SpecSupport/Matchers/HaveConstraintViolation.html +1 -1
  198. data/docs/api/Brut/SpecSupport/Matchers/HaveGenerated.html +1 -1
  199. data/docs/api/Brut/SpecSupport/Matchers/HaveHTMLAttribute.html +1 -1
  200. data/docs/api/Brut/SpecSupport/Matchers/HaveI18nString.html +1 -1
  201. data/docs/api/Brut/SpecSupport/Matchers/HaveLinkTo.html +1 -1
  202. data/docs/api/Brut/SpecSupport/Matchers/HaveRedirectedTo.html +1 -1
  203. data/docs/api/Brut/SpecSupport/Matchers/HaveReturnedHttpStatus.html +1 -1
  204. data/docs/api/Brut/SpecSupport/Matchers/HaveReturnedRackResponse.html +1 -1
  205. data/docs/api/Brut/SpecSupport/Matchers.html +1 -1
  206. data/docs/api/Brut/SpecSupport/RSpecSetup/OptionalSidekiqSupport.html +1 -1
  207. data/docs/api/Brut/SpecSupport/RSpecSetup.html +1 -1
  208. data/docs/api/Brut/SpecSupport/SessionSupport.html +1 -1
  209. data/docs/api/Brut/SpecSupport.html +1 -1
  210. data/docs/api/Brut.html +1 -1
  211. data/docs/api/Clock.html +1 -1
  212. data/docs/api/ModuleName.html +1 -1
  213. data/docs/api/RichString.html +1 -1
  214. data/docs/api/SemanticLogger/Appender/Async.html +1 -1
  215. data/docs/api/Sequel/Extensions/BrutInstrumentation.html +1 -1
  216. data/docs/api/Sequel/Extensions/BrutMigrations.html +1 -1
  217. data/docs/api/Sequel/Extensions.html +1 -1
  218. data/docs/api/Sequel/Plugins/CreatedAt/InstanceMethods.html +1 -1
  219. data/docs/api/Sequel/Plugins/CreatedAt.html +1 -1
  220. data/docs/api/Sequel/Plugins/ExternalId/ClassMethods.html +1 -1
  221. data/docs/api/Sequel/Plugins/ExternalId/InstanceMethods.html +1 -1
  222. data/docs/api/Sequel/Plugins/ExternalId.html +1 -1
  223. data/docs/api/Sequel/Plugins/FindBang/ClassMethods.html +1 -1
  224. data/docs/api/Sequel/Plugins/FindBang.html +1 -1
  225. data/docs/api/Sequel/Plugins.html +1 -1
  226. data/docs/api/Sequel.html +1 -1
  227. data/docs/api/_index.html +36 -1
  228. data/docs/api/class_list.html +1 -1
  229. data/docs/api/file.README.html +1 -1
  230. data/docs/api/index.html +1 -1
  231. data/docs/api/method_list.html +364 -252
  232. data/docs/api/top-level-namespace.html +1 -1
  233. data/docs/assets/{app.0-aKXKdt.js → app.CGHFI-MA.js} +1 -1
  234. data/docs/assets/chunks/@localSearchIndexroot.DcMsFVrO.js +1 -0
  235. data/docs/assets/chunks/{VPLocalSearchBox.CW-UBkNA.js → VPLocalSearchBox.-c-Kihwo.js} +1 -1
  236. data/docs/assets/chunks/{theme.a6feKWJO.js → theme.DpqrgXG8.js} +2 -2
  237. data/docs/assets/{components.md.BzVRwegp.js → components.md.X4h_TEG-.js} +3 -3
  238. data/docs/assets/{configuration.md.eM5wFVi5.js → configuration.md.CPCyUo5A.js} +1 -1
  239. data/docs/assets/{forms.md.B3BHvCV3.js → forms.md.BwmfAZ6Z.js} +2 -2
  240. data/docs/assets/{getting-started.md.BgR0ZHsl.js → getting-started.md.mPgbkbBv.js} +2 -2
  241. data/docs/assets/instrumentation.md._lNSriEZ.js +90 -0
  242. data/docs/assets/instrumentation.md._lNSriEZ.lean.js +1 -0
  243. data/docs/assets/{recipes_authentication.md.Dzvi_g69.js → recipes_authentication.md.BAISoxmN.js} +1 -0
  244. data/docs/assets/{tutorials_01-intro.md.BXvYWcO9.js → tutorials_01-intro.md.B4sUBY3X.js} +10 -10
  245. data/docs/assets/{tutorials_01-intro.md.BXvYWcO9.lean.js → tutorials_01-intro.md.B4sUBY3X.lean.js} +1 -1
  246. data/docs/assets/{tutorials_02-dialog.md.CIeg8R--.js → tutorials_02-dialog.md.DOonTBrE.js} +1 -1
  247. data/docs/assets.html +3 -3
  248. data/docs/brut-js/api/AjaxSubmit.html +16 -6
  249. data/docs/brut-js/api/AjaxSubmit.js.html +27 -7
  250. data/docs/brut-js/api/Autosubmit.html +1 -1
  251. data/docs/brut-js/api/Autosubmit.js.html +1 -1
  252. data/docs/brut-js/api/BaseCustomElement.html +1 -1
  253. data/docs/brut-js/api/BaseCustomElement.js.html +1 -1
  254. data/docs/brut-js/api/BrutCustomElements.html +1 -1
  255. data/docs/brut-js/api/BufferedLogger.html +1 -1
  256. data/docs/brut-js/api/ConfirmSubmit.html +1 -1
  257. data/docs/brut-js/api/ConfirmSubmit.js.html +1 -1
  258. data/docs/brut-js/api/ConfirmationDialog.html +1 -1
  259. data/docs/brut-js/api/ConfirmationDialog.js.html +1 -1
  260. data/docs/brut-js/api/ConstraintViolationMessage.html +1 -1
  261. data/docs/brut-js/api/ConstraintViolationMessage.js.html +1 -1
  262. data/docs/brut-js/api/ConstraintViolationMessages.html +1 -1
  263. data/docs/brut-js/api/ConstraintViolationMessages.js.html +1 -1
  264. data/docs/brut-js/api/CopyToClipboard.html +1 -1
  265. data/docs/brut-js/api/CopyToClipboard.js.html +1 -1
  266. data/docs/brut-js/api/Form.html +1 -1
  267. data/docs/brut-js/api/Form.js.html +1 -1
  268. data/docs/brut-js/api/I18nTranslation.html +1 -1
  269. data/docs/brut-js/api/I18nTranslation.js.html +1 -1
  270. data/docs/brut-js/api/LocaleDetection.html +1 -1
  271. data/docs/brut-js/api/LocaleDetection.js.html +1 -1
  272. data/docs/brut-js/api/Logger.html +1 -1
  273. data/docs/brut-js/api/Logger.js.html +1 -1
  274. data/docs/brut-js/api/Message.html +1 -1
  275. data/docs/brut-js/api/Message.js.html +1 -1
  276. data/docs/brut-js/api/PrefixedLogger.html +1 -1
  277. data/docs/brut-js/api/RichString.html +1 -1
  278. data/docs/brut-js/api/RichString.js.html +1 -1
  279. data/docs/brut-js/api/Tabs.html +1 -1
  280. data/docs/brut-js/api/Tabs.js.html +1 -1
  281. data/docs/brut-js/api/Tracing.html +1 -1
  282. data/docs/brut-js/api/Tracing.js.html +1 -1
  283. data/docs/brut-js/api/external-CustomElementRegistry.html +1 -1
  284. data/docs/brut-js/api/external-Performance.html +1 -1
  285. data/docs/brut-js/api/external-Promise.html +1 -1
  286. data/docs/brut-js/api/external-ValidityState.html +1 -1
  287. data/docs/brut-js/api/external-Window.html +1 -1
  288. data/docs/brut-js/api/external-fetch.html +1 -1
  289. data/docs/brut-js/api/global.html +1 -1
  290. data/docs/brut-js/api/index.html +1 -1
  291. data/docs/brut-js/api/index.js.html +1 -1
  292. data/docs/brut-js/api/module-testing.html +1 -1
  293. data/docs/brut-js/api/testing.AssetMetadata.html +1 -1
  294. data/docs/brut-js/api/testing.AssetMetadataLoader.html +1 -1
  295. data/docs/brut-js/api/testing.CustomElementTest.html +1 -1
  296. data/docs/brut-js/api/testing.DOMCreator.html +1 -1
  297. data/docs/brut-js/api/testing_AssetMetadata.js.html +1 -1
  298. data/docs/brut-js/api/testing_AssetMetadataLoader.js.html +1 -1
  299. data/docs/brut-js/api/testing_CustomElementTest.js.html +1 -1
  300. data/docs/brut-js/api/testing_DOMCreator.js.html +1 -1
  301. data/docs/brut-js/api/testing_index.js.html +1 -1
  302. data/docs/brut-js.html +3 -3
  303. data/docs/business-logic.html +3 -3
  304. data/docs/cli.html +3 -3
  305. data/docs/components.html +7 -7
  306. data/docs/configuration.html +5 -5
  307. data/docs/css.html +3 -3
  308. data/docs/custom-element-tests.html +3 -3
  309. data/docs/database-access.html +3 -3
  310. data/docs/database-schema.html +3 -3
  311. data/docs/deployment.html +3 -3
  312. data/docs/dev-environment.html +3 -3
  313. data/docs/dir-structure.html +3 -3
  314. data/docs/doc-conventions.html +3 -3
  315. data/docs/end-to-end-tests.html +3 -3
  316. data/docs/features.html +3 -3
  317. data/docs/flash-and-session.html +3 -3
  318. data/docs/form-constraints.html +3 -3
  319. data/docs/forms.html +6 -6
  320. data/docs/getting-started.html +6 -6
  321. data/docs/handlers.html +3 -3
  322. data/docs/hashmap.json +1 -1
  323. data/docs/hooks.html +3 -3
  324. data/docs/i18n.html +3 -3
  325. data/docs/index.html +3 -3
  326. data/docs/instrumentation.html +61 -6
  327. data/docs/javascript.html +3 -3
  328. data/docs/jobs.html +3 -3
  329. data/docs/keyword-injection.html +3 -3
  330. data/docs/layouts.html +3 -3
  331. data/docs/lsp.html +3 -3
  332. data/docs/markdown-examples.html +3 -3
  333. data/docs/middleware.html +3 -3
  334. data/docs/overview.html +3 -3
  335. data/docs/pages.html +3 -3
  336. data/docs/recipes/alternate-layouts.html +3 -3
  337. data/docs/recipes/authentication.html +5 -4
  338. data/docs/recipes/blank-layouts.html +3 -3
  339. data/docs/recipes/custom-flash.html +3 -3
  340. data/docs/recipes/form-errors.html +3 -3
  341. data/docs/recipes/indexed-forms.html +3 -3
  342. data/docs/recipes/migrations.html +3 -3
  343. data/docs/recipes/text-field-component.html +3 -3
  344. data/docs/roadmap.html +3 -3
  345. data/docs/routes.html +3 -3
  346. data/docs/security.html +3 -3
  347. data/docs/seed-data.html +3 -3
  348. data/docs/space-time-continuum.html +3 -3
  349. data/docs/tutorial.html +3 -3
  350. data/docs/tutorials/01-intro.html +12 -12
  351. data/docs/tutorials/02-dialog.html +5 -5
  352. data/docs/unit-tests.html +3 -3
  353. data/docs/why.html +3 -3
  354. data/lib/brut/front_end/components/input.rb +1 -0
  355. data/lib/brut/front_end/components/inputs/button_tag.rb +43 -0
  356. data/lib/brut/front_end/components/inputs/input_tag.rb +3 -1
  357. data/lib/brut/front_end/components/inputs/select_tag_with_options.rb +0 -1
  358. data/lib/brut/front_end/components/inputs/textarea_tag.rb +0 -1
  359. data/lib/brut/front_end/form.rb +7 -5
  360. data/lib/brut/front_end/forms/button.rb +21 -0
  361. data/lib/brut/front_end/forms/button_input_definition.rb +33 -0
  362. data/lib/brut/front_end/forms/input_declarations.rb +10 -0
  363. data/lib/brut/front_end/forms/input_definition.rb +7 -2
  364. data/lib/brut/version.rb +1 -1
  365. data/mkbrut/Gemfile.lock +1 -1
  366. data/mkbrut/lib/mkbrut/version.rb +1 -1
  367. metadata +29 -21
  368. data/docs/assets/chunks/@localSearchIndexroot.DPhqaz1b.js +0 -1
  369. data/docs/assets/instrumentation.md.BgcaGVYH.js +0 -35
  370. data/docs/assets/instrumentation.md.BgcaGVYH.lean.js +0 -1
  371. /data/docs/assets/{components.md.BzVRwegp.lean.js → components.md.X4h_TEG-.lean.js} +0 -0
  372. /data/docs/assets/{configuration.md.eM5wFVi5.lean.js → configuration.md.CPCyUo5A.lean.js} +0 -0
  373. /data/docs/assets/{forms.md.B3BHvCV3.lean.js → forms.md.BwmfAZ6Z.lean.js} +0 -0
  374. /data/docs/assets/{getting-started.md.BgR0ZHsl.lean.js → getting-started.md.mPgbkbBv.lean.js} +0 -0
  375. /data/docs/assets/{recipes_authentication.md.Dzvi_g69.lean.js → recipes_authentication.md.BAISoxmN.lean.js} +0 -0
  376. /data/docs/assets/{tutorials_02-dialog.md.CIeg8R--.lean.js → tutorials_02-dialog.md.DOonTBrE.lean.js} +0 -0
@@ -1,35 +0,0 @@
1
- import{_ as e,c as s,o as t,ag as a}from"./chunks/framework.1L-BeKqY.js";const u=JSON.parse('{"title":"Instrumentation and Observability","description":"","frontmatter":{},"headers":[],"relativePath":"instrumentation.md","filePath":"instrumentation.md"}'),n={name:"instrumentation.md"};function r(h,i,o,l,p,d){return t(),s("div",null,i[0]||(i[0]=[a(`<h1 id="instrumentation-and-observability" tabindex="-1">Instrumentation and Observability <a class="header-anchor" href="#instrumentation-and-observability" aria-label="Permalink to &quot;Instrumentation and Observability&quot;">​</a></h1><p>Brut has built-in support for OpenTelemetry, which is an open standard used by many observability vendors to allow you to understand the behavior of your app in production. Brut also includes a configuration for the <a href="https://github.com/CtrlSpice/otel-desktop-viewer/" target="_blank" rel="noreferrer">otel-desktop-viewer</a>, which allows you to see instrumentation in development.</p><h2 id="overview" tabindex="-1">Overview <a class="header-anchor" href="#overview" aria-label="Permalink to &quot;Overview&quot;">​</a></h2><h3 id="why-instrument" tabindex="-1">Why Instrument? <a class="header-anchor" href="#why-instrument" aria-label="Permalink to &quot;Why Instrument?&quot;">​</a></h3><p>In production, you&#39;ll need to know what your app is doing and how well it&#39;s working. Historically, logs can provide this information in a roundabout way. Over the last many years, Application Performance Monitoring (APM) vendors like New Relic and Data Dog allowed developers to see much richer detail about how an app is working.</p><p>You could see, for example, the 95th percentil of your dashboard controller&#39;s performance, or the top 10 slowest SQL statements your app is executing. OpenTelemetry attempts to unify the API used to communicate this information from your app to your chosen vendor, and most vendors support it.</p><p>Instrumentation, then, is a way to record what your app is doing, how long its taking, and perhaps even why it&#39;s doing what it&#39;s doing, down to a very specific level. If properly configured, you could examine the performance of the app for a particular user on a particular day.</p><h3 id="setting-up-instrumentation" tabindex="-1">Setting up Instrumentation <a class="header-anchor" href="#setting-up-instrumentation" aria-label="Permalink to &quot;Setting up Instrumentation&quot;">​</a></h3><p>Brut automatically sets up OpenTelemetry (OTel) tracing. The primary interface you will use is <a href="/api/Brut/Instrumentation/OpenTelemetry.html" target="_self" rel="noopener" data-no-router><code>Brut::Instrumentation::OpenTelemetry</code></a>, which is available via <code>Brut.container.instrumentation</code>. We&#39;ll discuss that in a moment.</p><p>To configure the specifics of where the traces will go, the OTel gem uses environment variables:</p><table tabindex="0"><thead><tr><th>Variable</th><th>Value</th><th>Purpose</th></tr></thead><tbody><tr><td><code>OTEL_EXPORTER_OTLP_ENDPOINT</code></td><td>Depends on environment</td><td>Where to send the tracers. This is provided by your vendor, but is <code>http://otel-desktop-viewer:4318</code> in development</td></tr><tr><td><code>OTEL_EXPORTER_OTLP_HEADERS</code></td><td>Depends on vendor</td><td>Your vendor may ask you to set this. It often contains identifying information or API keys</td></tr><tr><td><code>OTEL_EXPORTER_OTLP_PROTOCOL</code></td><td>http/protobuf</td><td>Your vendor may request a different protocol, but protobuf is common and supported by otel-desktop-viewer</td></tr><tr><td><code>OTEL_LOG_LEVEL</code></td><td>debug</td><td>Useful when setting everything up to understand why things aren&#39;t working if they aren&#39;t working</td></tr><tr><td><code>OTEL_RUBY_BSP_START_THREAD_ON_BOOT</code></td><td>false</td><td>Deals with esoteric issues with Puma. See <a href="https://github.com/open-telemetry/opentelemetry-ruby/issues/462" target="_blank" rel="noreferrer">this GitHub issue</a> for the details.</td></tr><tr><td><code>OTEL_SERVICE_NAME</code></td><td>Your app&#39;s <code>id</code> from <code>App</code></td><td>Identifiers your app&#39;s name to the vendor</td></tr><tr><td><code>OTEL_TRACES_EXPORTER</code></td><td>otlp</td><td>Configures the class inside the OTel gem that will export the instrumentation to the vendor. If you omit this, Brut will log the instrumentation to the console</td></tr></tbody></table><p>When you created your Brut app, your <code>.env.development</code> and <code>.env.test</code> should have values for all these environment variables that will send instrumentation to the otel-desktop-viewer that was also configured.</p><p>If you run your app using <code>bin/dev</code> and use the app for a bit, then go to <code>http://localhost:8000</code>, you will see the otel-desktop-viewer UI and can browse the spans and traces sent by Brut.</p><h3 id="what-is-instrumented-by-default" tabindex="-1">What is Instrumented By Default <a class="header-anchor" href="#what-is-instrumented-by-default" aria-label="Permalink to &quot;What is Instrumented By Default&quot;">​</a></h3><p>Brut attempts to automatically instrument useful things so you don&#39;t have to do anything to start getting data. Brut will attempt to conform to standard semantics for HTTP requests and SQL statements.</p><p>Here is a non-exhaustive list of what Brut automatically instruments:</p><ul><li>How long each page or handler request takes, broken down by components.</li><li>CLI execution time</li><li>Time to rebuild the schema for tests</li><li>Time to run tests</li><li>Time to apply migrations</li><li>Time spent inside a route hook</li><li>The locale detected from the browser</li><li>The layout class used when rendering a page</li><li>If a requested path is owned by Brut or not</li><li>Ignored parameters on all form submissions</li><li>How long reloading takes in development</li><li>CSP reporting results</li><li>SQL Statements</li></ul><div class="warning custom-block github-alert"><p class="custom-block-title">WARNING</p><p><code>Sequel::Extensions::BrutInstrumentation</code> sets up telemetry for Sequel, and it does it in a relatively simplistic way. The result is that <em>all</em> SQL statements are part of the telemetry, including the actual values inserted or used in <code>WHERE</code> clauses. While you should not be putting sensitive data into your database, be warned that this is happening. There are plans to improve this to be more flexible and reduce the chance of sensitive data being sent in traces.</p></div><h3 id="adding-your-own-instrumentation" tabindex="-1">Adding Your Own Instrumentation <a class="header-anchor" href="#adding-your-own-instrumentation" aria-label="Permalink to &quot;Adding Your Own Instrumentation&quot;">​</a></h3><p>You can add instrumentation in a few ways:</p><ul><li><em>Spans</em> record a block of code. They are shown as a sub-span if one is already in effect. When you create a span, that means it will be shown in the context of the HTTP request.</li><li><em>Attributes</em> can be added to the current span to provide more context about what is happening. For example, the HTTP request method is an attribute of the span used for the HTTP request. These attributes allow for sophisticated querying in the vendor&#39;s UI.</li><li><em>Events</em> record things that happen and metadata about that thing. These are like log statements. They are associated with the span you are in when you add the event.</li></ul><p>These can all be added via <code>Brut.container.instrumentation</code>, which is a <a href="/api/Brut/Instrumentation/OpenTelemetry.html" target="_self" rel="noopener" data-no-router><code>Brut::Instrumentation::OpenTelemetry</code></a> instance.</p><p>These methods are available:</p><ul><li><code>span(name,**attributes,&amp;block)</code> - Create a new span around the block yielded.</li><li><code>add_attributes(attributes)</code> - Add attributes to the current span. These will be prefixed with your app&#39;s prefix so it&#39;s clear in the observability UI that they are for your app and not standard.</li><li><code>add_event(name,**attributes)</code> - Add an event with optional attributes for the current span.</li><li><code>record_exception(ex,attributes=nil)</code> - Record an exception that was caught.</li><li><code>record_and_reraise_exception!(ex,attributes=nil)</code> - Record an exception and raise it.</li></ul><p>Suppose you want to instrument <code>RequireAuthBeforeHook</code> from the <a href="/hooks.html">hooks</a> documentation. Although the hook&#39;s <code>before</code> method is instrumented by Brut already, let&#39;s add some metadata to that span, and also add a span around the login check.</p><div class="language-ruby vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">ruby</span><pre class="shiki shiki-themes github-light github-dark vp-code" tabindex="0"><code><span class="line"><span style="--shiki-light:#6A737D;--shiki-dark:#6A737D;"># app/src/front_end/route_hooks/require_auth_before_hook.rb</span></span>
2
- <span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">class</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> RequireAuthBeforeHook</span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> &lt;</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> Brut</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">::</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">FrontEnd</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">::</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">RouteHook</span></span>
3
- <span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> def</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;"> before</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">request_context:</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">,</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">session:</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">,</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">request:</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">,</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">env:</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">)</span></span>
4
- <span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70;"> is_home_page</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> = request.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">path_info</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">match</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(</span><span style="--shiki-light:#032F62;--shiki-dark:#DBEDFF;">/^</span><span style="--shiki-light:#22863A;--shiki-light-font-weight:bold;--shiki-dark:#85E89D;--shiki-dark-font-weight:bold;">\\/</span><span style="--shiki-light:#032F62;--shiki-dark:#DBEDFF;">?$/</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">)</span></span>
5
- <span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70;"> is_auth_route</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> = request.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">path_info</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">match?</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(</span><span style="--shiki-light:#032F62;--shiki-dark:#DBEDFF;">/^</span><span style="--shiki-light:#22863A;--shiki-light-font-weight:bold;--shiki-dark:#85E89D;--shiki-dark-font-weight:bold;">\\/</span><span style="--shiki-light:#032F62;--shiki-dark:#DBEDFF;">auth</span><span style="--shiki-light:#22863A;--shiki-light-font-weight:bold;--shiki-dark:#85E89D;--shiki-dark-font-weight:bold;">\\/</span><span style="--shiki-light:#032F62;--shiki-dark:#DBEDFF;">/</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">)</span></span>
6
- <span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70;"> is_brut_owned_path</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> = env[</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&quot;brut.owned_path&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">]</span></span>
7
- <span class="line"></span>
8
- <span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70;"> requires_login</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> = </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">!</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">is_home_page </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">&amp;&amp;</span></span>
9
- <span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> !</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">is_auth_route </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">&amp;&amp;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> </span></span>
10
- <span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> !</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">is_brut_owned_path</span></span>
11
- <span class="line highlighted"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> Brut</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">container</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">instrumentation</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">add_attributes</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(</span></span>
12
- <span class="line highlighted"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> requires_login:</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">,</span></span>
13
- <span class="line highlighted"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> is_home_page:</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">,</span></span>
14
- <span class="line highlighted"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> is_auth_route:</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">,</span></span>
15
- <span class="line highlighted"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> is_brut_owned_path:</span></span>
16
- <span class="line highlighted"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> )</span></span>
17
- <span class="line"></span>
18
- <span class="line highlighted"><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> Brut</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">container</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">instrumentation</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">span</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(</span><span style="--shiki-light:#032F62;--shiki-dark:#9ECBFF;">&quot;login-check&quot;</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">) </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">do</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> |span|</span></span>
19
- <span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> session.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">logged_in?</span></span>
20
- <span class="line highlighted"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> span.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">add_attributes</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">logged_in:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> true</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">)</span></span>
21
- <span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> request_context[</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">:authenticated_account</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">] </span><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">=</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> session.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">authenticated_account</span></span>
22
- <span class="line"><span style="--shiki-light:#E36209;--shiki-dark:#FFAB70;"> requires_login</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> = </span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">false</span></span>
23
- <span class="line highlighted"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> else</span></span>
24
- <span class="line highlighted"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> span.</span><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;">add_attributes</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">logged_in:</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;"> false</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">)</span></span>
25
- <span class="line highlighted"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> end</span></span>
26
- <span class="line highlighted"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> end</span></span>
27
- <span class="line"></span>
28
- <span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> if</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> requires_login</span></span>
29
- <span class="line"><span style="--shiki-light:#6F42C1;--shiki-dark:#B392F0;"> redirect_to</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">(</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">Auth</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">::</span><span style="--shiki-light:#005CC5;--shiki-dark:#79B8FF;">LoginPage</span><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;">)</span></span>
30
- <span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> else</span></span>
31
- <span class="line"><span style="--shiki-light:#24292E;--shiki-dark:#E1E4E8;"> continue</span></span>
32
- <span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> end</span></span>
33
- <span class="line"></span>
34
- <span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;"> end</span></span>
35
- <span class="line"><span style="--shiki-light:#D73A49;--shiki-dark:#F97583;">end</span></span></code></pre></div><p>Now, for every request someone makes to our app, we will see a span for the <code>RequireAuthBeforeHook</code>, and inside that span, we&#39;ll see the attributes we added as well as a sub-span representing the login check (which itself will have an attribute about the user&#39;s logged-in status).</p><h3 id="client-side-observability" tabindex="-1">Client-Side Observability <a class="header-anchor" href="#client-side-observability" aria-label="Permalink to &quot;Client-Side Observability&quot;">​</a></h3><p>The class <a href="/api/Brut/FrontEnd/Handlers/InstrumentationHandler.html" target="_self" rel="noopener" data-no-router><code>Brut::FrontEnd::Handlers::InstrumentationHandler</code></a> is set up to receive information from the client-side to provide insights about client-side behavior as part of a server-side request. Brut attempts to join up any client-side instrumentation to the request that served it.</p><p>It does this via the <a href="/api/Brut/FrontEnd/Components/Traceparent.html" target="_self" rel="noopener" data-no-router><code>Brut::FrontEnd::Components::Traceparent</code></a> component, which is included in your default layout when you created your Brut app. This creates a <code>&lt;meta&gt;</code> tag containing standardized information used to connect the client-side behavior to the server-side request.</p><p>The Brut custom element <a href="/brut-js/api/Tracing.html" target="_self" rel="noopener" data-no-router><code style="white-space:nowrap;">&lt;brut-tracing&gt;</code></a> uses this information, along with statistics from the browser, to send a custom payload back to Brut at the route <code>/__brut/instrumentation</code>, which is handled by the aforementioned <code>InstrumentationHandler</code>.</p><p>You should then see client-side tracing information as a sub-span of your HTTP request. The information available depends on the browser, and some browsers don&#39;t send much. Also keep in mind that clock drift is real and while client-side timings are accurate, the timestamps will not be.</p><h2 id="testing" tabindex="-1">Testing <a class="header-anchor" href="#testing" aria-label="Permalink to &quot;Testing&quot;">​</a></h2><p>Generally you don&#39;t want to test instrumentation unless it&#39;s highly complex and critical to the app&#39;s ability to be maintained. Ideally, your end-to-end tests will cover all the instrumentation code you write so you can be sure that none of that causes a problem.</p><h2 id="recommended-practices" tabindex="-1">Recommended Practices <a class="header-anchor" href="#recommended-practices" aria-label="Permalink to &quot;Recommended Practices&quot;">​</a></h2><p>Entire books and conferences exist on how to properly instrument your app. Our suggestion is to take what you have by default and add additional instrumentation only to solve specific problems or identify specific issues.</p><h2 id="technical-notes" tabindex="-1">Technical Notes <a class="header-anchor" href="#technical-notes" aria-label="Permalink to &quot;Technical Notes&quot;">​</a></h2><div class="important custom-block github-alert"><p class="custom-block-title">IMPORTANT</p><p>Technical Notes are for deeper understanding and debugging. While we will try to keep them up-to-date with changes to Brut&#39;s internals, the source code is always more correct.</p></div><p><em>Last Updated June 12, 2025</em></p><p>Brut does not have plans to support non-OTel instrumentation, nor does it have plans to provide hooks to use proprietary formats.</p><p>The client-side portion of this is highly customized. The Otel open source code for the client side is massive and hugely complex, so Brut decided to try to produce something simple and straightforward as a start. This can and will evolve over time.</p>`,41)]))}const c=e(n,[["render",r]]);export{u as __pageData,c as default};
@@ -1 +0,0 @@
1
- import{_ as e,c as s,o as t,ag as a}from"./chunks/framework.1L-BeKqY.js";const u=JSON.parse('{"title":"Instrumentation and Observability","description":"","frontmatter":{},"headers":[],"relativePath":"instrumentation.md","filePath":"instrumentation.md"}'),n={name:"instrumentation.md"};function r(h,i,o,l,p,d){return t(),s("div",null,i[0]||(i[0]=[a("",41)]))}const c=e(n,[["render",r]]);export{u as __pageData,c as default};