lognorth 0.1.0 → 0.3.0.rc.2

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: dbd243e69bd9758c158d11868e091edc0f0c629916cebc8094fdcfb74664b0b3
4
- data.tar.gz: db2c6d4d5e68353da4ecb2757737595ec144c9ae002202bf3cde5abcacdfa978
3
+ metadata.gz: 8ee47ef92f738c9a430483e2f35f55e17155e7519b806017943e69a217649d95
4
+ data.tar.gz: 57995a21f2d38a165604ce4825e1ba4d924b0f13e9c02040c7eee7279997c156
5
5
  SHA512:
6
- metadata.gz: 1381eee25cc094217d00d294ee381846cdb0390110e2e727c104459c5199918ede21c6157b1dfbd723b39d8fde5cc45b74998e031b352df058d76676a437a57e
7
- data.tar.gz: 70a5edefba779fd982adcaf91465c34ec89082242428c953485ba4a9855ddb7852f4950c6e01ba21636ac1c2bbfb6963ef66cbbc3208f6594abc85196b1914f7
6
+ metadata.gz: ba3de220f46ffe6a576e2b48619d6a7b664f6bbdc80b5db9f910defb959d0b9940b81a4548bb5bd21631882324edf461f8bf3200f0892d509c0bed09a0a6360c
7
+ data.tar.gz: edc1a9b1795925af3f04c05feef9334a99d5ef4e133b03976f974689a21de952b0664408c3918cdaa03ef150c2c0c76adf3f8e3650af8c1dc69921a2dc01f772
data/README.md CHANGED
@@ -1,115 +1,156 @@
1
- # LogNorth Rails SDK
1
+ # lognorth-sdk-rails
2
2
 
3
- Send errors and logs from Rails to [LogNorth](https://lognorth.com) for monitoring and alerting.
3
+ [![CI](https://github.com/karloscodes/lognorth-sdk-rails/actions/workflows/ci.yml/badge.svg)](https://github.com/karloscodes/lognorth-sdk-rails/actions/workflows/ci.yml) [![Gem Version](https://img.shields.io/gem/v/lognorth)](https://rubygems.org/gems/lognorth) [![License](https://img.shields.io/github/license/karloscodes/lognorth-sdk-rails)](LICENSE)
4
4
 
5
- ## Installation
5
+ Sends errors, logs, and request events from Rails and Rack apps to LogNorth.
6
6
 
7
- ```ruby
8
- gem "lognorth", github: "karloscodes/lognorth-sdk-rails"
9
- ```
10
-
11
- ## Rails Setup
7
+ [LogNorth](https://lognorth.com) monitors your app and alerts you when it fails. See the [LogNorth Rails docs](https://lognorth.com/docs/integrations/rails/).
12
8
 
13
- Add credentials:
9
+ ## Install
14
10
 
15
- ```yaml
16
- # config/credentials.yml.enc
17
- lognorth:
18
- url: https://your-lognorth-instance.com
19
- api_key: your_api_key
11
+ ```sh
12
+ bundle add lognorth
20
13
  ```
21
14
 
22
- That's it. The gem auto-configures via Railtie.
15
+ ## Use
23
16
 
24
- ### Manual Configuration
17
+ With credentials set (see [Configuration](#configuration)), Rails sends request events, reported exceptions, and job events on its own. Call the SDK to send your own events:
25
18
 
26
19
  ```ruby
27
- # config/initializers/lognorth.rb
28
- LogNorth.config(
29
- ENV["LOGNORTH_URL"],
30
- ENV["LOGNORTH_API_KEY"]
31
- )
20
+ LogNorth.log("User signed up", { user_id: 123 })
21
+
22
+ begin
23
+ risky_operation
24
+ rescue => e
25
+ LogNorth.error("Payment failed", e, { order_id: 456 })
26
+ raise
27
+ end
28
+
29
+ LogNorth.flush # sends the queue now, for up to 5 seconds
32
30
  ```
33
31
 
34
- ### Options
32
+ ### Who hit the error, and in which release
33
+
34
+ The SDK reads `Current.user` when your app defines it, as the Rails authentication generator does. Otherwise, set the user yourself. Use an ID, not an email:
35
35
 
36
36
  ```ruby
37
- # config/application.rb
38
- config.lognorth.enabled = true # Default: on everywhere except development and test
39
- config.lognorth.middleware = true # Log HTTP requests
40
- config.lognorth.error_subscriber = true # Report exceptions (Rails 7+)
41
-
42
- # Paths to skip from request logging. Defaults to Rails' built-in
43
- # health-check endpoint; set to [] to log everything or replace with
44
- # your own list.
45
- config.lognorth.ignored_paths = ["/up", "/healthz"]
37
+ class ApplicationController < ActionController::Base
38
+ before_action { LogNorth.user = current_user&.id }
39
+ end
46
40
  ```
47
41
 
48
- Default: `["/up"]` (Rails 7.1's auto-generated health check — swamped by
49
- kamal-proxy and load-balancer pings otherwise). Setting `ignored_paths =
50
- []` disables ignoring entirely. Matching is exact path or `path/…`
51
- prefix, so `/up` also covers `/up/detail`.
42
+ The request event and its errors carry the user.
43
+
44
+ Errors also carry the release. See [Configuration](#configuration) for how the release is set.
45
+
46
+ ## Middleware
47
+
48
+ The middleware logs each request as one event. The event has the method, path, status, duration, controller, action, and route (`/users/:id`, Rails 7.1 and later). LogNorth groups requests into endpoints by the route. It also sets an `X-Trace-ID` response header. It reuses an incoming `X-Trace-ID` request header when one is set.
49
+
50
+ Requests that match no route are not logged. A 5xx request also carries its user agent, and it sends the queue at once.
52
51
 
53
52
  ### Client errors are not errors
54
53
 
55
- An exception that Rails answers with a 4xx is the request's fault, not the app's:
56
- `ActiveRecord::RecordNotFound` (404), `ActionController::InvalidAuthenticityToken`
57
- (422), `ActionController::ParameterMissing` (400). The SDK logs the request with
58
- that status and does not report an error, so these never become issues or alerts.
54
+ An exception that Rails answers with a 4xx is the request's fault, not the app's. Examples are `ActiveRecord::RecordNotFound` (404), `ActionController::InvalidAuthenticityToken` (422), and `ActionController::ParameterMissing` (400). The SDK logs the request with that status. It does not report an error, so these never become issues or alerts.
59
55
 
60
- Rails decides the status from `config.action_dispatch.rescue_responses`, and the SDK
61
- asks the same map. To report one of them as an error, map it to a 5xx:
56
+ Rails sets the status in `config.action_dispatch.rescue_responses`. The SDK reads the same map. To report one of them as an error, map it to a 5xx:
62
57
 
63
58
  ```ruby
64
59
  config.action_dispatch.rescue_responses["ActiveRecord::RecordNotFound"] = :internal_server_error
65
60
  ```
66
61
 
67
- ## Usage
62
+ ### Without Rails
68
63
 
69
- ```ruby
70
- # Log messages (batched, sent after 5s or at 10 events)
71
- LogNorth.log("User signed up", { user_id: 123 })
64
+ Use the middleware in any Rack app:
72
65
 
73
- # Report errors (sent at once)
74
- begin
75
- risky_operation
76
- rescue => e
77
- LogNorth.error("Payment failed", e, { order_id: 456 })
78
- raise
79
- end
66
+ ```ruby
67
+ require "lognorth"
80
68
 
81
- # Manual flush (called automatically at exit)
82
- LogNorth.flush
69
+ LogNorth.config("https://lognorth.example.com", "api_key")
70
+ use LogNorth::Middleware
83
71
  ```
84
72
 
85
- ## Batching and delivery
73
+ ## How it works
86
74
 
87
- Logging calls never block and never raise. They add the event to a queue in memory.
88
- One background thread sends the queue, one request at a time.
75
+ ### Batching
89
76
 
90
- - The thread sends when the queue holds 10 events, when you report an error, or 5 seconds after the first event.
77
+ Logging calls never wait for the network. Each call adds the event to a queue in memory. One background thread sends the queue, one request at a time.
78
+
79
+ - The thread sends when the queue holds 10 events, when you report an error, or 5 seconds after the first queued event.
91
80
  - A request holds at most 500 events and 1 MB of JSON.
92
81
  - The queue holds at most 10,000 events or 10 MB of JSON.
93
- - The SDK trims an event to at most 64 KB before it enters the queue. It marks a trimmed event with `context.truncated = true`.
82
+ - A message over 1,000 characters is cut. A long string in the context is cut.
83
+ - An event still over 64 KB keeps only its essential context keys. The SDK sets `context.truncated` to `true` on any cut event.
84
+
85
+ ### Failed sends
86
+
87
+ The SDK keeps the events and sends them again later. Events keep their order.
88
+
89
+ - A network error, a timeout, a 408, or a 5xx waits 1, 2, 4 seconds, and so on, up to 60 seconds. Each wait varies by up to 20%.
90
+ - A 429 or 503 waits as long as the `Retry-After` header says, from 1 to 300 seconds. Without the header, the SDK uses the normal wait.
91
+ - A 401, 403, 404, or other unexpected status waits 60 seconds, then 120, then 240, up to 300 seconds. It writes a line to stderr when delivery starts failing. Check the URL and the API key.
92
+ - Another 4xx, such as 400, 413, or 422, splits the batch in two and sends each half. A single event that the server still refuses is dropped.
93
+
94
+ When the queue is full, the SDK drops the oldest event that is not an error. After the next successful send, it logs `LogNorth client dropped N events`. The context holds the counts of dropped events and dropped errors.
95
+
96
+ ### Exit
94
97
 
95
- When a send fails, the SDK keeps the events and tries again later. Events keep their order.
98
+ At exit, the SDK sends the queue for up to 5 seconds. Ruby runs this exit hook on SIGTERM and SIGINT too, unless the app traps them. The SDK writes the number of unsent events to stderr.
96
99
 
97
- - On a network error, a timeout, or a 5xx, it waits 1 second, then 2, then 4, up to 60.
98
- - On a 429 or 503, it waits as long as the `Retry-After` header says.
99
- - On a 401, 403, or 404, it waits 60 seconds, then up to 5 minutes. It writes one line to stderr, so you can fix the url or the key.
100
- - On a 413 or other 4xx, it splits the batch in two and sends again. It drops a single event the server still refuses.
100
+ ### Errors and jobs
101
101
 
102
- When the queue is full, the SDK drops the oldest event that is not an error. It keeps errors longest.
103
- After the next successful send, it logs `LogNorth client dropped N events` with the counts.
102
+ - `Rails.error` reports go to LogNorth. Each event adds `handled`, `severity`, and `source` to the context.
103
+ - With `config.lognorth.active_job` on, each job logs `<Job> completed` or `<Job> failed`. A job carries the trace ID of the request that enqueued it.
104
+
105
+ ## Configuration
106
+
107
+ The SDK reads the URL and API key from Rails credentials:
108
+
109
+ ```yaml
110
+ # config/credentials.yml.enc
111
+ lognorth:
112
+ url: https://your-lognorth-instance.com
113
+ api_key: your_api_key
114
+ ```
104
115
 
105
- At exit (including SIGTERM and SIGINT), the SDK sends what is left in the queue for up to 5 seconds.
106
- It writes the number of events it could not send to stderr.
116
+ Without a URL and an API key, the SDK logs a warning at boot and sends nothing.
107
117
 
108
- ## Rack (without Rails)
118
+ Rails sets the environment name for you. Set `release:` yourself, or let the SDK read it from the environment. To configure the SDK by hand, call `LogNorth.config` in an initializer:
109
119
 
110
120
  ```ruby
111
- require "lognorth"
121
+ # config/initializers/lognorth.rb
122
+ LogNorth.config(
123
+ ENV["LOGNORTH_URL"],
124
+ ENV["LOGNORTH_API_KEY"],
125
+ environment: "production", # optional
126
+ release: "1.2.3" # optional
127
+ )
128
+ ```
112
129
 
113
- LogNorth.config("https://lognorth.example.com", "api_key")
114
- use LogNorth::Middleware
130
+ When no `release:` is set, the SDK reads the first of these variables that is set: `LOGNORTH_RELEASE`, `GIT_SHA`, `GIT_COMMIT`, `SOURCE_COMMIT`, `KAMAL_VERSION`, `RENDER_GIT_COMMIT`, `HEROKU_SLUG_COMMIT`, `SOURCE_VERSION`, `RAILWAY_GIT_COMMIT_SHA`, `VERCEL_GIT_COMMIT_SHA`. When a release is set, the SDK logs `Release <version> started` once, when it configures.
131
+
132
+ Options, in `config/application.rb`:
133
+
134
+ ```ruby
135
+ config.lognorth.enabled = true # Default: on, except in development and test
136
+ config.lognorth.middleware = true # Request events
137
+ config.lognorth.error_subscriber = true # Rails.error reports (Rails 7.0+)
138
+ config.lognorth.active_job = true # Job events
139
+
140
+ # Requests to skip. Default: Rails' health check, which load balancers call often.
141
+ # Set [] to log everything.
142
+ config.lognorth.ignored_paths = ["/up"]
143
+ ```
144
+
145
+ An ignored path matches the exact path, or the path followed by `/`. So `/up` also skips `/up/detail`.
146
+
147
+ ## Development
148
+
149
+ ```sh
150
+ bundle install
151
+ bundle exec rake test
115
152
  ```
153
+
154
+ ## License
155
+
156
+ MIT. See [LICENSE](LICENSE).
@@ -15,7 +15,12 @@ module LogNorth
15
15
  MAX_STACK_TRACE_BYTES = 16 * 1024
16
16
  MAX_STRING_BYTES = 8 * 1024
17
17
  # The context keys an event keeps when it is still too big after trimming.
18
- ESSENTIAL_KEYS = %w[error error_class error_file error_line method path status environment].freeze
18
+ ESSENTIAL_KEYS = %w[error error_class error_file error_line method path status environment release user].freeze
19
+ # Where deploy tools put the version that runs. The first one set wins.
20
+ RELEASE_ENV = %w[
21
+ LOGNORTH_RELEASE GIT_SHA GIT_COMMIT SOURCE_COMMIT KAMAL_VERSION
22
+ RENDER_GIT_COMMIT HEROKU_SLUG_COMMIT SOURCE_VERSION RAILWAY_GIT_COMMIT_SHA VERCEL_GIT_COMMIT_SHA
23
+ ].freeze
19
24
 
20
25
  module Client
21
26
  # An event in the queue, with its JSON size and whether it is an error.
@@ -39,6 +44,8 @@ module LogNorth
39
44
  @endpoint = nil
40
45
  @api_key = nil
41
46
  @environment = nil
47
+ @release = nil
48
+ @announced = nil # the release this process last said it started
42
49
 
43
50
  # Wait times in seconds. Tests set them lower.
44
51
  @flush_interval = 5
@@ -53,13 +60,15 @@ module LogNorth
53
60
  class << self
54
61
  attr_accessor :debug
55
62
 
56
- def config(url, key, environment: nil)
63
+ def config(url, key, environment: nil, release: nil)
57
64
  @mutex.synchronize do
58
65
  @endpoint = url.chomp("/")
59
66
  @api_key = key
60
67
  @environment = environment
68
+ @release = release || release_from_env
61
69
  end
62
70
  log_debug("configured with url=#{url} env=#{environment.inspect}")
71
+ announce_release
63
72
  end
64
73
 
65
74
  def configured?
@@ -82,6 +91,22 @@ module LogNorth
82
91
  Thread.current[:lognorth_trace_id] = id
83
92
  end
84
93
 
94
+ # The user of the request on this thread: the one set with LogNorth.user=,
95
+ # or else Current.user, which the Rails authentication generator defines.
96
+ def current_user
97
+ Thread.current[:lognorth_user] || user_from_current_attributes
98
+ end
99
+
100
+ def current_user=(id)
101
+ Thread.current[:lognorth_user] = id&.to_s
102
+ end
103
+
104
+ # Forgets the trace ID and the user when a request ends.
105
+ def end_request
106
+ Thread.current[:lognorth_trace_id] = nil
107
+ Thread.current[:lognorth_user] = nil
108
+ end
109
+
85
110
  def send_event(message, context = {}, trace_id: nil, duration_ms: nil, timestamp: nil)
86
111
  return unless configured?
87
112
 
@@ -89,7 +114,7 @@ module LogNorth
89
114
  event = {
90
115
  message: message,
91
116
  timestamp: (timestamp || Time.now).utc.iso8601(3),
92
- context: stamp_environment(context)
117
+ context: stamp_user(stamp_environment(context))
93
118
  }
94
119
  event[:trace_id] = trace_id if trace_id
95
120
  event[:duration_ms] = duration_ms if duration_ms
@@ -115,14 +140,14 @@ module LogNorth
115
140
  event = {
116
141
  message: message,
117
142
  timestamp: (timestamp || Time.now).utc.iso8601(3),
118
- context: stamp_environment(context.merge(
143
+ context: stamp_user(stamp_environment(context.merge(
119
144
  error: exception.message,
120
145
  error_class: exception.class.name,
121
146
  error_file: error_file,
122
147
  error_line: error_line,
123
148
  error_caller: error_caller,
124
149
  stack_trace: exception.backtrace&.first(20)&.join("\n")
125
- ))
150
+ )))
126
151
  }
127
152
  event[:trace_id] = trace_id if trace_id
128
153
  event[:duration_ms] = duration_ms if duration_ms
@@ -169,8 +194,48 @@ module LogNorth
169
194
  context.merge(environment: env)
170
195
  end
171
196
 
197
+ # Logs "Release <version> started" once per release, when the client is
198
+ # configured. LogNorth takes the first start of a release as its deploy
199
+ # time and marks it on its charts.
200
+ def announce_release
201
+ release = @mutex.synchronize do
202
+ next if @release.nil? || @release == @announced
203
+
204
+ @announced = @release
205
+ end
206
+ send_event("Release #{release} started", { release: release }) if release
207
+ end
208
+
209
+ def stamp_user(context)
210
+ user = current_user
211
+ return context if user.nil? || fetch(context, :user)
212
+
213
+ context.merge(user: user)
214
+ end
215
+
216
+ def user_from_current_attributes
217
+ return unless defined?(::Current) && ::Current.respond_to?(:user)
218
+
219
+ ::Current.user&.id&.to_s
220
+ rescue StandardError
221
+ nil
222
+ end
223
+
224
+ # Errors carry the release: that is where it answers which deploy broke
225
+ # something. Other events stay small.
226
+ def stamp_release(event)
227
+ release = @mutex.synchronize { @release }
228
+ return event if release.nil? || !error_event?(event[:context]) || fetch(event[:context], :release)
229
+
230
+ event.merge(context: event[:context].merge(release: release))
231
+ end
232
+
233
+ def release_from_env
234
+ RELEASE_ENV.lazy.map { |k| ENV[k].to_s.strip }.find { |v| !v.empty? }
235
+ end
236
+
172
237
  def enqueue(event, urgent: false)
173
- entry = new_entry(trim(event))
238
+ entry = new_entry(trim(stamp_release(event)))
174
239
  @mutex.synchronize do
175
240
  push(entry)
176
241
  @urgent = true if urgent
@@ -9,7 +9,10 @@ module LogNorth
9
9
  end
10
10
 
11
11
  def call(env)
12
+ # A mounted engine or Rack app leaves PATH_INFO and SCRIPT_NAME as it
13
+ # saw them, so read both before the call.
12
14
  path = env["PATH_INFO"]
15
+ script_name = env["SCRIPT_NAME"].to_s
13
16
 
14
17
  # Skip ignored paths (health checks, metrics, etc.)
15
18
  if ignored_path?(path)
@@ -26,18 +29,19 @@ module LogNorth
26
29
 
27
30
  # Don't track requests that didn't match any route (scanner noise)
28
31
  if status == 404 && !env["action_controller.instance"]
29
- LogNorth::Client.current_trace_id = nil
32
+ LogNorth::Client.end_request
30
33
  return [status, headers, response]
31
34
  end
32
35
 
33
36
  duration_ms = ((Process.clock_gettime(Process::CLOCK_MONOTONIC) - start) * 1000).round
34
37
  headers["X-Trace-ID"] = trace_id
35
38
 
36
- context = { method: env["REQUEST_METHOD"], path: env["PATH_INFO"], status: status }
37
- merge_route_info!(context, env)
39
+ context = { method: env["REQUEST_METHOD"], path: path, status: status }
40
+ merge_route_info!(context, env, script_name)
41
+ merge_user_agent!(context, env, status)
38
42
 
39
43
  LogNorth::Client.send_event(
40
- "#{env['REQUEST_METHOD']} #{env['PATH_INFO']} → #{status}",
44
+ "#{env['REQUEST_METHOD']} #{path} → #{status}",
41
45
  context,
42
46
  trace_id: trace_id,
43
47
  duration_ms: duration_ms,
@@ -46,7 +50,7 @@ module LogNorth
46
50
 
47
51
  LogNorth.flush if status >= 500
48
52
 
49
- LogNorth::Client.current_trace_id = nil
53
+ LogNorth::Client.end_request
50
54
  [status, headers, response]
51
55
  rescue StandardError => e
52
56
  duration_ms = ((Process.clock_gettime(Process::CLOCK_MONOTONIC) - start) * 1000).round
@@ -54,23 +58,23 @@ module LogNorth
54
58
  # Rails turns this into a 4xx further out. Log the request with that
55
59
  # status, like any other request, instead of an error.
56
60
  status = LogNorth.response_status_for(e)
57
- context = { method: env["REQUEST_METHOD"], path: env["PATH_INFO"], status: status }
58
- merge_route_info!(context, env)
61
+ context = { method: env["REQUEST_METHOD"], path: path, status: status }
62
+ merge_route_info!(context, env, script_name)
59
63
  LogNorth::Client.send_event(
60
- "#{env['REQUEST_METHOD']} #{env['PATH_INFO']} → #{status}", context,
64
+ "#{env['REQUEST_METHOD']} #{path} → #{status}", context,
61
65
  trace_id: trace_id, duration_ms: duration_ms, timestamp: start_time
62
66
  )
63
- LogNorth::Client.current_trace_id = nil
67
+ LogNorth::Client.end_request
64
68
  raise
65
69
  end
66
70
  LogNorth::Client.send_error_event(
67
- "Request failed: #{env['REQUEST_METHOD']} #{env['PATH_INFO']}", e,
68
- { method: env["REQUEST_METHOD"], path: env["PATH_INFO"] },
71
+ "Request failed: #{env['REQUEST_METHOD']} #{path}", e,
72
+ merge_user_agent!({ method: env["REQUEST_METHOD"], path: path }, env, 500),
69
73
  trace_id: trace_id,
70
74
  duration_ms: duration_ms,
71
75
  timestamp: start_time
72
76
  )
73
- LogNorth::Client.current_trace_id = nil
77
+ LogNorth::Client.end_request
74
78
  raise
75
79
  end
76
80
 
@@ -83,10 +87,18 @@ module LogNorth
83
87
  ignored_paths.any? { |p| path == p || path.start_with?("#{p}/") }
84
88
  end
85
89
 
90
+ # The user agent tells a bot from a browser on a failed request. Only
91
+ # failed ones carry it, to keep every other event small.
92
+ def merge_user_agent!(context, env, status)
93
+ agent = env["HTTP_USER_AGENT"].to_s
94
+ context[:user_agent] = agent if status.to_i >= 500 && !agent.empty?
95
+ context
96
+ end
97
+
86
98
  # Rails populates action_controller.instance after dispatch so the
87
- # controller/action names — and the named route when available —
88
- # can be pulled straight off the env.
89
- def merge_route_info!(context, env)
99
+ # controller/action names — and the route — can be pulled straight off
100
+ # the env.
101
+ def merge_route_info!(context, env, script_name)
90
102
  controller = env["action_controller.instance"]
91
103
  return unless controller
92
104
 
@@ -94,6 +106,21 @@ module LogNorth
94
106
  action = controller.respond_to?(:action_name) ? controller.action_name : nil
95
107
  context[:controller] = klass if klass
96
108
  context[:action] = action if action
109
+ route = route_of(env, script_name)
110
+ context[:route] = route if route
111
+ end
112
+
113
+ # The route as `bin/rails routes` prints it (Rails 7.1+), without the
114
+ # optional format: "/users/:id". An engine's route is relative to its
115
+ # mount point, which Rails added to SCRIPT_NAME.
116
+ def route_of(env, script_name)
117
+ return unless defined?(ActionDispatch::Request) && ActionDispatch::Request.method_defined?(:route_uri_pattern)
118
+
119
+ pattern = ActionDispatch::Request.new(env).route_uri_pattern
120
+ return unless pattern
121
+
122
+ mount = env["SCRIPT_NAME"].to_s.delete_prefix(script_name)
123
+ mount + pattern.delete_suffix("(.:format)")
97
124
  end
98
125
  end
99
126
  end
data/lib/lognorth.rb CHANGED
@@ -7,8 +7,17 @@ require_relative "lognorth/railtie" if defined?(Rails::Railtie)
7
7
 
8
8
  module LogNorth
9
9
  class << self
10
- def config(url, key, environment: nil)
11
- Client.config(url, key, environment: environment)
10
+ def config(url, key, environment: nil, release: nil)
11
+ Client.config(url, key, environment: environment, release: release)
12
+ end
13
+
14
+ # Names the user of the current request: an ID, not an email. The request
15
+ # event and its errors carry it, so an issue shows how many users it hit.
16
+ # Without it, LogNorth reads Current.user when the app defines one.
17
+ #
18
+ # before_action { LogNorth.user = current_user&.id }
19
+ def user=(id)
20
+ Client.current_user = id
12
21
  end
13
22
 
14
23
  def log(message, context = {})
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: lognorth
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.3.0.rc.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - LogNorth