diff --git a/.changesets/add-default-attributes-to-logger.md b/.changesets/add-default-attributes-to-logger.md new file mode 100644 index 000000000..fa0e4df62 --- /dev/null +++ b/.changesets/add-default-attributes-to-logger.md @@ -0,0 +1,12 @@ +--- +bump: patch +type: add +--- + +Allow for default attributes to be given when initialising a `Logger` instance: + +```ruby +order_logger = Appsignal::Logger.new("app", attributes: { order_id: 123 }) +``` + +All log lines reported by this logger will contain the given attribute. Attributes given when reporting the log line will be merged with the default attributes for the logger, with those in the log line taking priority. diff --git a/lib/appsignal/extension/jruby.rb b/lib/appsignal/extension/jruby.rb index 0da83ef3e..b97432d8a 100644 --- a/lib/appsignal/extension/jruby.rb +++ b/lib/appsignal/extension/jruby.rb @@ -80,7 +80,7 @@ def self.lib_extension # Logging methods attach_function :appsignal_log, - [:appsignal_string, :int32, :appsignal_string, :pointer], + [:appsignal_string, :int32, :int32, :appsignal_string, :pointer], :void # Transaction methods @@ -273,10 +273,11 @@ def get_server_state(key) make_ruby_string state if state[:len] > 0 end - def log(group, level, message, attributes) + def log(group, level, format, message, attributes) appsignal_log( make_appsignal_string(group), level, + format, make_appsignal_string(message), attributes.pointer ) diff --git a/lib/appsignal/logger.rb b/lib/appsignal/logger.rb index d882df21e..a0dc2fb4f 100644 --- a/lib/appsignal/logger.rb +++ b/lib/appsignal/logger.rb @@ -25,15 +25,18 @@ class Logger < ::Logger # Create a new logger instance # # @param group Name of the group for this logger. - # @param level Log level to filter with + # @param level Minimum log level to report. Log lines below this level will be ignored. + # @param format Format to use to parse log line attributes. + # @param attributes Default attributes for all log lines. # @return [void] - def initialize(group, level: INFO, format: PLAINTEXT) + def initialize(group, level: INFO, format: PLAINTEXT, attributes: {}) raise TypeError, "group must be a string" unless group.is_a? String @group = group @level = level @format = format @mutex = Mutex.new + @default_attributes = attributes end # We support the various methods in the Ruby @@ -156,8 +159,10 @@ def silence(_severity = ERROR, &block) private + attr_reader :default_attributes + def add_with_attributes(severity, message, group, attributes) - Thread.current[:appsignal_logger_attributes] = attributes + Thread.current[:appsignal_logger_attributes] = default_attributes.merge(attributes) add(severity, message, group) ensure Thread.current[:appsignal_logger_attributes] = nil diff --git a/spec/lib/appsignal/logger_spec.rb b/spec/lib/appsignal/logger_spec.rb index 8f0861f4f..56af7bdb3 100644 --- a/spec/lib/appsignal/logger_spec.rb +++ b/spec/lib/appsignal/logger_spec.rb @@ -182,6 +182,36 @@ end end + describe "a logger with default attributes" do + it "adds the attributes when a message is logged" do + logger = Appsignal::Logger.new("group", :attributes => { :some_key => "some_value" }) + + expect(Appsignal::Extension).to receive(:log).with("group", 6, 0, "Some message", + Appsignal::Utils::Data.generate({ :other_key => "other_value", :some_key => "some_value" })) + logger.error("Some message", { :other_key => "other_value" }) + end + + it "does not modify the original attribute hashes passed" do + default_attributes = { :some_key => "some_value" } + logger = Appsignal::Logger.new("group", :attributes => default_attributes) + + line_attributes = { :other_key => "other_value" } + logger.error("Some message", line_attributes) + + expect(default_attributes).to eq({ :some_key => "some_value" }) + expect(line_attributes).to eq({ :other_key => "other_value" }) + end + + it "prioritises line attributes over default attributes" do + logger = Appsignal::Logger.new("group", :attributes => { :some_key => "some_value" }) + + expect(Appsignal::Extension).to receive(:log).with("group", 6, 0, "Some message", + Appsignal::Utils::Data.generate({ :some_key => "other_value" })) + + logger.error("Some message", { :some_key => "other_value" }) + end + end + describe "#error with exception object" do it "logs the exception class and its message" do error =