Skip to content
This repository has been archived by the owner on Dec 8, 2020. It is now read-only.

Commit

Permalink
Use new custom event and context structures
Browse files Browse the repository at this point in the history
  • Loading branch information
binarylogic committed Feb 19, 2017
1 parent 025c59f commit 791da67
Show file tree
Hide file tree
Showing 10 changed files with 104 additions and 21 deletions.
8 changes: 5 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -133,12 +133,13 @@ Timber will never deviate from the public `::Logger` interface in *any* way.
1. Log a structured Hash (simplest)

```ruby
Logger.warn message: "Payment rejected", type: :payment_rejected,
data: {customer_id: "abcd1234", amount: 100, reason: "Card expired"}
Logger.warn message: "Payment rejected", payment_rejected: {customer_id: "abcd1234", amount: 100, reason: "Card expired"}

# Payment rejected @metadata {"level": "warn", "event": {"payment_rejected": {"customer_id": "abcd1234", "amount": 100, "reason": "Card expired"}}, "context": {...}}
```

* The hash can *only* have a `:message` and "event type" key, where `:payment_rejected` is the event type in the above example.

2. Log a Struct (recommended)

Defining structs for your important events just feels oh so good :) It creates a strong contract
Expand Down Expand Up @@ -190,7 +191,7 @@ value.
1. Add a Hash (simplest)

```ruby
Timber::CurrentContext.with({type: :build, data: {version: "1.0.0"}}) do
Timber::CurrentContext.with({build: {version: "1.0.0"}}) do
logger.info("My log message")
end

Expand Down Expand Up @@ -219,6 +220,7 @@ value.
</p></details>



## Installation

```ruby
Expand Down
15 changes: 9 additions & 6 deletions lib/timber/contexts.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,21 +12,24 @@ module Contexts
# Protocol for casting objects into a `Timber::Context`.
#
# @example Casting a hash
# Timber::Contexts.build({type: :build, data: {version: "1.0.0"}})
# Timber::Contexts.build(deploy: {version: "1.0.0"})
def self.build(obj)
if obj.is_a?(::Timber::Context)
obj
elsif obj.respond_to?(:to_timber_context)
obj.to_timber_context
elsif obj.is_a?(Hash) && obj.key?(:type) && obj.key?(:data)
elsif obj.is_a?(Hash) && obj.length == 1
type = obj.keys.first
data = obj.values.first

Contexts::Custom.new(
type: obj[:type],
data: obj[:data]
type: type,
data: data
)
elsif obj.is_a?(Struct) && obj.respond_to?(:type)
Events::Custom.new(
Contexts::Custom.new(
type: obj.type,
data: obj.respond_to?(:hash) ? obj.hash : obj.to_h # ruby 1.9.3 does not have to_h
data: obj.respond_to?(:to_h) ? obj.to_h : Timber::Util::Struct.to_hash(obj) # ruby 1.9.3 does not have to_h
)
else
nil
Expand Down
14 changes: 11 additions & 3 deletions lib/timber/current_context.rb
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,17 @@ def hash(*args)
# @note Because context is included with every log line, it is recommended that you limit this
# to only neccessary data.
#
# @example Adding a custom context
# custom_context = Timber::Contexts::Custom.new(type: :organization, data: %{id: 1, name: "Timber"})
# Timber::CurrentContext.with(custom_context) do
# @example Adding a custom context with a map
# Timber::CurrentContext.with({build: {version: "1.0.0"}}) do
# # ... anything logged here will include the context ...
# end
#
# @example Adding a custom context with a struct
# BuildContext = Struct.new(:version) do
# def type; :build; end
# end
# build_context = BuildContext.new("1.0.0")
# Timber::CurrentContext.with(build_context) do
# # ... anything logged here will include the context ...
# end
# # Be sure to checkout Timber::Contexts! These are officially supported and many of these
Expand Down
12 changes: 8 additions & 4 deletions lib/timber/events.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,21 @@ def self.build(obj)
obj
elsif obj.respond_to?(:to_timber_event)
obj.to_timber_event
elsif obj.is_a?(Hash) && obj.key?(:message) && obj.key?(:type) && obj.key?(:data)
elsif obj.is_a?(Hash) && obj.key?(:message) && obj.length == 2
event = obj.select { |k,v| k != :message }
type = event.keys.first
data = event.values.first

Events::Custom.new(
type: obj[:type],
type: type,
message: obj[:message],
data: obj[:data]
data: data
)
elsif obj.is_a?(Struct) && obj.respond_to?(:message) && obj.respond_to?(:type)
Events::Custom.new(
type: obj.type,
message: obj.message,
data: obj.respond_to?(:hash) ? obj.hash : obj.to_h # ruby 1.9.3 does not have to_h
data: obj.respond_to?(:to_h) ? obj.to_h : Timber::Util::Struct.to_hash(obj) # ruby 1.9.3 does not have to_h :(
)
else
nil
Expand Down
4 changes: 2 additions & 2 deletions lib/timber/logger.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ module Timber
# logger.info "Payment rejected for customer #{customer_id}"
#
# @example Using a Hash
# # The :message, :type, and :data keys are required
# # The :message key is required, the other additional key is your event type and data
# # :type is the namespace used in timber for the :data
# logger.info message: "Payment rejected", type: :payment_rejected, data: {customer_id: customer_id, amount: 100}
# logger.info message: "Payment rejected", payment_rejected: {customer_id: customer_id, amount: 100}
#
# @example Using a Struct (a simple, more structured way, to define events)
# PaymentRejectedEvent = Struct.new(:customer_id, :amount, :reason) do
Expand Down
1 change: 1 addition & 0 deletions lib/timber/util.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
require "timber/util/active_support_log_subscriber"
require "timber/util/hash"
require "timber/util/struct"

module Timber
# @private
Expand Down
16 changes: 16 additions & 0 deletions lib/timber/util/struct.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
module Timber
module Util
# @private
module Struct
extend self

def to_hash(struct)
h = {}
struct.each_pair do |k ,v|
h[k] = v
end
h
end
end
end
end
49 changes: 49 additions & 0 deletions spec/timber/contexts_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
require "spec_helper"

describe Timber::Contexts, :rails_23 => true do
describe ".build" do
it "should build a Timber::Context" do
context = Timber::Contexts::Custom.new(
type: :build,
data: {version: "1.0.0"}
)
built_context = Timber::Contexts.build(context)
expect(built_context).to eq(context)
end

it "should use #to_timber_context" do
BuildContext = Struct.new(:version) do
def to_timber_context
Timber::Contexts::Custom.new(
type: :build,
data: respond_to?(:to_h) ? to_h : Timber::Util::Struct.to_hash(self)
)
end
end
built_context = Timber::Contexts.build(BuildContext.new("1.0.0"))
expect(built_context).to be_kind_of(Timber::Contexts::Custom)
expect(built_context.type).to eq(:build)
Object.send(:remove_const, :BuildContext)
end

it "should accept a properly structured hash" do
built_context = Timber::Contexts.build(build: {version: "1.0.0"})
expect(built_context).to be_kind_of(Timber::Contexts::Custom)
expect(built_context.type).to eq(:build)
end

it "should accept a struct" do
BuildContext = Struct.new(:version) do
def type; :build; end
end
built_context = Timber::Contexts.build(BuildContext.new("1.0.0"))
expect(built_context).to be_kind_of(Timber::Contexts::Custom)
expect(built_context.type).to eq(:build)
Object.send(:remove_const, :BuildContext)
end

it "should return nil for unsupported" do
expect(Timber::Contexts.build(1)).to be_nil
end
end
end
2 changes: 1 addition & 1 deletion spec/timber/events_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ def to_timber_event
end

it "should accept a properly structured hash" do
built_event = Timber::Events.build({type: :payment_rejected, message: "Payment rejected", data: {customer_id: "abcd1234", amount: 100}})
built_event = Timber::Events.build({message: "Payment rejected", payment_rejected: {customer_id: "abcd1234", amount: 100}})
expect(built_event).to be_kind_of(Timber::Events::Custom)
expect(built_event.type).to eq(:payment_rejected)
expect(built_event.message).to eq("Payment rejected")
Expand Down
4 changes: 2 additions & 2 deletions spec/timber/logger_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@
end

it "should call and use Timber::Events.build" do
message = {message: "payment rejected", type: :payment_rejected, data: {customer_id: "abcde1234", amount: 100}}
message = {message: "payment rejected", payment_rejected: {customer_id: "abcde1234", amount: 100}}
expect(Timber::Events).to receive(:build).with(message).and_call_original
logger.info(message)
expect(io.string).to start_with("payment rejected @timber.io {\"level\":\"info\",\"dt\":\"2016-09-01T12:00:00.000000Z\",")
Expand All @@ -59,7 +59,7 @@

it "should allow functions" do
logger.info do
{message: "payment rejected", type: :payment_rejected, data: {customer_id: "abcde1234", amount: 100}}
{message: "payment rejected", payment_rejected: {customer_id: "abcde1234", amount: 100}}
end
expect(io.string).to start_with("payment rejected @timber.io {\"level\":\"info\",\"dt\":\"2016-09-01T12:00:00.000000Z\",")
expect(io.string).to include("\"event\":{\"server_side_app\":{\"custom\":{\"payment_rejected\":{\"customer_id\":\"abcde1234\",\"amount\":100}}}}")
Expand Down

0 comments on commit 791da67

Please sign in to comment.