From aaa6d02bdecb4c84e2e8b84b7d58da24d18544f1 Mon Sep 17 00:00:00 2001
From: y8 <y8@ya.ru>
Date: Sat, 20 Sep 2014 21:26:28 +0200
Subject: [PATCH 01/11] Log formatting and new ShellFormat

to make bloody logs pastable to mongo shell.
---
 lib/moped/log_format.rb                       | 157 +++++++++++++++++
 lib/moped/log_format/default_format.rb        | 159 ++++++++++++++++++
 lib/moped/log_format/shell_format.rb          | 139 +++++++++++++++
 lib/moped/log_format/shell_format/command.rb  |  71 ++++++++
 lib/moped/log_format/shell_format/core_ext.rb |   7 +
 .../core_ext/active_support/json/encoding.rb  |  25 +++
 .../shell_format/core_ext/bson/object_id.rb   |  26 +++
 .../shell_format/core_ext/regexp.rb           |  26 +++
 .../log_format/shell_format/core_ext/time.rb  |  26 +++
 .../core_ext/unsafe_json_string.rb            |   5 +
 lib/moped/log_format/shell_format/delete.rb   |  19 +++
 lib/moped/log_format/shell_format/insert.rb   |  34 ++++
 lib/moped/log_format/shell_format/query.rb    | 116 +++++++++++++
 .../log_format/shell_format/shellable.rb      |  79 +++++++++
 lib/moped/log_format/shell_format/update.rb   |  34 ++++
 lib/moped/log_format/utils/color_string.rb    |  86 ++++++++++
 lib/moped/loggable.rb                         |  41 ++++-
 lib/moped/protocol/command.rb                 |   3 +-
 lib/moped/protocol/delete.rb                  |   4 +-
 lib/moped/protocol/get_more.rb                |   3 +-
 lib/moped/protocol/insert.rb                  |   4 +-
 lib/moped/protocol/kill_cursors.rb            |   4 +-
 lib/moped/protocol/query.rb                   |  14 +-
 lib/moped/protocol/update.rb                  |   7 +-
 24 files changed, 1050 insertions(+), 39 deletions(-)
 create mode 100644 lib/moped/log_format.rb
 create mode 100644 lib/moped/log_format/default_format.rb
 create mode 100644 lib/moped/log_format/shell_format.rb
 create mode 100644 lib/moped/log_format/shell_format/command.rb
 create mode 100644 lib/moped/log_format/shell_format/core_ext.rb
 create mode 100644 lib/moped/log_format/shell_format/core_ext/active_support/json/encoding.rb
 create mode 100644 lib/moped/log_format/shell_format/core_ext/bson/object_id.rb
 create mode 100644 lib/moped/log_format/shell_format/core_ext/regexp.rb
 create mode 100644 lib/moped/log_format/shell_format/core_ext/time.rb
 create mode 100644 lib/moped/log_format/shell_format/core_ext/unsafe_json_string.rb
 create mode 100644 lib/moped/log_format/shell_format/delete.rb
 create mode 100644 lib/moped/log_format/shell_format/insert.rb
 create mode 100644 lib/moped/log_format/shell_format/query.rb
 create mode 100644 lib/moped/log_format/shell_format/shellable.rb
 create mode 100644 lib/moped/log_format/shell_format/update.rb
 create mode 100644 lib/moped/log_format/utils/color_string.rb

diff --git a/lib/moped/log_format.rb b/lib/moped/log_format.rb
new file mode 100644
index 0000000..067034a
--- /dev/null
+++ b/lib/moped/log_format.rb
@@ -0,0 +1,157 @@
+require "moped/log_format/utils/color_string"
+
+module Moped
+  module LogFormat
+    # Get colorize flag
+    #
+    # @example Get colorize flag.
+    #   DefaultFormat.colorize
+    #
+    # @return [ true | false ] Status of colorization.
+    #
+    # @since 2.0.0
+    def colorize
+      @@colorize
+    end
+
+    # Set colorize flag
+    #
+    # @example Disable colorization.
+    #   DefaultFormat.colorize = false
+    #
+    # @param [ true | false ] flag Flag to set
+    #
+    # @since 2.0.0
+    def colorize=(flag)
+      @@colorize = !!flag
+    end
+
+    # Format the provided operations log entry.
+    #
+    # @example Log the operations.
+    #   Default.log("MOPED", {}, 30)
+    #
+    # @param [ String ] prefix The prefix for all operations in the log.
+    # @param [ Array ] ops The operations.
+    # @param [ String ] runtime The runtime in formatted ms.
+    #
+    # @return [ String ] Formatted string
+    #
+    # @since 2.0.0
+    def log(prefix, payload, runtime = nil)
+      raise NotImplemented
+    end
+
+    # Format the command event
+    #
+    # @example Format command.
+    #   Default.command(event)
+    #
+    # @param [ Moped::Protocol::Command ] event Command event to format
+    #
+    # @return [ String ] Formatted string
+    #
+    # @since 2.0.0
+    def command(event)
+      raise NotImplemented
+    end
+
+    # Format the insert event
+    #
+    # @example Format insert.
+    #   Default.insert(event)
+    #
+    # @param [ Moped::Protocol::Insert ] event Insert event to format
+    #
+    # @return [ String ] Formatted string
+    #
+    # @since 2.0.0
+    def insert(event)
+      raise NotImplemented
+    end
+
+    # Format the query event
+    #
+    # @example Format query.
+    #   Default.query(event)
+    #
+    # @param [ Moped::Protocol::Query ] event Query event to format
+    #
+    # @return [ String ] Formatted string
+    #
+    # @since 2.0.0
+    def query(event)
+      raise NotImplemented
+    end
+
+    # Format the query event
+    #
+    # @example Format query.
+    #   Default.query(event)
+    #
+    # @param [ Moped::Protocol::Query ] event Query event to format
+    #
+    # @return [ String ] Formatted string
+    #
+    # @since 2.0.0
+    def update(event)
+      raise NotImplemented
+    end
+
+    # Format the delete event
+    #
+    # @example Format delete.
+    #   Default.delete(event)
+    #
+    # @param [ Moped::Protocol::Delete ] event Delete event to format
+    #
+    # @return [ String ] Formatted string
+    #
+    # @since 2.0.0
+    def delete(event)
+      raise NotImplemented
+    end
+
+    # Format the cursor.next() event
+    #
+    # @example Format get_more.
+    #   Default.get_more(event)
+    #
+    # @param [ Moped::Protocol::GetMore ] event GetMore event to format
+    #
+    # @return [ String ] Formatted string
+    #
+    # @since 2.0.0
+    def get_more(event)
+      raise NotImplemented
+    end
+
+    # Format the cursor.close() event
+    #
+    # @example Format kill_cursors.
+    #   Default.kill_cursors(event)
+    #
+    # @param [ Moped::Protocol::KillCursors ] event KillCursors event to format
+    #
+    # @return [ String ] Formatted string
+    #
+    # @since 2.0.0
+    def kill_cursors(event)
+      raise NotImplemented
+    end
+
+    # Format the cursor.close() event
+    #
+    # @example Format kill_cursors.
+    #   Default.kill_cursors(event)
+    #
+    # @param [ Moped::Protocol::KillCursors ] event KillCursors event to format
+    #
+    # @return [ ColorString ] ColorString
+    #
+    # @since 2.0.0
+    def color(string)
+      ColorString.new(string, self.colorize)
+    end
+  end
+end
\ No newline at end of file
diff --git a/lib/moped/log_format/default_format.rb b/lib/moped/log_format/default_format.rb
new file mode 100644
index 0000000..5e7154e
--- /dev/null
+++ b/lib/moped/log_format/default_format.rb
@@ -0,0 +1,159 @@
+require "moped/log_format"
+
+module Moped
+  module LogFormat
+    class DefaultFormat
+      extend LogFormat
+
+      self.colorize = true
+
+      # Format the provided operations log entry.
+      #
+      # @example Log the operations.
+      #   Default.log("MOPED", {}, 30)
+      #
+      # @param [ String ] prefix The prefix for all operations in the log.
+      # @param [ Array ] ops The operations.
+      # @param [ String ] runtime The runtime in formatted ms.
+      #
+      # @return [ String ] Formatted string
+      #
+      # @since 2.0.0
+      def self.log(prefix, payload, runtime = nil)
+        [
+          color(prefix).magenta.bold,
+          payload,
+          color("runtime: #{runtime}").blue
+        ].join(' ')
+      end
+
+      # Format the command event
+      #
+      # @example Format command.
+      #   Default.command(event)
+      #
+      # @param [ Moped::Protocol::Command ] event Command event to format
+      #
+      # @return [ String ] Formatted string
+      #
+      # @since 2.0.0
+      def self.command(event)
+        type = color("COMMAND").bold
+        "%-12s database=%s command=%s" % [type, event.database, event.selector.inspect]
+      end
+
+      # Format the insert event
+      #
+      # @example Format insert.
+      #   Default.insert(event)
+      #
+      # @param [ Moped::Protocol::Insert ] event Insert event to format
+      #
+      # @return [ String ] Formatted string
+      #
+      # @since 2.0.0
+      def self.insert(event)
+        type = color("INSERT").bold
+
+        "%-12s database=%s collection=%s documents=%s flags=%s" % [type, event.database,
+                                                                   event.collection, event.documents.inspect,
+                                                                   event.flags.inspect]
+      end
+
+      # Format the query event
+      #
+      # @example Format query.
+      #   Default.query(event)
+      #
+      # @param [ Moped::Protocol::Query ] event Query event to format
+      #
+      # @return [ String ] Formatted string
+      #
+      # @since 2.0.0
+      def self.query(event)
+        type = color("QUERY").bold
+        fields = []
+        fields << ["%-12s", type]
+        fields << ["database=%s", event.database]
+        fields << ["collection=%s", event.collection]
+        fields << ["selector=%s", event.selector.inspect]
+        fields << ["flags=%s", event.flags.inspect]
+        fields << ["limit=%s", event.limit.inspect]
+        fields << ["skip=%s", event.skip.inspect]
+        fields << ["batch_size=%s", event.batch_size.inspect]
+        fields << ["fields=%s", event.fields.inspect]
+        f, v = fields.transpose
+        f.join(" ") % v
+      end
+
+      # Format the query event
+      #
+      # @example Format query.
+      #   Default.query(event)
+      #
+      # @param [ Moped::Protocol::Query ] event Query event to format
+      #
+      # @return [ String ] Formatted string
+      #
+      # @since 2.0.0
+      def self.update(event)
+        type = color("UPDATE").bold
+
+        "%-12s database=%s collection=%s selector=%s update=%s flags=%s" % [type, event.database,
+                                                                            event.collection,
+                                                                            event.selector.inspect,
+                                                                            event.update.inspect,
+                                                                            event.flags.inspect]
+      end
+
+      # Format the delete event
+      #
+      # @example Format delete.
+      #   Default.delete(event)
+      #
+      # @param [ Moped::Protocol::Delete ] event Delete event to format
+      #
+      # @return [ String ] Formatted string
+      #
+      # @since 2.0.0
+      def self.delete(event)
+        type = color("DELETE").bold
+
+        "%-12s database=%s collection=%s selector=%s flags=%s" % [type, event.database, event.collection,
+                                                                  event.selector.inspect, event.flags.inspect]
+      end
+
+      # Format the cursor.next() event
+      #
+      # @example Format get_more.
+      #   Default.get_more(event)
+      #
+      # @param [ Moped::Protocol::GetMore ] event GetMore event to format
+      #
+      # @return [ String ] Formatted string
+      #
+      # @since 2.0.0
+      def self.get_more(event)
+        type = color("GET_MORE").bold
+        "%-12s database=%s collection=%s limit=%s cursor_id=%s" % [type, event.database,
+                                                                   event.collection, event.limit,
+                                                                   event.cursor_id]
+      end
+
+      # Format the cursor.close() event
+      #
+      # @example Format kill_cursors.
+      #   Default.kill_cursors(event)
+      #
+      # @param [ Moped::Protocol::KillCursors ] event KillCursors event to format
+      #
+      # @return [ String ] Formatted string
+      #
+      # @since 2.0.0
+      def self.kill_cursors(event)
+        type = color("KILL_CURSORS").bold
+        "%-12s cursor_ids=%s" % [type, event.cursor_ids.inspect]
+      end
+    end
+  end
+end
\ No newline at end of file
diff --git a/lib/moped/log_format/shell_format.rb b/lib/moped/log_format/shell_format.rb
new file mode 100644
index 0000000..1f86dc0
--- /dev/null
+++ b/lib/moped/log_format/shell_format.rb
@@ -0,0 +1,139 @@
+require "moped/log_format/shell_format/core_ext/unsafe_json_string"
+require "moped/log_format/shell_format/core_ext/active_support/json/encoding"
+require "moped/log_format/shell_format/core_ext/bson/object_id"
+require "moped/log_format/shell_format/core_ext/regexp"
+require "moped/log_format/shell_format/core_ext/time"
+
+require "moped/log_format/shell_format/command"
+require "moped/log_format/shell_format/query"
+require "moped/log_format/shell_format/insert"
+require "moped/log_format/shell_format/update"
+require "moped/log_format/shell_format/delete"
+
+module Moped
+  module LogFormat
+    class ShellFormat
+      extend LogFormat
+
+      # Format the provided operations log entry.
+      #
+      # @example Log the operations.
+      #   Default.log("MOPED", {}, 30)
+      #
+      # @param [ String ] prefix The prefix for all operations in the log.
+      # @param [ Array ] ops The operations.
+      # @param [ String ] runtime The runtime in formatted ms.
+      #
+      # @return [ String ] Formatted string
+      #
+      # @since 2.0.0
+      def self.log(prefix, payload, runtime = nil)
+        line = []
+
+        line << color(prefix).magenta.bold
+        line << payload
+        line << color("runtime: #{runtime}").blue if runtime
+
+        line.join(' ')
+      end
+
+      # Format the command event
+      #
+      # @example Format command.
+      #   Default.command(event)
+      #
+      # @param [ Moped::Protocol::Command ] event Command event to format
+      #
+      # @return [ String ] Formatted string
+      #
+      # @since 2.0.0
+      def self.command(event)
+        ShellFormat::Command.format event
+      end
+
+      # Format the insert event
+      #
+      # @example Format insert.
+      #   Default.insert(event)
+      #
+      # @param [ Moped::Protocol::Insert ] event Insert event to format
+      #
+      # @return [ String ] Formatted string
+      #
+      # @since 2.0.0
+      def self.insert(event)
+        ShellFormat::Insert.format event
+      end
+
+      # Format the query event
+      #
+      # @example Format query.
+      #   Default.query(event)
+      #
+      # @param [ Moped::Protocol::Query ] event Query event to format
+      #
+      # @return [ String ] Formatted string
+      #
+      # @since 2.0.0
+      def self.query(event)
+        ShellFormat::Query.format event
+      end
+
+      # Format the query event
+      #
+      # @example Format query.
+      #   Default.query(event)
+      #
+      # @param [ Moped::Protocol::Query ] event Query event to format
+      #
+      # @return [ String ] Formatted string
+      #
+      # @since 2.0.0
+      def self.update(event)
+        ShellFormat::Update.format event
+      end
+
+      # Format the delete event
+      #
+      # @example Format delete.
+      #   Default.delete(event)
+      #
+      # @param [ Moped::Protocol::Delete ] event Delete event to format
+      #
+      # @return [ String ] Formatted string
+      #
+      # @since 2.0.0
+      def self.delete(event)
+        ShellFormat::Delete.format event
+      end
+
+      # Format the cursor.next() event
+      #
+      # @example Format get_more.
+      #   Default.get_more(event)
+      #
+      # @param [ Moped::Protocol::GetMore ] event GetMore event to format
+      #
+      # @return [ String ] Formatted string
+      #
+      # @since 2.0.0
+      def self.get_more(event)
+        "cursor.next() cursor_id: #{event.cursor_id}"
+      end
+
+      # Format the db.killOp(opid) event
+      #
+      # @example Format kill_cursors.
+      #   Default.kill_cursors(event)
+      #
+      # @param [ Moped::Protocol::KillCursors ] event KillCursors event to format
+      #
+      # @return [ String ] Formatted string
+      #
+      # @since 2.0.0
+      def self.kill_cursors(event)
+        "db.killOp(#{event.cursor_id})"
+      end
+    end
+  end
+end
\ No newline at end of file
diff --git a/lib/moped/log_format/shell_format/command.rb b/lib/moped/log_format/shell_format/command.rb
new file mode 100644
index 0000000..98e94ab
--- /dev/null
+++ b/lib/moped/log_format/shell_format/command.rb
@@ -0,0 +1,71 @@
+module Moped
+  module LogFormat
+    class ShellFormat
+      class Command
+        include Moped::LogFormat::ShellFormat::Shellable
+
+        Commands = %w{addUser auth changeUserPassword cloneCollection cloneDatabase commandHelp copyDatabase
+                      createCollection currentOp dropDatabase eval fsyncLock fsyncUnlock getCollection getCollectionNames
+                      getLastError getLastErrorObj getMongo getName getPrevError getProfilingLevel getProfilingStatus
+                      getReplicationInfo getSiblingDB help hostInfo isMaster killOp listCommands loadServerScripts
+                      logout printCollectionStats printReplicationInfo printShardingStatus printSlaveReplicationInfo
+                      removeUser repairDatabase resetError runCommand serverBuildInfo serverCmdLineOpts serverStatus
+                      setProfilingLevel shutdownServer stats upgradeCheck upgradeCheckAllDBs version}
+
+        Aliases = Commands.inject({}) do |acc, cmd|
+          if cmd.downcase != cmd
+            name = cmd.downcase.to_sym
+            acc[name] = cmd
+          end
+
+          acc
+        end
+
+        def sequence
+          [
+            :db, :command
+          ]
+        end
+
+        def to_shell_command
+          return shell(command_name, argument, rest)
+        end
+
+        private
+
+        def name
+          event.selector.keys.first
+        end
+
+        def command_name
+          normalized_name = name.downcase.to_sym
+
+          if Aliases.include? normalized_name
+            command_name = Aliases[normalized_name]
+          else
+            name
+          end
+        end
+
+        def argument
+          if event.selector[name].blank?
+            return nil
+          else
+            dump_json event.selector[name]
+          end
+        end
+
+        def rest
+          selector = event.selector.dup
+          selector.delete name
+
+          if selector.blank?
+            return nil
+          else
+            dump_json selector
+          end
+        end
+      end
+    end
+  end
+end
\ No newline at end of file
diff --git a/lib/moped/log_format/shell_format/core_ext.rb b/lib/moped/log_format/shell_format/core_ext.rb
new file mode 100644
index 0000000..7fbd19b
--- /dev/null
+++ b/lib/moped/log_format/shell_format/core_ext.rb
@@ -0,0 +1,7 @@
+require "moped/log_format/shell_format/core_ext/unsafe_json_string"
+require "moped/log_format/shell_format/core_ext/active_support/json/encoding"
+require "moped/log_format/shell_format/core_ext/bson/object_id"
+require "moped/log_format/shell_format/core_ext/regexp"
+require "moped/log_format/shell_format/core_ext/time"
+
+
diff --git a/lib/moped/log_format/shell_format/core_ext/active_support/json/encoding.rb b/lib/moped/log_format/shell_format/core_ext/active_support/json/encoding.rb
new file mode 100644
index 0000000..003f5b4
--- /dev/null
+++ b/lib/moped/log_format/shell_format/core_ext/active_support/json/encoding.rb
@@ -0,0 +1,25 @@
+module Moped
+  module ShellFormatPatch
+    module JSONGemEncoder
+      def jsonify_with_mongo_shell(value)
+        case value
+        when UnsafeJSONString
+          value
+        else
+          jsonify_without_mongo_shell(value)
+        end
+      end
+
+      def self.included(base)
+        base.class_eval do
+          alias_method :jsonify_without_mongo_shell, :jsonify
+          alias_method :jsonify, :jsonify_with_mongo_shell
+        end
+      end
+    end
+  end
+end
+
+if defined? ::ActiveSupport
+  ::ActiveSupport::JSON::Encoding::JSONGemEncoder.send(:include, Moped::ShellFormatPatch::JSONGemEncoder)
+end
\ No newline at end of file
diff --git a/lib/moped/log_format/shell_format/core_ext/bson/object_id.rb b/lib/moped/log_format/shell_format/core_ext/bson/object_id.rb
new file mode 100644
index 0000000..7065e35
--- /dev/null
+++ b/lib/moped/log_format/shell_format/core_ext/bson/object_id.rb
@@ -0,0 +1,26 @@
+module Moped
+  module ShellFormatPatch
+    module ObjectId
+      def to_mongo_shell
+        UnsafeJSONString.new(%Q{ObjectId("#{to_s}")})
+      end
+
+      def as_json_with_mongo_shell(*args)
+        if args.first && args.first[:mongo_shell_format]
+          to_mongo_shell
+        else
+          as_json_without_mongo_shell(*args)
+        end
+      end
+
+      def self.included(base)
+        base.class_eval do
+          alias_method :as_json_without_mongo_shell, :as_json
+          alias_method :as_json, :as_json_with_mongo_shell
+        end
+      end
+    end
+  end
+end
+
+::BSON::ObjectId.send(:include, Moped::ShellFormatPatch::ObjectId)
\ No newline at end of file
diff --git a/lib/moped/log_format/shell_format/core_ext/regexp.rb b/lib/moped/log_format/shell_format/core_ext/regexp.rb
new file mode 100644
index 0000000..a146891
--- /dev/null
+++ b/lib/moped/log_format/shell_format/core_ext/regexp.rb
@@ -0,0 +1,26 @@
+module Moped
+  module ShellFormatPatch
+    module Regexp
+      def to_mongo_shell
+        UnsafeJSONString.new(inspect)
+      end
+
+      def as_json_with_mongo_shell(*args)
+        if args.first && args.first[:mongo_shell_format]
+          to_mongo_shell
+        else
+          as_json_without_mongo_shell(*args)
+        end
+      end
+
+      def self.included(base)
+        base.class_eval do
+          alias_method :as_json_without_mongo_shell, :as_json
+          alias_method :as_json, :as_json_with_mongo_shell
+        end
+      end
+    end
+  end
+end
+
+::Regexp.send(:include, Moped::ShellFormatPatch::Regexp)
\ No newline at end of file
diff --git a/lib/moped/log_format/shell_format/core_ext/time.rb b/lib/moped/log_format/shell_format/core_ext/time.rb
new file mode 100644
index 0000000..c96658d
--- /dev/null
+++ b/lib/moped/log_format/shell_format/core_ext/time.rb
@@ -0,0 +1,26 @@
+module Moped
+  module ShellFormatPatch
+    module Time
+      def to_mongo_shell
+        UnsafeJSONString.new(%Q{ISODate("#{xmlschema}")})
+      end
+
+      def as_json_with_mongo_shell(*args)
+        if args.first && args.first[:mongo_shell_format]
+          to_mongo_shell
+        else
+          as_json_without_mongo_shell(*args)
+        end
+      end
+
+      def self.included(base)
+        base.class_eval do
+          alias_method :as_json_without_mongo_shell, :as_json
+          alias_method :as_json, :as_json_with_mongo_shell
+        end
+      end
+    end
+  end
+end
+
+::Time.send(:include, Moped::ShellFormatPatch::Time)
\ No newline at end of file
diff --git a/lib/moped/log_format/shell_format/core_ext/unsafe_json_string.rb b/lib/moped/log_format/shell_format/core_ext/unsafe_json_string.rb
new file mode 100644
index 0000000..8c1cb67
--- /dev/null
+++ b/lib/moped/log_format/shell_format/core_ext/unsafe_json_string.rb
@@ -0,0 +1,5 @@
+class UnsafeJSONString < String
+  def to_json(*args)
+    to_s
+  end
+end
\ No newline at end of file
diff --git a/lib/moped/log_format/shell_format/delete.rb b/lib/moped/log_format/shell_format/delete.rb
new file mode 100644
index 0000000..cc2276a
--- /dev/null
+++ b/lib/moped/log_format/shell_format/delete.rb
@@ -0,0 +1,19 @@
+module Moped
+  module LogFormat
+    class ShellFormat
+      class Delete
+        include Moped::LogFormat::ShellFormat::Shellable
+
+        def sequence
+          [
+            :db, :collection, :delete
+          ]
+        end
+
+        def to_shell_delete
+          return shell :remove, selector, flags
+        end
+      end
+    end
+  end
+end
\ No newline at end of file
diff --git a/lib/moped/log_format/shell_format/insert.rb b/lib/moped/log_format/shell_format/insert.rb
new file mode 100644
index 0000000..297141a
--- /dev/null
+++ b/lib/moped/log_format/shell_format/insert.rb
@@ -0,0 +1,34 @@
+module Moped
+  module LogFormat
+    class ShellFormat
+      class Insert
+        include Moped::LogFormat::ShellFormat::Shellable
+
+        def sequence
+          [
+            :db, :collection, :insert
+          ]
+        end
+
+        def to_shell_insert
+          return shell :insert, documents, flags
+        end
+
+        private
+
+        def documents
+          if event.documents.size == 1
+            dump_json(event.documents.first)
+          else
+            dump_json(event.documents.first)
+          end
+        end
+
+        def flags
+          return if event.flags.blank?
+          dump_json(event.flags)
+        end
+      end
+    end
+  end
+end
\ No newline at end of file
diff --git a/lib/moped/log_format/shell_format/query.rb b/lib/moped/log_format/shell_format/query.rb
new file mode 100644
index 0000000..cfe7f78
--- /dev/null
+++ b/lib/moped/log_format/shell_format/query.rb
@@ -0,0 +1,116 @@
+module Moped
+  module LogFormat
+    class ShellFormat
+      class Query
+        include Moped::LogFormat::ShellFormat::Shellable
+
+        CommandToModifiers = {
+          min: '$min',
+          max: '$max',
+          sort: '$orderby',
+          hint: '$hint',
+          snapshot: '$snapshot',
+          maxTimeMS: '$maxTimeMS',
+          explain: '$explain'
+        }
+
+        MethodsToCommand = {
+          batch_size: 'batchSize',
+          limit: 'limit',
+          skip: 'skip',
+        }
+
+        FlagsToOptions = {
+          tailable: 'tailable',
+          slave_ok: 'slaveOk',
+          no_cursor_timeout: 'noTimeout',
+          await_data: 'awaitData',
+          exhaust: 'exhaust'
+        }
+
+        def sequence
+          [
+            :db, :collection, :find, :sort, :hint,
+            :min, :max, :limit, :skip,
+            :specials,
+            :batch_size,
+            :maxTimeMS,
+            :flags,
+            :snapshot,
+            :explain
+          ]
+        end
+
+        def to_shell_find
+          selector = event.selector.dup
+
+          query = selector.delete("$query") { Hash.new }
+          selector.keep_if {|k, v| not k.to_s.start_with? '$' }
+          selector.merge! query
+
+          arguments = []
+          arguments << dump_json(selector)
+          arguments << dump_json(event.fields) if not event.fields.blank?
+
+          return shell :find, arguments
+        end
+
+        def to_shell_limit
+          limit = event.limit
+
+          return nil if limit.nil? or limit == 0
+
+          shell :limit, limit
+        end
+
+        def to_shell_skip
+          skip = event.skip
+          return nil if skip.nil? or skip == 0
+
+          shell :skip, skip
+        end
+
+        def to_shell_flags
+          # TODO: FlagsToOptions
+          return nil
+        end
+
+        def to_shell_specials
+          unknown_modifiers = event.selector.dup.keep_if do |modifier, value|
+            modifier = modifier.to_s
+            modifier.start_with?("$") and ('$query' != modifier)  and (not CommandToModifiers.has_value?(modifier))
+          end
+
+          unknown_modifiers.inject [] do |acc, (modifier, params)|
+            acc << shell(:_addSpecial, dump_json({modifier => params}))
+            acc
+          end
+        end
+
+        def extact_command(command)
+          if command_name = MethodsToCommand[command]
+            result = event.send(command)
+
+            if not result.blank?
+              return shell command_name, dump_json(result)
+            else
+              return nil
+            end
+          end
+
+          if modifier_name = CommandToModifiers[command]
+            result = event.selector[modifier_name]
+
+            if not result.blank?
+              return shell command, dump_json(result)
+            else
+              return nil
+            end
+          end
+
+          raise ArgumentError, "can't convert '#{command}' to mongodb shell command for #{self.class}"
+        end
+      end
+    end
+  end
+end
\ No newline at end of file
diff --git a/lib/moped/log_format/shell_format/shellable.rb b/lib/moped/log_format/shell_format/shellable.rb
new file mode 100644
index 0000000..6dba485
--- /dev/null
+++ b/lib/moped/log_format/shell_format/shellable.rb
@@ -0,0 +1,79 @@
+module Moped
+  module LogFormat
+    class ShellFormat
+      module Shellable
+        module ClassMethods
+          def format(event)
+            new(event).to_s
+          end
+        end
+
+        def self.included(receiver)
+          receiver.extend ClassMethods
+        end
+
+        attr_reader :event
+        def initialize(event)
+          @event = event
+        end
+
+        def to_s
+          commands = []
+
+          sequence.each do |item|
+            commands << comandify(item)
+          end
+
+          commands.compact.join(".")
+        end
+
+        def comandify(command)
+          if respond_to? :"to_shell_#{command}"
+            return nil_if_blank(send(:"to_shell_#{command}"))
+          end
+
+          if respond_to? :extact_command
+            return nil_if_blank(extact_command(command))
+          end
+
+          raise ArgumentError, "can't convert '#{command}' to mongodb shell command for #{self.class}"
+        end
+
+        def to_shell_db
+          :db
+        end
+
+        def to_shell_collection
+          event.collection
+        end
+
+        private
+
+        def selector
+          dump_json(event.selector)
+        end
+
+        def flags
+          return if event.flags.blank?
+          flags = Hash[event.flags.map {|f| [f, 1]}]
+
+          dump_json(flags)
+        end
+
+        def dump_json(object)
+          object.to_json(escape: false, mongo_shell_format: true)
+        end
+
+        def shell(command, *args)
+          args.compact!
+
+          "%s(%s)" % [command, args.join(", ")]
+        end
+
+        def nil_if_blank(obj)
+          obj.blank? ? nil : obj
+        end
+      end
+    end
+  end
+end
\ No newline at end of file
diff --git a/lib/moped/log_format/shell_format/update.rb b/lib/moped/log_format/shell_format/update.rb
new file mode 100644
index 0000000..8275428
--- /dev/null
+++ b/lib/moped/log_format/shell_format/update.rb
@@ -0,0 +1,34 @@
+module Moped
+  module LogFormat
+    class ShellFormat
+      class Update
+        include Moped::LogFormat::ShellFormat::Shellable
+
+        def sequence
+          [
+            :db, :collection, :update
+          ]
+        end
+
+        def to_shell_update
+          return shell :update, selector, update, flags
+        end
+
+        private
+
+        def selector
+          dump_json(event.selector)
+        end
+
+        def update
+          dump_json(event.selector)
+        end
+
+        def flags
+          return if event.flags.blank?
+          dump_json(event.flags)
+        end
+      end
+    end
+  end
+end
\ No newline at end of file
diff --git a/lib/moped/log_format/utils/color_string.rb b/lib/moped/log_format/utils/color_string.rb
new file mode 100644
index 0000000..91cf811
--- /dev/null
+++ b/lib/moped/log_format/utils/color_string.rb
@@ -0,0 +1,86 @@
+module Moped
+  module LogFormat
+    class ColorString
+      Colors = {
+        black:    "\e[30m",
+        red:      "\e[31m",
+        green:    "\e[32m",
+        yellow:   "\e[33m",
+        blue:     "\e[34m",
+        magenta:  "\e[35m",
+        cyan:     "\e[36m",
+        white:    "\e[37m",
+        default:  "\e[38m"
+      }.freeze
+
+      Backgrounds = {
+        black:    "\e[40m",
+        red:      "\e[41m",
+        green:    "\e[42m",
+        yellow:   "\e[43m",
+        blue:     "\e[44m",
+        magenta:  "\e[45m",
+        cyan:     "\e[46m",
+        white:    "\e[47m",
+        default:  "\e[48m"
+      }.freeze
+
+      Clear = "\e[0m".freeze
+
+      Styles = {
+        bold:        "\e[1m",
+        dim:         "\e[1m",
+        underscore:  "\e[4m",
+        blink:       "\e[5m",
+        reverse:     "\e[7m",
+        hidden:      "\e[8m"
+      }.freeze
+
+      attr_reader :string, :color, :background, :styles, :enable
+
+      def initialize(string, enabled = false)
+        @enable = enabled
+        @string = string
+
+        @color = Colors[:default]
+        @background = Backgrounds[:default]
+        @styles = []
+      end
+
+      def to_s
+        if self.enable == true
+          [styles.join, color, background, string, Clear].join
+        else
+          string
+        end
+      end
+      alias_method :inspect, :to_s
+
+      Colors.each do |color_name, value|
+        define_method color_name do
+          @color = Colors[color_name]
+          self
+        end
+      end
+
+      Backgrounds.each do |background_name, value|
+        define_method :"on_#{background_name}" do
+          @background = Backgrounds[background_name]
+          self
+        end
+      end
+
+      Styles.each do |style_name, value|
+        define_method style_name do
+          @styles << Styles[style_name]
+          self
+        end
+
+        define_method :"no_#{style_name}" do
+          @styles.delete Styles[style_name]
+          self
+        end
+      end
+    end
+  end
+end
\ No newline at end of file
diff --git a/lib/moped/loggable.rb b/lib/moped/loggable.rb
index dfebd72..61e3cd8 100644
--- a/lib/moped/loggable.rb
+++ b/lib/moped/loggable.rb
@@ -1,3 +1,5 @@
+require "moped/log_format/default_format"
+
 # encoding: utf-8
 module Moped
 
@@ -19,12 +21,12 @@ module Loggable
     def self.log_operations(prefix, ops, runtime)
       indent  = " "*prefix.length
       if ops.length == 1
-        Moped.logger.debug([ prefix, ops.first.log_inspect, "runtime: #{runtime}" ].join(' '))
+        Moped.logger.debug(Moped.log_format.log(prefix, ops.first.log_inspect, runtime))
       else
         first, *middle, last = ops
-        Moped.logger.debug([ prefix, first.log_inspect ].join(' '))
-        middle.each { |m| Moped.logger.debug([ indent, m.log_inspect ].join(' ')) }
-        Moped.logger.debug([ indent, last.log_inspect, "runtime: #{runtime}" ].join(' '))
+        Moped.logger.debug(Moped.log_format.log(prefix, first.log_inspect))
+        middle.each { |m| Moped.logger.debug(Moped.log_format.log(indent, m.log_inspect)) }
+        Moped.logger.debug(Moped.log_format.log(indent, last.log_inspect, runtime))
       end
     end
 
@@ -39,7 +41,7 @@ def self.log_operations(prefix, ops, runtime)
     #
     # @since 2.0.0
     def self.debug(prefix, payload, runtime)
-      Moped.logger.debug([ prefix, payload, "runtime: #{runtime}" ].join(' '))
+      Moped.logger.debug(Moped.log_format.log(prefix, payload, runtime))
     end
 
     # Log the payload to warn.
@@ -53,7 +55,7 @@ def self.debug(prefix, payload, runtime)
     #
     # @since 2.0.0
     def self.warn(prefix, payload, runtime)
-      Moped.logger.warn([ prefix, payload, "runtime: #{runtime}" ].join(' '))
+      Moped.logger.warn(Moped.log_format.log(prefix, payload, runtime))
     end
 
     # Get the logger.
@@ -106,5 +108,30 @@ def default_logger
     def logger=(logger)
       @logger = logger
     end
+
+    # Get the log format.
+    #
+    # @example Get the log format.
+    #   Loggable.logger
+    #
+    # @return [ Logger ] The log format.
+    #
+    # @since 2.0.0
+    def log_format
+      return @log_format if defined?(@log_format)
+      @log_format = Moped::LogFormat::DefaultFormat
+    end
+
+    # Set the log formatt.
+    #
+    # @example Set the log formatt.
+    #   Loggable.log_format = format
+    #
+    # @return [ LogFormat ] The log format.
+    #
+    # @since 2.0.0
+    def log_format=(format)
+      @log_format = format
+    end
   end
-end
+end
\ No newline at end of file
diff --git a/lib/moped/protocol/command.rb b/lib/moped/protocol/command.rb
index 8c0461c..af86a25 100644
--- a/lib/moped/protocol/command.rb
+++ b/lib/moped/protocol/command.rb
@@ -63,8 +63,7 @@ def initialize(database, command, options = {})
       #
       # @since 1.0.0
       def log_inspect
-        type = "COMMAND"
-        "%-12s database=%s command=%s" % [type, database, selector.inspect]
+        Moped.log_format.command(self)
       end
 
       # Take the provided reply and return the expected results to api
diff --git a/lib/moped/protocol/delete.rb b/lib/moped/protocol/delete.rb
index 0695e66..f68e6a6 100644
--- a/lib/moped/protocol/delete.rb
+++ b/lib/moped/protocol/delete.rb
@@ -85,9 +85,7 @@ def op_code
       end
 
       def log_inspect
-        type = "DELETE"
-
-        "%-12s database=%s collection=%s selector=%s flags=%s" % [type, database, collection, selector.inspect, flags.inspect]
+        Moped.log_format.delete(self)
       end
 
       private
diff --git a/lib/moped/protocol/get_more.rb b/lib/moped/protocol/get_more.rb
index 78710b4..882b09b 100644
--- a/lib/moped/protocol/get_more.rb
+++ b/lib/moped/protocol/get_more.rb
@@ -115,8 +115,7 @@ def initialize(database, collection, cursor_id, limit, options = {})
       #
       # @since 1.0.0
       def log_inspect
-        type = "GET_MORE"
-        "%-12s database=%s collection=%s limit=%s cursor_id=%s" % [type, database, collection, limit, cursor_id]
+        Moped.log_format.get_more(self)
       end
 
       undef op_code
diff --git a/lib/moped/protocol/insert.rb b/lib/moped/protocol/insert.rb
index 7d494e8..b512f1e 100644
--- a/lib/moped/protocol/insert.rb
+++ b/lib/moped/protocol/insert.rb
@@ -86,9 +86,7 @@ def initialize(database, collection, documents, options = {})
       end
 
       def log_inspect
-        type = "INSERT"
-
-        "%-12s database=%s collection=%s documents=%s flags=%s" % [type, database, collection, documents.inspect, flags.inspect]
+        Moped.log_format.insert(self)
       end
 
       private
diff --git a/lib/moped/protocol/kill_cursors.rb b/lib/moped/protocol/kill_cursors.rb
index 1058c52..5cdcf85 100644
--- a/lib/moped/protocol/kill_cursors.rb
+++ b/lib/moped/protocol/kill_cursors.rb
@@ -55,9 +55,7 @@ def initialize(cursor_ids, options = {})
       end
 
       def log_inspect
-        type = "KILL_CURSORS"
-
-        "%-12s cursor_ids=%s" % [type, cursor_ids.inspect]
+        Moped.log_format.kill_cursors(self)
       end
     end
   end
diff --git a/lib/moped/protocol/query.rb b/lib/moped/protocol/query.rb
index ac455fc..d3572e5 100644
--- a/lib/moped/protocol/query.rb
+++ b/lib/moped/protocol/query.rb
@@ -170,19 +170,7 @@ def initialize(database, collection, selector, options = {})
       #
       # @since 1.0.0
       def log_inspect
-        type = "QUERY"
-        fields = []
-        fields << ["%-12s", type]
-        fields << ["database=%s", database]
-        fields << ["collection=%s", collection]
-        fields << ["selector=%s", selector.inspect]
-        fields << ["flags=%s", flags.inspect]
-        fields << ["limit=%s", limit.inspect]
-        fields << ["skip=%s", skip.inspect]
-        fields << ["batch_size=%s", batch_size.inspect]
-        fields << ["fields=%s", self.fields.inspect]
-        f, v = fields.transpose
-        f.join(" ") % v
+        Moped.log_format.query(self)
       end
 
       undef op_code
diff --git a/lib/moped/protocol/update.rb b/lib/moped/protocol/update.rb
index 58609fc..f2dc969 100644
--- a/lib/moped/protocol/update.rb
+++ b/lib/moped/protocol/update.rb
@@ -98,12 +98,7 @@ def initialize(database, collection, selector, update, options = {})
       end
 
       def log_inspect
-        type = "UPDATE"
-
-        "%-12s database=%s collection=%s selector=%s update=%s flags=%s" % [type, database, collection,
-                                                                            selector.inspect,
-                                                                            update.inspect,
-                                                                            flags.inspect]
+        Moped.log_format.update(self)
       end
 
       private

From 226a2ddf390e5907fb474935cfde7f917604f335 Mon Sep 17 00:00:00 2001
From: y8 <y8@ya.ru>
Date: Sat, 20 Sep 2014 22:20:44 +0200
Subject: [PATCH 02/11] Put back database name

---
 lib/moped/log_format/shell_format.rb           | 4 ++--
 lib/moped/log_format/shell_format/shellable.rb | 2 +-
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/lib/moped/log_format/shell_format.rb b/lib/moped/log_format/shell_format.rb
index 1f86dc0..712f0b5 100644
--- a/lib/moped/log_format/shell_format.rb
+++ b/lib/moped/log_format/shell_format.rb
@@ -118,7 +118,7 @@ def self.delete(event)
       #
       # @since 2.0.0
       def self.get_more(event)
-        "cursor.next() cursor_id: #{event.cursor_id}"
+        "#{event.database}: cursor.next() cursor_id: #{event.cursor_id}"
       end
 
       # Format the db.killOp(opid) event
@@ -132,7 +132,7 @@ def self.get_more(event)
       #
       # @since 2.0.0
       def self.kill_cursors(event)
-        "db.killOp(#{event.cursor_id})"
+        "#{event.database}: db.killOp(#{event.cursor_id})"
       end
     end
   end
diff --git a/lib/moped/log_format/shell_format/shellable.rb b/lib/moped/log_format/shell_format/shellable.rb
index 6dba485..1270cb6 100644
--- a/lib/moped/log_format/shell_format/shellable.rb
+++ b/lib/moped/log_format/shell_format/shellable.rb
@@ -24,7 +24,7 @@ def to_s
             commands << comandify(item)
           end
 
-          commands.compact.join(".")
+          "#{event.database}: #{commands.compact.join(".")}"
         end
 
         def comandify(command)

From 5c2a17907a5aa88173a54ed96c10a13cc0ebfa5d Mon Sep 17 00:00:00 2001
From: y8 <y8@ya.ru>
Date: Sun, 21 Sep 2014 00:45:18 +0200
Subject: [PATCH 03/11] If there no ActiveSupport just redefine :to_json

---
 .../shell_format/core_ext/bson/object_id.rb   | 19 ++++++++++++++++---
 .../shell_format/core_ext/regexp.rb           | 19 ++++++++++++++++---
 .../log_format/shell_format/core_ext/time.rb  | 19 ++++++++++++++++---
 3 files changed, 48 insertions(+), 9 deletions(-)

diff --git a/lib/moped/log_format/shell_format/core_ext/bson/object_id.rb b/lib/moped/log_format/shell_format/core_ext/bson/object_id.rb
index 7065e35..f3ccb10 100644
--- a/lib/moped/log_format/shell_format/core_ext/bson/object_id.rb
+++ b/lib/moped/log_format/shell_format/core_ext/bson/object_id.rb
@@ -1,7 +1,7 @@
 module Moped
   module ShellFormatPatch
     module ObjectId
-      def to_mongo_shell
+      def to_mongo_shell(*args)
         UnsafeJSONString.new(%Q{ObjectId("#{to_s}")})
       end
 
@@ -13,10 +13,23 @@ def as_json_with_mongo_shell(*args)
         end
       end
 
+      def to_json_with_mongo_shell(*args)
+        if args.first && args.first[:mongo_shell_format]
+          to_mongo_shell
+        else
+          to_json_without_mongo_shell(*args)
+        end
+      end
+
       def self.included(base)
         base.class_eval do
-          alias_method :as_json_without_mongo_shell, :as_json
-          alias_method :as_json, :as_json_with_mongo_shell
+          if defined? ActiveSupport
+            alias_method :as_json_without_mongo_shell, :as_json
+            alias_method :as_json, :as_json_with_mongo_shell
+          else
+            alias_method :to_json_without_mongo_shell, :to_json
+            alias_method :to_json, :to_json_with_mongo_shell
+          end
         end
       end
     end
diff --git a/lib/moped/log_format/shell_format/core_ext/regexp.rb b/lib/moped/log_format/shell_format/core_ext/regexp.rb
index a146891..b67b826 100644
--- a/lib/moped/log_format/shell_format/core_ext/regexp.rb
+++ b/lib/moped/log_format/shell_format/core_ext/regexp.rb
@@ -1,7 +1,7 @@
 module Moped
   module ShellFormatPatch
     module Regexp
-      def to_mongo_shell
+      def to_mongo_shell(*args)
         UnsafeJSONString.new(inspect)
       end
 
@@ -13,10 +13,23 @@ def as_json_with_mongo_shell(*args)
         end
       end
 
+      def to_json_with_mongo_shell(*args)
+        if args.first && args.first[:mongo_shell_format]
+          to_mongo_shell
+        else
+          to_json_without_mongo_shell(*args)
+        end
+      end
+
       def self.included(base)
         base.class_eval do
-          alias_method :as_json_without_mongo_shell, :as_json
-          alias_method :as_json, :as_json_with_mongo_shell
+          if defined? ActiveSupport
+            alias_method :as_json_without_mongo_shell, :as_json
+            alias_method :as_json, :as_json_with_mongo_shell
+          else
+            alias_method :to_json_without_mongo_shell, :to_json
+            alias_method :to_json, :to_json_with_mongo_shell
+          end
         end
       end
     end
diff --git a/lib/moped/log_format/shell_format/core_ext/time.rb b/lib/moped/log_format/shell_format/core_ext/time.rb
index c96658d..7079802 100644
--- a/lib/moped/log_format/shell_format/core_ext/time.rb
+++ b/lib/moped/log_format/shell_format/core_ext/time.rb
@@ -1,7 +1,7 @@
 module Moped
   module ShellFormatPatch
     module Time
-      def to_mongo_shell
+      def to_mongo_shell(*args)
         UnsafeJSONString.new(%Q{ISODate("#{xmlschema}")})
       end
 
@@ -13,10 +13,23 @@ def as_json_with_mongo_shell(*args)
         end
       end
 
+      def to_json_with_mongo_shell(*args)
+        if args.first && args.first[:mongo_shell_format]
+          to_mongo_shell
+        else
+          to_json_without_mongo_shell(*args)
+        end
+      end
+
       def self.included(base)
         base.class_eval do
-          alias_method :as_json_without_mongo_shell, :as_json
-          alias_method :as_json, :as_json_with_mongo_shell
+          if defined? ActiveSupport
+            alias_method :as_json_without_mongo_shell, :as_json
+            alias_method :as_json, :as_json_with_mongo_shell
+          else
+            alias_method :to_json_without_mongo_shell, :to_json
+            alias_method :to_json, :as_json_with_mongo_shell
+          end
         end
       end
     end

From 968c60a50c20762595329036bf98c0fdfdebed28 Mon Sep 17 00:00:00 2001
From: y8 <y8@ya.ru>
Date: Sun, 21 Sep 2014 00:45:56 +0200
Subject: [PATCH 04/11] Use nil_if_blank instead of blank?

---
 lib/moped/log_format/shell_format/command.rb   |  4 ++--
 lib/moped/log_format/shell_format/insert.rb    |  2 +-
 lib/moped/log_format/shell_format/query.rb     |  6 +++---
 lib/moped/log_format/shell_format/shellable.rb | 14 ++++++++++++--
 lib/moped/log_format/shell_format/update.rb    |  2 +-
 5 files changed, 19 insertions(+), 9 deletions(-)

diff --git a/lib/moped/log_format/shell_format/command.rb b/lib/moped/log_format/shell_format/command.rb
index 98e94ab..d00bfb9 100644
--- a/lib/moped/log_format/shell_format/command.rb
+++ b/lib/moped/log_format/shell_format/command.rb
@@ -48,7 +48,7 @@ def command_name
         end
 
         def argument
-          if event.selector[name].blank?
+          if nil_if_blank(event.selector[name]).nil?
             return nil
           else
             dump_json event.selector[name]
@@ -59,7 +59,7 @@ def rest
           selector = event.selector.dup
           selector.delete name
 
-          if selector.blank?
+          if selector.empty?
             return nil
           else
             dump_json selector
diff --git a/lib/moped/log_format/shell_format/insert.rb b/lib/moped/log_format/shell_format/insert.rb
index 297141a..5dfeda1 100644
--- a/lib/moped/log_format/shell_format/insert.rb
+++ b/lib/moped/log_format/shell_format/insert.rb
@@ -25,7 +25,7 @@ def documents
         end
 
         def flags
-          return if event.flags.blank?
+          return if nil_if_blank(event.flags).nil?
           dump_json(event.flags)
         end
       end
diff --git a/lib/moped/log_format/shell_format/query.rb b/lib/moped/log_format/shell_format/query.rb
index cfe7f78..4fa2fa8 100644
--- a/lib/moped/log_format/shell_format/query.rb
+++ b/lib/moped/log_format/shell_format/query.rb
@@ -50,7 +50,7 @@ def to_shell_find
 
           arguments = []
           arguments << dump_json(selector)
-          arguments << dump_json(event.fields) if not event.fields.blank?
+          arguments << dump_json(event.fields) if not nil_if_blank(event.fields).nil?
 
           return shell :find, arguments
         end
@@ -91,7 +91,7 @@ def extact_command(command)
           if command_name = MethodsToCommand[command]
             result = event.send(command)
 
-            if not result.blank?
+            if not nil_if_blank(result).nil?
               return shell command_name, dump_json(result)
             else
               return nil
@@ -101,7 +101,7 @@ def extact_command(command)
           if modifier_name = CommandToModifiers[command]
             result = event.selector[modifier_name]
 
-            if not result.blank?
+            if not nil_if_blank(result).nil?
               return shell command, dump_json(result)
             else
               return nil
diff --git a/lib/moped/log_format/shell_format/shellable.rb b/lib/moped/log_format/shell_format/shellable.rb
index 1270cb6..d7315db 100644
--- a/lib/moped/log_format/shell_format/shellable.rb
+++ b/lib/moped/log_format/shell_format/shellable.rb
@@ -54,7 +54,7 @@ def selector
         end
 
         def flags
-          return if event.flags.blank?
+          return if nil_if_blank(event.flags).nil?
           flags = Hash[event.flags.map {|f| [f, 1]}]
 
           dump_json(flags)
@@ -71,7 +71,17 @@ def shell(command, *args)
         end
 
         def nil_if_blank(obj)
-          obj.blank? ? nil : obj
+          return if obj.nil? || obj == false
+
+          if obj.respond_to? :blank?
+            return obj.blank? ? nil : obj
+          end
+
+          if obj.respond_to? :empty?
+            return obj.empty? ? nil : obj
+          end
+
+          return obj
         end
       end
     end
diff --git a/lib/moped/log_format/shell_format/update.rb b/lib/moped/log_format/shell_format/update.rb
index 8275428..856bc4d 100644
--- a/lib/moped/log_format/shell_format/update.rb
+++ b/lib/moped/log_format/shell_format/update.rb
@@ -25,7 +25,7 @@ def update
         end
 
         def flags
-          return if event.flags.blank?
+          return if nil_if_blank(event.flags).nil?
           dump_json(event.flags)
         end
       end

From 4de6a748f0b811b91bce2cd1c6560dcc99cf27df Mon Sep 17 00:00:00 2001
From: y8 <y8@ya.ru>
Date: Sun, 21 Sep 2014 00:46:36 +0200
Subject: [PATCH 05/11] Add missing requires

---
 lib/moped.rb                         | 1 +
 lib/moped/log_format/shell_format.rb | 3 ++-
 lib/moped/loggable.rb                | 1 +
 3 files changed, 4 insertions(+), 1 deletion(-)

diff --git a/lib/moped.rb b/lib/moped.rb
index fb0e25c..b7dbfbc 100644
--- a/lib/moped.rb
+++ b/lib/moped.rb
@@ -8,6 +8,7 @@
 require "optionable"
 require "moped/errors"
 require "moped/indexes"
+require "moped/log_format"
 require "moped/loggable"
 require "moped/uri"
 require "moped/protocol"
diff --git a/lib/moped/log_format/shell_format.rb b/lib/moped/log_format/shell_format.rb
index 712f0b5..8964f41 100644
--- a/lib/moped/log_format/shell_format.rb
+++ b/lib/moped/log_format/shell_format.rb
@@ -4,6 +4,7 @@
 require "moped/log_format/shell_format/core_ext/regexp"
 require "moped/log_format/shell_format/core_ext/time"
 
+require "moped/log_format/shell_format/shellable"
 require "moped/log_format/shell_format/command"
 require "moped/log_format/shell_format/query"
 require "moped/log_format/shell_format/insert"
@@ -132,7 +133,7 @@ def self.get_more(event)
       #
       # @since 2.0.0
       def self.kill_cursors(event)
-        "#{event.database}: db.killOp(#{event.cursor_id})"
+        "db.killOp(#{event.cursor_ids})"
       end
     end
   end
diff --git a/lib/moped/loggable.rb b/lib/moped/loggable.rb
index 61e3cd8..1137279 100644
--- a/lib/moped/loggable.rb
+++ b/lib/moped/loggable.rb
@@ -1,4 +1,5 @@
 require "moped/log_format/default_format"
+require "moped/log_format/shell_format"
 
 # encoding: utf-8
 module Moped

From b290aab03b5c2f5d76c3969557b7b735248e4c16 Mon Sep 17 00:00:00 2001
From: y8 <y8@ya.ru>
Date: Sun, 21 Sep 2014 00:53:48 +0200
Subject: [PATCH 06/11] Require "json" stdlib

---
 lib/moped/log_format/shell_format.rb | 1 +
 1 file changed, 1 insertion(+)

diff --git a/lib/moped/log_format/shell_format.rb b/lib/moped/log_format/shell_format.rb
index 8964f41..449e312 100644
--- a/lib/moped/log_format/shell_format.rb
+++ b/lib/moped/log_format/shell_format.rb
@@ -1,3 +1,4 @@
+require "json"
 require "moped/log_format/shell_format/core_ext/unsafe_json_string"
 require "moped/log_format/shell_format/core_ext/active_support/json/encoding"
 require "moped/log_format/shell_format/core_ext/bson/object_id"

From 2f02af52cc097114e97614b2664410ac38a679e5 Mon Sep 17 00:00:00 2001
From: y8 <y8@ya.ru>
Date: Sun, 21 Sep 2014 00:54:23 +0200
Subject: [PATCH 07/11] Add option to print logs and charge format while
 running tests

---
 spec/spec_helper.rb | 10 +++++++++-
 1 file changed, 9 insertions(+), 1 deletion(-)

diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index 341a488..bee3382 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -20,7 +20,15 @@
 
 # Log to a StringIO instance to make sure no exceptions are rasied by our
 # logging code.
-Moped.logger = Logger.new(StringIO.new, Logger::DEBUG)
+if ENV.has_key? "MOPED_PRINT_LOG"
+  Moped.logger = Logger.new($stdout, Logger::DEBUG)
+else
+  Moped.logger = Logger.new(StringIO.new, Logger::DEBUG)
+end
+
+if ENV.has_key? "MOPED_LOG_FORMAT"
+  Moped.log_format = Module.const_get ENV["MOPED_LOG_FORMAT"]
+end
 
 RSpec.configure do |config|
   Support::Stats.install!

From ace19d746562037f7ae2674c846129e42f4912dc Mon Sep 17 00:00:00 2001
From: y8 <y8@ya.ru>
Date: Sun, 21 Sep 2014 01:24:47 +0200
Subject: [PATCH 08/11] Add log ShellFormat to build matrix

---
 .travis.yml | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/.travis.yml b/.travis.yml
index 61ad71e..853f990 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -6,7 +6,9 @@ rvm:
   - 2.1.0
   - jruby
   - jruby-head
-env: CI="travis"
+env:
+  - CI="travis"
+  - CI="travis" MOPED_LOG_FORMAT="Moped::LogFormat::ShellFormat"
 services:
   - mongodb
 matrix:

From 5ceef14659879e2b182f36fd4be2a94709b806de Mon Sep 17 00:00:00 2001
From: y8 <y8@ya.ru>
Date: Sun, 21 Sep 2014 01:28:03 +0200
Subject: [PATCH 09/11] Fix typo in Time json patch

---
 lib/moped/log_format/shell_format/core_ext/time.rb | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lib/moped/log_format/shell_format/core_ext/time.rb b/lib/moped/log_format/shell_format/core_ext/time.rb
index 7079802..eb7a6ad 100644
--- a/lib/moped/log_format/shell_format/core_ext/time.rb
+++ b/lib/moped/log_format/shell_format/core_ext/time.rb
@@ -28,7 +28,7 @@ def self.included(base)
             alias_method :as_json, :as_json_with_mongo_shell
           else
             alias_method :to_json_without_mongo_shell, :to_json
-            alias_method :to_json, :as_json_with_mongo_shell
+            alias_method :to_json, :to_json_with_mongo_shell
           end
         end
       end

From 2d8c9d02074dd52b833d570b872d9ba4b918afd8 Mon Sep 17 00:00:00 2001
From: y8 <y8@ya.ru>
Date: Sun, 21 Sep 2014 14:07:24 +0200
Subject: [PATCH 10/11] Don't resolve constant, just use shortcuts

---
 spec/spec_helper.rb | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index bee3382..89530e6 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -26,8 +26,9 @@
   Moped.logger = Logger.new(StringIO.new, Logger::DEBUG)
 end
 
-if ENV.has_key? "MOPED_LOG_FORMAT"
-  Moped.log_format = Module.const_get ENV["MOPED_LOG_FORMAT"]
+case ENV["MOPED_LOG_FORMAT"]
+when 'shell'
+  Moped.log_format = Moped::LogFormat::ShellFormat
 end
 
 RSpec.configure do |config|

From 13e87d8aa48283faf52fcb14933421e18c3249aa Mon Sep 17 00:00:00 2001
From: y8 <y8@ya.ru>
Date: Sun, 21 Sep 2014 14:08:42 +0200
Subject: [PATCH 11/11] Fix travis matrix to use shortcut for log format

---
 .travis.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.travis.yml b/.travis.yml
index 853f990..7d9bc42 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -8,7 +8,7 @@ rvm:
   - jruby-head
 env:
   - CI="travis"
-  - CI="travis" MOPED_LOG_FORMAT="Moped::LogFormat::ShellFormat"
+  - CI="travis" MOPED_LOG_FORMAT="shell"
 services:
   - mongodb
 matrix: