tracelit 0.1.7 → 0.1.8

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: baa72093ff99cee94e4ba7221714f16691cac2907b37fc8973be2db9d58995cd
4
- data.tar.gz: f18620a662414111c86741d4acf81da3add9db0577a384b973842cab3053721e
3
+ metadata.gz: 66ee178565bf80581941df2481fb09e981e013c335ac8a2e4c6d8845a62fb4f1
4
+ data.tar.gz: a40528eb98fe34dd2dd3d7399cf0e5d40ea5fd0fb83900daec8ee6728d98b846
5
5
  SHA512:
6
- metadata.gz: 7a7961d36cb65b10d80b43cfecea3f767798ca5eb38d12352a8933ab2969d86d9a73d947e6d25eb0444ab1ea4374300ce215a8f34a65621b3a465f009ac24d28
7
- data.tar.gz: c45e5931a15e408165258bea86c2ffd70285286a27e08e36c9dd30e0e9a705fff8ec2a2104cb35ae177a33f46379700c032bd91fd1b76d307bf2e409722df16a
6
+ metadata.gz: 19b1d323be4bc9f8f1cf531f9728d371537a5430a1e0f6eb82aba66af17ee20b5bddb819e83bd1b8d5f624444ac0a6afaeb68319bbdb380469f7c8ab1ea0945e
7
+ data.tar.gz: d8c722105fb3b85f0cab312ec82ea81a2de12fae214f9f555bfb7ce6c3929184f231c13f228b182c9c268ab62e3ee94751295b00f939347012cfa43ed797e81a
data/README.md CHANGED
@@ -6,12 +6,6 @@ Official Ruby SDK for [Tracelit](https://tracelit.io) — drop-in OpenTelemetry
6
6
 
7
7
  ---
8
8
 
9
- ## Set up with AI
10
-
11
- Want an AI assistant (Cursor, Claude, ChatGPT, etc.) to integrate Tracelit into your app automatically? Copy the contents of [`llm_prompt.txt`](./llm_prompt.txt) and paste it as your prompt. It covers gem installation, initializer setup, manual spans, custom metrics, and test guard — everything the AI needs in one shot.
12
-
13
- ---
14
-
15
9
  ## Installation
16
10
 
17
11
  Add to your `Gemfile` and run `bundle install`:
@@ -273,9 +267,6 @@ TRACELIT_ENABLED=false
273
267
 
274
268
  ---
275
269
 
276
- ## Running the SDK's own tests
270
+ ## Changelog
277
271
 
278
- ```bash
279
- bundle install
280
- bundle exec rspec
281
- ```
272
+ See the [release history](https://docs.tracelit.io/changelog) on the Tracelit docs.
@@ -48,6 +48,7 @@ module Tracelit
48
48
  install_sidekiq_middleware if defined?(::Sidekiq)
49
49
  install_connection_pool_poller if defined?(::ActiveRecord)
50
50
  install_memory_poller
51
+ install_cpu_poller
51
52
  rescue StandardError => e
52
53
  OpenTelemetry.logger.warn("[Tracelit] failed to set up metrics: #{e.message}")
53
54
  end
@@ -62,8 +63,10 @@ module Tracelit
62
63
  def self.restart_pollers(config)
63
64
  @connection_pool_poller_installed = false
64
65
  @memory_poller_installed = false
66
+ @cpu_poller_installed = false
65
67
  install_connection_pool_poller if defined?(::ActiveRecord)
66
68
  install_memory_poller
69
+ install_cpu_poller
67
70
  rescue StandardError => e
68
71
  OpenTelemetry.logger.warn("[Tracelit] failed to restart pollers after fork: #{e.message}")
69
72
  end
@@ -349,5 +352,100 @@ module Tracelit
349
352
  rescue StandardError => e
350
353
  OpenTelemetry.logger.warn("[Tracelit] failed to install memory poller: #{e.message}")
351
354
  end
355
+
356
+ # Polls process CPU utilisation every 30 seconds on a daemon thread.
357
+ # Computes a percentage by tracking the delta in CPU time (user + system)
358
+ # against wall-clock elapsed time — same approach as the Go and Node SDKs.
359
+ #
360
+ # On Linux: reads /proc/self/stat (utime + stime in jiffies at 100 Hz).
361
+ # On macOS: reads `ps -o %cpu= -p <pid>` as a direct percentage.
362
+ #
363
+ # Emits: process.runtime.cpu.usage (%)
364
+ # Attributes: process.pid, process.runtime
365
+ def self.install_cpu_poller
366
+ return if @cpu_poller_installed
367
+ @cpu_poller_installed = true
368
+
369
+ cpu_gauge = @meter.create_gauge(
370
+ "process.runtime.cpu.usage",
371
+ description: "Process CPU utilisation percentage",
372
+ unit: "%"
373
+ )
374
+
375
+ pid = Process.pid
376
+ linux = File.exist?("/proc/self/stat")
377
+ interval = 30 # seconds
378
+
379
+ thread = Thread.new do
380
+ Thread.current[:tracelit_cpu_poller] = true
381
+
382
+ last_cpu_time = read_cpu_time_s(pid, linux)
383
+ last_wall_time = Process.clock_gettime(Process::CLOCK_MONOTONIC)
384
+
385
+ loop do
386
+ sleep interval
387
+ begin
388
+ now = Process.clock_gettime(Process::CLOCK_MONOTONIC)
389
+ elapsed = now - last_wall_time
390
+ cpu_time = read_cpu_time_s(pid, linux)
391
+
392
+ next if elapsed <= 0 || cpu_time.nil? || last_cpu_time.nil?
393
+
394
+ delta = cpu_time - last_cpu_time
395
+ last_cpu_time = cpu_time
396
+ last_wall_time = now
397
+
398
+ next if delta < 0
399
+
400
+ pct = [[delta / elapsed * 100.0, 100.0].min, 0.0].max
401
+
402
+ cpu_gauge.record(pct, attributes: {
403
+ "process.pid" => pid.to_s,
404
+ "process.runtime" => "ruby",
405
+ })
406
+ rescue StandardError
407
+ # Retry next cycle — never crash on a metric poll failure
408
+ end
409
+ end
410
+ end
411
+ thread.abort_on_exception = false
412
+ thread
413
+ rescue StandardError => e
414
+ OpenTelemetry.logger.warn("[Tracelit] failed to install CPU poller: #{e.message}")
415
+ end
416
+
417
+ # Returns cumulative CPU time (user + system) for this process in seconds.
418
+ # On Linux reads /proc/self/stat; on macOS/BSD falls back to ps %cpu
419
+ # which gives an instantaneous percentage instead (treated as fractional
420
+ # seconds over a 1-second window — good enough for a 30 s gauge).
421
+ def self.read_cpu_time_s(pid, linux)
422
+ if linux
423
+ stat = begin
424
+ File.read("/proc/self/stat")
425
+ rescue
426
+ return nil
427
+ end
428
+ # Format: pid (comm) state ppid ... utime stime ...
429
+ # comm can contain spaces — find last ')' and split from there.
430
+ after_comm = stat[stat.rindex(")").to_i + 1..]
431
+ return nil unless after_comm
432
+
433
+ fields = after_comm.split
434
+ # After ')': state(0) ppid(1) ... utime(11) stime(12)
435
+ utime = fields[11]&.to_i
436
+ stime = fields[12]&.to_i
437
+ return nil unless utime && stime
438
+
439
+ # Jiffies at 100 Hz → seconds
440
+ (utime + stime) / 100.0
441
+ else
442
+ # macOS/BSD: `ps` gives current CPU % directly.
443
+ # Return it as a fractional "seconds per second" proxy so the
444
+ # delta calculation above yields the right percentage.
445
+ out = `ps -o %cpu= -p #{Integer(pid)} 2>/dev/null`.strip
446
+ return nil if out.empty?
447
+ out.to_f / 100.0
448
+ end
449
+ end
352
450
  end
353
451
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Tracelit
4
- VERSION = "0.1.7"
4
+ VERSION = "0.1.8"
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tracelit
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.7
4
+ version: 0.1.8
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tracelit
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-05-01 00:00:00.000000000 Z
11
+ date: 2026-05-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: opentelemetry-sdk