Skip to content

Commit d698b00

Browse files
committed
wip - add Logging
1 parent ca21b65 commit d698b00

5 files changed

Lines changed: 194 additions & 1 deletion

File tree

sentry-ruby/lib/sentry-ruby.rb

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
require "sentry/utils/encoding_helper"
1212
require "sentry/utils/logging_helper"
1313
require "sentry/configuration"
14-
require "sentry/logger"
14+
require "sentry/logging"
1515
require "sentry/event"
1616
require "sentry/error_event"
1717
require "sentry/transaction_event"
@@ -94,6 +94,10 @@ def exception_locals_tp
9494
# @return [Metrics::Aggregator, nil]
9595
attr_reader :metrics_aggregator
9696

97+
# @!attribute [r] logger
98+
# @return [Logging::Device]
99+
attr_reader :logger
100+
97101
# @!attribute [r] logger
98102
# @return [Logger]
99103
# @!visibility private
@@ -244,6 +248,9 @@ def init(&block)
244248
config = Configuration.new
245249
yield(config) if block_given?
246250

251+
# Public-facing Structured Logger
252+
@logger = Logging.setup_logger(config)
253+
247254
# Internal SDK logger
248255
@sdk_logger = config.sdk_logger
249256

sentry-ruby/lib/sentry/logging.rb

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# frozen_string_literal: true
2+
3+
require_relative "logger"
4+
require_relative "logging/device"
5+
require_relative "logging/handler"
6+
7+
module Sentry
8+
module Logging
9+
def self.setup_logger(config)
10+
if config._experiments[:enable_logs]
11+
Device.new(handlers: [config.logger, Handler.new(config)])
12+
end
13+
end
14+
end
15+
end
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
# frozen_string_literal: true
2+
3+
module Sentry
4+
module Logging
5+
class Device
6+
attr_reader :handlers
7+
8+
def initialize(options)
9+
@handlers = options.fetch(:handlers)
10+
end
11+
12+
def trace(message, payload = {})
13+
log(:trace, message, payload)
14+
end
15+
16+
def debug(message, payload = {})
17+
log(:debug, message, payload)
18+
end
19+
20+
def info(message, payload = {})
21+
log(:info, message, payload)
22+
end
23+
24+
def warn(message, payload = {})
25+
log(:warn, message, payload)
26+
end
27+
28+
def error(message, payload = {})
29+
log(:error, message, payload)
30+
end
31+
32+
def fatal(message, payload = {})
33+
log(:fatal, message, payload)
34+
end
35+
36+
def log(level, message, payload)
37+
handlers.each do |handler|
38+
case handler
39+
when Sentry::Logger
40+
handler.public_send(level, message)
41+
else
42+
handler.public_send(level, message, payload)
43+
end
44+
end
45+
end
46+
end
47+
end
48+
end
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
# frozen_string_literal: true
2+
3+
module Sentry
4+
module Logging
5+
class Handler
6+
# https://develop.sentry.dev/sdk/telemetry/logs/#log-severity-number
7+
LEVELS = {
8+
"trace" => 1,
9+
"debug" => 5,
10+
"info" => 9,
11+
"warn" => 13,
12+
"error" => 17,
13+
"fatal" => 21
14+
}.freeze
15+
16+
attr_reader :config
17+
18+
def initialize(config)
19+
@config = config
20+
end
21+
22+
def trace(message, payload = {})
23+
log(:trace, message, payload)
24+
end
25+
26+
def debug(message, payload = {})
27+
log(:debug, message, payload)
28+
end
29+
30+
def info(message, payload = {})
31+
log(:info, message, payload)
32+
end
33+
34+
def warn(message, payload = {})
35+
log(:warn, message, payload)
36+
end
37+
38+
def error(message, payload = {})
39+
log(:error, message, payload)
40+
end
41+
42+
def fatal(message, payload = {})
43+
log(:fatal, message, payload)
44+
end
45+
46+
def log(level, message, payload)
47+
Sentry.capture_log(message, level: level, severity: LEVELS[level], **payload)
48+
end
49+
end
50+
end
51+
end
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
# frozen_string_literal: true
2+
3+
require "spec_helper"
4+
5+
RSpec.describe Sentry::Logging do
6+
let(:default_logger) { Sentry::Logger.new(output) }
7+
let(:output) { StringIO.new }
8+
9+
def expect_log(level, message)
10+
yield(message)
11+
expect(output.string).to include(level.upcase)
12+
expect(output.string).to include(message)
13+
end
14+
15+
context "when log events are not enabled" do
16+
before do
17+
perform_basic_setup do |config|
18+
config.logger = default_logger
19+
end
20+
end
21+
22+
it "logger is not set up" do
23+
expect(Sentry.logger).to be_nil
24+
end
25+
end
26+
27+
context "when log events are enabled" do
28+
before do
29+
perform_basic_setup do |config|
30+
config.max_log_events = 1
31+
config.logger = default_logger
32+
config._experiments = { enable_logs: true }
33+
end
34+
end
35+
36+
let(:logs) do
37+
Sentry.get_current_client.log_event_buffer.pending_events
38+
end
39+
40+
# TODO: At the moment the Sentry::Logger enforces info - is that intentional?
41+
["info", "warn", "error", "fatal"].each do |level|
42+
describe "##{level}" do
43+
it "logs using default logger and LogEvent logger" do
44+
expect_log(level, "Hello World") { |msg| Sentry.logger.public_send(level, msg) }
45+
46+
expect(logs).to_not be_empty
47+
48+
log_event = logs.last
49+
50+
expect(log_event.type).to eql("log")
51+
expect(log_event.level).to eql(level.to_sym)
52+
expect(log_event.body).to eql("Hello World")
53+
end
54+
55+
it "logs using default logger and LogEvent logger with extra attributes" do
56+
payload = { user_id: 123, action: "create" }
57+
58+
expect_log(level, "Hello World") { |msg| Sentry.logger.public_send(level, msg, payload) }
59+
60+
expect(logs).to_not be_empty
61+
62+
log_event = logs.last
63+
64+
expect(log_event.type).to eql("log")
65+
expect(log_event.level).to eql(level.to_sym)
66+
expect(log_event.body).to eql("Hello World")
67+
expect(log_event.attributes).to include(payload)
68+
end
69+
end
70+
end
71+
end
72+
end

0 commit comments

Comments
 (0)