logster 2.5.1 → 2.6.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/.travis.yml +2 -0
- data/CHANGELOG.md +9 -0
- data/README.md +15 -1
- data/Rakefile +1 -0
- data/assets/javascript/client-app.js +204 -168
- data/assets/javascript/vendor.js +5132 -5833
- data/assets/stylesheets/client-app.css +1 -1
- data/client-app/.eslintrc.js +17 -5
- data/client-app/.travis.yml +4 -3
- data/client-app/app/app.js +5 -7
- data/client-app/app/components/actions-menu.js +24 -17
- data/client-app/app/components/back-trace.js +148 -0
- data/client-app/app/components/env-tab.js +16 -12
- data/client-app/app/components/message-info.js +84 -7
- data/client-app/app/components/message-row.js +13 -15
- data/client-app/app/components/panel-resizer.js +63 -45
- data/client-app/app/components/patterns-list.js +6 -6
- data/client-app/app/components/update-time.js +13 -13
- data/client-app/app/controllers/index.js +4 -2
- data/client-app/app/index.html +1 -1
- data/client-app/app/initializers/app-init.js +1 -1
- data/client-app/app/lib/decorators.js +11 -0
- data/client-app/app/lib/preload.js +14 -3
- data/client-app/app/lib/utilities.js +63 -36
- data/client-app/app/models/group.js +6 -1
- data/client-app/app/models/message-collection.js +9 -7
- data/client-app/app/models/message.js +25 -20
- data/client-app/app/router.js +4 -6
- data/client-app/app/styles/app.css +18 -4
- data/client-app/app/templates/components/actions-menu.hbs +6 -2
- data/client-app/app/templates/components/back-trace.hbs +8 -0
- data/client-app/app/templates/components/message-info.hbs +7 -2
- data/client-app/app/templates/index.hbs +4 -1
- data/client-app/config/environment.js +1 -1
- data/client-app/config/optional-features.json +4 -1
- data/client-app/ember-cli-build.js +2 -3
- data/client-app/package-lock.json +9712 -2884
- data/client-app/package.json +25 -22
- data/client-app/preload-json-manager.rb +62 -0
- data/client-app/testem.js +0 -1
- data/client-app/tests/index.html +1 -1
- data/client-app/tests/integration/components/back-trace-test.js +109 -0
- data/client-app/tests/integration/components/message-info-test.js +4 -3
- data/client-app/tests/integration/components/patterns-list-test.js +7 -2
- data/lib/logster.rb +1 -0
- data/lib/logster/base_store.rb +16 -9
- data/lib/logster/configuration.rb +12 -2
- data/lib/logster/defer_logger.rb +1 -1
- data/lib/logster/logger.rb +12 -0
- data/lib/logster/message.rb +89 -30
- data/lib/logster/middleware/viewer.rb +44 -8
- data/lib/logster/redis_store.rb +69 -51
- data/lib/logster/suppression_pattern.rb +1 -1
- data/lib/logster/version.rb +1 -1
- data/logster.gemspec +1 -1
- data/test/logster/middleware/test_viewer.rb +100 -0
- data/test/logster/test_base_store.rb +16 -0
- data/test/logster/test_defer_logger.rb +1 -1
- data/test/logster/test_message.rb +142 -54
- data/test/logster/test_redis_store.rb +99 -39
- metadata +11 -6
| @@ -38,7 +38,7 @@ module Logster | |
| 38 38 |  | 
| 39 39 | 
             
                      elsif resource =~ /\/message\/([0-9a-f]+)$/
         | 
| 40 40 | 
             
                        if env[REQUEST_METHOD] != "DELETE"
         | 
| 41 | 
            -
                          return method_not_allowed("DELETE | 
| 41 | 
            +
                          return method_not_allowed("DELETE")
         | 
| 42 42 | 
             
                        end
         | 
| 43 43 |  | 
| 44 44 | 
             
                        key = $1
         | 
| @@ -87,7 +87,7 @@ module Logster | |
| 87 87 |  | 
| 88 88 | 
             
                      elsif resource =~ /\/clear$/
         | 
| 89 89 | 
             
                        if env[REQUEST_METHOD] != "POST"
         | 
| 90 | 
            -
                          return method_not_allowed("POST | 
| 90 | 
            +
                          return method_not_allowed("POST")
         | 
| 91 91 | 
             
                        end
         | 
| 92 92 | 
             
                        Logster.store.clear
         | 
| 93 93 | 
             
                        return [200, {}, ["Messages cleared"]]
         | 
| @@ -139,12 +139,12 @@ module Logster | |
| 139 139 |  | 
| 140 140 | 
             
                        set_name = $1
         | 
| 141 141 | 
             
                        req = Rack::Request.new(env)
         | 
| 142 | 
            -
                        return method_not_allowed if req.request_method == "GET"
         | 
| 142 | 
            +
                        return method_not_allowed(%w[POST PUT DELETE]) if req.request_method == "GET"
         | 
| 143 143 |  | 
| 144 144 | 
             
                        update_patterns(set_name, req)
         | 
| 145 145 | 
             
                      elsif resource == "/reset-count.json"
         | 
| 146 146 | 
             
                        req = Rack::Request.new(env)
         | 
| 147 | 
            -
                        return method_not_allowed("PUT | 
| 147 | 
            +
                        return method_not_allowed("PUT") if req.request_method != "PUT"
         | 
| 148 148 | 
             
                        pattern = nil
         | 
| 149 149 | 
             
                        if [true, "true"].include?(req.params["hard"])
         | 
| 150 150 | 
             
                          pattern = Logster.store.ignore.find do |patt|
         | 
| @@ -170,6 +170,16 @@ module Logster | |
| 170 170 | 
             
                        else
         | 
| 171 171 | 
             
                          not_found
         | 
| 172 172 | 
             
                        end
         | 
| 173 | 
            +
                      elsif resource == '/solve-group'
         | 
| 174 | 
            +
                        return not_allowed unless Logster.config.enable_custom_patterns_via_ui
         | 
| 175 | 
            +
                        req = Rack::Request.new(env)
         | 
| 176 | 
            +
                        return method_not_allowed("POST") if req.request_method != "POST"
         | 
| 177 | 
            +
                        group = Logster.store.find_pattern_groups do |patt|
         | 
| 178 | 
            +
                          patt.inspect == req.params["regex"]
         | 
| 179 | 
            +
                        end.first
         | 
| 180 | 
            +
                        return not_found("No such pattern group exists") if !group
         | 
| 181 | 
            +
                        group.messages_keys.each { |k| Logster.store.solve(k) }
         | 
| 182 | 
            +
                        return [200, {}, []]
         | 
| 173 183 | 
             
                      else
         | 
| 174 184 | 
             
                        not_found
         | 
| 175 185 | 
             
                      end
         | 
| @@ -243,7 +253,7 @@ module Logster | |
| 243 253 | 
             
                    when "DELETE"
         | 
| 244 254 | 
             
                      record.destroy
         | 
| 245 255 | 
             
                    else
         | 
| 246 | 
            -
                      return method_not_allowed( | 
| 256 | 
            +
                      return method_not_allowed(%w[POST PUT DELETE])
         | 
| 247 257 | 
             
                    end
         | 
| 248 258 |  | 
| 249 259 | 
             
                    [200, { "Content-Type" => "application/json" }, [JSON.generate(pattern: record.to_s)]]
         | 
| @@ -277,8 +287,11 @@ module Logster | |
| 277 287 | 
             
                    [403, {}, [message]]
         | 
| 278 288 | 
             
                  end
         | 
| 279 289 |  | 
| 280 | 
            -
                  def method_not_allowed( | 
| 281 | 
            -
                     | 
| 290 | 
            +
                  def method_not_allowed(allowed_methods)
         | 
| 291 | 
            +
                    if Array === allowed_methods
         | 
| 292 | 
            +
                      allowed_methods = allowed_methods.join(", ")
         | 
| 293 | 
            +
                    end
         | 
| 294 | 
            +
                    [405, { "Allow" => allowed_methods }, []]
         | 
| 282 295 | 
             
                  end
         | 
| 283 296 |  | 
| 284 297 | 
             
                  def parse_regex(string)
         | 
| @@ -317,13 +330,36 @@ module Logster | |
| 317 330 | 
             
                    Rack::Utils.escape_html(JSON.fast_generate(payload))
         | 
| 318 331 | 
             
                  end
         | 
| 319 332 |  | 
| 333 | 
            +
                  def preload_backtrace_data
         | 
| 334 | 
            +
                    gems_data = []
         | 
| 335 | 
            +
                    Gem::Specification.find_all do |gem|
         | 
| 336 | 
            +
                      url = gem.metadata["source_code_uri"] || gem.homepage
         | 
| 337 | 
            +
                      if url && url.match(/^https?:\/\/github.com\//)
         | 
| 338 | 
            +
                        gems_data << { name: gem.name, url: url }
         | 
| 339 | 
            +
                      end
         | 
| 340 | 
            +
                    end
         | 
| 341 | 
            +
                    {
         | 
| 342 | 
            +
                      gems_data: gems_data,
         | 
| 343 | 
            +
                      directories: Logster.config.project_directories
         | 
| 344 | 
            +
                    }
         | 
| 345 | 
            +
                  end
         | 
| 346 | 
            +
             | 
| 320 347 | 
             
                  def body(preload)
         | 
| 321 348 | 
             
                    root_url = @logs_path
         | 
| 322 349 | 
             
                    root_url += "/" if root_url[-1] != "/"
         | 
| 323 350 | 
             
                    preload.merge!(
         | 
| 324 351 | 
             
                      env_expandable_keys: Logster.config.env_expandable_keys,
         | 
| 325 | 
            -
                      patterns_enabled: Logster.config.enable_custom_patterns_via_ui
         | 
| 352 | 
            +
                      patterns_enabled: Logster.config.enable_custom_patterns_via_ui,
         | 
| 353 | 
            +
                      application_version: Logster.config.application_version
         | 
| 326 354 | 
             
                    )
         | 
| 355 | 
            +
                    backtrace_links_enabled = Logster.config.enable_backtrace_links
         | 
| 356 | 
            +
                    gems_dir = Logster.config.gems_dir
         | 
| 357 | 
            +
                    gems_dir += "/" if gems_dir[-1] != "/"
         | 
| 358 | 
            +
                    preload.merge!(gems_dir: gems_dir, backtrace_links_enabled: backtrace_links_enabled)
         | 
| 359 | 
            +
             | 
| 360 | 
            +
                    if backtrace_links_enabled
         | 
| 361 | 
            +
                      preload.merge!(preload_backtrace_data)
         | 
| 362 | 
            +
                    end
         | 
| 327 363 | 
             
                    <<~HTML
         | 
| 328 364 | 
             
                      <!doctype html>
         | 
| 329 365 | 
             
                      <html>
         | 
    
        data/lib/logster/redis_store.rb
    CHANGED
    
    | @@ -6,6 +6,7 @@ require 'logster/redis_rate_limiter' | |
| 6 6 |  | 
| 7 7 | 
             
            module Logster
         | 
| 8 8 | 
             
              class RedisStore < BaseStore
         | 
| 9 | 
            +
                ENV_PREFIX = "logster-env-"
         | 
| 9 10 |  | 
| 10 11 | 
             
                attr_accessor :redis, :max_backlog, :redis_raw_connection
         | 
| 11 12 | 
             
                attr_writer :redis_prefix
         | 
| @@ -21,15 +22,14 @@ module Logster | |
| 21 22 | 
             
                def save(message)
         | 
| 22 23 | 
             
                  if keys = message.solved_keys
         | 
| 23 24 | 
             
                    keys.each do |solved|
         | 
| 24 | 
            -
                      return  | 
| 25 | 
            +
                      return false if @redis.hget(solved_key, solved)
         | 
| 25 26 | 
             
                    end
         | 
| 26 27 | 
             
                  end
         | 
| 27 | 
            -
                  apply_max_size_limit(message)
         | 
| 28 28 |  | 
| 29 29 | 
             
                  @redis.multi do
         | 
| 30 30 | 
             
                    @redis.hset(grouping_key, message.grouping_key, message.key)
         | 
| 31 31 | 
             
                    @redis.rpush(list_key, message.key)
         | 
| 32 | 
            -
                    update_message(message)
         | 
| 32 | 
            +
                    update_message(message, save_env: true)
         | 
| 33 33 | 
             
                  end
         | 
| 34 34 |  | 
| 35 35 | 
             
                  trim
         | 
| @@ -46,7 +46,7 @@ module Logster | |
| 46 46 | 
             
                      save_pattern_group(group) if group.changed?
         | 
| 47 47 | 
             
                    end
         | 
| 48 48 | 
             
                    @redis.hdel(hash_key, msg.key)
         | 
| 49 | 
            -
                     | 
| 49 | 
            +
                    delete_env(msg.key)
         | 
| 50 50 | 
             
                    @redis.hdel(grouping_key, msg.grouping_key)
         | 
| 51 51 | 
             
                    @redis.lrem(list_key, -1, msg.key)
         | 
| 52 52 | 
             
                  end
         | 
| @@ -60,26 +60,26 @@ module Logster | |
| 60 60 | 
             
                      save_pattern_group(group) if group.changed?
         | 
| 61 61 | 
             
                    end
         | 
| 62 62 | 
             
                    @redis.hdel(hash_key, message_keys)
         | 
| 63 | 
            -
                    @redis.hdel(env_key, message_keys)
         | 
| 64 63 | 
             
                    @redis.hdel(grouping_key, grouping_keys)
         | 
| 65 64 | 
             
                    message_keys.each do |k|
         | 
| 66 65 | 
             
                      @redis.lrem(list_key, -1, k)
         | 
| 66 | 
            +
                      delete_env(k)
         | 
| 67 67 | 
             
                    end
         | 
| 68 68 | 
             
                  end
         | 
| 69 69 | 
             
                end
         | 
| 70 70 |  | 
| 71 | 
            -
                def replace_and_bump(message | 
| 71 | 
            +
                def replace_and_bump(message)
         | 
| 72 72 | 
             
                  # TODO make it atomic
         | 
| 73 73 | 
             
                  exists = @redis.hexists(hash_key, message.key)
         | 
| 74 74 | 
             
                  return false unless exists
         | 
| 75 75 |  | 
| 76 76 | 
             
                  @redis.multi do
         | 
| 77 77 | 
             
                    @redis.hset(hash_key, message.key, message.to_json(exclude_env: true))
         | 
| 78 | 
            -
                     | 
| 78 | 
            +
                    push_env(message.key, message.env_buffer) if message.has_env_buffer?
         | 
| 79 79 | 
             
                    @redis.lrem(list_key, -1, message.key)
         | 
| 80 80 | 
             
                    @redis.rpush(list_key, message.key)
         | 
| 81 81 | 
             
                  end
         | 
| 82 | 
            -
             | 
| 82 | 
            +
                  message.env_buffer = [] if message.has_env_buffer?
         | 
| 83 83 | 
             
                  check_rate_limits(message.severity)
         | 
| 84 84 |  | 
| 85 85 | 
             
                  true
         | 
| @@ -164,22 +164,21 @@ module Logster | |
| 164 164 | 
             
                def clear
         | 
| 165 165 | 
             
                  RedisRateLimiter.clear_all(@redis)
         | 
| 166 166 | 
             
                  @redis.del(solved_key)
         | 
| 167 | 
            +
                  all_keys = @redis.lrange(list_key, 0, -1)
         | 
| 167 168 | 
             
                  @redis.del(list_key)
         | 
| 168 | 
            -
                   | 
| 169 | 
            -
                  if  | 
| 169 | 
            +
                  protected_keys = @redis.smembers(protected_key) || []
         | 
| 170 | 
            +
                  if protected_keys.empty?
         | 
| 170 171 | 
             
                    @redis.del(hash_key)
         | 
| 171 | 
            -
                     | 
| 172 | 
            +
                    all_keys.each { |k| delete_env(k) }
         | 
| 172 173 | 
             
                    @redis.del(pattern_groups_key)
         | 
| 173 174 | 
             
                    @redis.del(grouping_key)
         | 
| 174 175 | 
             
                  else
         | 
| 175 | 
            -
                     | 
| 176 | 
            -
                    protected_env = @redis.mapped_hmget(env_key, *keys)
         | 
| 176 | 
            +
                    protected_messages = @redis.mapped_hmget(hash_key, *protected_keys)
         | 
| 177 177 | 
             
                    @redis.del(hash_key)
         | 
| 178 | 
            -
                    @redis. | 
| 179 | 
            -
                     | 
| 180 | 
            -
                    @redis.mapped_hmset(env_key, protected_env)
         | 
| 178 | 
            +
                    @redis.mapped_hmset(hash_key, protected_messages)
         | 
| 179 | 
            +
                    (all_keys - protected_keys).each { |k| delete_env(k) }
         | 
| 181 180 |  | 
| 182 | 
            -
                    sorted =  | 
| 181 | 
            +
                    sorted = protected_messages
         | 
| 183 182 | 
             
                      .values
         | 
| 184 183 | 
             
                      .map { |string|
         | 
| 185 184 | 
             
                        Message.from_json(string) rescue nil
         | 
| @@ -203,10 +202,10 @@ module Logster | |
| 203 202 | 
             
                # Delete everything, included protected messages
         | 
| 204 203 | 
             
                # (use in tests)
         | 
| 205 204 | 
             
                def clear_all
         | 
| 205 | 
            +
                  @redis.lrange(list_key, 0, -1).each { |k| delete_env(k) }
         | 
| 206 206 | 
             
                  @redis.del(list_key)
         | 
| 207 207 | 
             
                  @redis.del(protected_key)
         | 
| 208 208 | 
             
                  @redis.del(hash_key)
         | 
| 209 | 
            -
                  @redis.del(env_key)
         | 
| 210 209 | 
             
                  @redis.del(grouping_key)
         | 
| 211 210 | 
             
                  @redis.del(solved_key)
         | 
| 212 211 | 
             
                  @redis.del(ignored_logs_count_key)
         | 
| @@ -235,17 +234,35 @@ module Logster | |
| 235 234 | 
             
                  bulk_get(@redis.lrange(list_key, 0, -1), with_env: with_env)
         | 
| 236 235 | 
             
                end
         | 
| 237 236 |  | 
| 237 | 
            +
                BULK_ENV_GET_LUA = <<~LUA
         | 
| 238 | 
            +
                  local results = {};
         | 
| 239 | 
            +
                  for i = 1, table.getn(KEYS), 1 do
         | 
| 240 | 
            +
                    results[i] = { KEYS[i], redis.call('LRANGE', KEYS[i], 0, -1) };
         | 
| 241 | 
            +
                  end
         | 
| 242 | 
            +
                  return results;
         | 
| 243 | 
            +
                LUA
         | 
| 244 | 
            +
             | 
| 238 245 | 
             
                def bulk_get(message_keys, with_env: true)
         | 
| 239 246 | 
             
                  return [] if !message_keys || message_keys.size == 0
         | 
| 240 | 
            -
                  envs =  | 
| 247 | 
            +
                  envs = nil
         | 
| 248 | 
            +
                  if with_env
         | 
| 249 | 
            +
                    envs = {}
         | 
| 250 | 
            +
                    @redis.eval(
         | 
| 251 | 
            +
                      BULK_ENV_GET_LUA,
         | 
| 252 | 
            +
                      keys: message_keys.map { |k| env_prefix(k) }
         | 
| 253 | 
            +
                    ).to_h.each do |k, v|
         | 
| 254 | 
            +
                      next if v.size == 0
         | 
| 255 | 
            +
                      parsed = v.size == 1 ? JSON.parse(v[0]) : v.map { |e| JSON.parse(e) }
         | 
| 256 | 
            +
                      envs[env_unprefix(k)] = parsed
         | 
| 257 | 
            +
                    end
         | 
| 258 | 
            +
                  end
         | 
| 241 259 | 
             
                  messages = @redis.hmget(hash_key, message_keys).map! do |json|
         | 
| 242 260 | 
             
                    next if !json || json.size == 0
         | 
| 243 261 | 
             
                    message = Message.from_json(json)
         | 
| 244 | 
            -
                    if with_env
         | 
| 262 | 
            +
                    if with_env && envs
         | 
| 245 263 | 
             
                      env = envs[message.key]
         | 
| 246 264 | 
             
                      if !message.env || message.env.size == 0
         | 
| 247 | 
            -
                        env = env  | 
| 248 | 
            -
                        message.env = env
         | 
| 265 | 
            +
                        message.env = env || {}
         | 
| 249 266 | 
             
                      end
         | 
| 250 267 | 
             
                    end
         | 
| 251 268 | 
             
                    message
         | 
| @@ -255,20 +272,20 @@ module Logster | |
| 255 272 | 
             
                end
         | 
| 256 273 |  | 
| 257 274 | 
             
                def get_env(message_key)
         | 
| 258 | 
            -
                   | 
| 259 | 
            -
                  return if ! | 
| 260 | 
            -
                  JSON.parse( | 
| 275 | 
            +
                  envs = @redis.lrange(env_prefix(message_key), 0, -1)
         | 
| 276 | 
            +
                  return if !envs || envs.size == 0
         | 
| 277 | 
            +
                  envs.size == 1 ? JSON.parse(envs[0]) : envs.map { |j| JSON.parse(j) }
         | 
| 261 278 | 
             
                end
         | 
| 262 279 |  | 
| 263 280 | 
             
                def protect(message_key)
         | 
| 264 | 
            -
                  if message = get(message_key)
         | 
| 281 | 
            +
                  if message = get(message_key, load_env: false)
         | 
| 265 282 | 
             
                    message.protected = true
         | 
| 266 283 | 
             
                    update_message(message)
         | 
| 267 284 | 
             
                  end
         | 
| 268 285 | 
             
                end
         | 
| 269 286 |  | 
| 270 287 | 
             
                def unprotect(message_key)
         | 
| 271 | 
            -
                  if message = get(message_key)
         | 
| 288 | 
            +
                  if message = get(message_key, load_env: false)
         | 
| 272 289 | 
             
                    message.protected = false
         | 
| 273 290 | 
             
                    update_message(message)
         | 
| 274 291 | 
             
                  else
         | 
| @@ -376,13 +393,11 @@ module Logster | |
| 376 393 |  | 
| 377 394 | 
             
                protected
         | 
| 378 395 |  | 
| 379 | 
            -
                def clear_solved | 
| 380 | 
            -
             | 
| 396 | 
            +
                def clear_solved
         | 
| 381 397 | 
             
                  ignores = Set.new(@redis.hkeys(solved_key) || [])
         | 
| 382 398 |  | 
| 383 399 | 
             
                  if ignores.length > 0
         | 
| 384 | 
            -
                     | 
| 385 | 
            -
                    message_keys = @redis.lrange(list_key, start, -1) || []
         | 
| 400 | 
            +
                    message_keys = @redis.lrange(list_key, 0, -1) || []
         | 
| 386 401 |  | 
| 387 402 | 
             
                    bulk_get(message_keys).each do |message|
         | 
| 388 403 | 
             
                      unless (ignores & (message.solved_keys || [])).empty?
         | 
| @@ -397,7 +412,7 @@ module Logster | |
| 397 412 | 
             
                    removed_keys = []
         | 
| 398 413 | 
             
                    while removed_key = @redis.lpop(list_key)
         | 
| 399 414 | 
             
                      unless @redis.sismember(protected_key, removed_key)
         | 
| 400 | 
            -
                        rmsg = get  | 
| 415 | 
            +
                        rmsg = get(removed_key, load_env: false)
         | 
| 401 416 | 
             
                        delete(rmsg)
         | 
| 402 417 | 
             
                        break
         | 
| 403 418 | 
             
                      else
         | 
| @@ -410,9 +425,9 @@ module Logster | |
| 410 425 | 
             
                  end
         | 
| 411 426 | 
             
                end
         | 
| 412 427 |  | 
| 413 | 
            -
                def update_message(message)
         | 
| 428 | 
            +
                def update_message(message, save_env: false)
         | 
| 414 429 | 
             
                  @redis.hset(hash_key, message.key, message.to_json(exclude_env: true))
         | 
| 415 | 
            -
                   | 
| 430 | 
            +
                  push_env(message.key, message.env) if save_env
         | 
| 416 431 | 
             
                  if message.protected
         | 
| 417 432 | 
             
                    @redis.sadd(protected_key, message.key)
         | 
| 418 433 | 
             
                  else
         | 
| @@ -571,7 +586,7 @@ module Logster | |
| 571 586 | 
             
                end
         | 
| 572 587 |  | 
| 573 588 | 
             
                def protected_key
         | 
| 574 | 
            -
                  @ | 
| 589 | 
            +
                  @protected_key ||= "__LOGSTER__SAVED"
         | 
| 575 590 | 
             
                end
         | 
| 576 591 |  | 
| 577 592 | 
             
                def grouping_key
         | 
| @@ -608,22 +623,6 @@ module Logster | |
| 608 623 | 
             
                  )
         | 
| 609 624 | 
             
                end
         | 
| 610 625 |  | 
| 611 | 
            -
                def apply_max_size_limit(message)
         | 
| 612 | 
            -
                  size = message.to_json(exclude_env: true).bytesize
         | 
| 613 | 
            -
                  env_size = message.env_json.bytesize
         | 
| 614 | 
            -
                  max_size = Logster.config.maximum_message_size_bytes
         | 
| 615 | 
            -
                  if size + env_size > max_size
         | 
| 616 | 
            -
                    # env is most likely the reason for the large size
         | 
| 617 | 
            -
                    # truncate it so the overall size is < the limit
         | 
| 618 | 
            -
                    if Array === message.env
         | 
| 619 | 
            -
                      # the - 1 at the end ensures the size goes a little bit below the limit
         | 
| 620 | 
            -
                      truncate_at = (message.env.size.to_f * max_size.to_f / (env_size + size)).to_i - 1
         | 
| 621 | 
            -
                      truncate_at = 1 if truncate_at < 1
         | 
| 622 | 
            -
                      message.env = message.env[0...truncate_at]
         | 
| 623 | 
            -
                    end
         | 
| 624 | 
            -
                  end
         | 
| 625 | 
            -
                end
         | 
| 626 | 
            -
             | 
| 627 626 | 
             
                def register_rate_limit(severities, limit, duration, callback)
         | 
| 628 627 | 
             
                  severities = [severities] unless severities.is_a?(Array)
         | 
| 629 628 | 
             
                  redis = (@redis_raw_connection && @redis_prefix) ? @redis_raw_connection : @redis
         | 
| @@ -636,5 +635,24 @@ module Logster | |
| 636 635 | 
             
                  rate_limits[self.redis_prefix] << rate_limiter
         | 
| 637 636 | 
             
                  rate_limiter
         | 
| 638 637 | 
             
                end
         | 
| 638 | 
            +
             | 
| 639 | 
            +
                def push_env(message_key, env)
         | 
| 640 | 
            +
                  prefixed = env_prefix(message_key)
         | 
| 641 | 
            +
                  env = [env] unless Array === env
         | 
| 642 | 
            +
                  @redis.lpush(prefixed, env.map(&:to_json).reverse)
         | 
| 643 | 
            +
                  @redis.ltrim(prefixed, 0, Logster.config.max_env_count_per_message - 1)
         | 
| 644 | 
            +
                end
         | 
| 645 | 
            +
             | 
| 646 | 
            +
                def delete_env(message_key)
         | 
| 647 | 
            +
                  @redis.del(env_prefix(message_key))
         | 
| 648 | 
            +
                end
         | 
| 649 | 
            +
             | 
| 650 | 
            +
                def env_unprefix(key)
         | 
| 651 | 
            +
                  key.sub(ENV_PREFIX, "")
         | 
| 652 | 
            +
                end
         | 
| 653 | 
            +
             | 
| 654 | 
            +
                def env_prefix(key)
         | 
| 655 | 
            +
                  ENV_PREFIX + key
         | 
| 656 | 
            +
                end
         | 
| 639 657 | 
             
              end
         | 
| 640 658 | 
             
            end
         | 
| @@ -24,7 +24,7 @@ module Logster | |
| 24 24 | 
             
                def retro_delete_messages
         | 
| 25 25 | 
             
                  keys = []
         | 
| 26 26 | 
             
                  grouping_keys = []
         | 
| 27 | 
            -
                  @store.get_all_messages.each do |message|
         | 
| 27 | 
            +
                  @store.get_all_messages(with_env: false).each do |message|
         | 
| 28 28 | 
             
                    if message =~ self.pattern
         | 
| 29 29 | 
             
                      keys << message.key
         | 
| 30 30 | 
             
                      grouping_keys << message.grouping_key
         | 
    
        data/lib/logster/version.rb
    CHANGED
    
    
    
        data/logster.gemspec
    CHANGED
    
    | @@ -32,7 +32,7 @@ Gem::Specification.new do |spec| | |
| 32 32 | 
             
              spec.add_development_dependency "guard"
         | 
| 33 33 | 
             
              spec.add_development_dependency "guard-minitest"
         | 
| 34 34 | 
             
              spec.add_development_dependency "timecop"
         | 
| 35 | 
            -
              spec.add_development_dependency "byebug"
         | 
| 35 | 
            +
              spec.add_development_dependency "byebug", "~> 11.0.0"
         | 
| 36 36 | 
             
              spec.add_development_dependency "rubocop", "~> 0.69.0"
         | 
| 37 37 | 
             
              spec.add_development_dependency "rubocop-discourse"
         | 
| 38 38 | 
             
            end
         | 
| @@ -370,4 +370,104 @@ class TestViewer < Minitest::Test | |
| 370 370 | 
             
                response = request.get("/logsie/fetch-env/123456abc.json")
         | 
| 371 371 | 
             
                assert_equal(404, response.status)
         | 
| 372 372 | 
             
              end
         | 
| 373 | 
            +
             | 
| 374 | 
            +
              def test_solve_group_api_requires_post_request
         | 
| 375 | 
            +
                Logster.config.enable_custom_patterns_via_ui = true
         | 
| 376 | 
            +
                Logster::GroupingPattern.new(/gotta be post/).save
         | 
| 377 | 
            +
                msg = Logster.store.report(
         | 
| 378 | 
            +
                  Logger::WARN,
         | 
| 379 | 
            +
                  '',
         | 
| 380 | 
            +
                  'gotta be post 22',
         | 
| 381 | 
            +
                  env: { "application_version" => "abc" },
         | 
| 382 | 
            +
                  backtrace: "aa"
         | 
| 383 | 
            +
                )
         | 
| 384 | 
            +
                latest = Logster.store.latest
         | 
| 385 | 
            +
                assert_equal(1, latest.size)
         | 
| 386 | 
            +
                assert_equal(msg.key, latest.first["messages"].first.key)
         | 
| 387 | 
            +
                %i[get head options].each do |m|
         | 
| 388 | 
            +
                  response = request.public_send(m, "/logsie/solve-group", params: { regex: "/gotta be post/" })
         | 
| 389 | 
            +
                  assert_equal(405, response.status)
         | 
| 390 | 
            +
                  assert_equal("POST", response.headers["Allow"])
         | 
| 391 | 
            +
                end
         | 
| 392 | 
            +
                latest = Logster.store.latest
         | 
| 393 | 
            +
                assert_equal(1, latest.size)
         | 
| 394 | 
            +
                assert_equal(msg.key, latest.first["messages"].first.key)
         | 
| 395 | 
            +
              ensure
         | 
| 396 | 
            +
                Logster.config.enable_custom_patterns_via_ui = false
         | 
| 397 | 
            +
              end
         | 
| 398 | 
            +
             | 
| 399 | 
            +
              def test_solve_group_returns_404_when_pattern_doesnt_exist
         | 
| 400 | 
            +
                Logster.config.enable_custom_patterns_via_ui = true
         | 
| 401 | 
            +
                Logster::GroupingPattern.new(/some pattern/).save
         | 
| 402 | 
            +
                msg = Logster.store.report(
         | 
| 403 | 
            +
                  Logger::WARN,
         | 
| 404 | 
            +
                  '',
         | 
| 405 | 
            +
                  'some pattern 22',
         | 
| 406 | 
            +
                  env: { "application_version" => "abc" },
         | 
| 407 | 
            +
                  backtrace: "aa"
         | 
| 408 | 
            +
                )
         | 
| 409 | 
            +
                latest = Logster.store.latest
         | 
| 410 | 
            +
                assert_equal(1, latest.size)
         | 
| 411 | 
            +
                assert_equal(msg.key, latest.first["messages"].first.key)
         | 
| 412 | 
            +
                response = request.post("/logsie/solve-group", params: { regex: "/i dont exist/" })
         | 
| 413 | 
            +
                assert_equal(404, response.status)
         | 
| 414 | 
            +
                latest = Logster.store.latest
         | 
| 415 | 
            +
                assert_equal(1, latest.size)
         | 
| 416 | 
            +
                assert_equal(msg.key, latest.first["messages"].first.key)
         | 
| 417 | 
            +
              ensure
         | 
| 418 | 
            +
                Logster.config.enable_custom_patterns_via_ui = false
         | 
| 419 | 
            +
              end
         | 
| 420 | 
            +
             | 
| 421 | 
            +
              def test_solving_grouped_messages
         | 
| 422 | 
            +
                Logster.config.enable_custom_patterns_via_ui = true
         | 
| 423 | 
            +
                backtrace = "a b c d"
         | 
| 424 | 
            +
                Logster::GroupingPattern.new(/test pattern/).save
         | 
| 425 | 
            +
                msg1 = Logster.store.report(Logger::WARN, '', 'test pattern 1', backtrace: backtrace)
         | 
| 426 | 
            +
                msg2 = Logster.store.report(
         | 
| 427 | 
            +
                  Logger::WARN,
         | 
| 428 | 
            +
                  '',
         | 
| 429 | 
            +
                  'test pattern 2',
         | 
| 430 | 
            +
                  env: { "application_version" => "abc" },
         | 
| 431 | 
            +
                  backtrace: backtrace
         | 
| 432 | 
            +
                )
         | 
| 433 | 
            +
                msg3 = Logster.store.report(
         | 
| 434 | 
            +
                  Logger::WARN,
         | 
| 435 | 
            +
                  '',
         | 
| 436 | 
            +
                  'test pattern 3',
         | 
| 437 | 
            +
                  env: [{ "application_version" => "def" }, { "application_version" => "ghi" }],
         | 
| 438 | 
            +
                  backtrace: backtrace
         | 
| 439 | 
            +
                )
         | 
| 440 | 
            +
                group = Logster.store.find_pattern_groups { |p| p == /test pattern/ }.first
         | 
| 441 | 
            +
                assert_equal([msg3, msg2, msg1].map(&:key), group.messages_keys)
         | 
| 442 | 
            +
             | 
| 443 | 
            +
                latest = Logster.store.latest
         | 
| 444 | 
            +
                assert_equal(1, latest.size)
         | 
| 445 | 
            +
                assert_equal([msg1, msg2, msg3].map(&:key).sort, latest.first["messages"].map(&:key).sort)
         | 
| 446 | 
            +
             | 
| 447 | 
            +
                response = request.post("/logsie/solve-group", params: { regex: "/test pattern/" })
         | 
| 448 | 
            +
                group = Logster.store.find_pattern_groups { |p| p == /test pattern/ }.first
         | 
| 449 | 
            +
                assert_equal([msg1.key], group.messages_keys)
         | 
| 450 | 
            +
                assert_equal(200, response.status)
         | 
| 451 | 
            +
             | 
| 452 | 
            +
                latest = Logster.store.latest
         | 
| 453 | 
            +
                # msg1 remains cause it doesn't have application_version
         | 
| 454 | 
            +
                assert_equal([msg1.key], latest.first["messages"].map(&:key))
         | 
| 455 | 
            +
                assert_equal(1, latest.size)
         | 
| 456 | 
            +
             | 
| 457 | 
            +
                msg4 = Logster.store.report(Logger::WARN, '', 'test pattern 4', backtrace: backtrace)
         | 
| 458 | 
            +
                %w[abc def ghi].each do |version|
         | 
| 459 | 
            +
                  Logster.store.report(
         | 
| 460 | 
            +
                    Logger::WARN,
         | 
| 461 | 
            +
                    '',
         | 
| 462 | 
            +
                    'test pattern 5',
         | 
| 463 | 
            +
                    env: { "application_version" => version },
         | 
| 464 | 
            +
                    backtrace: backtrace
         | 
| 465 | 
            +
                  )
         | 
| 466 | 
            +
                end
         | 
| 467 | 
            +
                latest = Logster.store.latest
         | 
| 468 | 
            +
                assert_equal([msg1.key, msg4.key].sort, latest.first["messages"].map(&:key).sort)
         | 
| 469 | 
            +
                assert_equal(1, latest.size)
         | 
| 470 | 
            +
              ensure
         | 
| 471 | 
            +
                Logster.config.enable_custom_patterns_via_ui = false
         | 
| 472 | 
            +
              end
         | 
| 373 473 | 
             
            end
         |