miniprofiler 0.1.7.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +4 -0
- data/CHANGELOG +32 -0
- data/Gemfile +15 -0
- data/Gemfile.lock +64 -0
- data/README.md +110 -0
- data/Rakefile +40 -0
- data/autotest/discover.rb +2 -0
- data/lib/mini_profiler/body_add_proxy.rb +45 -0
- data/lib/mini_profiler/client_timer_struct.rb +76 -0
- data/lib/mini_profiler/config.rb +52 -0
- data/lib/mini_profiler/context.rb +10 -0
- data/lib/mini_profiler/page_timer_struct.rb +53 -0
- data/lib/mini_profiler/profiler.rb +405 -0
- data/lib/mini_profiler/profiling_methods.rb +73 -0
- data/lib/mini_profiler/request_timer_struct.rb +96 -0
- data/lib/mini_profiler/sql_timer_struct.rb +48 -0
- data/lib/mini_profiler/storage/abstract_store.rb +27 -0
- data/lib/mini_profiler/storage/file_store.rb +108 -0
- data/lib/mini_profiler/storage/memory_store.rb +68 -0
- data/lib/mini_profiler/storage/redis_store.rb +44 -0
- data/lib/mini_profiler/timer_struct.rb +31 -0
- data/lib/mini_profiler_rails/railtie.rb +84 -0
- data/lib/patches/sql_patches.rb +185 -0
- data/lib/rack-mini-profiler.rb +6 -0
- data/rack-mini-profiler.gemspec +23 -0
- data/spec/components/body_add_proxy_spec.rb +90 -0
- data/spec/components/client_timer_struct_spec.rb +165 -0
- data/spec/components/file_store_spec.rb +47 -0
- data/spec/components/memory_store_spec.rb +40 -0
- data/spec/components/page_timer_struct_spec.rb +33 -0
- data/spec/components/profiler_spec.rb +126 -0
- data/spec/components/redis_store_spec.rb +44 -0
- data/spec/components/request_timer_struct_spec.rb +148 -0
- data/spec/components/sql_timer_struct_spec.rb +63 -0
- data/spec/components/timer_struct_spec.rb +47 -0
- data/spec/fixtures/simple_client_request.yml +17 -0
- data/spec/fixtures/weird_client_request.yml +13 -0
- data/spec/integration/mini_profiler_spec.rb +142 -0
- data/spec/spec_helper.rb +31 -0
- data/spec/support/expand_each_to_matcher.rb +8 -0
- data/test_old/config.ru +41 -0
- metadata +155 -0
data/.gitignore
ADDED
data/CHANGELOG
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
28-June-2012 - Sam
|
2
|
+
|
3
|
+
* Started change log
|
4
|
+
* Corrected profiler so it properly captures POST requests (was supressing non 200s)
|
5
|
+
* Amended Rack.MiniProfiler.config[:user_provider] to use ip addres for identity
|
6
|
+
* Fixed bug where unviewed missing ids never got cleared
|
7
|
+
* Supress all '/assets/' in the rails tie (makes debugging easier)
|
8
|
+
* record_sql was mega buggy
|
9
|
+
|
10
|
+
9-July-2012 - Sam
|
11
|
+
|
12
|
+
* Cleaned up mechanism for profiling in production, all you need to do now
|
13
|
+
is call Rack::MiniProfiler.authorize_request to get profiling working in
|
14
|
+
production
|
15
|
+
* Added option to display full backtraces pp=full-backtrace
|
16
|
+
* Cleaned up railties, got rid of the post authorize callback
|
17
|
+
* Version 0.1.3
|
18
|
+
|
19
|
+
12-July-2012 - Sam
|
20
|
+
|
21
|
+
* Fixed incorrect profiling steps (was not indenting or measuring start time right
|
22
|
+
* Implemented native PG and MySql2 interceptors, this gives way more accurate times
|
23
|
+
* Refactored context so its a proper class and not a hash
|
24
|
+
* Added some more client probing built in to rails
|
25
|
+
* More tests
|
26
|
+
|
27
|
+
18-July-2012 - Sam
|
28
|
+
|
29
|
+
* Added First Paint time for chrome
|
30
|
+
* Bug fix to ensure non Rails installs have mini profiler
|
31
|
+
* Version 0.1.7
|
32
|
+
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
miniprofiler (0.1.7.1)
|
5
|
+
rack (>= 1.1.3)
|
6
|
+
|
7
|
+
GEM
|
8
|
+
remote: http://rubygems.org/
|
9
|
+
specs:
|
10
|
+
ZenTest (4.8.1)
|
11
|
+
activemodel (3.2.6)
|
12
|
+
activesupport (= 3.2.6)
|
13
|
+
builder (~> 3.0.0)
|
14
|
+
activerecord (3.2.6)
|
15
|
+
activemodel (= 3.2.6)
|
16
|
+
activesupport (= 3.2.6)
|
17
|
+
arel (~> 3.0.2)
|
18
|
+
tzinfo (~> 0.3.29)
|
19
|
+
activesupport (3.2.6)
|
20
|
+
i18n (~> 0.6)
|
21
|
+
multi_json (~> 1.0)
|
22
|
+
arel (3.0.2)
|
23
|
+
autotest (4.4.6)
|
24
|
+
ZenTest (>= 4.4.1)
|
25
|
+
builder (3.0.0)
|
26
|
+
commonjs (0.2.6)
|
27
|
+
diff-lcs (1.1.3)
|
28
|
+
i18n (0.6.0)
|
29
|
+
less (2.2.1)
|
30
|
+
commonjs (~> 0.2.6)
|
31
|
+
libv8 (3.3.10.4)
|
32
|
+
multi_json (1.3.6)
|
33
|
+
rack (1.4.1)
|
34
|
+
rack-test (0.6.1)
|
35
|
+
rack (>= 1.0)
|
36
|
+
rake (0.9.2.2)
|
37
|
+
redis (3.0.1)
|
38
|
+
rspec (2.11.0)
|
39
|
+
rspec-core (~> 2.11.0)
|
40
|
+
rspec-expectations (~> 2.11.0)
|
41
|
+
rspec-mocks (~> 2.11.0)
|
42
|
+
rspec-core (2.11.0)
|
43
|
+
rspec-expectations (2.11.1)
|
44
|
+
diff-lcs (~> 1.1.3)
|
45
|
+
rspec-mocks (2.11.1)
|
46
|
+
therubyracer (0.10.1)
|
47
|
+
libv8 (~> 3.3.10)
|
48
|
+
tzinfo (0.3.33)
|
49
|
+
|
50
|
+
PLATFORMS
|
51
|
+
ruby
|
52
|
+
|
53
|
+
DEPENDENCIES
|
54
|
+
ZenTest
|
55
|
+
activerecord (~> 3.0)
|
56
|
+
autotest
|
57
|
+
less
|
58
|
+
miniprofiler!
|
59
|
+
rack
|
60
|
+
rack-test
|
61
|
+
rake
|
62
|
+
redis
|
63
|
+
rspec
|
64
|
+
therubyracer
|
data/README.md
ADDED
@@ -0,0 +1,110 @@
|
|
1
|
+
# NOTE FOR THIS GEM FORK
|
2
|
+
|
3
|
+
This is a fork the Ruby folder of https://github.com/SamSaffron/MiniProfiler which includes some quick fixes to make the original rack-mini-profiler gem a little more usable. I will consolidate any useful changes here into pull requests for the original repo.
|
4
|
+
|
5
|
+
* Add `gem 'miniprofiler'` to your Gemfile (install of 'rack-mini-profiler')
|
6
|
+
* `miniprofiler`'s version uses `x.y.z.t` syntax in order to more easily sync with `rack-mini-profiler`'s `x.y.z` SemVer versioning. I will increase `t` whenever I make a patch
|
7
|
+
* This gem fork will work in staging, not just development and production
|
8
|
+
* Gem release date will be correct (as of now 'rack-mini-profiler' hardcodes the release date to be 04/02/2012, which is very confusing: https://rubygems.org/gems/rack-mini-profiler -- I will report to them)
|
9
|
+
* Allow turning off loading jquery if the app already uses jquery
|
10
|
+
* More to come
|
11
|
+
|
12
|
+
# Original README below:
|
13
|
+
|
14
|
+
# rack-mini-profiler
|
15
|
+
|
16
|
+
Middleware that displays speed badge for every html page. Designed to work both in production and in development.
|
17
|
+
|
18
|
+
## Using rack-mini-profiler in your app
|
19
|
+
|
20
|
+
Install/add to Gemfile
|
21
|
+
|
22
|
+
```ruby
|
23
|
+
gem 'rack-mini-profiler'
|
24
|
+
```
|
25
|
+
Using Rails:
|
26
|
+
|
27
|
+
All you have to do is include the Gem and you're good to go in development.
|
28
|
+
|
29
|
+
rack-mini-profiler is designed with production profiling in mind. To enable that just run `Rack::MiniProfiler.authorize_request` once you know a request is allowed to profile.
|
30
|
+
|
31
|
+
For example:
|
32
|
+
|
33
|
+
```ruby
|
34
|
+
# A hook in your ApplicationController
|
35
|
+
def authorize
|
36
|
+
if current_user.is_admin?
|
37
|
+
Rack::MiniProfiler.authorize_request
|
38
|
+
end
|
39
|
+
end
|
40
|
+
````
|
41
|
+
|
42
|
+
|
43
|
+
Using Builder:
|
44
|
+
|
45
|
+
```ruby
|
46
|
+
require 'rack-mini-profiler'
|
47
|
+
builder = Rack::Builder.new do
|
48
|
+
use Rack::MiniProfiler
|
49
|
+
|
50
|
+
map('/') { run get }
|
51
|
+
end
|
52
|
+
```
|
53
|
+
|
54
|
+
Using Sinatra:
|
55
|
+
|
56
|
+
```ruby
|
57
|
+
require 'rack-mini-profiler'
|
58
|
+
class MyApp < Sinatra::Base
|
59
|
+
use Rack::MiniProfiler
|
60
|
+
end
|
61
|
+
```
|
62
|
+
|
63
|
+
## Storage
|
64
|
+
|
65
|
+
By default, rack-mini-profiler stores its results in a memory store:
|
66
|
+
|
67
|
+
```ruby
|
68
|
+
# our default
|
69
|
+
Rack::MiniProfiler.config.storage = Rack::MiniProfiler::MemoryStore
|
70
|
+
```
|
71
|
+
|
72
|
+
There are 2 other available storage engines, `RedisStore` and `FileStore`.
|
73
|
+
|
74
|
+
MemoryStore is stores results in a processes heap - something that does not work well in a multi process environment.
|
75
|
+
FileStore stores results in the file system - something that may not work well in a multi machine environment.
|
76
|
+
|
77
|
+
Additionally you may implement an AbstractStore for your own provider.
|
78
|
+
|
79
|
+
Rails hooks up a FileStore for all environments.
|
80
|
+
|
81
|
+
## Running the Specs
|
82
|
+
|
83
|
+
```
|
84
|
+
$ rake build
|
85
|
+
$ rake spec
|
86
|
+
```
|
87
|
+
|
88
|
+
Additionally you can also run `autotest` if you like.
|
89
|
+
|
90
|
+
## Configuration Options
|
91
|
+
|
92
|
+
You can set configuration options using the configuration accessor on Rack::MiniProfiler:
|
93
|
+
|
94
|
+
```
|
95
|
+
# Have Mini Profiler show up on the right
|
96
|
+
Rack::MiniProfiler.config.position = 'right'
|
97
|
+
```
|
98
|
+
|
99
|
+
In a Rails app, this can be done conveniently in an initializer such as config/initializers/mini_profiler.rb.
|
100
|
+
|
101
|
+
## Available Options
|
102
|
+
|
103
|
+
* pre_authorize_cb - A lambda callback you can set to determine whether or not mini_profiler should be visible on a given request. Default in a Rails environment is only on in development mode. If in a Rack app, the default is always on.
|
104
|
+
* position - Can either be 'right' or 'left'. Default is 'left'.
|
105
|
+
* skip_schema_queries - Whether or not you want to log the queries about the schema of your tables. Default is 'false', 'true' in rails development.
|
106
|
+
|
107
|
+
## Special query strings
|
108
|
+
|
109
|
+
If you include the query string `pp=help` at the end of your request you will see the various option you have. You can use these options to extend or contract the amount of diagnostics rack-mini-profiler gathers.
|
110
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
# Rakefile
|
2
|
+
require 'rubygems'
|
3
|
+
require 'bundler'
|
4
|
+
Bundler.setup(:default, :test)
|
5
|
+
require "bundler/gem_tasks"
|
6
|
+
|
7
|
+
task :default => [:spec]
|
8
|
+
|
9
|
+
require 'rspec/core'
|
10
|
+
require 'rspec/core/rake_task'
|
11
|
+
RSpec::Core::RakeTask.new(:spec) do |spec|
|
12
|
+
spec.pattern = FileList['spec/**/*_spec.rb']
|
13
|
+
end
|
14
|
+
|
15
|
+
desc "builds a gem"
|
16
|
+
task :build => :compile_less do
|
17
|
+
`gem build rack-mini-profiler.gemspec 1>&2`
|
18
|
+
end
|
19
|
+
|
20
|
+
desc "compile less"
|
21
|
+
task :compile_less => :copy_files do
|
22
|
+
`lessc lib/html/includes.less > lib/html/includes.css`
|
23
|
+
end
|
24
|
+
|
25
|
+
|
26
|
+
desc "copy files from other parts of the tree"
|
27
|
+
task :copy_files do
|
28
|
+
`rm -R -f lib/html && mkdir lib/html 1>&2`
|
29
|
+
path = File.expand_path('../StackExchange.Profiling/UI')
|
30
|
+
`ln -s #{path}/includes.less lib/html/includes.less`
|
31
|
+
`ln -s #{path}/includes.js lib/html/includes.js`
|
32
|
+
`ln -s #{path}/includes.tmpl lib/html/includes.tmpl`
|
33
|
+
`ln -s #{path}/jquery.1.7.1.js lib/html/jquery.1.7.1.js`
|
34
|
+
`ln -s #{path}/jquery.tmpl.js lib/html/jquery.tmpl.js`
|
35
|
+
`ln -s #{path}/list.css lib/html/list.css`
|
36
|
+
`ln -s #{path}/list.js lib/html/list.js`
|
37
|
+
`ln -s #{path}/list.tmpl lib/html/list.tmpl`
|
38
|
+
`ln -s #{path}/include.partial.html lib/html/profile_handler.js`
|
39
|
+
end
|
40
|
+
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module Rack
|
2
|
+
class MiniProfiler
|
3
|
+
|
4
|
+
# This class acts as a proxy to the Body so that we can
|
5
|
+
# safely append to the end without knowing about the internals
|
6
|
+
# of the body class.
|
7
|
+
class BodyAddProxy
|
8
|
+
def initialize(body, additional_text)
|
9
|
+
@body = body
|
10
|
+
@additional_text = additional_text
|
11
|
+
end
|
12
|
+
|
13
|
+
def respond_to?(*args)
|
14
|
+
super or @body.respond_to?(*args)
|
15
|
+
end
|
16
|
+
|
17
|
+
def method_missing(*args, &block)
|
18
|
+
@body.__send__(*args, &block)
|
19
|
+
end
|
20
|
+
|
21
|
+
# In the case of to_str we don't want to use method_missing as it might avoid
|
22
|
+
# a call to each (such as in Rack::Test)
|
23
|
+
def to_str
|
24
|
+
result = ""
|
25
|
+
each {|token| result << token}
|
26
|
+
result
|
27
|
+
end
|
28
|
+
|
29
|
+
def each(&block)
|
30
|
+
|
31
|
+
# In ruby 1.9 we don't support String#each
|
32
|
+
if @body.is_a?(String)
|
33
|
+
yield @body
|
34
|
+
else
|
35
|
+
@body.each(&block)
|
36
|
+
end
|
37
|
+
|
38
|
+
yield @additional_text
|
39
|
+
self
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
require 'mini_profiler/timer_struct'
|
2
|
+
|
3
|
+
module Rack
|
4
|
+
class MiniProfiler
|
5
|
+
|
6
|
+
# This class holds the client timings
|
7
|
+
class ClientTimerStruct < TimerStruct
|
8
|
+
|
9
|
+
def self.init_instrumentation
|
10
|
+
"<script type=\"text/javascript\">mPt=function(){var t=[];return{t:t,probe:function(n){t.push({d:new Date(),n:n})}}}()</script>"
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.instrument(name,orig)
|
14
|
+
probe = "<script>mPt.probe('#{name}')</script>"
|
15
|
+
wrapped = probe
|
16
|
+
wrapped << orig
|
17
|
+
wrapped << probe
|
18
|
+
wrapped
|
19
|
+
end
|
20
|
+
|
21
|
+
|
22
|
+
def initialize(env={})
|
23
|
+
super
|
24
|
+
end
|
25
|
+
|
26
|
+
def init_from_form_data(env, page_struct)
|
27
|
+
timings = []
|
28
|
+
clientTimes, clientPerf, baseTime = nil
|
29
|
+
form = env['rack.request.form_hash']
|
30
|
+
|
31
|
+
clientPerf = form['clientPerformance'] if form
|
32
|
+
clientTimes = clientPerf['timing'] if clientPerf
|
33
|
+
|
34
|
+
baseTime = clientTimes['navigationStart'].to_i if clientTimes
|
35
|
+
return unless clientTimes && baseTime
|
36
|
+
|
37
|
+
probes = form['clientProbes']
|
38
|
+
translated = {}
|
39
|
+
if probes && probes != "null"
|
40
|
+
probes.each do |id, val|
|
41
|
+
name = val["n"]
|
42
|
+
translated[name] ||= {}
|
43
|
+
if translated[name][:start]
|
44
|
+
translated[name][:finish] = val["d"]
|
45
|
+
else
|
46
|
+
translated[name][:start] = val["d"]
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
translated.each do |name, data|
|
52
|
+
h = {"Name" => name, "Start" => data[:start].to_i - baseTime}
|
53
|
+
h["Duration"] = data[:finish].to_i - data[:start].to_i if data[:finish]
|
54
|
+
timings.push(h)
|
55
|
+
end
|
56
|
+
|
57
|
+
clientTimes.keys.find_all{|k| k =~ /Start$/ }.each do |k|
|
58
|
+
start = clientTimes[k].to_i - baseTime
|
59
|
+
finish = clientTimes[k.sub(/Start$/, "End")].to_i - baseTime
|
60
|
+
duration = 0
|
61
|
+
duration = finish - start if finish > start
|
62
|
+
name = k.sub(/Start$/, "").split(/(?=[A-Z])/).map{|s| s.capitalize}.join(' ')
|
63
|
+
timings.push({"Name" => name, "Start" => start, "Duration" => duration}) if start >= 0
|
64
|
+
end
|
65
|
+
|
66
|
+
clientTimes.keys.find_all{|k| !(k =~ /(End|Start)$/)}.each do |k|
|
67
|
+
timings.push("Name" => k, "Start" => clientTimes[k].to_i - baseTime, "Duration" => -1)
|
68
|
+
end
|
69
|
+
|
70
|
+
self['RedirectCount'] = env['rack.request.form_hash']['clientPerformance']['navigation']['redirectCount']
|
71
|
+
self['Timings'] = timings
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module Rack
|
2
|
+
class MiniProfiler
|
3
|
+
class Config
|
4
|
+
|
5
|
+
def self.attr_accessor(*vars)
|
6
|
+
@attributes ||= []
|
7
|
+
@attributes.concat vars
|
8
|
+
super(*vars)
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.attributes
|
12
|
+
@attributes
|
13
|
+
end
|
14
|
+
|
15
|
+
attr_accessor :auto_inject, :base_url_path, :pre_authorize_cb, :position,
|
16
|
+
:backtrace_remove, :backtrace_filter, :skip_schema_queries,
|
17
|
+
:storage, :user_provider, :storage_instance, :storage_options, :skip_paths, :authorization_mode, :use_existing_jquery
|
18
|
+
|
19
|
+
def self.default
|
20
|
+
new.instance_eval {
|
21
|
+
@auto_inject = true # automatically inject on every html page
|
22
|
+
@base_url_path = "/mini-profiler-resources/"
|
23
|
+
|
24
|
+
# called prior to rack chain, to ensure we are allowed to profile
|
25
|
+
@pre_authorize_cb = lambda {|env| true}
|
26
|
+
|
27
|
+
# called after rack chain, to ensure we are REALLY allowed to profile
|
28
|
+
@position = 'left' # Where it is displayed
|
29
|
+
@skip_schema_queries = false
|
30
|
+
@storage = MiniProfiler::MemoryStore
|
31
|
+
@user_provider = Proc.new{|env| Rack::Request.new(env).ip}
|
32
|
+
@authorization_mode = :allow_all
|
33
|
+
@use_existing_jquery = false
|
34
|
+
self
|
35
|
+
}
|
36
|
+
end
|
37
|
+
|
38
|
+
def merge!(config)
|
39
|
+
return unless config
|
40
|
+
if Hash === config
|
41
|
+
config.each{|k,v| instance_variable_set "@#{k}",v}
|
42
|
+
else
|
43
|
+
self.class.attributes.each{ |k|
|
44
|
+
v = config.send k
|
45
|
+
instance_variable_set "@#{k}", v if v
|
46
|
+
}
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'mini_profiler/timer_struct'
|
2
|
+
|
3
|
+
module Rack
|
4
|
+
class MiniProfiler
|
5
|
+
|
6
|
+
# PageTimerStruct
|
7
|
+
# Root: RequestTimer
|
8
|
+
# :has_many RequestTimer children
|
9
|
+
# :has_many SqlTimer children
|
10
|
+
class PageTimerStruct < TimerStruct
|
11
|
+
def initialize(env)
|
12
|
+
super("Id" => MiniProfiler.generate_id,
|
13
|
+
"Name" => env['PATH_INFO'],
|
14
|
+
"Started" => (Time.now.to_f * 1000).to_i,
|
15
|
+
"MachineName" => env['SERVER_NAME'],
|
16
|
+
"Level" => 0,
|
17
|
+
"User" => "unknown user",
|
18
|
+
"HasUserViewed" => false,
|
19
|
+
"ClientTimings" => ClientTimerStruct.new,
|
20
|
+
"DurationMilliseconds" => 0,
|
21
|
+
"HasTrivialTimings" => true,
|
22
|
+
"HasAllTrivialTimigs" => false,
|
23
|
+
"TrivialDurationThresholdMilliseconds" => 2,
|
24
|
+
"Head" => nil,
|
25
|
+
"DurationMillisecondsInSql" => 0,
|
26
|
+
"HasSqlTimings" => true,
|
27
|
+
"HasDuplicateSqlTimings" => false,
|
28
|
+
"ExecutedReaders" => 0,
|
29
|
+
"ExecutedScalars" => 0,
|
30
|
+
"ExecutedNonQueries" => 0)
|
31
|
+
name = "#{env['REQUEST_METHOD']} http://#{env['SERVER_NAME']}:#{env['SERVER_PORT']}#{env['SCRIPT_NAME']}#{env['PATH_INFO']}"
|
32
|
+
self['Root'] = RequestTimerStruct.createRoot(name, self)
|
33
|
+
end
|
34
|
+
|
35
|
+
def duration_ms
|
36
|
+
@attributes['Root']['DurationMilliseconds']
|
37
|
+
end
|
38
|
+
|
39
|
+
def root
|
40
|
+
@attributes['Root']
|
41
|
+
end
|
42
|
+
|
43
|
+
def to_json(*a)
|
44
|
+
attribs = @attributes.merge(
|
45
|
+
"Started" => '/Date(%d)/' % @attributes['Started'],
|
46
|
+
"DurationMilliseconds" => @attributes['Root']['DurationMilliseconds']
|
47
|
+
)
|
48
|
+
::JSON.generate(attribs, :max_nesting => 100)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
end
|