diff --git a/lib/bugsnag/configuration.rb b/lib/bugsnag/configuration.rb index 050305b8..9c7ccbfa 100644 --- a/lib/bugsnag/configuration.rb +++ b/lib/bugsnag/configuration.rb @@ -283,8 +283,10 @@ def initialize # Set up logging self.logger = Logger.new(STDOUT) self.logger.level = Logger::INFO - self.logger.formatter = proc do |severity, datetime, progname, msg| - "** #{progname} #{datetime}: #{msg}\n" + self.logger.formatter = proc do |_severity, datetime, _progname, msg| + message = msg.to_s + message = message[PROG_NAME.length..-1].sub(/\A:?\s*/, "") if message.start_with?(PROG_NAME) + "** #{PROG_NAME} #{datetime}: #{message}\n" end # Configure the bugsnag middleware stack @@ -440,7 +442,7 @@ def clear_request_data # # @param message [String, #to_s] The message to log def info(message) - logger.info(PROG_NAME) { message } + logger.info(PROG_NAME) { format_log_message(message) } end ## @@ -448,7 +450,7 @@ def info(message) # # @param message [String, #to_s] The message to log def warn(message) - logger.warn(PROG_NAME) { message } + logger.warn(PROG_NAME) { format_log_message(message) } end ## @@ -456,7 +458,7 @@ def warn(message) # # @param message [String, #to_s] The message to log def error(message) - logger.error(PROG_NAME) { message } + logger.error(PROG_NAME) { format_log_message(message) } end ## @@ -464,7 +466,7 @@ def error(message) # # @param message [String, #to_s] The message to log def debug(message) - logger.debug(PROG_NAME) { message } + logger.debug(PROG_NAME) { format_log_message(message) } end ## @@ -764,6 +766,14 @@ def auto_track_sessions=(track_sessions) PROG_NAME = "[Bugsnag]" + def format_log_message(message) + message = message.to_s + return message if message.start_with?(PROG_NAME) + return PROG_NAME if message.empty? + + "#{PROG_NAME} #{message}" + end + def default_hostname # Send the heroku dyno name instead of hostname if available ENV["DYNO"] || Socket.gethostname; diff --git a/spec/breadcrumbs/on_breadcrumb_callback_list_spec.rb b/spec/breadcrumbs/on_breadcrumb_callback_list_spec.rb index 6011124a..1d81ebe1 100644 --- a/spec/breadcrumbs/on_breadcrumb_callback_list_spec.rb +++ b/spec/breadcrumbs/on_breadcrumb_callback_list_spec.rb @@ -246,8 +246,8 @@ def call(breadcrumb) message_index = 0 expected_messages = [ - /^Error occurred in on_breadcrumb callback: 'Oh no!'$/, - /^on_breadcrumb callback stacktrace:/ + /^\[Bugsnag\] Error occurred in on_breadcrumb callback: 'Oh no!'$/, + /^\[Bugsnag\] on_breadcrumb callback stacktrace:/ ] expect(logger).to have_received(:warn).with("[Bugsnag]").twice do |&block| diff --git a/spec/breadcrumbs/validator_spec.rb b/spec/breadcrumbs/validator_spec.rb index 99e0e101..44f2aea8 100644 --- a/spec/breadcrumbs/validator_spec.rb +++ b/spec/breadcrumbs/validator_spec.rb @@ -49,8 +49,8 @@ expect(breadcrumb.ignore?).to eq(true) - expect(Bugsnag.configuration.logger).to have_received(:debug) do |&block| - expect(block.call).to eq("Automatic breadcrumb of type #{type} ignored: #{name}") + expect(Bugsnag.configuration.logger).to have_received(:debug).with("[Bugsnag]") do |&block| + expect(block.call).to eq("[Bugsnag] Automatic breadcrumb of type #{type} ignored: #{name}") end end diff --git a/spec/bugsnag_spec.rb b/spec/bugsnag_spec.rb index 328b15a6..3b3437cc 100644 --- a/spec/bugsnag_spec.rb +++ b/spec/bugsnag_spec.rb @@ -64,11 +64,11 @@ end expected_messages = [ - /^Error in notify block: This is the error message$/, - /^Error in notify block stacktrace: \[/ + /^\[Bugsnag\] Error in notify block: This is the error message$/, + /^\[Bugsnag\] Error in notify block stacktrace: \[/ ].each - expect(Bugsnag.configuration.logger).to have_received(:warn).with('[Bugsnag]').twice do |&block| + expect(Bugsnag.configuration.logger).to have_received(:warn).with("[Bugsnag]").twice do |&block| expect(block.call).to match(expected_messages.next) end @@ -84,11 +84,11 @@ end expected_messages = [ - /^Error in internal notify block: This is an auto_notify error$/, - /^Error in internal notify block stacktrace: \[/ + /^\[Bugsnag\] Error in internal notify block: This is an auto_notify error$/, + /^\[Bugsnag\] Error in internal notify block stacktrace: \[/ ].each - expect(Bugsnag.configuration.logger).to have_received(:warn).with('[Bugsnag]').twice do |&block| + expect(Bugsnag.configuration.logger).to have_received(:warn).with("[Bugsnag]").twice do |&block| expect(block.call).to match(expected_messages.next) end diff --git a/spec/configuration_spec.rb b/spec/configuration_spec.rb index da2ac311..d184637c 100644 --- a/spec/configuration_spec.rb +++ b/spec/configuration_spec.rb @@ -364,8 +364,8 @@ describe "logger" do before do @output = StringIO.new - @formatter = proc do |severity, _datetime, progname, message| - "#{progname} #{severity}: #{message}" + @formatter = proc do |severity, _datetime, _progname, message| + "#{severity}: #{message}" end logger = Logger.new(@output) @@ -379,6 +379,47 @@ def output_lines @output.readlines.map(&:chomp) # old rubies don't support `readlines(chomp: true)` end + it "preserves the default log format" do + datetime = Time.utc(2026, 9, 6) + output = subject.logger.formatter.call( + "INFO", datetime, nil, "[Bugsnag] Info message" + ) + + expect(output).to eq("** [Bugsnag] #{datetime}: Info message\n") + expect(subject.logger.formatter.call("INFO", datetime, nil, "[Bugsnag]: Info message")).to eq( + "** [Bugsnag] #{datetime}: Info message\n" + ) + expect(subject.logger.formatter.call("INFO", datetime, nil, "[Bugsnag]")).to eq( + "** [Bugsnag] #{datetime}: \n" + ) + end + + it "sets the Bugsnag progname on the logger as well as the message" do + logger = double("logger") + subject.logger = logger + + %i[info warn error debug].each do |level| + expect(logger).to receive(level).with("[Bugsnag]") do |_progname, &block| + expect(block.call).to eq("[Bugsnag] #{level} message") + end + + subject.public_send(level, "#{level} message") + end + end + + it "does not leave a trailing space when the message is empty" do + logger = double("logger") + subject.logger = logger + + [nil, ""].each do |message| + expect(logger).to receive(:info).with("[Bugsnag]") do |_progname, &block| + expect(block.call).to eq("[Bugsnag]") + end + + subject.info(message) + end + end + context "using configure" do before do Bugsnag.configuration.api_key = nil @@ -398,7 +439,7 @@ def output_lines expect(output_lines.length).to be(1) expect(output_lines.first).to eq( - '[Bugsnag] WARN: No valid API key has been set, notifications will not be sent' + 'WARN: [Bugsnag] No valid API key has been set, notifications will not be sent' ) end @@ -407,7 +448,7 @@ def output_lines expect(output_lines.length).to be(1) expect(output_lines.first).to eq( - '[Bugsnag] WARN: No valid API key has been set, notifications will not be sent' + 'WARN: [Bugsnag] No valid API key has been set, notifications will not be sent' ) end end @@ -428,7 +469,7 @@ def output_lines expect(output_lines.length).to be(1) expect(output_lines.first).to eq( - '[Bugsnag] WARN: No valid API key has been set, notifications will not be sent' + 'WARN: [Bugsnag] No valid API key has been set, notifications will not be sent' ) end @@ -457,7 +498,7 @@ def output_lines Bugsnag.configuration.info("Info message") expect(output_lines.length).to be(1) - expect(output_lines.first).to eq('[Bugsnag] INFO: Info message') + expect(output_lines.first).to eq('INFO: [Bugsnag] Info message') end it "should log warning messages to the set logger" do @@ -466,7 +507,7 @@ def output_lines Bugsnag.configuration.warn("Warning message") expect(output_lines.length).to be(1) - expect(output_lines.first).to eq('[Bugsnag] WARN: Warning message') + expect(output_lines.first).to eq('WARN: [Bugsnag] Warning message') end it "should log error messages to the set logger" do @@ -475,7 +516,7 @@ def output_lines Bugsnag.configuration.error("Error message") expect(output_lines.length).to be(1) - expect(output_lines.first).to eq('[Bugsnag] ERROR: Error message') + expect(output_lines.first).to eq('ERROR: [Bugsnag] Error message') end it "should log debug messages to the set logger" do @@ -484,7 +525,14 @@ def output_lines Bugsnag.configuration.debug("Debug message") expect(output_lines.length).to be(1) - expect(output_lines.first).to eq('[Bugsnag] DEBUG: Debug message') + expect(output_lines.first).to eq('DEBUG: [Bugsnag] Debug message') + end + + it "does not duplicate an existing Bugsnag prefix" do + Bugsnag.configuration.info("[Bugsnag] Info message") + + expect(output_lines.length).to be(1) + expect(output_lines.first).to eq('INFO: [Bugsnag] Info message') end end diff --git a/spec/integrations/logger_spec.rb b/spec/integrations/logger_spec.rb index 6fbe49e4..7b444863 100644 --- a/spec/integrations/logger_spec.rb +++ b/spec/integrations/logger_spec.rb @@ -8,7 +8,7 @@ end context 'in a Rails app' do - key_warning = '[Bugsnag]: No valid API key has been set, notifications will not be sent' + key_warning = '[Bugsnag] No valid API key has been set, notifications will not be sent' is_jruby = defined?(RUBY_ENGINE) && RUBY_ENGINE == 'jruby' incompatible = (RUBY_VERSION < '2.0.0') || is_jruby diff --git a/spec/on_error_spec.rb b/spec/on_error_spec.rb index 798befd1..017f5697 100644 --- a/spec/on_error_spec.rb +++ b/spec/on_error_spec.rb @@ -199,7 +199,7 @@ expect(called_count).to be(2) expect(logger).to have_received(:debug).with("[Bugsnag]") do |&block| - expect(block.call).to eq("Not notifying RuntimeError due to ignore being signified in user provided middleware") + expect(block.call).to eq("[Bugsnag] Not notifying RuntimeError due to ignore being signified in user provided middleware") end end @@ -257,8 +257,8 @@ message_index = 0 expected_messages = [ - /^Error occurred in on_error callback: 'bad things'$/, - /^on_error callback stacktrace:/ + /^\[Bugsnag\] Error occurred in on_error callback: 'bad things'$/, + /^\[Bugsnag\] on_error callback stacktrace:/ ] expect(logger).to have_received(:warn).with("[Bugsnag]").twice do |&block| diff --git a/spec/session_tracker_spec.rb b/spec/session_tracker_spec.rb index 81bcb657..9e646c37 100644 --- a/spec/session_tracker_spec.rb +++ b/spec/session_tracker_spec.rb @@ -117,6 +117,21 @@ expect(payload["sessionCounts"].size).to eq(1) end + it 'attributes invalid API key delivery failures to Bugsnag' do + output = StringIO.new + logger = Logger.new(output) + logger.formatter = proc do |_severity, _datetime, _progname, message| + message + end + Bugsnag.configuration.logger = logger + allow(Bugsnag.configuration).to receive(:valid_api_key?).and_return(false) + + Bugsnag.start_session + Bugsnag.session_tracker.send_sessions + + expect(output.string).to eq('[Bugsnag] Not delivering sessions due to an invalid api_key') + end + it 'sets details from config' do Bugsnag.configure do |conf| conf.auto_capture_sessions = true