semantic_logger 2.6.1 → 2.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +63 -6
- data/lib/semantic_logger/appender/base.rb +2 -2
- data/lib/semantic_logger/base.rb +74 -57
- data/lib/semantic_logger/core_ext/thread.rb +11 -7
- data/lib/semantic_logger/logger.rb +34 -4
- data/lib/semantic_logger/semantic_logger.rb +15 -2
- data/lib/semantic_logger/version.rb +1 -1
- data/test/appender_file_test.rb +1 -1
- data/test/logger_test.rb +17 -3
- metadata +13 -13
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA1:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: c16da0b153fc6641eb7fc40fdf7cb8ee2ffe74cd
         | 
| 4 | 
            +
              data.tar.gz: 4907de6046cbbf1b7c5ea6f513faecc18bea3efb
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: b145848428794d626f5a5d3cd276c4d4929c868346962a24a502d618274c6cf967a7e6d028017c2c02b25a801b0dba014bb52a6cd29d8ef06153d5e9a9c25df7
         | 
| 7 | 
            +
              data.tar.gz: bb09287501c04bb4c6db41231f8e3245a42f8b20d645d3fe63389dc875a27ba0f0187840a02756e05db346168435e755ab7f2a6f88d25340328a77115a1d06cf
         | 
    
        data/README.md
    CHANGED
    
    | @@ -311,20 +311,30 @@ Parameters | |
| 311 311 | 
             
                :full
         | 
| 312 312 | 
             
                  Log the exception class, message, and backtrace
         | 
| 313 313 | 
             
                :partial
         | 
| 314 | 
            -
                  Log the exception class and  | 
| 314 | 
            +
                  Log the exception class and message
         | 
| 315 315 | 
             
                  The backtrace will not be logged
         | 
| 316 316 | 
             
                :off
         | 
| 317 | 
            -
                  Any unhandled exception  | 
| 317 | 
            +
                  Any unhandled exception raised in the block will not be logged
         | 
| 318 | 
            +
                Default: :partial
         | 
| 318 319 |  | 
| 319 | 
            -
              :min_duration
         | 
| 320 | 
            +
              :min_duration [Float]
         | 
| 320 321 | 
             
                Only log if the block takes longer than this duration in ms
         | 
| 321 322 | 
             
                Default: 0.0
         | 
| 322 323 |  | 
| 323 | 
            -
              :payload
         | 
| 324 | 
            +
              :payload [Hash]
         | 
| 324 325 | 
             
                Optional, Hash payload
         | 
| 325 326 |  | 
| 326 | 
            -
              :exception
         | 
| 327 | 
            +
              :exception [Exception]
         | 
| 327 328 | 
             
                Optional, Ruby Exception object to log along with the duration of the supplied block
         | 
| 329 | 
            +
             | 
| 330 | 
            +
              :duration [Float]
         | 
| 331 | 
            +
                Optional, supply the duration in ms that is logged when a block is not supplied
         | 
| 332 | 
            +
                If a block is not supplied then :duration is mandatory
         | 
| 333 | 
            +
                If a block is supplied :duration is ignored
         | 
| 334 | 
            +
             | 
| 335 | 
            +
              :metric [Object]
         | 
| 336 | 
            +
                Optional, when this parameter is supplied all subscribers will be notified of this
         | 
| 337 | 
            +
                metric, along with the Log Struct described below
         | 
| 328 338 | 
             
            ```
         | 
| 329 339 |  | 
| 330 340 | 
             
            ### Logging levels
         | 
| @@ -488,6 +498,9 @@ Sample output: | |
| 488 498 | 
             
            2013-11-07 16:26:02.744139 I [35841:User calculation thread 32] (0.0ms) ExternalSupplier -- Calling external interface
         | 
| 489 499 | 
             
            ```
         | 
| 490 500 |  | 
| 501 | 
            +
            When running JRuby, Thread.current.name will also set the underlying thread name in the JVM
         | 
| 502 | 
            +
            which is very useful when monitoring the JVM via JMX using tools such as jconsole.
         | 
| 503 | 
            +
             | 
| 491 504 | 
             
            #### NOTE:
         | 
| 492 505 |  | 
| 493 506 | 
             
            Make sure that the assigned thread name is unique otherwise it will be difficult
         | 
| @@ -499,6 +512,45 @@ For example, use the current thread object_id to ensure uniqueness: | |
| 499 512 | 
             
            Thread.current.name = "Worker Thread:#{Thread.current.object_id}"
         | 
| 500 513 | 
             
            ```
         | 
| 501 514 |  | 
| 515 | 
            +
            ### Metrics integration
         | 
| 516 | 
            +
             | 
| 517 | 
            +
            In production environments it is often necessary to not only measure the performance of a
         | 
| 518 | 
            +
            block of code using for example:
         | 
| 519 | 
            +
             | 
| 520 | 
            +
            ```ruby
         | 
| 521 | 
            +
            logger.benchmark_info "Calling external interface" do
         | 
| 522 | 
            +
              # Code to call the external supplier ...
         | 
| 523 | 
            +
            end
         | 
| 524 | 
            +
            ```
         | 
| 525 | 
            +
             | 
| 526 | 
            +
            Sending the performance information gathered above to something like NewRelic
         | 
| 527 | 
            +
            is also very useful, and we can end up with:
         | 
| 528 | 
            +
             | 
| 529 | 
            +
            ```ruby
         | 
| 530 | 
            +
            logger.benchmark_info "Calling external interface" do
         | 
| 531 | 
            +
              self.class.trace_execution_scoped(['Custom/slow_action/beginning_work']) do
         | 
| 532 | 
            +
                # Code to call the external supplier ...
         | 
| 533 | 
            +
              end
         | 
| 534 | 
            +
            end
         | 
| 535 | 
            +
            ```
         | 
| 536 | 
            +
             | 
| 537 | 
            +
            Rather than wrapping the code everywhere with two blocks, a single subscriber can
         | 
| 538 | 
            +
            be setup in config/initializers/semantic_logger_metrics.rb:
         | 
| 539 | 
            +
             | 
| 540 | 
            +
            ```ruby
         | 
| 541 | 
            +
            SemanticLogger.on_metric do |log_struct|
         | 
| 542 | 
            +
              ::NewRelic::Agent.record_metric(log_struct.metric, log_struct.duration)
         | 
| 543 | 
            +
            end
         | 
| 544 | 
            +
            ```
         | 
| 545 | 
            +
             | 
| 546 | 
            +
            Then update the log entry as follows:
         | 
| 547 | 
            +
             | 
| 548 | 
            +
            ```ruby
         | 
| 549 | 
            +
            logger.benchmark_info "Calling external interface", :metric => 'Custom/slow_action/beginning_work' do
         | 
| 550 | 
            +
              # Code to call the external supplier ...
         | 
| 551 | 
            +
            end
         | 
| 552 | 
            +
            ```
         | 
| 553 | 
            +
             | 
| 502 554 | 
             
            ## Standalone SemanticLogger
         | 
| 503 555 |  | 
| 504 556 | 
             
            When using SemanticLogger inside of Rails all we need to do is include the
         | 
| @@ -843,6 +895,11 @@ Once the logging data is in the NOSQL data store it can be queried quickly and | |
| 843 895 | 
             
            efficiently. Some SQL data stores also allow complex data types that could be used
         | 
| 844 896 | 
             
            for storing and querying the logging data
         | 
| 845 897 |  | 
| 898 | 
            +
            Before writing SemanticLogger all of the following logging frameworks were thoroughly
         | 
| 899 | 
            +
            evaluated. None of them met the above Semantic requirements, or the performance requirements
         | 
| 900 | 
            +
            of hundreds of threads all logging at the same time:
         | 
| 901 | 
            +
            logback, logging, log4r, central_logger, whoops
         | 
| 902 | 
            +
             | 
| 846 903 | 
             
            ## Architecture & Performance
         | 
| 847 904 |  | 
| 848 905 | 
             
            In order to ensure that logging does not hinder the performance of the application
         | 
| @@ -931,7 +988,7 @@ To log to MongoDB, it also needs the Ruby Mongo Driver | |
| 931 988 |  | 
| 932 989 | 
             
            - Add support for a configuration file that can set log level by class name
         | 
| 933 990 | 
             
            - Configuration file to support adding appenders
         | 
| 934 | 
            -
            - Based on end-user demand add appenders for:  | 
| 991 | 
            +
            - Based on end-user demand add appenders for: hadoop, redis, etc..
         | 
| 935 992 |  | 
| 936 993 | 
             
            Meta
         | 
| 937 994 | 
             
            ----
         | 
| @@ -36,7 +36,7 @@ module SemanticLogger | |
| 36 36 |  | 
| 37 37 | 
             
                      message = log.message.to_s.dup
         | 
| 38 38 | 
             
                      message << " -- " << log.payload.inspect if log.payload
         | 
| 39 | 
            -
                      message << " -- " << "#{log.exception.class}: #{log.exception.message}\n#{(log.exception.backtrace || []).join("\n")}" if log.exception
         | 
| 39 | 
            +
                      message << " -- Exception: " << "#{log.exception.class}: #{log.exception.message}\n#{(log.exception.backtrace || []).join("\n")}" if log.exception
         | 
| 40 40 |  | 
| 41 41 | 
             
                      duration_str = log.duration ? "(#{'%.1f' % log.duration}ms) " : ''
         | 
| 42 42 |  | 
| @@ -56,7 +56,7 @@ module SemanticLogger | |
| 56 56 |  | 
| 57 57 | 
             
                      message = log.message.to_s.dup
         | 
| 58 58 | 
             
                      message << " -- " << log.payload.inspect if log.payload
         | 
| 59 | 
            -
                      message << " -- " << "#{colors::BOLD}#{log.exception.class}: #{log.exception.message}#{colors::CLEAR}\n#{(log.exception.backtrace || []).join("\n")}" if log.exception
         | 
| 59 | 
            +
                      message << " -- Exception: " << "#{colors::BOLD}#{log.exception.class}: #{log.exception.message}#{colors::CLEAR}\n#{(log.exception.backtrace || []).join("\n")}" if log.exception
         | 
| 60 60 |  | 
| 61 61 | 
             
                      duration_str = log.duration ? "(#{colors::BOLD}#{'%.1f' % log.duration}ms#{colors::CLEAR}) " : ''
         | 
| 62 62 |  | 
    
        data/lib/semantic_logger/base.rb
    CHANGED
    
    | @@ -70,28 +70,9 @@ module SemanticLogger | |
| 70 70 | 
             
                #
         | 
| 71 71 | 
             
                SemanticLogger::LEVELS.each_with_index do |level, index|
         | 
| 72 72 | 
             
                  class_eval <<-EOT, __FILE__, __LINE__
         | 
| 73 | 
            -
                    def #{level}(message=nil, payload=nil, exception=nil)
         | 
| 73 | 
            +
                    def #{level}(message=nil, payload=nil, exception=nil, &block)
         | 
| 74 74 | 
             
                      if @level_index <= #{index}
         | 
| 75 | 
            -
                         | 
| 76 | 
            -
                          exception = payload
         | 
| 77 | 
            -
                          payload = nil
         | 
| 78 | 
            -
                        end
         | 
| 79 | 
            -
             | 
| 80 | 
            -
                        if block_given? && (result = yield)
         | 
| 81 | 
            -
                          if result.is_a?(String)
         | 
| 82 | 
            -
                            message = message.nil? ? result : "\#{message} -- \#{result}"
         | 
| 83 | 
            -
                          elsif payload && payload.respond_to?(:merge)
         | 
| 84 | 
            -
                            payload.merge(result)
         | 
| 85 | 
            -
                          else
         | 
| 86 | 
            -
                            payload = result
         | 
| 87 | 
            -
                          end
         | 
| 88 | 
            -
                        end
         | 
| 89 | 
            -
             | 
| 90 | 
            -
                        # Add scoped payload
         | 
| 91 | 
            -
                        if self.payload
         | 
| 92 | 
            -
                          payload = payload.nil? ? self.payload : self.payload.merge(payload)
         | 
| 93 | 
            -
                        end
         | 
| 94 | 
            -
                        log Log.new(:#{level}, Thread.current.name, name, message, payload, Time.now, nil, tags, #{index}, exception)
         | 
| 75 | 
            +
                        log_internal(:#{level}, #{index}, message, payload, exception, &block)
         | 
| 95 76 | 
             
                        true
         | 
| 96 77 | 
             
                      else
         | 
| 97 78 | 
             
                        false
         | 
| @@ -102,41 +83,11 @@ module SemanticLogger | |
| 102 83 | 
             
                      @level_index <= #{index}
         | 
| 103 84 | 
             
                    end
         | 
| 104 85 |  | 
| 105 | 
            -
                    def benchmark_#{level}(message, params =  | 
| 106 | 
            -
                      raise "Mandatory block missing" unless block_given?
         | 
| 86 | 
            +
                    def benchmark_#{level}(message, params = {}, &block)
         | 
| 107 87 | 
             
                      if @level_index <= #{index}
         | 
| 108 | 
            -
                         | 
| 109 | 
            -
                        min_duration  = params.nil? ? 0.0      : (params[:min_duration] || 0.0)
         | 
| 110 | 
            -
                        payload       = params.nil? ? nil      : params[:payload]
         | 
| 111 | 
            -
                        exception     = params.nil? ? nil      : params[:exception]
         | 
| 112 | 
            -
                        start         = Time.now
         | 
| 113 | 
            -
                        begin
         | 
| 114 | 
            -
                          yield
         | 
| 115 | 
            -
                        rescue Exception => exc
         | 
| 116 | 
            -
                          exception = exc
         | 
| 117 | 
            -
                        ensure
         | 
| 118 | 
            -
                          end_time = Time.now
         | 
| 119 | 
            -
                          duration = 1000.0 * (end_time - start)
         | 
| 120 | 
            -
             | 
| 121 | 
            -
                          # Add scoped payload
         | 
| 122 | 
            -
                          if self.payload
         | 
| 123 | 
            -
                            payload = payload.nil? ? self.payload : self.payload.merge(payload)
         | 
| 124 | 
            -
                          end
         | 
| 125 | 
            -
                          if exception
         | 
| 126 | 
            -
                            case log_exception
         | 
| 127 | 
            -
                            when :full
         | 
| 128 | 
            -
                              log Log.new(:#{level}, Thread.current.name, name, message, payload, end_time, duration, tags, #{index}, exception)
         | 
| 129 | 
            -
                            when :partial
         | 
| 130 | 
            -
                              log Log.new(:#{level}, Thread.current.name, name, "\#{message} -- Exception: \#{exception.class}: \#{exception.message}", payload, end_time, duration, tags, #{index}, nil)
         | 
| 131 | 
            -
                            end
         | 
| 132 | 
            -
                            raise exception
         | 
| 133 | 
            -
                          elsif duration >= min_duration
         | 
| 134 | 
            -
                            # Only log if the block took longer than 'min_duration' to complete
         | 
| 135 | 
            -
                            log Log.new(:#{level}, Thread.current.name, name, message, payload, end_time, duration, tags, #{index}, nil)
         | 
| 136 | 
            -
                          end
         | 
| 137 | 
            -
                        end
         | 
| 88 | 
            +
                        benchmark_internal(:#{level}, #{index}, message, params, &block)
         | 
| 138 89 | 
             
                      else
         | 
| 139 | 
            -
                         | 
| 90 | 
            +
                        block.call(params) if block
         | 
| 140 91 | 
             
                      end
         | 
| 141 92 | 
             
                    end
         | 
| 142 93 | 
             
                  EOT
         | 
| @@ -213,8 +164,6 @@ module SemanticLogger | |
| 213 164 | 
             
                alias :unknown :error
         | 
| 214 165 | 
             
                alias :unknown? :error?
         | 
| 215 166 |  | 
| 216 | 
            -
                # #TODO implement a thread safe #silence method
         | 
| 217 | 
            -
             | 
| 218 167 | 
             
                # DEPRECATED See SemanticLogger.default_level=
         | 
| 219 168 | 
             
                def self.default_level=(level)
         | 
| 220 169 | 
             
                  warn "[DEPRECATION] `SemanticLogger::Logger.default_level=` is deprecated.  Please use `SemanticLogger.default_level=` instead."
         | 
| @@ -272,7 +221,10 @@ module SemanticLogger | |
| 272 221 | 
             
                #
         | 
| 273 222 | 
             
                # level_index
         | 
| 274 223 | 
             
                #   Internal index of the log level
         | 
| 275 | 
            -
                 | 
| 224 | 
            +
                #
         | 
| 225 | 
            +
                # metric [Object]
         | 
| 226 | 
            +
                #   Object supplied when benchmark_x was called
         | 
| 227 | 
            +
                Log = Struct.new(:level, :thread_name, :name, :message, :payload, :time, :duration, :tags, :level_index, :exception, :metric)
         | 
| 276 228 |  | 
| 277 229 | 
             
                # Internal method to return the log level as an internal index
         | 
| 278 230 | 
             
                # Also supports mapping the ::Logger levels to SemanticLogger levels
         | 
| @@ -297,5 +249,70 @@ module SemanticLogger | |
| 297 249 | 
             
                  index
         | 
| 298 250 | 
             
                end
         | 
| 299 251 |  | 
| 252 | 
            +
                # Log message at the specified level
         | 
| 253 | 
            +
                def log_internal(level, index, message=nil, payload=nil, exception=nil, &block)
         | 
| 254 | 
            +
                  if exception.nil? && payload && payload.is_a?(Exception)
         | 
| 255 | 
            +
                    exception = payload
         | 
| 256 | 
            +
                    payload = nil
         | 
| 257 | 
            +
                  end
         | 
| 258 | 
            +
             | 
| 259 | 
            +
                  if block && (result = block.call)
         | 
| 260 | 
            +
                    if result.is_a?(String)
         | 
| 261 | 
            +
                      message = message.nil? ? result : "#{message} -- #{result}"
         | 
| 262 | 
            +
                    elsif payload && payload.respond_to?(:merge)
         | 
| 263 | 
            +
                      payload.merge(result)
         | 
| 264 | 
            +
                    else
         | 
| 265 | 
            +
                      payload = result
         | 
| 266 | 
            +
                    end
         | 
| 267 | 
            +
                  end
         | 
| 268 | 
            +
             | 
| 269 | 
            +
                  # Add scoped payload
         | 
| 270 | 
            +
                  if self.payload
         | 
| 271 | 
            +
                    payload = payload.nil? ? self.payload : self.payload.merge(payload)
         | 
| 272 | 
            +
                  end
         | 
| 273 | 
            +
                  log Log.new(level, Thread.current.name, name, message, payload, Time.now, nil, tags, index, exception)
         | 
| 274 | 
            +
                end
         | 
| 275 | 
            +
             | 
| 276 | 
            +
                # Measure the supplied block and log the message
         | 
| 277 | 
            +
                def benchmark_internal(level, index, message, params, &block)
         | 
| 278 | 
            +
                  start     = Time.now
         | 
| 279 | 
            +
                  begin
         | 
| 280 | 
            +
                    rc = block.call(params) if block
         | 
| 281 | 
            +
                    exception = params[:exception]
         | 
| 282 | 
            +
                    rc
         | 
| 283 | 
            +
                  rescue Exception => exc
         | 
| 284 | 
            +
                    exception = exc
         | 
| 285 | 
            +
                  ensure
         | 
| 286 | 
            +
                    end_time = Time.now
         | 
| 287 | 
            +
                    # Extract options after block completes so that block can modify any of the options
         | 
| 288 | 
            +
                    log_exception = params[:log_exception] || :partial
         | 
| 289 | 
            +
                    min_duration  = params[:min_duration] || 0.0
         | 
| 290 | 
            +
                    payload       = params[:payload]
         | 
| 291 | 
            +
                    metric        = params[:metric]
         | 
| 292 | 
            +
                    duration      = if block_given?
         | 
| 293 | 
            +
                      1000.0 * (end_time - start)
         | 
| 294 | 
            +
                    else
         | 
| 295 | 
            +
                      params[:duration] || raise("Mandatory block missing when :duration option is not supplied")
         | 
| 296 | 
            +
                    end
         | 
| 297 | 
            +
             | 
| 298 | 
            +
                    # Add scoped payload
         | 
| 299 | 
            +
                    if self.payload
         | 
| 300 | 
            +
                      payload = payload.nil? ? self.payload : self.payload.merge(payload)
         | 
| 301 | 
            +
                    end
         | 
| 302 | 
            +
                    if exception
         | 
| 303 | 
            +
                      case log_exception
         | 
| 304 | 
            +
                      when :full
         | 
| 305 | 
            +
                        log Log.new(level, Thread.current.name, name, message, payload, end_time, duration, tags, index, exception, metric)
         | 
| 306 | 
            +
                      when :partial
         | 
| 307 | 
            +
                        log Log.new(level, Thread.current.name, name, "#{message} -- Exception: #{exception.class}: #{exception.message}", payload, end_time, duration, tags, index, nil, metric)
         | 
| 308 | 
            +
                      end
         | 
| 309 | 
            +
                      raise exception
         | 
| 310 | 
            +
                    elsif duration >= min_duration
         | 
| 311 | 
            +
                      # Only log if the block took longer than 'min_duration' to complete
         | 
| 312 | 
            +
                      log Log.new(level, Thread.current.name, name, message, payload, end_time, duration, tags, index, nil, metric)
         | 
| 313 | 
            +
                    end
         | 
| 314 | 
            +
                  end
         | 
| 315 | 
            +
                end
         | 
| 316 | 
            +
             | 
| 300 317 | 
             
              end
         | 
| 301 318 | 
             
            end
         | 
| @@ -2,12 +2,9 @@ require 'thread' | |
| 2 2 | 
             
            class Thread
         | 
| 3 3 | 
             
              # Returns the name of the current thread
         | 
| 4 4 | 
             
              # Default:
         | 
| 5 | 
            -
              #    JRuby: The Java thread name
         | 
| 5 | 
            +
              #    JRuby: The underlying Java thread name
         | 
| 6 6 | 
             
              #    Other: String representation of this thread's object_id
         | 
| 7 7 | 
             
              if defined? JRuby
         | 
| 8 | 
            -
                # Design Note:
         | 
| 9 | 
            -
                #   In JRuby with "thread.pool.enabled=true" each Ruby Thread instance is
         | 
| 10 | 
            -
                #   new, even though the Java threads are being re-used from the pool
         | 
| 11 8 | 
             
                def name
         | 
| 12 9 | 
             
                  @name ||= JRuby.reference(self).native_thread.name
         | 
| 13 10 | 
             
                end
         | 
| @@ -17,8 +14,15 @@ class Thread | |
| 17 14 | 
             
                end
         | 
| 18 15 | 
             
              end
         | 
| 19 16 |  | 
| 20 | 
            -
              # Set the name of this thread | 
| 21 | 
            -
               | 
| 22 | 
            -
             | 
| 17 | 
            +
              # Set the name of this thread
         | 
| 18 | 
            +
              #   On JRuby it also sets the underlying Java Thread name
         | 
| 19 | 
            +
              if defined? JRuby
         | 
| 20 | 
            +
                def name=(name)
         | 
| 21 | 
            +
                  JRuby.reference(self).native_thread.name = @name = name.to_s
         | 
| 22 | 
            +
                end
         | 
| 23 | 
            +
              else
         | 
| 24 | 
            +
                def name=(name)
         | 
| 25 | 
            +
                  @name = name.to_s
         | 
| 26 | 
            +
                end
         | 
| 23 27 | 
             
              end
         | 
| 24 28 | 
             
            end
         | 
| @@ -105,11 +105,26 @@ module SemanticLogger | |
| 105 105 | 
             
                  queue_size
         | 
| 106 106 | 
             
                end
         | 
| 107 107 |  | 
| 108 | 
            +
                # Supply a block to be called whenever a metric is seen during benchmark logging
         | 
| 109 | 
            +
                #
         | 
| 110 | 
            +
                #  Parameters
         | 
| 111 | 
            +
                #    block
         | 
| 112 | 
            +
                #      The block to be called
         | 
| 113 | 
            +
                #
         | 
| 114 | 
            +
                # Example:
         | 
| 115 | 
            +
                #   SemanticLogger.on_metric do |log_struct|
         | 
| 116 | 
            +
                #     puts "#{log_struct.metric} was received. Log Struct: #{log_struct.inspect}"
         | 
| 117 | 
            +
                #   end
         | 
| 118 | 
            +
                def self.on_metric(&block)
         | 
| 119 | 
            +
                  (@@metric_subscribers  ||= ThreadSafe::Array.new) << block
         | 
| 120 | 
            +
                end
         | 
| 121 | 
            +
             | 
| 108 122 | 
             
                ############################################################################
         | 
| 109 123 | 
             
                protected
         | 
| 110 124 |  | 
| 111 | 
            -
                @@appender_thread | 
| 112 | 
            -
                @@queue | 
| 125 | 
            +
                @@appender_thread    = nil
         | 
| 126 | 
            +
                @@queue              = Queue.new
         | 
| 127 | 
            +
                @@metric_subscribers = nil
         | 
| 113 128 |  | 
| 114 129 | 
             
                # Queue to hold messages that need to be logged to the various appenders
         | 
| 115 130 | 
             
                def self.queue
         | 
| @@ -167,6 +182,7 @@ module SemanticLogger | |
| 167 182 | 
             
                            logger.error "Appender thread: Failed to log to appender: #{appender.inspect}", exc
         | 
| 168 183 | 
             
                          end
         | 
| 169 184 | 
             
                        end
         | 
| 185 | 
            +
                        call_metric_subscribers(message) if message.metric
         | 
| 170 186 | 
             
                        count += 1
         | 
| 171 187 | 
             
                        # Check every few log messages whether this appender thread is falling behind
         | 
| 172 188 | 
             
                        if count > lag_check_interval
         | 
| @@ -180,7 +196,7 @@ module SemanticLogger | |
| 180 196 | 
             
                        when :flush
         | 
| 181 197 | 
             
                          SemanticLogger.appenders.each do |appender|
         | 
| 182 198 | 
             
                            begin
         | 
| 183 | 
            -
                              logger. | 
| 199 | 
            +
                              logger.debug "Appender thread: Flushing appender: #{appender.name}"
         | 
| 184 200 | 
             
                              appender.flush
         | 
| 185 201 | 
             
                            rescue Exception => exc
         | 
| 186 202 | 
             
                              logger.error "Appender thread: Failed to flush appender: #{appender.inspect}", exc
         | 
| @@ -188,7 +204,7 @@ module SemanticLogger | |
| 188 204 | 
             
                          end
         | 
| 189 205 |  | 
| 190 206 | 
             
                          message[:reply_queue] << true if message[:reply_queue]
         | 
| 191 | 
            -
                          logger. | 
| 207 | 
            +
                          logger.debug "Appender thread: All appenders flushed"
         | 
| 192 208 | 
             
                        else
         | 
| 193 209 | 
             
                          logger.warn "Appender thread: Ignoring unknown command: #{message[:command]}"
         | 
| 194 210 | 
             
                        end
         | 
| @@ -213,5 +229,19 @@ module SemanticLogger | |
| 213 229 | 
             
                  end
         | 
| 214 230 | 
             
                end
         | 
| 215 231 |  | 
| 232 | 
            +
                # Call Metric subscribers
         | 
| 233 | 
            +
                def self.call_metric_subscribers(log_struct)
         | 
| 234 | 
            +
                  # If no subscribers registered, then return immediately
         | 
| 235 | 
            +
                  return unless @@metric_subscribers
         | 
| 236 | 
            +
             | 
| 237 | 
            +
                  @@metric_subscribers.each do |subscriber|
         | 
| 238 | 
            +
                    begin
         | 
| 239 | 
            +
                      subscriber.call(log_struct)
         | 
| 240 | 
            +
                    rescue Exception => exc
         | 
| 241 | 
            +
                      logger.error "Exception calling subscriber", exc
         | 
| 242 | 
            +
                    end
         | 
| 243 | 
            +
                  end
         | 
| 244 | 
            +
                end
         | 
| 245 | 
            +
             | 
| 216 246 | 
             
              end
         | 
| 217 247 | 
             
            end
         | 
| @@ -102,13 +102,12 @@ module SemanticLogger | |
| 102 102 |  | 
| 103 103 | 
             
                # Start appender thread if it is not already running
         | 
| 104 104 | 
             
                SemanticLogger::Logger.start_appender_thread
         | 
| 105 | 
            -
             | 
| 105 | 
            +
             | 
| 106 106 | 
             
                appender_instance
         | 
| 107 107 | 
             
              end
         | 
| 108 108 |  | 
| 109 109 | 
             
              # Remove an existing appender
         | 
| 110 110 | 
             
              # Currently only supports appender instances
         | 
| 111 | 
            -
              # TODO allow removing by filename, STDOUT etc..
         | 
| 112 111 | 
             
              def self.remove_appender(appender)
         | 
| 113 112 | 
             
                @@appenders.delete(appender)
         | 
| 114 113 | 
             
              end
         | 
| @@ -137,6 +136,20 @@ module SemanticLogger | |
| 137 136 | 
             
                SemanticLogger::Logger.start_appender_thread
         | 
| 138 137 | 
             
              end
         | 
| 139 138 |  | 
| 139 | 
            +
              # Supply a block to be called whenever a metric is seen during benchmark logging
         | 
| 140 | 
            +
              #
         | 
| 141 | 
            +
              #  Parameters
         | 
| 142 | 
            +
              #    block
         | 
| 143 | 
            +
              #      The block to be called
         | 
| 144 | 
            +
              #
         | 
| 145 | 
            +
              # Example:
         | 
| 146 | 
            +
              #   SemanticLogger.on_metric do |log_struct|
         | 
| 147 | 
            +
              #     puts "#{log_struct.metric} was received. Log Struct: #{log_struct.inspect}"
         | 
| 148 | 
            +
              #   end
         | 
| 149 | 
            +
              def self.on_metric(&block)
         | 
| 150 | 
            +
                SemanticLogger::Logger.on_metric(&block)
         | 
| 151 | 
            +
              end
         | 
| 152 | 
            +
             | 
| 140 153 | 
             
              ############################################################################
         | 
| 141 154 | 
             
              protected
         | 
| 142 155 |  | 
    
        data/test/appender_file_test.rb
    CHANGED
    
    | @@ -39,7 +39,7 @@ class AppenderFileTest < Test::Unit::TestCase | |
| 39 39 |  | 
| 40 40 | 
             
                  should "handle message, payload, and exception" do
         | 
| 41 41 | 
             
                    @appender.debug 'hello world', @hash, StandardError.new("StandardError")
         | 
| 42 | 
            -
                    assert_match /\d+-\d+-\d+ \d+:\d+:\d+.\d+ D \[\d+:#{@thread_name}\] SemanticLogger::Appender::File -- hello world -- #{@hash_str} -- StandardError: StandardError\n\n/, @io.string
         | 
| 42 | 
            +
                    assert_match /\d+-\d+-\d+ \d+:\d+:\d+.\d+ D \[\d+:#{@thread_name}\] SemanticLogger::Appender::File -- hello world -- #{@hash_str} -- Exception: StandardError: StandardError\n\n/, @io.string
         | 
| 43 43 | 
             
                  end
         | 
| 44 44 | 
             
                end
         | 
| 45 45 |  | 
    
        data/test/logger_test.rb
    CHANGED
    
    | @@ -17,10 +17,16 @@ class LoggerTest < Test::Unit::TestCase | |
| 17 17 | 
             
                  @mock_logger = MockLogger.new
         | 
| 18 18 | 
             
                  SemanticLogger.add_appender(@mock_logger)
         | 
| 19 19 |  | 
| 20 | 
            +
                  # Add mock metric subscriber
         | 
| 21 | 
            +
                  $last_metric = nil
         | 
| 22 | 
            +
                  SemanticLogger.on_metric do |log_struct|
         | 
| 23 | 
            +
                    $last_metric = log_struct.dup
         | 
| 24 | 
            +
                  end
         | 
| 25 | 
            +
             | 
| 20 26 | 
             
                  # Use this test's class name as the application name in the log output
         | 
| 21 | 
            -
                  @logger | 
| 22 | 
            -
                  @hash | 
| 23 | 
            -
                  @hash_str | 
| 27 | 
            +
                  @logger   = SemanticLogger::Logger.new(self.class, :trace)
         | 
| 28 | 
            +
                  @hash     = { :session_id => 'HSSKLEU@JDK767', :tracking_number => 12345 }
         | 
| 29 | 
            +
                  @hash_str = @hash.inspect.sub("{", "\\{").sub("}", "\\}")
         | 
| 24 30 | 
             
                  assert_equal [], @logger.tags
         | 
| 25 31 | 
             
                end
         | 
| 26 32 |  | 
| @@ -120,6 +126,14 @@ class LoggerTest < Test::Unit::TestCase | |
| 120 126 | 
             
                        SemanticLogger.flush
         | 
| 121 127 | 
             
                        assert_match /\d+-\d+-\d+ \d+:\d+:\d+.\d+ \w \[\d+:.+\] \(\d+\.\dms\) LoggerTest -- hello world -- Exception: RuntimeError: Test -- #{@hash_str}/, @mock_logger.message
         | 
| 122 128 | 
             
                      end
         | 
| 129 | 
            +
             | 
| 130 | 
            +
                      should "log #{level} info with metric" do
         | 
| 131 | 
            +
                        metric_name = '/my/custom/metric'
         | 
| 132 | 
            +
                        assert_equal "result", @logger.send("benchmark_#{level}".to_sym, 'hello world', metric: metric_name) { "result" } # Measure duration of the supplied block
         | 
| 133 | 
            +
                        SemanticLogger.flush
         | 
| 134 | 
            +
                        assert_match /\d+-\d+-\d+ \d+:\d+:\d+.\d+ \w \[\d+:.+\] \(\d+\.\dms\) LoggerTest -- hello world/, @mock_logger.message
         | 
| 135 | 
            +
                        assert metric_name, $last_metric.metric
         | 
| 136 | 
            +
                      end
         | 
| 123 137 | 
             
                    end
         | 
| 124 138 |  | 
| 125 139 | 
             
                    should "log when the block performs a return" do
         | 
    
        metadata
    CHANGED
    
    | @@ -1,41 +1,41 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification
         | 
| 2 2 | 
             
            name: semantic_logger
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            -
              version: 2. | 
| 4 | 
            +
              version: 2.7.0
         | 
| 5 5 | 
             
            platform: ruby
         | 
| 6 6 | 
             
            authors:
         | 
| 7 7 | 
             
            - Reid Morrison
         | 
| 8 8 | 
             
            autorequire: 
         | 
| 9 9 | 
             
            bindir: bin
         | 
| 10 10 | 
             
            cert_chain: []
         | 
| 11 | 
            -
            date:  | 
| 11 | 
            +
            date: 2014-03-07 00:00:00.000000000 Z
         | 
| 12 12 | 
             
            dependencies:
         | 
| 13 13 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 14 14 | 
             
              name: sync_attr
         | 
| 15 15 | 
             
              requirement: !ruby/object:Gem::Requirement
         | 
| 16 16 | 
             
                requirements:
         | 
| 17 | 
            -
                - -  | 
| 17 | 
            +
                - - ">="
         | 
| 18 18 | 
             
                  - !ruby/object:Gem::Version
         | 
| 19 19 | 
             
                    version: '1.0'
         | 
| 20 20 | 
             
              type: :runtime
         | 
| 21 21 | 
             
              prerelease: false
         | 
| 22 22 | 
             
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 23 23 | 
             
                requirements:
         | 
| 24 | 
            -
                - -  | 
| 24 | 
            +
                - - ">="
         | 
| 25 25 | 
             
                  - !ruby/object:Gem::Version
         | 
| 26 26 | 
             
                    version: '1.0'
         | 
| 27 27 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 28 28 | 
             
              name: thread_safe
         | 
| 29 29 | 
             
              requirement: !ruby/object:Gem::Requirement
         | 
| 30 30 | 
             
                requirements:
         | 
| 31 | 
            -
                - -  | 
| 31 | 
            +
                - - ">="
         | 
| 32 32 | 
             
                  - !ruby/object:Gem::Version
         | 
| 33 33 | 
             
                    version: 0.1.0
         | 
| 34 34 | 
             
              type: :runtime
         | 
| 35 35 | 
             
              prerelease: false
         | 
| 36 36 | 
             
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 37 37 | 
             
                requirements:
         | 
| 38 | 
            -
                - -  | 
| 38 | 
            +
                - - ">="
         | 
| 39 39 | 
             
                  - !ruby/object:Gem::Version
         | 
| 40 40 | 
             
                    version: 0.1.0
         | 
| 41 41 | 
             
            description: Semantic Logger takes logging in Ruby to a new level by adding several
         | 
| @@ -46,6 +46,10 @@ executables: [] | |
| 46 46 | 
             
            extensions: []
         | 
| 47 47 | 
             
            extra_rdoc_files: []
         | 
| 48 48 | 
             
            files:
         | 
| 49 | 
            +
            - LICENSE.txt
         | 
| 50 | 
            +
            - README.md
         | 
| 51 | 
            +
            - Rakefile
         | 
| 52 | 
            +
            - lib/semantic_logger.rb
         | 
| 49 53 | 
             
            - lib/semantic_logger/appender/base.rb
         | 
| 50 54 | 
             
            - lib/semantic_logger/appender/file.rb
         | 
| 51 55 | 
             
            - lib/semantic_logger/appender/mongodb.rb
         | 
| @@ -57,10 +61,6 @@ files: | |
| 57 61 | 
             
            - lib/semantic_logger/logger.rb
         | 
| 58 62 | 
             
            - lib/semantic_logger/semantic_logger.rb
         | 
| 59 63 | 
             
            - lib/semantic_logger/version.rb
         | 
| 60 | 
            -
            - lib/semantic_logger.rb
         | 
| 61 | 
            -
            - LICENSE.txt
         | 
| 62 | 
            -
            - Rakefile
         | 
| 63 | 
            -
            - README.md
         | 
| 64 64 | 
             
            - test/appender_file_test.rb
         | 
| 65 65 | 
             
            - test/appender_mongodb_test.rb
         | 
| 66 66 | 
             
            - test/appender_syslog_test.rb
         | 
| @@ -78,17 +78,17 @@ require_paths: | |
| 78 78 | 
             
            - lib
         | 
| 79 79 | 
             
            required_ruby_version: !ruby/object:Gem::Requirement
         | 
| 80 80 | 
             
              requirements:
         | 
| 81 | 
            -
              - -  | 
| 81 | 
            +
              - - ">="
         | 
| 82 82 | 
             
                - !ruby/object:Gem::Version
         | 
| 83 83 | 
             
                  version: '0'
         | 
| 84 84 | 
             
            required_rubygems_version: !ruby/object:Gem::Requirement
         | 
| 85 85 | 
             
              requirements:
         | 
| 86 | 
            -
              - -  | 
| 86 | 
            +
              - - ">="
         | 
| 87 87 | 
             
                - !ruby/object:Gem::Version
         | 
| 88 88 | 
             
                  version: '0'
         | 
| 89 89 | 
             
            requirements: []
         | 
| 90 90 | 
             
            rubyforge_project: 
         | 
| 91 | 
            -
            rubygems_version: 2. | 
| 91 | 
            +
            rubygems_version: 2.2.0
         | 
| 92 92 | 
             
            signing_key: 
         | 
| 93 93 | 
             
            specification_version: 4
         | 
| 94 94 | 
             
            summary: Improved logging for Ruby
         |