Violet Rails: The Restarone Way - restarone/violet_rails Wiki

Monitoring and Telemetry

We use monitoring and telemetry to catch errors and triage, contributing to the general operational excellence of an organization.

Appointing system guardians

It is recommended you appoint an individual or team to receive Violet Rails error traces by registering them like so:

Dissecting an error trace

Once registered, they will receive error messages via email as they happen. A sample may look something like this:

restarone.solutionsgraphql#execute (ActiveRecord::StatementInvalid) "PG::UndefinedColumn: ERROR: column "time" does n...

An ActiveRecord::StatementInvalid occurred in graphql#execute:

  PG::UndefinedColumn: ERROR:  column "time" does not exist
LINE 1: ...ELECT "ahoy_visits".* FROM "ahoy_visits" ORDER BY time desc ...
                                                             ^




-------------------------------
Request:
-------------------------------

  * URL        : https://www.restarone.solutions/graphql
  * HTTP Method: POST
  * IP address : 142.181.192.212
  * Parameters : {"query"=>"{\n  ahoyVisits(limit: 100, orderDirection:\"desc\", orderDimension: \"time\") {\n    id\n    country\n    city\n    referringDomain\n  }\n}", "variables"=>nil, "controller"=>"graphql", "action"=>"execute", "graphql"=>{"query"=>"{\n  ahoyVisits(limit: 100, orderDirection:\"desc\", orderDimension: \"time\") {\n    id\n    country\n    city\n    referringDomain\n  }\n}", "variables"=>nil}}
  * Timestamp  : 2022-06-23 12:11:50 UTC
  * Server : ip-172-31-86-57
    * Rails root : /var/www/violet/releases/20220623114044
  * Process: 2839150

-------------------------------
Session:
-------------------------------

  * session id: [FILTERED]
  * data: {"session_id"=>"4a19ecf6020e7c0feb26fa23c9c102ef",
   "user_return_to"=>"/admin",
   "warden.user.user.key"=>[[1], "$2a$12$89B5jU.44t5BXSkZNPRJqu"],
   "warden.user.user.session"=>{"last_request_at"=>1655986310},
   "site_id"=>1,
   "_csrf_token"=>"sSPxmpKSVLNWc7QvjywHrvkJWSLQpGBMHJg3zeqIZVg="}

-------------------------------
Environment:
-------------------------------

  * CONTENT_LENGTH                                          : 169
    * CONTENT_TYPE                                            : application/json
    * GATEWAY_INTERFACE                                       : CGI/1.2
    * HTTP_ACCEPT                                             : */*
    * HTTP_ACCEPT_ENCODING                                    : gzip, deflate, br
    * HTTP_ACCEPT_LANGUAGE                                    : en-CA,en-US;q=0.9,en;q=0.8
    * HTTP_CONNECTION                                         : close
    * HTTP_COOKIE                                             : _r_solutions_session=WApsZfetZMdxoWS7HkHlrNqnvaaKHUSSSxn9vY1prhMfT9d%2FZz%2BXMRmxnNUCjAWsSC8AuzQmSzFQjvZi2FsrcjTgulD8jFeT6gkz55DbIRFsjfYH6nBSjjnMd7NvhuXySf%2BQtLovhZgBAhOMrJvQP0CbYGE0VxJa6ISKVBjZifjiUKbbXsy4J3QeHzBCjt%2BE1t0tYP4AlZxAEjRfK0GK5v4BxXDF49F91s1VWUD5A4EGM9crQg%2F3PJw7iIQYHaDvKcuF9gHdz7xc0RjEmVBawLlF9sDy1ehLc%2BTM0ynRNy6WTxd09Kuj%2Brc20%2Bepjy8NHk3a3i8Ab4vrPg6U1mztiHbjXhTg1ZvDCmM0ZBBiTmOdHAs5irSZcdpAGgwFa7SI8x%2BBTdS6CP6VW9l6pdJAgSXUwbL%2BXBk7JLX5k8qFMb6y4EVneyH%2BLFpHwgY8W0hiM1%2BYzhrAlp933nrFroeE5ng8K3Dy1368VICWSk5wmC9D31gL4rCawHcECu%2Fgr%2Bo5Qb2TS%2BfizPGavbN%2FNFUk2szjug0wP%2FbyxthHDrj9TSPYDTdKnjVBOIQB0dE%3D--H8TqpUcF6nQid9XH--EcvI8XO4sXCNdAepxxU0EQ%3D%3D; ahoy_visit=5baffb58-186f-4fb7-8e0a-b855245a9432; ahoy_visitor=1f6244c1-bf33-4801-b0ca-c7d89f4b8b4e
    * HTTP_HOST                                               : www.restarone.solutions
    * HTTP_ORIGIN                                             : https://www.restarone.solutions/
    * HTTP_REFERER                                            : https://www.restarone.solutions/admin/graphiql
    * HTTP_USER_AGENT                                         : Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.0 Safari/605.1.15
    * HTTP_VERSION                                            : HTTP/1.0
    * HTTP_X_CSRF_TOKEN                                       : [FILTERED]
    * HTTP_X_FORWARDED_FOR                                    : 142.181.192.212
    * HTTP_X_FORWARDED_PROTO                                  : https
    * HTTP_X_REAL_IP                                          : 142.181.192.212
    * ORIGINAL_FULLPATH                                       : /graphql
    * ORIGINAL_SCRIPT_NAME                                    :
    * PATH_INFO                                               : /graphql
    * QUERY_STRING                                            :
    * RAW_POST_DATA                                           : [FILTERED]
    * REMOTE_ADDR                                             : 127.0.0.1
    * REQUEST_METHOD                                          : POST
    * REQUEST_PATH                                            : /graphql
    * REQUEST_URI                                             : /graphql
    * ROUTES_47032437798660_SCRIPT_NAME                       :
    * SCRIPT_NAME                                             :
    * SERVER_NAME                                             : www.restarone.solutions
    * SERVER_PORT                                             : 443
    * SERVER_PROTOCOL                                         : HTTP/1.1
    * SERVER_SOFTWARE                                         : puma 5.6.4 Birdie's Version
    * action_controller.instance                              : #<GraphqlController:0x00007fd03c34f260>
    * action_dispatch.authenticated_encrypted_cookie_salt     : [FILTERED]
    * action_dispatch.backtrace_cleaner                       : #<Rails::BacktraceCleaner:0x0000558d2c51e618>
    * action_dispatch.content_security_policy                 :
    * action_dispatch.content_security_policy_nonce_directives:
    * action_dispatch.content_security_policy_nonce_generator :
    * action_dispatch.content_security_policy_report_only     : false
    * action_dispatch.cookies                                 : #<ActionDispatch::Cookies::CookieJar:0x00007fd03c4dacd8>
    * action_dispatch.cookies_digest                          :
    * action_dispatch.cookies_rotations                       : #<ActiveSupport::Messages::RotationConfiguration:0x0000558d2b244c40>
    * action_dispatch.cookies_same_site_protection            : #<Proc:[email protected]/var/www/violet/shared/bundle/ruby/2.6.0/gems/railties-6.1.5/lib/rails/application.rb:636>
    * action_dispatch.cookies_serializer                      : json
    * action_dispatch.encrypted_cookie_cipher                 : [FILTERED]
    * action_dispatch.encrypted_cookie_salt                   : [FILTERED]
    * action_dispatch.encrypted_signed_cookie_salt            : [FILTERED]
    * action_dispatch.http_auth_salt                          : [FILTERED]
    * action_dispatch.key_generator                           : #<ActiveSupport::CachingKeyGenerator:0x0000558d2f160dc0>
    * action_dispatch.logger                                  : #<ActiveSupport::Logger:0x0000558d2c7c7db0>
    * action_dispatch.parameter_filter                        : [:passw, :secret, :token, :_key, :crypt, :salt, :certificate, :otp, :ssn]
    * action_dispatch.permissions_policy                      :
    * action_dispatch.redirect_filter                         : []
    * action_dispatch.remote_ip                               : 142.181.192.212
    * action_dispatch.request.accepts                         : [#<Mime::Type:0x00007fd03c3530b8 @synonyms=[], @symbol=nil, @string="*/*", @hash=-3532471350324634667>]
    * action_dispatch.request.content_type                    : application/json
    * action_dispatch.request.formats                         : [#<Mime::Type:0x00007fd03c3530b8 @synonyms=[], @symbol=nil, @string="*/*", @hash=-3532471350324634667>]
    * action_dispatch.request.parameters                      : {"query"=>"{\n  ahoyVisits(limit: 100, orderDirection:\"desc\", orderDimension: \"time\") {\n    id\n    country\n    city\n    referringDomain\n  }\n}", "variables"=>nil, "controller"=>"graphql", "action"=>"execute", "graphql"=>{"query"=>"{\n  ahoyVisits(limit: 100, orderDirection:\"desc\", orderDi...
    * action_dispatch.request.path_parameters                 : {:controller=>"graphql", :action=>"execute"}
    * action_dispatch.request.query_parameters                : {}
    * action_dispatch.request.request_parameters              : {"query"=>"{\n  ahoyVisits(limit: 100, orderDirection:\"desc\", orderDimension: \"time\") {\n    id\n    country\n    city\n    referringDomain\n  }\n}", "variables"=>nil, "graphql"=>{"query"=>"{\n  ahoyVisits(limit: 100, orderDirection:\"desc\", orderDimension: \"time\") {\n    id\n    country\n   ...
    * action_dispatch.request.unsigned_session_cookie         : {"session_id"=>"4a19ecf6020e7c0feb26fa23c9c102ef", "user_return_to"=>"/admin", "warden.user.user.key"=>[[1], "$2a$12$89B5jU.44t5BXSkZNPRJqu"], "warden.user.user.session"=>{"last_request_at"=>1655986310}, "site_id"=>1, "_csrf_token"=>"[FILTERED]"}
    * action_dispatch.request_id                              : 697ce9b3-2407-41a0-9dd6-3b3c553bc921
    * action_dispatch.routes                                  : #<ActionDispatch::Routing::RouteSet:0x0000558d2f7eee08>
    * action_dispatch.secret_key_base                         : [FILTERED]
    * action_dispatch.show_detailed_exceptions                : false
    * action_dispatch.show_exceptions                         : true
    * action_dispatch.signed_cookie_digest                    :
    * action_dispatch.signed_cookie_salt                      : [FILTERED]
    * action_dispatch.use_authenticated_cookie_encryption     : [FILTERED]
    * action_dispatch.use_cookies_with_metadata               : true
    * exception_notifier.exception_data                       : {:current_user=>#<User id: 1, global_admin: true, created_at: "2021-04-12 23:11:27.966580000 +0000", updated_at: "2022-06-23 12:07:34.745535000 +0000", email: "[[email protected]](mailto:[email protected])", can_manage_web: true, can_manage_email: true, can_manage_users: true, can_manage_blog: true, name: "[email protected]
    * puma.config                                             : #<Puma::Configuration:0x0000558d29a312e8>
    * puma.request_body_wait                                  : 0
    * puma.socket                                             : #<TCPSocket:0x00007fd03c305db8>
    * rack.after_reply                                        : []
    * rack.cors                                               : #<Rack::Cors::Result:0x00007fd03c3051d8>
    * rack.errors                                             : #<IO:0x0000558d28b580c8>
    * rack.hijack                                             : #<Puma::Client:0x00007fd03c305d40>
    * rack.hijack?                                            : true
    * rack.input                                              : #<StringIO:0x00007fd03c3056d8>
    * rack.multiprocess                                       : false
    * rack.multithread                                        : true
    * rack.request.cookie_hash                                : {"_r_solutions_session"=>"WApsZfetZMdxoWS7HkHlrNqnvaaKHUSSSxn9vY1prhMfT9d/Zz+XMRmxnNUCjAWsSC8AuzQmSzFQjvZi2FsrcjTgulD8jFeT6gkz55DbIRFsjfYH6nBSjjnMd7NvhuXySf+QtLovhZgBAhOMrJvQP0CbYGE0VxJa6ISKVBjZifjiUKbbXsy4J3QeHzBCjt+E1t0tYP4AlZxAEjRfK0GK5v4BxXDF49F91s1VWUD5A4EGM9crQg/3PJw7iIQYHaDvKcuF9gHdz7xc0RjEmV...
    * rack.request.cookie_string                              : _r_solutions_session=WApsZfetZMdxoWS7HkHlrNqnvaaKHUSSSxn9vY1prhMfT9d%2FZz%2BXMRmxnNUCjAWsSC8AuzQmSzFQjvZi2FsrcjTgulD8jFeT6gkz55DbIRFsjfYH6nBSjjnMd7NvhuXySf%2BQtLovhZgBAhOMrJvQP0CbYGE0VxJa6ISKVBjZifjiUKbbXsy4J3QeHzBCjt%2BE1t0tYP4AlZxAEjRfK0GK5v4BxXDF49F91s1VWUD5A4EGM9crQg%2F3PJw7iIQYHaDvKcuF9gHdz7xc0RjEmVBawLlF9sDy1ehLc%2BTM0ynRNy6WTxd09Kuj%2Brc20%2Bepjy8NHk3a3i8Ab4vrPg6U1mztiHbjXhTg1ZvDCmM0ZBBiTmOdHAs5irSZcdpAGgwFa7SI8x%2BBTdS6CP6VW9l6pdJAgSXUwbL%2BXBk7JLX5k8qFMb6y4EVneyH%2BLFpHwgY8W0hiM1%2BYzhrAlp933nrFroeE5ng8K3Dy1368VICWSk5wmC9D31gL4rCawHcECu%2Fgr%2Bo5Qb2TS%2BfizPGavbN%2FNFUk2szjug0wP%2FbyxthHDrj9TSPYDTdKnjVBOIQB0dE%3D--H8TqpUcF6nQid9XH--EcvI8XO4sXCNdAepxxU0EQ%3D%3D; ahoy_visit=5baffb58-186f-4fb7-8e0a-b855245a9432; ahoy_visitor=1f6244c1-bf33-4801-b0ca-c7d89f4b8b4e
    * rack.request.query_hash                                 : {}
    * rack.request.query_string                               :
    * rack.run_once                                           : false
    * rack.session                                            : #<ActionDispatch::Request::Session:0x00007fd03c333f88>
    * rack.session.options                                    : #<ActionDispatch::Request::Session::Options:0x00007fd03c333f38>
    * rack.tempfiles                                          : []
    * rack.url_scheme                                         : https
    * rack.version                                            : [1, 6]
    * warden                                                  : Warden::Proxy:70266169958160 @config={:default_scope=>:user, :scope_defaults=>{}, :default_strategies=>{:user=>[:rememberable, :database_authenticatable]}, :intercept_401=>false, :failure_app=>#<Devise::Delegator:0x0000558d2d22ea10>}

-------------------------------
Data:
-------------------------------

  * data: {:current_user=>
    #<User id: 1, global_admin: true, created_at: "2021-04-12 23:11:27.966580000 +0000", updated_at: "2022-06-23 12:07:34.745535000 +0000", email: "[[email protected]](mailto:[email protected])", can_manage_web: true, can_manage_email: true, can_manage_users: true, can_manage_blog: true, name: "[[email protected]](mailto:[email protected])", moderator: true, can_view_restricted_pages: true, deliver_analytics_report: true, can_manage_api: true, can_manage_subdomain_settings: true, session_timeoutable_in: "1-week", can_access_admin: true, deliver_error_notifications: true, can_manage_analytics: true, can_manage_files: true>,
   :current_visit=>
    #<Ahoy::Visit:0x00007fd03c4fb190
     id: 24986,
     visit_token: "5baffb58-186f-4fb7-8e0a-b855245a9432",
     visitor_token: "1f6244c1-bf33-4801-b0ca-c7d89f4b8b4e",
     user_id: 1,
     ip: "142.181.192.212",
     user_agent:
      "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.0 Safari/605.1.15",
     referrer: nil,
     referring_domain: nil,
     landing_page: "https://www.restarone.solutions/",
     browser: "Safari",
     os: "Mac",
     device_type: "Desktop",
     country: "CA",
     region: "Ontario",
     city: "Toronto",
     latitude: 43.6655,
     longitude: -79.4378,
     utm_source: nil,
     utm_medium: nil,
     utm_term: nil,
     utm_content: nil,
     utm_campaign: nil,
     app_version: nil,
     os_version: nil,
     platform: nil,
     started_at: Thu, 23 Jun 2022 12:07:23.782883000 UTC +00:00>}

Important error data (user + visit)

We see the user who encountered the error

* data: {:current_user=>
    #<User id: 1, global_admin: true, created_at: "2021-04-12 23:11:27.966580000 +0000", updated_at: "2022-06-23 12:07:34.745535000 +0000", email: "[[email protected]](mailto:[email protected])", can_manage_web: true, can_manage_email: true, can_manage_users: true, can_manage_blog: true, name: "[[email protected]](mailto:[email protected])", moderator: true, can_view_restricted_pages: true, deliver_analytics_report: true, can_manage_api: true, can_manage_subdomain_settings: true, session_timeoutable_in: "1-week", can_access_admin: true, deliver_error_notifications: true, can_manage_analytics: true, can_manage_files: true>,

we also see their visit

    #<Ahoy::Visit:0x00007fd03c4fb190
     id: 24986,
     visit_token: "5baffb58-186f-4fb7-8e0a-b855245a9432",
     visitor_token: "1f6244c1-bf33-4801-b0ca-c7d89f4b8b4e",
     user_id: 1,
     ip: "142.181.192.212",
     user_agent:
      "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.0 Safari/605.1.15",
     referrer: nil,
     referring_domain: nil,
     landing_page: "https://www.restarone.solutions/",
     browser: "Safari",
     os: "Mac",
     device_type: "Desktop",
     country: "CA",
     region: "Ontario",
     city: "Toronto",
     latitude: 43.6655,
     longitude: -79.4378,
     utm_source: nil,
     utm_medium: nil,
     utm_term: nil,
     utm_content: nil,
     utm_campaign: nil,
     app_version: nil,
     os_version: nil,
     platform: nil,
     started_at: Thu, 23 Jun 2022 12:07:23.782883000 UTC +00:00>}

Sensible tuning for your mental health

Not all errors need to be reported, so we suppress errors that are a fact of life for a web server on the internet. See the errors we suppress here: https://github.com/restarone/violet_rails/blob/master/config/environments/production.rb#L136-L141

Change management

Change management is the process we use to create, deploy and manage changes on a Violet Rails system.

CMS

In the CMS, each page can be part of 3 releases. The current release (named after the public path), the staged release (next version, named after the public path + name + -staging) and a backup of the current release (named after the public path-release-date-etc`)

Example

Here we see for the page / (that is named root) the current release is named root and is mapped to /. A new page root-staging has been created that maps to /root-staging. Right before cutting over, we will copy the contents of the root page (in raw HTML format, not with WSIWYG encoding) and create a new page named 'root-pre-etc' that will map to /root-pre-etc.