From 7e4bea0e8ef0b0a3f0e9a72157008b9ad67d3260 Mon Sep 17 00:00:00 2001 From: RMSD Date: Tue, 2 May 2017 12:31:24 -0700 Subject: [PATCH 01/10] Update README.md Improves readablitity. --- README.md | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/README.md b/README.md index 18d8210..a457295 100644 --- a/README.md +++ b/README.md @@ -3,15 +3,8 @@ Get your application events and errors to Trakerr via the *Trakerr API*. You will need your API key to send events to trakerr. -## Overview -- REST API version: 2.0.0 -- Package (SDK) version: 2.0.0 - ## Requirements. - -Ruby 1.9.3+ -and -git 2.0+ +Ruby 1.9.3+, git 2.0+, and curl 7.47.0+ ## Installation & Usage ### 1) Install git and curl From 9c48b6b7fd637301b917f204e3765c74d2dc6336 Mon Sep 17 00:00:00 2001 From: RMSD Date: Tue, 2 May 2017 14:13:36 -0700 Subject: [PATCH 02/10] Update gemspec Update gemspec to proper version. --- trakerr_client.gemspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/trakerr_client.gemspec b/trakerr_client.gemspec index c3309e3..41eb5af 100644 --- a/trakerr_client.gemspec +++ b/trakerr_client.gemspec @@ -29,7 +29,7 @@ $:.push File.expand_path("../trakerr/lib", __FILE__) Gem::Specification.new do |s| s.name = "trakerr_client" - s.version = "2.0.0" + s.version = "2.1.0" s.platform = Gem::Platform::RUBY s.authors = ["Trakerr Dev Team", "Swagger-Codegen"] s.email = [""] From 42d21f564c4620e6ae7319fd415fd6bf4fe09ffe Mon Sep 17 00:00:00 2001 From: RMSD Date: Fri, 5 May 2017 12:14:16 -0700 Subject: [PATCH 03/10] Logger implemented Logger added and implemented. New trace builder code to parse text stacks. Refactoring on the client code to follow ruby style. --- test_app.rb | 40 ++++++-- trakerr/lib/event_trace_builder.rb | 55 ++++++++--- trakerr/lib/trakerr.rb | 152 ++++++++++++++--------------- trakerr/lib/trakerr_formatter.rb | 13 +++ trakerr/lib/trakerr_writer.rb | 49 ++++++++++ 5 files changed, 211 insertions(+), 98 deletions(-) create mode 100644 trakerr/lib/trakerr_formatter.rb create mode 100644 trakerr/lib/trakerr_writer.rb diff --git a/test_app.rb b/test_app.rb index 585a081..9d424e6 100644 --- a/test_app.rb +++ b/test_app.rb @@ -17,7 +17,10 @@ =end require 'rubygems' +require 'logger' require_relative 'trakerr/lib/trakerr' +require_relative 'trakerr/lib/trakerr_formatter' +require_relative 'trakerr/lib/trakerr_writer' def main() argarr = ARGV @@ -26,39 +29,60 @@ def main() api_key = argarr[0] if argarr.length > 0 and api_key == "" testApp = Trakerr::TrakerrClient.new(api_key, "1.0", "development") + stream = Trakerr::TrakerrWriter.new(api_key, "2.0", "development") + + rlog = Logger.new(stream) + + rlog.formatter = Trakerr::TrakerrFormatter.new + + begin + raise IOError, "Failed to open file" + rescue IOError => err + rlog.fatal err + end + + #Since we use streams (StringIO) to hook into the ruby logger, + #accessing stream after it has finished logging and event is simple. + #Rewind the stream, and then read it to extract data to whatever device you wish for. + #The example in the comments below prints out to console. The formatter does change how the event is formatted + #and the information given as the output to be pertinant and easy to parse by the stream hook, but I could probably write a complex regex for default + #if the demand is there. + #stream.rewind + #log = stream.read + #puts log #Send exception to Trakerr with default values. begin raise ZeroDivisionError, "Oh no!" - rescue ZeroDivisionError => exception + rescue ZeroDivisionError => er #You can leave the hash empty if you would like to use the default values. #We recommend that you supply a user and a session for all events, #and supplying an "evntname" and "evntmessage" for non errors. - testApp.log({"user"=>"jack@trakerr.io", "session"=>"7"}, exception) + testApp.log({"user"=>"jack@trakerr.io", "session"=>"7"}, er) end #Get an AppEvent to populate the class with custom data and then send it to Trakerr. #Simple custom data can be send through log. begin - raise ArgumentError - rescue ArgumentError => e - appev = testApp.CreateAppEvent(e, "Error") + raise RegexpError, "Help!" + rescue RegexpError => e + appev = testApp.create_app_event(e, "Error") appev.event_user = "john@trakerr.io" appev.event_session = "5" appev.context_app_browser = "Chrome" appev.context_app_browser_version = "57.x" - testApp.SendEvent(appev) + testApp.send_event(appev) end #Send a non Exception to Trakerr. - appev2 = testApp.CreateAppEvent(false, "Info", "User failed auth", "400 err", "User error") + appev2 = testApp.create_app_event(false, "Info", "User failed auth", "400 err", "User error") appev2.event_user = "jill@trakerr.io" appev2.event_session = "3" appev2.context_app_browser = "Edge" appev2.context_app_browser_version = "40.15063.0.0" - testApp.SendEvent(appev2) + testApp.send_event(appev2) end diff --git a/trakerr/lib/event_trace_builder.rb b/trakerr/lib/event_trace_builder.rb index 2b76171..16da010 100644 --- a/trakerr/lib/event_trace_builder.rb +++ b/trakerr/lib/event_trace_builder.rb @@ -31,25 +31,26 @@ def self.get_stacktrace(exc) raise ArgumentError, "get_stacktrace expects an exception instance." unless exc.is_a? Exception strace = Trakerr::Stacktrace.new - add_stack_trace(strace, exc) + add_stack_trace(strace, exc.class.name, exc.message, best_regexp_for(exc), exc.backtrace) return strace end - private + def self.get_logger_stacktrace(errtype, errmessage, stackarray) + raise ArgumentError, "errtype and errmessage are expected strings" unless (errtype.is_a? String) && (errmessage.is_a? String) + raise ArgumentError, "stackarray is expected to be an iterable with strings values" unless stackarray.respond_to?('each') - ## - #Adds a InnerStackTrace to the Stacktrace object (which is a collection) - #strace:Stacktrace: The Stacktrace object to append the latest InnerStackTrace to. - #exc:Exception: The exception caught or rescued. - ## - def self.add_stack_trace(strace, exc) - raise ArgumentError, "add_stack_trace did not get passed in the correct arguments" unless exc.is_a? Exception and strace.instance_of? Stacktrace + strace = Trakerr::Stacktrace.new + self.add_stack_trace(strace, errtype, errmessage, best_regexp_guess(stackarray[0]), stackarray) + return strace + end + private + def self.add_stack_trace(strace, errtype, errmessage, regex, stackarray) newtrace = Trakerr::InnerStackTrace.new - newtrace.type = exc.class.name - newtrace.message = exc.message - newtrace.trace_lines = get_event_tracelines(best_regexp_for(exc), exc.backtrace) + newtrace.type = errtype + newtrace.message = errmessage + newtrace.trace_lines = get_event_tracelines(regex, stackarray) strace.push(newtrace) end @@ -75,7 +76,7 @@ def self.get_event_tracelines(regex, errarray) end ## - #Parses each given line by the regex + #Parses each given line by the regex. #RETURNS: A match object with the capture groups file function and line set. #regex:RegularExpression: The regular expression to parse the stacktrace text with. #line:String: A string with the traceline to parce @@ -90,7 +91,7 @@ def self.parse_stacktrace(regex, line) end def self.best_regexp_for(exc) - #add error check + #TODO: add error check if defined?(Java::JavaLang::Throwable) && exc.is_a?(Java::JavaLang::Throwable) @@JAVA elsif defined?(OCIError) && exc.is_a?(OCIError) @@ -102,6 +103,32 @@ def self.best_regexp_for(exc) end end + def self.best_regexp_guess(str) + #Guess the regex. Test each regex on the string and see which regex captures the most data. + #Use the one that captures the most. RegexErrors if none of the regexs are able to match + + java_match = @@JAVA.match(str) if defined?(Java::JavaLang::Throwable) + oci_match = @@OCI.match(str) #if defined?(OCIError) + ruby_match = @@RUBY.match(str) + java_count = 0 + ruby_count = 0 + oci_count = 0 + + ruby_match.captures.each {|item| ruby_count+= 1 if item} if ruby_match + oci_match.captures.each {|item| oci_count+= 1 if item} if oci_match + java_match.captures.each {|item| java_count+= 1 if item} if java_match + + if ruby_count >= oci_count && ruby_count >= java_count && ruby_count > 0 + @@RUBY + elsif oci_count >= ruby_count && oci_count >= java_count && oci_count > 0 + @@OCI + elsif java_count >= ruby_count && java_count >= oci_count && java_count > 0 + @@JAVA + else + raise RegexpError, "line does not fit any of the supported stacktraces." + end + end + ## # @return [Regexp] the pattern that matches standard Ruby stack frames, # such as ./spec/notice_spec.rb:43:in `block (3 levels) in ' diff --git a/trakerr/lib/trakerr.rb b/trakerr/lib/trakerr.rb index 714fdef..73875b7 100644 --- a/trakerr/lib/trakerr.rb +++ b/trakerr/lib/trakerr.rb @@ -25,111 +25,111 @@ module Trakerr class TrakerrClient ##API Key - attr_accessor :apiKey + attr_accessor :api_key ##App Version of the client the API is tying into. - attr_accessor :contextAppVersion + attr_accessor :context_app_version ##Deployment stage of the codebade the API is tying into. - attr_accessor :contextDeploymentStage + attr_accessor :context_deployment_stage ##String name of the language being used. - attr_accessor :contextEnvLanguage + attr_accessor :context_env_language ##The name of the interpreter - attr_accessor :contextEnvName + attr_accessor :context_env_name - ## ContextEnvVersion is the version of the interpreter the program is run on. - attr_accessor :contextEnvVersion + ## context_env_version is the version of the interpreter the program is run on. + attr_accessor :context_env_version - ## ContextEnvHostname is hostname of the pc running the code. - attr_accessor :contextEnvHostname + ## context_env_version is hostname of the pc running the code. + attr_accessor :context_env_version - ## ContextAppOS is the OS the program is running on. - attr_accessor :contextAppOS + ## context_app_os is the OS the program is running on. + attr_accessor :context_app_os - ## ContextAppOSVersion is the version of the OS the code is running on. - attr_accessor :contextAppOSVersion + ## context_app_os_version is the version of the OS the code is running on. + attr_accessor :context_app_os_version - ## contextAppBrowser is optional MVC and ASP.net applications the browser name the application is running on. - attr_accessor :contextAppBrowser + ## context_app_browser is optional MVC and ASP.net applications the browser name the application is running on. + attr_accessor :context_app_browser - ## contextAppBrowserVersion is optional for MVC and ASP.net applications the browser version the application is running on. - attr_accessor :contextAppBrowserVersion + ## context_app_browser_version is optional for MVC and ASP.net applications the browser version the application is running on. + attr_accessor :context_app_browser_version - ## ContextDatacenter is the optional datacenter the code may be running on. - attr_accessor :contextDataCenter + ## context_data_center is the optional datacenter the code may be running on. + attr_accessor :context_data_center - ## ContextDatacenterRegion is the optional datacenter region the code may be running on. - attr_accessor :contextDataCenterRegion + ## context_data_center_region is the optional datacenter region the code may be running on. + attr_accessor :context_data_center_region ## #Initializes the TrakerrClient class. - #apiKey:String: Should be your API key string. - #contextAppVersion:String: Should be the version of your application. - #contextEnvName:String: Should be the deployment stage of your program. + #api_key:String: Should be your API key string. + #context_app_version:String: Should be the version of your application. + #context_env_name:String: Should be the deployment stage of your program. ## - def initialize(apiKey, - contextAppVersion="1.0", - contextDeploymentStage="development") + def initialize(api_key, + context_app_version="1.0", + context_deployment_stage="development") default_config = Trakerr::Configuration.default default_config.base_path = default_config.base_path - @apiKey = apiKey - @contextAppVersion = contextAppVersion - @contextDeploymentStage = contextDeploymentStage + @api_key = api_key + @context_app_version = context_app_version + @context_deployment_stage = context_deployment_stage - @contextEnvLanguage = "Ruby" + @context_env_language = "Ruby" if RUBY_PLATFORM == "java" - @contextEnvName = "jruby" - @contextEnvVersion = JRUBY_VERSION + @context_env_name = "jruby" + @context_env_version = JRUBY_VERSION else - @contextEnvName = "ruby" - @contextEnvVersion = RUBY_VERSION + @context_env_name = "ruby" + @context_env_version = RUBY_VERSION end - @contextEnvHostname = Socket.gethostname + @context_env_version = Socket.gethostname host_os = RbConfig::CONFIG['host_os'] case host_os when /mswin|msys|mingw|cygwin|bccwin|wince|emc/ text = `systeminfo` - @contextAppOS = GetTextFromLine(text, "OS Name:", "\n") - @contextAppOS.chomp! if @contextAppOS != nil - @contextAppOS.strip! if @contextAppOS != nil + @context_app_os = get_text_from_line(text, "OS Name:", "\n") + @context_app_os.chomp! if @context_app_os != nil + @context_app_os.strip! if @context_app_os != nil - version = GetTextFromLine(text, "OS Version:", "\n").split + version = get_text_from_line(text, "OS Version:", "\n").split version[0].chomp! if version != nil version[0].strip! if version != nil - @contextAppOSVersion = contextAppOSVersion || version[0] + @context_app_os_version = context_app_os_version || version[0] when /darwin|mac os/ text = `system_profiler SPSoftwareDataType` - @contextAppOS = GetTextFromLine(text, "System Version:", "(").chomp.strip - @contextAppOSVersion = contextAppOSVersion || GetTextFromLine(text, "Kernel Version:", "\n").chomp.strip + @context_app_os = get_text_from_line(text, "System Version:", "(").chomp.strip + @context_app_os_version = context_app_os_version || get_text_from_line(text, "Kernel Version:", "\n").chomp.strip when /linux/, /solaris|bsd/ #uname -s and -r - @contextAppOS = `uname -s`.chomp.strip - @contextAppOSVersion = contextAppOSVersion || `uname -r`.chomp.strip + @context_app_os = `uname -s`.chomp.strip + @context_app_os_version = context_app_os_version || `uname -r`.chomp.strip end - if @contextAppOS == nil - @contextAppOS = RbConfig::CONFIG["target_os"] + if @context_app_os == nil + @context_app_os = RbConfig::CONFIG["target_os"] end - if @contextAppOSVersion == nil - @contextAppOSVersion = RbConfig::CONFIG['host_os'] + if @context_app_os_version == nil + @context_app_os_version = RbConfig::CONFIG['host_os'] end - @contextAppBrowser = contextAppBrowser - @contextAppBrowserVersion = contextAppBrowserVersion - @contextDataCenter = contextDataCenter - @contextDataCenterRegion = contextDataCenterRegion + @context_app_browser = context_app_browser + @context_app_browser_version = context_app_browser_version + @context_data_center = context_data_center + @context_data_center_region = context_data_center_region api_client = Trakerr::ApiClient.new(default_config) @events_api = Trakerr::EventsApi.new(api_client) end @@ -147,14 +147,14 @@ def initialize(apiKey, #eventMessage:String: String representation of the message of the error. #Defaults to err.message if err is an exception, unknown if not. ## - def CreateAppEvent(err = false, log_level="Error", classification="issue", eventType="unknown", eventMessage="unknown") + def create_app_event(err = false, log_level="Error", classification="issue", eventType="unknown", eventMessage="unknown") raise ArgumentError, "All non err arguments are expected strings." unless (log_level.is_a? String) && (classification.is_a? String) && (eventType.is_a? String) && (eventMessage.is_a? String) if err != false raise ArgumentError, "err is expected instance of exception." unless err.is_a? Exception - eventType = err.class.name if eventType == "unknown" + eventType = err.class.name if eventType == "unknown" || eventType == "" - eventMessage = err.message if eventMessage == "unknown" + eventMessage = err.message if eventMessage == "unknown" || eventMessage == "" end @@ -197,22 +197,22 @@ def log(arg_hash, error, log_level = "error", classification = "issue") app_event = nil if error != false raise ArgumentError, "err is expected instance of exception." unless error.is_a? Exception - app_event = CreateAppEvent(error, log_level, classification, arg_hash.fetch("evntname", "unknown"), arg_hash.fetch("evntmessage", "unknown")) + app_event = create_app_event(error, log_level, classification, arg_hash.fetch("evntname", "unknown"), arg_hash.fetch("evntmessage", "unknown")) end - app_event = CreateAppEvent(false,log_level, classification, arg_hash.fetch("evntname", "unknown"), arg_hash.fetch("evntmessage", "unknown")) if app_event.nil? + app_event = create_app_event(false,log_level, classification, arg_hash.fetch("evntname", "unknown"), arg_hash.fetch("evntmessage", "unknown")) if app_event.nil? app_event.event_user = arg_hash["user"] if arg_hash.has_key? "user" app_event.event_session = arg_hash["session"] if arg_hash.has_key? "session" - SendEvent(app_event) + send_event(app_event) end ## #Sends the given AppEvent to Trakerr #appEvent:AppEvent: The AppEvent to send. ## - def SendEvent(appEvent) - @events_api.events_post(FillDefaults(appEvent)) + def send_event(appEvent) + @events_api.events_post(fill_defaults(appEvent)) end ## @@ -220,25 +220,25 @@ def SendEvent(appEvent) #RETURNS: The AppEvent with Defaults filled. #appEvent:AppEvent: The AppEvent to fill. ## - def FillDefaults(appEvent) - appEvent.api_key = appEvent.api_key || @apiKey + def fill_defaults(appEvent) + appEvent.api_key = appEvent.api_key || @api_key - appEvent.context_app_version = appEvent.context_app_version || @contextAppVersion - appEvent.deployment_stage = appEvent.deployment_stage || @contextDeploymentStage + appEvent.context_app_version = appEvent.context_app_version || @context_app_version + appEvent.deployment_stage = appEvent.deployment_stage || @context_deployment_stage - appEvent.context_env_language = appEvent.context_env_language || @contextEnvLanguage - appEvent.context_env_name = appEvent.context_env_name || @contextEnvName - appEvent.context_env_version = appEvent.context_env_version || @contextEnvVersion - appEvent.context_env_hostname = appEvent.context_env_hostname || @contextEnvHostname + appEvent.context_env_language = appEvent.context_env_language || @context_env_language + appEvent.context_env_name = appEvent.context_env_name || @context_env_name + appEvent.context_env_version = appEvent.context_env_version || @context_env_version + appEvent.context_env_hostname = appEvent.context_env_hostname || @context_env_version - appEvent.context_app_os = appEvent.context_app_os || @contextAppOS - appEvent.context_app_os_version = appEvent.context_app_os_version || @contextAppOSVersion + appEvent.context_app_os = appEvent.context_app_os || @context_app_os + appEvent.context_app_os_version = appEvent.context_app_os_version || @context_app_os_version - appEvent.context_app_browser = appEvent.context_app_browser || @contextAppBrowser - appEvent.context_app_browser_version = appEvent.context_app_browser_version || @contextAppBrowserVersion + appEvent.context_app_browser = appEvent.context_app_browser || @context_app_browser + appEvent.context_app_browser_version = appEvent.context_app_browser_version || @context_app_browser_version - appEvent.context_data_center = appEvent.context_data_center || @contextDataCenter - appEvent.context_data_center_region = appEvent.context_data_center_region || @contextDataCenterRegion + appEvent.context_data_center = appEvent.context_data_center || @context_data_center + appEvent.context_data_center_region = appEvent.context_data_center_region || @context_data_center_region appEvent.event_time = DateTime.now.strftime("%Q").to_i return appEvent @@ -253,7 +253,7 @@ def FillDefaults(appEvent) #prefix:String: The prefix string to start getting the text after #suffix:String: The suffix string to find the ending index for. ## - def GetTextFromLine(text, prefix, suffix) + def get_text_from_line(text, prefix, suffix) raise ArgumentError, "All arguments are expected strings." unless (text.is_a? String) && (prefix.is_a? String) && (suffix.is_a? String) prefixindex = text.index(prefix) diff --git a/trakerr/lib/trakerr_formatter.rb b/trakerr/lib/trakerr_formatter.rb new file mode 100644 index 0000000..d9878d3 --- /dev/null +++ b/trakerr/lib/trakerr_formatter.rb @@ -0,0 +1,13 @@ +require 'logger' + +module Trakerr + class TrakerrFormatter < Logger::Formatter + def initialize() + super() + end + + def call(severity, time, progname, msg) + "#{severity}\n#{progname}\n#{msg2str(msg)}\n" + end + end +end \ No newline at end of file diff --git a/trakerr/lib/trakerr_writer.rb b/trakerr/lib/trakerr_writer.rb new file mode 100644 index 0000000..3818537 --- /dev/null +++ b/trakerr/lib/trakerr_writer.rb @@ -0,0 +1,49 @@ +require 'trakerr_client' +require 'event_trace_builder' + +module Trakerr + class TrakerrWriter < StringIO + def initialize(apiKey, contextAppVersion="1.0", contextDeploymentStage="development") + super() + @client = Trakerr::TrakerrClient.new(apiKey, contextAppVersion, contextDeploymentStage) + end + + def write(str) + strarray = str.dup.split("\n") + + loglevel = nil + classification = nil + evname = nil + evmessage = nil + stacktrace = [] + + strarray.each_index do |i| + if i == 0 #TrakerrFormatter dictates severity as the first line. + loglevel = strarray[i] + + elsif i == 1 #TrakerrFormatter dictates progname as the second line. This is optional, but will be used as a classification. + classification = strarray[i] + + elsif i == 2 #TrakerrFormatter dictates `message` as the their line. Message is actually the error message AND the name of the error in parenthesis. + ob = strarray[i].match(/(?.*)(?\(.*\))/) + evname = ob[:name].gsub(/^\(+|\)+$/, '') + evmessage = ob[:message] + + else #All following lines are stacktrace shoved into the buffer automatically if provided. + #This is only given if the logger gets an error object, + #but I don't believe the TrakerrFormatter has access to it + + stacktrace << strarray[i] + end + + end + + event = @client.create_app_event(false, loglevel, classification, evname, evmessage) + event.event_stacktrace = EventTraceBuilder.get_logger_stacktrace(evname, evmessage, stacktrace) \ + unless stacktrace.empty? + @client.send_event(event) + + super(str) + end + end +end \ No newline at end of file From b393ef3c7e94caf04d0737a6983ff42c7b25d3f8 Mon Sep 17 00:00:00 2001 From: RMSD Date: Fri, 5 May 2017 14:17:44 -0700 Subject: [PATCH 04/10] Doc update and update to writer Document now has first part of three minute guide refactor, added first part of stuff in the writer to allow control of the level of events sent to trakerr. --- README.md | 65 ++++++++++++++++++++++++++++++----- trakerr/lib/trakerr_writer.rb | 9 +++-- 2 files changed, 62 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index a457295..b4419a6 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,58 @@ You will need your API key to send events to trakerr. ## Requirements. Ruby 1.9.3+, git 2.0+, and curl 7.47.0+ + +## 3-minute Integration Guide +First install [git and curl](#Install-git-and-curl). Once you have that complete, issue the command: + +```bash +gem "trakerr_client", :git => "git://github.com/trakerr-io/trakerr-ruby.git" +``` + +or + +```bash +gem install trakerr_client +``` + +Then import the packages: +```ruby +require_relative 'trakerr/lib/trakerr_formatter' +require_relative 'trakerr/lib/trakerr_writer' +``` +Finally, the intergration. Trakerr uses a string stream to catch the output of a logger and send it to trakerr. Trakerr afterwards writes the code to the stream, so it's possible to write that data elsewhere as required from the stream. + +```ruby +stream = Trakerr::TrakerrWriter.new(api_key, "2.0", "development") +rlog = Logger.new(stream) +rlog.formatter = Trakerr::TrakerrFormatter.new +``` + +Note that the formatter does change the layout of the output to make easier for Trakerr to parse. The layout will be at the bottom. Wherever you use the logger, it will also send it to trakerr. If you want to use the string stream afterwards, you may. + +```ruby +begin + raise IOError, "Failed to open file" +rescue IOError => err + rlog.fatal err +end + +stream.rewind +log = stream.read +puts log +``` + +The layout of the output is as follows +``` +Severity +progname +errormessage (errortype) +[Stacktrace] +... +``` +where the stacktrace can be multiple lines + + ## Installation & Usage ### 1) Install git and curl You will need git for the gem to work properly. If you do not have it installed, we recomment installing it from your package manager. You can use your package manager to install it on unix based machines. For machines using apt (ex: Ubuntu) @@ -25,29 +77,24 @@ For Windows, or if you aren't using a package manager, visit https://git-scm.com If you are on Windows, you may also need to install curl and configure your ruby to use it. Trakerr uses typhous to actually send the exception to us. Follow the instructions on the curl website for more information and Typhous's project page to finish setup. ### 2) gem install - Install [bundler](http://bundler.io/) and then you can issue this command to get the freshest version: -```sh +```bash gem "trakerr_client", :git => "git://github.com/trakerr-io/trakerr-ruby.git" ``` You can also install from ruby gems: -```sh +```bash gem install trakerr_client ``` for the latest stable release. -Then import the package: -```ruby -require 'trakerr/lib/trakerr' -``` - ## Detailed Integration Guide Please follow the [installation procedure](#installation--usage) and you're set to add Trakerr to your project. All of these examples are included in test_app.rb. If you would like to generate some quick sample events, you may download test_app.rb and run it from the command line like so: -```sh + +```bash ruby test_app.rb ``` diff --git a/trakerr/lib/trakerr_writer.rb b/trakerr/lib/trakerr_writer.rb index 3818537..5a7a1e4 100644 --- a/trakerr/lib/trakerr_writer.rb +++ b/trakerr/lib/trakerr_writer.rb @@ -3,9 +3,12 @@ module Trakerr class TrakerrWriter < StringIO - def initialize(apiKey, contextAppVersion="1.0", contextDeploymentStage="development") + def initialize(api_key, context_app_version="1.0", context_deployment_stage="development", log_limit = "warn") super() - @client = Trakerr::TrakerrClient.new(apiKey, contextAppVersion, contextDeploymentStage) + @client = Trakerr::TrakerrClient.new(api_key, context_app_version, context_deployment_stage) + + @log_limit = log_limit.downcase + @log_limit.strip! end def write(str) @@ -45,5 +48,5 @@ def write(str) super(str) end - end + end end \ No newline at end of file From b083f010214e741c32d438f006c19e1d83007121 Mon Sep 17 00:00:00 2001 From: RMSD Date: Tue, 9 May 2017 15:43:52 -0700 Subject: [PATCH 05/10] Update README.md Fixed spelling mistake. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b4419a6..62084af 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,7 @@ Then import the packages: require_relative 'trakerr/lib/trakerr_formatter' require_relative 'trakerr/lib/trakerr_writer' ``` -Finally, the intergration. Trakerr uses a string stream to catch the output of a logger and send it to trakerr. Trakerr afterwards writes the code to the stream, so it's possible to write that data elsewhere as required from the stream. +Finally, the integration. Trakerr uses a string stream to catch the output of a logger and send it to trakerr. Trakerr afterwards writes the code to the stream, so it's possible to write that data elsewhere as required from the stream. ```ruby stream = Trakerr::TrakerrWriter.new(api_key, "2.0", "development") From 0e84881d224f60ba1f8298be86a35a5512b8ea84 Mon Sep 17 00:00:00 2001 From: RMSD Date: Fri, 26 May 2017 17:22:00 -0700 Subject: [PATCH 06/10] Update logger. The logger feature is now tested and complete! --- test_app.rb | 2 +- trakerr/lib/event_trace_builder.rb | 333 ++++++++++++++--------------- trakerr/lib/trakerr.rb | 281 ++++++++++++------------ trakerr/lib/trakerr_formatter.rb | 22 +- trakerr/lib/trakerr_writer.rb | 157 ++++++++++---- 5 files changed, 429 insertions(+), 366 deletions(-) diff --git a/test_app.rb b/test_app.rb index 9d424e6..87a6bee 100644 --- a/test_app.rb +++ b/test_app.rb @@ -32,7 +32,7 @@ def main() stream = Trakerr::TrakerrWriter.new(api_key, "2.0", "development") rlog = Logger.new(stream) - + #rlog = Logger.new($stdout) rlog.formatter = Trakerr::TrakerrFormatter.new begin diff --git a/trakerr/lib/event_trace_builder.rb b/trakerr/lib/event_trace_builder.rb index 16da010..cdae790 100644 --- a/trakerr/lib/event_trace_builder.rb +++ b/trakerr/lib/event_trace_builder.rb @@ -1,208 +1,207 @@ -=begin -Trakerr API - -Get your application events and errors to Trakerr via the *Trakerr API*. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - -http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -=end - -require "trakerr_client" - +# Trakerr API +# +# Get your application events and errors to Trakerr via the *Trakerr API*. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +require 'trakerr_client' module Trakerr class EventTraceBuilder ## - #Gets the stactrace from the exception instance passed in. - #RETURNS: A Stacktrace object that contains the trace of the exception passed in. - #exc:Exception: The exception caught or rescued. + # Gets the stactrace from the exception instance passed in. + # RETURNS: A Stacktrace object that contains the trace of the exception passed in. + # exc:Exception: The exception caught or rescued. ## def self.get_stacktrace(exc) - raise ArgumentError, "get_stacktrace expects an exception instance." unless exc.is_a? Exception + raise ArgumentError, 'get_stacktrace expects an exception instance.' unless exc.is_a? Exception strace = Trakerr::Stacktrace.new add_stack_trace(strace, exc.class.name, exc.message, best_regexp_for(exc), exc.backtrace) - return strace + strace end def self.get_logger_stacktrace(errtype, errmessage, stackarray) - raise ArgumentError, "errtype and errmessage are expected strings" unless (errtype.is_a? String) && (errmessage.is_a? String) - raise ArgumentError, "stackarray is expected to be an iterable with strings values" unless stackarray.respond_to?('each') + raise ArgumentError, 'errtype and errmessage are expected strings' unless (errtype.is_a? String) && (errmessage.is_a? String) + raise ArgumentError, 'stackarray is expected to be an iterable with strings values' unless stackarray.respond_to?('each') strace = Trakerr::Stacktrace.new - self.add_stack_trace(strace, errtype, errmessage, best_regexp_guess(stackarray[0]), stackarray) - return strace + add_stack_trace(strace, errtype, errmessage, best_regexp_guess(stackarray[0]), stackarray) + strace end private - def self.add_stack_trace(strace, errtype, errmessage, regex, stackarray) - newtrace = Trakerr::InnerStackTrace.new - newtrace.type = errtype - newtrace.message = errmessage - newtrace.trace_lines = get_event_tracelines(regex, stackarray) - strace.push(newtrace) - end + def self.add_stack_trace(strace, errtype, errmessage, regex, stackarray) + newtrace = Trakerr::InnerStackTrace.new - ## - #Formats and returns a StackTraceLines object that holds the current stacktrace from the error. - #RETURNS: A StackTraceLines object that contains the parsed traceback. - #regex:RegularExpression: The regular expression to parse the stacktrace text with. - #errarray:String[]: An array of strings which each of which is a StackTrace string line. - ## - def self.get_event_tracelines(regex, errarray) - raise ArgumentError, "errarray should be an iterable object." unless errarray.respond_to?('each') + newtrace.type = errtype + newtrace.message = errmessage + newtrace.trace_lines = get_event_tracelines(regex, stackarray) + strace.push(newtrace) + end + + ## + # Formats and returns a StackTraceLines object that holds the current stacktrace from the error. + # RETURNS: A StackTraceLines object that contains the parsed traceback. + # regex:RegularExpression: The regular expression to parse the stacktrace text with. + # errarray:String[]: An array of strings which each of which is a StackTrace string line. + ## + def self.get_event_tracelines(regex, errarray) + raise ArgumentError, 'errarray should be an iterable object.' unless errarray.respond_to?('each') - stlines = Trakerr::StackTraceLines.new + stlines = Trakerr::StackTraceLines.new - errarray.each {|line| + errarray.each do |line| stline = Trakerr::StackTraceLine.new match = parse_stacktrace(regex, line) - stline.file, stline.line, stline.function = match[:file], match[:line], match[:function] + stline.file = match[:file] + stline.line = match[:line] + stline.function = match[:function] stlines.push(stline) - } - return stlines end + stlines + end - ## - #Parses each given line by the regex. - #RETURNS: A match object with the capture groups file function and line set. - #regex:RegularExpression: The regular expression to parse the stacktrace text with. - #line:String: A string with the traceline to parce - ## - def self.parse_stacktrace(regex, line) - raise ArgumentError, "line should be a string." unless line.is_a? String + ## + # Parses each given line by the regex. + # RETURNS: A match object with the capture groups file function and line set. + # regex:RegularExpression: The regular expression to parse the stacktrace text with. + # line:String: A string with the traceline to parce + ## + def self.parse_stacktrace(regex, line) + raise ArgumentError, 'line should be a string.' unless line.is_a? String - match = regex.match(line) - return match if match + match = regex.match(line) + return match if match - raise RegexpError, "line does not fit any of the supported stacktraces." #TODO: Error handle this? - end + raise RegexpError, 'line does not fit any of the supported stacktraces.' # TODO: Error handle this? + end - def self.best_regexp_for(exc) - #TODO: add error check - if defined?(Java::JavaLang::Throwable) && exc.is_a?(Java::JavaLang::Throwable) - @@JAVA - elsif defined?(OCIError) && exc.is_a?(OCIError) - @@OCI - #elsif execjs_exception?(exception) - # Patterns::EXECJS disabled pending more complex test - else - @@RUBY - end + def self.best_regexp_for(exc) + # TODO: add error check + if defined?(Java::JavaLang::Throwable) && exc.is_a?(Java::JavaLang::Throwable) + @@JAVA + elsif defined?(OCIError) && exc.is_a?(OCIError) + @@OCI + # elsif execjs_exception?(exception) + # Patterns::EXECJS disabled pending more complex test + else + @@RUBY end + end - def self.best_regexp_guess(str) - #Guess the regex. Test each regex on the string and see which regex captures the most data. - #Use the one that captures the most. RegexErrors if none of the regexs are able to match - - java_match = @@JAVA.match(str) if defined?(Java::JavaLang::Throwable) - oci_match = @@OCI.match(str) #if defined?(OCIError) - ruby_match = @@RUBY.match(str) - java_count = 0 - ruby_count = 0 - oci_count = 0 - - ruby_match.captures.each {|item| ruby_count+= 1 if item} if ruby_match - oci_match.captures.each {|item| oci_count+= 1 if item} if oci_match - java_match.captures.each {|item| java_count+= 1 if item} if java_match - - if ruby_count >= oci_count && ruby_count >= java_count && ruby_count > 0 - @@RUBY - elsif oci_count >= ruby_count && oci_count >= java_count && oci_count > 0 - @@OCI - elsif java_count >= ruby_count && java_count >= oci_count && java_count > 0 - @@JAVA - else - raise RegexpError, "line does not fit any of the supported stacktraces." - end + def self.best_regexp_guess(str) + # Guess the regex. Test each regex on the string and see which regex captures the most data. + # Use the one that captures the most. RegexErrors if none of the regexs are able to match + + java_match = @@JAVA.match(str) if defined?(Java::JavaLang::Throwable) + oci_match = @@OCI.match(str) # if defined?(OCIError) + ruby_match = @@RUBY.match(str) + java_count = 0 + ruby_count = 0 + oci_count = 0 + + ruby_match.captures.each { |item| ruby_count += 1 if item } if ruby_match + oci_match.captures.each { |item| oci_count += 1 if item } if oci_match + java_match.captures.each { |item| java_count += 1 if item } if java_match + + if ruby_count >= oci_count && ruby_count >= java_count && ruby_count > 0 + @@RUBY + elsif oci_count >= ruby_count && oci_count >= java_count && oci_count > 0 + @@OCI + elsif java_count >= ruby_count && java_count >= oci_count && java_count > 0 + @@JAVA + else + raise RegexpError, 'line does not fit any of the supported stacktraces.' end + end - ## - # @return [Regexp] the pattern that matches standard Ruby stack frames, - # such as ./spec/notice_spec.rb:43:in `block (3 levels) in ' - @@RUBY = %r{\A - (?.+) # Matches './spec/notice_spec.rb' - : - (?\d+) # Matches '43' - :in\s - `(?.*)' # Matches "`block (3 levels) in '" - \z}x - - ## - # @return [Regexp] the pattern that matches JRuby Java stack frames, such - # as org.jruby.ast.NewlineNode.interpret(NewlineNode.java:105) - @@JAVA = %r{\A - (?.+) # Matches 'org.jruby.ast.NewlineNode.interpret' - \( - (? - (?:uri:classloader:/.+(?=:)) # Matches '/META-INF/jruby.home/protocol.rb' - | - (?:uri_3a_classloader_3a_.+(?=:)) # Matches 'uri_3a_classloader_3a_/gems/...' - | - [^:]+ # Matches 'NewlineNode.java' - ) - :? - (?\d+)? # Matches '105' - \) - \z}x - - ## - # @return [Regexp] the pattern that tries to assume what a generic stack - # frame might look like, when exception's backtrace is set manually. - @@GENERIC = %r{\A - (?:from\s)? - (?.+) # Matches '/foo/bar/baz.ext' + ## + # @return [Regexp] the pattern that matches standard Ruby stack frames, + # such as ./spec/notice_spec.rb:43:in `block (3 levels) in ' + @@RUBY = %r{\A + (?.+) # Matches './spec/notice_spec.rb' : - (?\d+)? # Matches '43' or nothing - (?: - in\s`(?.+)' # Matches "in `func'" - | - :in\s(?.+) # Matches ":in func" - )? # ... or nothing + (?\d+) # Matches '43' + :in\s + `(?.*)' # Matches "`block (3 levels) in '" \z}x - ## - # @return [Regexp] the pattern that matches exceptions from PL/SQL such as - # ORA-06512: at "STORE.LI_LICENSES_PACK", line 1945 - # @note This is raised by https://github.com/kubo/ruby-oci8 - @@OCI = /\A - (?: - ORA-\d{5} - :\sat\s - (?:"(?.+)",\s)? - line\s(?\d+) - | - #{@@GENERIC} - ) - \z/x - - ## - # @return [Regexp] the pattern that matches CoffeeScript backtraces - # usually coming from Rails & ExecJS - @@EXECJS = /\A - (?: - # Matches 'compile ((execjs):6692:19)' - (?.+)\s\((?.+):(?\d+):\d+\) - | - # Matches 'bootstrap_node.js:467:3' - (?.+):(?\d+):\d+(?) - | - # Matches the Ruby part of the backtrace - #{@@RUBY} + ## + # @return [Regexp] the pattern that matches JRuby Java stack frames, such + # as org.jruby.ast.NewlineNode.interpret(NewlineNode.java:105) + @@JAVA = %r{\A + (?.+) # Matches 'org.jruby.ast.NewlineNode.interpret' + \( + (? + (?:uri:classloader:/.+(?=:)) # Matches '/META-INF/jruby.home/protocol.rb' + | + (?:uri_3a_classloader_3a_.+(?=:)) # Matches 'uri_3a_classloader_3a_/gems/...' + | + [^:]+ # Matches 'NewlineNode.java' ) - \z/x + :? + (?\d+)? # Matches '105' + \) + \z}x + + ## + # @return [Regexp] the pattern that tries to assume what a generic stack + # frame might look like, when exception's backtrace is set manually. + @@GENERIC = %r{\A + (?:from\s)? + (?.+) # Matches '/foo/bar/baz.ext' + : + (?\d+)? # Matches '43' or nothing + (?: + in\s`(?.+)' # Matches "in `func'" + | + :in\s(?.+) # Matches ":in func" + )? # ... or nothing + \z}x + ## + # @return [Regexp] the pattern that matches exceptions from PL/SQL such as + # ORA-06512: at "STORE.LI_LICENSES_PACK", line 1945 + # @note This is raised by https://github.com/kubo/ruby-oci8 + @@OCI = /\A + (?: + ORA-\d{5} + :\sat\s + (?:"(?.+)",\s)? + line\s(?\d+) + | + #{@@GENERIC} + ) + \z/x + + ## + # @return [Regexp] the pattern that matches CoffeeScript backtraces + # usually coming from Rails & ExecJS + @@EXECJS = /\A + (?: + # Matches 'compile ((execjs):6692:19)' + (?.+)\s\((?.+):(?\d+):\d+\) + | + # Matches 'bootstrap_node.js:467:3' + (?.+):(?\d+):\d+(?) + | + # Matches the Ruby part of the backtrace + #{@@RUBY} + ) + \z/x end -end \ No newline at end of file +end diff --git a/trakerr/lib/trakerr.rb b/trakerr/lib/trakerr.rb index 73875b7..132c5c8 100644 --- a/trakerr/lib/trakerr.rb +++ b/trakerr/lib/trakerr.rb @@ -1,42 +1,39 @@ -=begin -Trakerr API - -Get your application events and errors to Trakerr via the *Trakerr API*. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - -http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -=end - -require "event_trace_builder" -require "trakerr_client" -require "socket" -require "date" +# Trakerr API +# +# Get your application events and errors to Trakerr via the *Trakerr API*. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +require 'event_trace_builder' +require 'trakerr_client' +require 'socket' +require 'date' module Trakerr class TrakerrClient - - ##API Key + # #API Key attr_accessor :api_key - ##App Version of the client the API is tying into. + # #App Version of the client the API is tying into. attr_accessor :context_app_version - ##Deployment stage of the codebade the API is tying into. + # #Deployment stage of the codebade the API is tying into. attr_accessor :context_deployment_stage - ##String name of the language being used. + # #String name of the language being used. attr_accessor :context_env_language - ##The name of the interpreter + # #The name of the interpreter attr_accessor :context_env_name ## context_env_version is the version of the interpreter the program is run on. @@ -64,14 +61,14 @@ class TrakerrClient attr_accessor :context_data_center_region ## - #Initializes the TrakerrClient class. - #api_key:String: Should be your API key string. - #context_app_version:String: Should be the version of your application. - #context_env_name:String: Should be the deployment stage of your program. + # Initializes the TrakerrClient class. + # api_key:String: Should be your API key string. + # context_app_version:String: Should be the version of your application. + # context_env_name:String: Should be the deployment stage of your program. ## def initialize(api_key, - context_app_version="1.0", - context_deployment_stage="development") + context_app_version = '1.0', + context_deployment_stage = 'development') default_config = Trakerr::Configuration.default default_config.base_path = default_config.base_path @@ -80,52 +77,49 @@ def initialize(api_key, @context_app_version = context_app_version @context_deployment_stage = context_deployment_stage - @context_env_language = "Ruby" - - if RUBY_PLATFORM == "java" - @context_env_name = "jruby" + @context_env_language = 'Ruby' + + if RUBY_PLATFORM == 'java' + @context_env_name = 'jruby' @context_env_version = JRUBY_VERSION else - @context_env_name = "ruby" + @context_env_name = 'ruby' @context_env_version = RUBY_VERSION end @context_env_version = Socket.gethostname - + host_os = RbConfig::CONFIG['host_os'] case host_os - when /mswin|msys|mingw|cygwin|bccwin|wince|emc/ - text = `systeminfo` - - @context_app_os = get_text_from_line(text, "OS Name:", "\n") - @context_app_os.chomp! if @context_app_os != nil - @context_app_os.strip! if @context_app_os != nil - - version = get_text_from_line(text, "OS Version:", "\n").split - version[0].chomp! if version != nil - version[0].strip! if version != nil - @context_app_os_version = context_app_os_version || version[0] - - - when /darwin|mac os/ - text = `system_profiler SPSoftwareDataType` - - @context_app_os = get_text_from_line(text, "System Version:", "(").chomp.strip - @context_app_os_version = context_app_os_version || get_text_from_line(text, "Kernel Version:", "\n").chomp.strip - - when /linux/, /solaris|bsd/ - #uname -s and -r - @context_app_os = `uname -s`.chomp.strip - @context_app_os_version = context_app_os_version || `uname -r`.chomp.strip - end + when /mswin|msys|mingw|cygwin|bccwin|wince|emc/ + text = `systeminfo` + + @context_app_os = get_text_from_line(text, 'OS Name:', "\n") + @context_app_os.chomp! unless @context_app_os.nil? + @context_app_os.strip! unless @context_app_os.nil? + + version = get_text_from_line(text, 'OS Version:', "\n").split + version[0].chomp! unless version.nil? + version[0].strip! unless version.nil? + @context_app_os_version = context_app_os_version || version[0] - if @context_app_os == nil - @context_app_os = RbConfig::CONFIG["target_os"] + when /darwin|mac os/ + text = `system_profiler SPSoftwareDataType` + + @context_app_os = get_text_from_line(text, 'System Version:', '(').chomp.strip + @context_app_os_version = context_app_os_version || get_text_from_line(text, 'Kernel Version:', "\n").chomp.strip + + when /linux/, /solaris|bsd/ + # uname -s and -r + @context_app_os = `uname -s`.chomp.strip + @context_app_os_version = context_app_os_version || `uname -r`.chomp.strip end - if @context_app_os_version == nil + + @context_app_os = RbConfig::CONFIG['target_os'] if @context_app_os.nil? + if @context_app_os_version.nil? @context_app_os_version = RbConfig::CONFIG['host_os'] end - + @context_app_browser = context_app_browser @context_app_browser_version = context_app_browser_version @context_data_center = context_data_center @@ -135,90 +129,90 @@ def initialize(api_key, end ## - #Creates a new AppEvent and returns it with a stacktrace if err is an exception object. - #If passed false, it returns an AppEvent without a stacktrace. - #RETURNS: An AppEvent instance with the default event information. - #err:Exception: The exception that is captured or rescued, or false if you don't need a stacktrace. - #log_level:String: Logging level, currently one of 'debug','info','warning','error', 'fatal', defaults to 'error'. See loglevel in AppEvent for an always current list of values. - #Will argument error if passed another value. - #classification:String: Optional extra descriptor string. Will default to issue if not passed a value. - #eventType:string: String representation of the type of error. - #Defaults to err.class.name if err is an exception, unknown if not. - #eventMessage:String: String representation of the message of the error. - #Defaults to err.message if err is an exception, unknown if not. + # Creates a new AppEvent and returns it with a stacktrace if err is an exception object. + # If passed false, it returns an AppEvent without a stacktrace. + # RETURNS: An AppEvent instance with the default event information. + # err:Exception: The exception that is captured or rescued, or false if you don't need a stacktrace. + # log_level:String: Logging level, currently one of 'debug','info','warning','error', 'fatal', defaults to 'error'. See loglevel in AppEvent for an always current list of values. + # Will argument error if passed another value. + # classification:String: Optional extra descriptor string. Will default to issue if not passed a value. + # eventType:string: String representation of the type of error. + # Defaults to err.class.name if err is an exception, unknown if not. + # eventMessage:String: String representation of the message of the error. + # Defaults to err.message if err is an exception, unknown if not. ## - def create_app_event(err = false, log_level="Error", classification="issue", eventType="unknown", eventMessage="unknown") - raise ArgumentError, "All non err arguments are expected strings." unless (log_level.is_a? String) && (classification.is_a? String) && (eventType.is_a? String) && (eventMessage.is_a? String) + def create_app_event(err = false, log_level = 'Error', classification = 'issue', eventType = 'unknown', eventMessage = 'unknown') + raise ArgumentError, 'All non err arguments are expected strings.' unless (log_level.is_a? String) && (classification.is_a? String) && (eventType.is_a? String) && (eventMessage.is_a? String) if err != false - raise ArgumentError, "err is expected instance of exception." unless err.is_a? Exception - - eventType = err.class.name if eventType == "unknown" || eventType == "" - - eventMessage = err.message if eventMessage == "unknown" || eventMessage == "" + raise ArgumentError, 'err is expected instance of exception.' unless err.is_a? Exception + + eventType = err.class.name if eventType == 'unknown' || eventType == '' + + eventMessage = err.message if eventMessage == 'unknown' || eventMessage == '' end log_level = log_level.downcase - app_event_new = AppEvent.new({classification: classification, eventType: eventType, eventMessage: eventMessage}) + app_event_new = AppEvent.new(classification: classification, eventType: eventType, eventMessage: eventMessage) begin app_event_new.log_level = log_level rescue ArgumentError - app_event_new.log_level = "error" + app_event_new.log_level = 'error' end - + app_event_new.event_stacktrace = EventTraceBuilder.get_stacktrace(err) if err != false - return app_event_new + app_event_new end - + ## - #A single line method to send an event to trakerr. - #Use may it in a begin-rescue and pass in an error, - #or set error to false if you don't need a stacktrace. - #arg_hash takes in a few common values that you may want to populate - #your app event with in a hash. - #arg_hash:Hash: A hash with a key value pair for each of the following elements - #{"user": "...", "session": "...", "evntname": "...", "evntmessage": "..."}. - #Omit any element you don't need to fill in the event. - #If you are NOT sending an error it is recommended that you pass in an evntname and evntmessage. - #Remember that all keys are expected strings, and so it may be safer to you use the arrow - #operator (=>) so you don't forget to add the space. - #error:Exception: The exception you may be sending. Set this to false if you are sending a non-error. - #This throws an Argument error if error is not an Exception and it's child classes or false. - #log_level:String: The string representation of the level of the error. - #classification:String: The string representation on the classification of the issue. + # A single line method to send an event to trakerr. + # Use may it in a begin-rescue and pass in an error, + # or set error to false if you don't need a stacktrace. + # arg_hash takes in a few common values that you may want to populate + # your app event with in a hash. + # arg_hash:Hash: A hash with a key value pair for each of the following elements + # {"user": "...", "session": "...", "evntname": "...", "evntmessage": "..."}. + # Omit any element you don't need to fill in the event. + # If you are NOT sending an error it is recommended that you pass in an evntname and evntmessage. + # Remember that all keys are expected strings, and so it may be safer to you use the arrow + # operator (=>) so you don't forget to add the space. + # error:Exception: The exception you may be sending. Set this to false if you are sending a non-error. + # This throws an Argument error if error is not an Exception and it's child classes or false. + # log_level:String: The string representation of the level of the error. + # classification:String: The string representation on the classification of the issue. ## - def log(arg_hash, error, log_level = "error", classification = "issue") - raise ArgumentError, "arg_hash is expected to be a hash" unless arg_hash.is_a? Hash - raise ArgumentError, "log_level and classification is expected strings." unless (log_level.is_a? String) && (classification.is_a? String) + def log(arg_hash, error, log_level = 'error', classification = 'issue') + raise ArgumentError, 'arg_hash is expected to be a hash' unless arg_hash.is_a? Hash + raise ArgumentError, 'log_level and classification is expected strings.' unless (log_level.is_a? String) && (classification.is_a? String) app_event = nil if error != false - raise ArgumentError, "err is expected instance of exception." unless error.is_a? Exception - app_event = create_app_event(error, log_level, classification, arg_hash.fetch("evntname", "unknown"), arg_hash.fetch("evntmessage", "unknown")) - + raise ArgumentError, 'err is expected instance of exception.' unless error.is_a? Exception + app_event = create_app_event(error, log_level, classification, arg_hash.fetch('eventname', 'unknown'), arg_hash.fetch('evntmessage', 'unknown')) + end - app_event = create_app_event(false,log_level, classification, arg_hash.fetch("evntname", "unknown"), arg_hash.fetch("evntmessage", "unknown")) if app_event.nil? - app_event.event_user = arg_hash["user"] if arg_hash.has_key? "user" - app_event.event_session = arg_hash["session"] if arg_hash.has_key? "session" + app_event = create_app_event(false, log_level, classification, arg_hash.fetch('eventname', 'unknown'), arg_hash.fetch('evntmessage', 'unknown')) if app_event.nil? + app_event.event_user = arg_hash['user'] if arg_hash.key? 'user' + app_event.event_session = arg_hash['session'] if arg_hash.key? 'session' send_event(app_event) end ## - #Sends the given AppEvent to Trakerr - #appEvent:AppEvent: The AppEvent to send. + # Sends the given AppEvent to Trakerr + # appEvent:AppEvent: The AppEvent to send. ## def send_event(appEvent) @events_api.events_post(fill_defaults(appEvent)) end ## - #Populates the given AppEvent with the client level default values - #RETURNS: The AppEvent with Defaults filled. - #appEvent:AppEvent: The AppEvent to fill. + # Populates the given AppEvent with the client level default values + # RETURNS: The AppEvent with Defaults filled. + # appEvent:AppEvent: The AppEvent to fill. ## def fill_defaults(appEvent) appEvent.api_key = appEvent.api_key || @api_key @@ -240,30 +234,31 @@ def fill_defaults(appEvent) appEvent.context_data_center = appEvent.context_data_center || @context_data_center appEvent.context_data_center_region = appEvent.context_data_center_region || @context_data_center_region - appEvent.event_time = DateTime.now.strftime("%Q").to_i - return appEvent + appEvent.event_time = DateTime.now.strftime('%Q').to_i + appEvent end private - ## - #Used for parsing large strings. Gets the text in between a prefix string and a suffix string. - #Currently used to parse responses from shell commands on OS. - #RETURNS: The String from text between prefix and suffix or nil if not found or errors occur. - #text:String: The text to search in. - #prefix:String: The prefix string to start getting the text after - #suffix:String: The suffix string to find the ending index for. - ## - def get_text_from_line(text, prefix, suffix) - raise ArgumentError, "All arguments are expected strings." unless (text.is_a? String) && (prefix.is_a? String) && (suffix.is_a? String) - - prefixindex = text.index(prefix) - return nil if prefixindex == nil - prefixindex = prefixindex + prefix.length - - suffixindex = text.index(suffix, prefixindex) - return nil if suffixindex == nil - - text[prefixindex...suffixindex] - end + + ## + # Used for parsing large strings. Gets the text in between a prefix string and a suffix string. + # Currently used to parse responses from shell commands on OS. + # RETURNS: The String from text between prefix and suffix or nil if not found or errors occur. + # text:String: The text to search in. + # prefix:String: The prefix string to start getting the text after + # suffix:String: The suffix string to find the ending index for. + ## + def get_text_from_line(text, prefix, suffix) + raise ArgumentError, 'All arguments are expected strings.' unless (text.is_a? String) && (prefix.is_a? String) && (suffix.is_a? String) + + prefixindex = text.index(prefix) + return nil if prefixindex.nil? + prefixindex += prefix.length + + suffixindex = text.index(suffix, prefixindex) + return nil if suffixindex.nil? + + text[prefixindex...suffixindex] + end end -end \ No newline at end of file +end diff --git a/trakerr/lib/trakerr_formatter.rb b/trakerr/lib/trakerr_formatter.rb index d9878d3..37da678 100644 --- a/trakerr/lib/trakerr_formatter.rb +++ b/trakerr/lib/trakerr_formatter.rb @@ -1,13 +1,19 @@ require 'logger' module Trakerr - class TrakerrFormatter < Logger::Formatter - def initialize() - super() - end + class TrakerrFormatter < Logger::Formatter + def initialize(standard_format = true) + super() + @standard_format = standard_format + end - def call(severity, time, progname, msg) - "#{severity}\n#{progname}\n#{msg2str(msg)}\n" - end + def call(severity, time, progname, msg) + if @standard_format + severityid = severity[0] + "#{severityid}, [#{time}] #{severity} #{progname} : #{msg2str(msg)}\n" + else + "#{severity}\n#{progname}\n#{msg2str(msg)}\n" + end end -end \ No newline at end of file + end +end diff --git a/trakerr/lib/trakerr_writer.rb b/trakerr/lib/trakerr_writer.rb index 5a7a1e4..f7b10bb 100644 --- a/trakerr/lib/trakerr_writer.rb +++ b/trakerr/lib/trakerr_writer.rb @@ -2,51 +2,114 @@ require 'event_trace_builder' module Trakerr - class TrakerrWriter < StringIO - def initialize(api_key, context_app_version="1.0", context_deployment_stage="development", log_limit = "warn") - super() - @client = Trakerr::TrakerrClient.new(api_key, context_app_version, context_deployment_stage) - - @log_limit = log_limit.downcase - @log_limit.strip! - end - - def write(str) - strarray = str.dup.split("\n") - - loglevel = nil - classification = nil - evname = nil - evmessage = nil - stacktrace = [] - - strarray.each_index do |i| - if i == 0 #TrakerrFormatter dictates severity as the first line. - loglevel = strarray[i] - - elsif i == 1 #TrakerrFormatter dictates progname as the second line. This is optional, but will be used as a classification. - classification = strarray[i] - - elsif i == 2 #TrakerrFormatter dictates `message` as the their line. Message is actually the error message AND the name of the error in parenthesis. - ob = strarray[i].match(/(?.*)(?\(.*\))/) - evname = ob[:name].gsub(/^\(+|\)+$/, '') - evmessage = ob[:message] - - else #All following lines are stacktrace shoved into the buffer automatically if provided. - #This is only given if the logger gets an error object, - #but I don't believe the TrakerrFormatter has access to it - - stacktrace << strarray[i] - end - - end - - event = @client.create_app_event(false, loglevel, classification, evname, evmessage) - event.event_stacktrace = EventTraceBuilder.get_logger_stacktrace(evname, evmessage, stacktrace) \ - unless stacktrace.empty? - @client.send_event(event) - - super(str) - end + class TrakerrWriter < StringIO + def initialize(api_key, context_app_version = '1.0', context_deployment_stage = 'development', log_limit = 'warn', standard_format = true) + super() + @client = Trakerr::TrakerrClient.new(api_key, context_app_version, context_deployment_stage) + + @log_limit = str2level(log_limit) + @standard_format = standard_format + end + + def write(str) + strarray = str.dup.split("\n") + loglevel, classification, evname, evmessage = nil + stacktrace = [] + + if @standard_format + loglevel, classification, evname, evmessage, stacktrace = parse_standard(strarray) + else + loglevel, classification, evname, evmessage, stacktrace = parse_custom(strarray) + end + + if str2level(loglevel) >= @log_limit + event = @client.create_app_event(false, loglevel, classification, evname, evmessage) + event.event_stacktrace = EventTraceBuilder.get_logger_stacktrace(evname, evmessage, stacktrace) \ + unless stacktrace.empty? + @client.send_event(event) + end + + super(str) + end + + private + + def parse_custom(_buffer) + loglevel = nil + classification = nil + evname = nil + evmessage = nil + stacktrace = [] + + _buffer.each_index do |i| + if i == 0 # TrakerrFormatter dictates severity as the first line. + loglevel = _buffered_string[i] + + elsif i == 1 # TrakerrFormatter dictates progname as the second line. This is optional, but will be used as a classification. + classification = _buffer[i] + + elsif i == 2 # TrakerrFormatter dictates `message` as the their line. Message is actually the error message AND the name of the error in parenthesis. + ob = _buffer[i].match(/(?.*)(?\(.*\))/) + evname = ob[:name].gsub(/^\(+|\)+$/, '') + evmessage = ob[:message] + + else # All following lines are stacktrace shoved into the buffer automatically if provided. + # This is only given if the logger gets an error object, + # but I don't believe the TrakerrFormatter has access to it + + stacktrace << _buffer[i] + end + end + + [loglevel, classification, evname, evmessage, stacktrace] end -end \ No newline at end of file + + def parse_standard(_buffer) + loglevel = nil + classification = nil + evname = nil + evmessage = nil + stacktrace = [] + + _buffer.each_index do |i| + if i == 0 # TrakerrFormatter dictates severity as the first line. + ob = _buffer[i].match(/\S+ \[.*\] (?\w+) (?\w*) : (?[\w\s]+) \((?\w+)\)/i) + loglevel = ob[:level] + classification = ob[:progname] + evname = ob[:name] + evmessage = ob[:message] + + else # All following lines are stacktrace shoved into the buffer automatically if provided. + # This is only given if the logger gets an error object, + # but I don't believe the TrakerrFormatter has access to it to parse out in the RE. + + stacktrace << _buffer[i] + end + end + + [loglevel, classification, evname, evmessage, stacktrace] + end + + def str2level(level) + level.downcase! + level.strip! + + case level + when "unknown" + 50 + when "fatal" + 40 + when "error" + 30 + when "warn", "warning" + 20 + when "info" + 10 + when "debug" + 0 + else + 30 + end + end + end +end From 901fda1e76cc3b18e89b36fa0ac4dad879ced3f0 Mon Sep 17 00:00:00 2001 From: RMSD Date: Fri, 26 May 2017 17:34:04 -0700 Subject: [PATCH 07/10] Commit for api v1.3 (#22) * Using tokenized api key in docs * Commit for api v1.3 --- README.md | 6 +- generated/README.md | 2 +- generated/docs/AppEvent.md | 10 ++- .../lib/trakerr_client/models/app_event.rb | 89 ++++++++++++++++++- .../models/stack_trace_lines.rb | 2 +- .../lib/trakerr_client/models/stacktrace.rb | 2 +- 6 files changed, 100 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 62084af..da28de0 100644 --- a/README.md +++ b/README.md @@ -109,7 +109,7 @@ require 'trakerr/lib/trakerr' A trivial case would involve calling `log` for a caught exception. ```ruby def main() - testApp = Trakerr::TrakerrClient.new("Api key here", "Application version number", "deployment type") + testApp = Trakerr::TrakerrClient.new("", "Application version number", "deployment type") begin raise ZeroDivisionError, "Oh no!" rescue ZeroDivisionError => exception @@ -130,7 +130,7 @@ If you want to populate the `AppEvent` fully with custom properties (log only ac ```ruby def main() - testApp = Trakerr::TrakerrClient.new("Api key here", "Application version number", "deployment type") + testApp = Trakerr::TrakerrClient.new("", "Application version number", "deployment type") begin raise ArgumentError rescue Exception => e @@ -147,7 +147,7 @@ end Trakerr accepts events that aren't errors. To do so, pass false to the CreateAppEvent Exception field to not attach a stacktrace to the event (if you don't need it). Be sure to pass values in to the rest of the parameters since the default values will most likely not be useful for you if you don't have a stacktrace! ```ruby def main() - testApp = Trakerr::TrakerrClient.new("Api key here", "Application version number", "deployment type") + testApp = Trakerr::TrakerrClient.new("", "Application version number", "deployment type") #Send a non Exception to Trakerr. appev2 = testApp.CreateAppEvent(false, "Info", "User failed auth", "Passwords are different", "User error") diff --git a/generated/README.md b/generated/README.md index 188cf92..fb613fb 100644 --- a/generated/README.md +++ b/generated/README.md @@ -8,7 +8,7 @@ This SDK is automatically generated by the [Swagger Codegen](https://github.com/ - API version: 1.0.0 - Package version: 1.0.0 -- Build date: 2017-03-13T17:18:49.200-07:00 +- Build date: 2017-05-05T15:16:47.647-07:00 - Build package: class io.swagger.codegen.languages.RubyClientCodegen ## Installation diff --git a/generated/docs/AppEvent.md b/generated/docs/AppEvent.md index 2b7927d..eb4bb2e 100644 --- a/generated/docs/AppEvent.md +++ b/generated/docs/AppEvent.md @@ -5,7 +5,7 @@ Name | Type | Description | Notes ------------ | ------------- | ------------- | ------------- **api_key** | **String** | API key generated for the application | **log_level** | **String** | (optional) Logging level, one of 'debug','info','warning','error', 'fatal', defaults to 'error' | [optional] -**classification** | **String** | (optional) one of 'error' or a custom string for non-errors, defaults to 'error' | +**classification** | **String** | (optional) one of 'issue' or a custom string for non-issues, defaults to 'issue' | **event_type** | **String** | type of the event or error (eg. NullPointerException) | **event_message** | **String** | message containing details of the event or error | **event_time** | **Integer** | (optional) event time in ms since epoch | [optional] @@ -24,6 +24,14 @@ Name | Type | Description | Notes **context_app_os_version** | **String** | (optional) OS version the application is running on | [optional] **context_data_center** | **String** | (optional) Data center the application is running on or connected to | [optional] **context_data_center_region** | **String** | (optional) Data center region | [optional] +**context_tags** | **Array<String>** | | [optional] +**context_url** | **String** | (optional) The full URL when running in a browser when the event was generated. | [optional] +**context_operation_time_millis** | **Integer** | (optional) duration that this event took to occur in millis. Example - database call time in millis. | [optional] +**context_cpu_percentage** | **Integer** | (optional) CPU utilization as a percent when event occured | [optional] +**context_memory_percentage** | **Integer** | (optional) Memory utilization as a percent when event occured | [optional] +**context_cross_app_correlation_id** | **String** | (optional) Cross application correlation ID | [optional] +**context_device** | **String** | (optional) Device information | [optional] +**context_app_sku** | **String** | (optional) Application SKU | [optional] **custom_properties** | [**CustomData**](CustomData.md) | | [optional] **custom_segments** | [**CustomData**](CustomData.md) | | [optional] diff --git a/generated/lib/trakerr_client/models/app_event.rb b/generated/lib/trakerr_client/models/app_event.rb index d292e6e..0d6645c 100644 --- a/generated/lib/trakerr_client/models/app_event.rb +++ b/generated/lib/trakerr_client/models/app_event.rb @@ -1,7 +1,7 @@ =begin -Trakerr API +#Trakerr API -Get your application events and errors to Trakerr via the *Trakerr API*. +#Get your application events and errors to Trakerr via the *Trakerr API*. OpenAPI spec version: 1.0.0 @@ -32,7 +32,7 @@ class AppEvent # (optional) Logging level, one of 'debug','info','warning','error', 'fatal', defaults to 'error' attr_accessor :log_level - # (optional) one of 'error' or a custom string for non-errors, defaults to 'error' + # (optional) one of 'issue' or a custom string for non-issues, defaults to 'issue' attr_accessor :classification # type of the event or error (eg. NullPointerException) @@ -88,6 +88,29 @@ class AppEvent # (optional) Data center region attr_accessor :context_data_center_region + attr_accessor :context_tags + + # (optional) The full URL when running in a browser when the event was generated. + attr_accessor :context_url + + # (optional) duration that this event took to occur in millis. Example - database call time in millis. + attr_accessor :context_operation_time_millis + + # (optional) CPU utilization as a percent when event occured + attr_accessor :context_cpu_percentage + + # (optional) Memory utilization as a percent when event occured + attr_accessor :context_memory_percentage + + # (optional) Cross application correlation ID + attr_accessor :context_cross_app_correlation_id + + # (optional) Device information + attr_accessor :context_device + + # (optional) Application SKU + attr_accessor :context_app_sku + attr_accessor :custom_properties attr_accessor :custom_segments @@ -138,6 +161,14 @@ def self.attribute_map :'context_app_os_version' => :'contextAppOSVersion', :'context_data_center' => :'contextDataCenter', :'context_data_center_region' => :'contextDataCenterRegion', + :'context_tags' => :'contextTags', + :'context_url' => :'contextURL', + :'context_operation_time_millis' => :'contextOperationTimeMillis', + :'context_cpu_percentage' => :'contextCpuPercentage', + :'context_memory_percentage' => :'contextMemoryPercentage', + :'context_cross_app_correlation_id' => :'contextCrossAppCorrelationId', + :'context_device' => :'contextDevice', + :'context_app_sku' => :'contextAppSku', :'custom_properties' => :'customProperties', :'custom_segments' => :'customSegments' } @@ -167,6 +198,14 @@ def self.swagger_types :'context_app_os_version' => :'String', :'context_data_center' => :'String', :'context_data_center_region' => :'String', + :'context_tags' => :'Array', + :'context_url' => :'String', + :'context_operation_time_millis' => :'Integer', + :'context_cpu_percentage' => :'Integer', + :'context_memory_percentage' => :'Integer', + :'context_cross_app_correlation_id' => :'String', + :'context_device' => :'String', + :'context_app_sku' => :'String', :'custom_properties' => :'CustomData', :'custom_segments' => :'CustomData' } @@ -264,6 +303,40 @@ def initialize(attributes = {}) self.context_data_center_region = attributes[:'contextDataCenterRegion'] end + if attributes.has_key?(:'contextTags') + if (value = attributes[:'contextTags']).is_a?(Array) + self.context_tags = value + end + end + + if attributes.has_key?(:'contextURL') + self.context_url = attributes[:'contextURL'] + end + + if attributes.has_key?(:'contextOperationTimeMillis') + self.context_operation_time_millis = attributes[:'contextOperationTimeMillis'] + end + + if attributes.has_key?(:'contextCpuPercentage') + self.context_cpu_percentage = attributes[:'contextCpuPercentage'] + end + + if attributes.has_key?(:'contextMemoryPercentage') + self.context_memory_percentage = attributes[:'contextMemoryPercentage'] + end + + if attributes.has_key?(:'contextCrossAppCorrelationId') + self.context_cross_app_correlation_id = attributes[:'contextCrossAppCorrelationId'] + end + + if attributes.has_key?(:'contextDevice') + self.context_device = attributes[:'contextDevice'] + end + + if attributes.has_key?(:'contextAppSku') + self.context_app_sku = attributes[:'contextAppSku'] + end + if attributes.has_key?(:'customProperties') self.custom_properties = attributes[:'customProperties'] end @@ -329,6 +402,14 @@ def ==(o) context_app_os_version == o.context_app_os_version && context_data_center == o.context_data_center && context_data_center_region == o.context_data_center_region && + context_tags == o.context_tags && + context_url == o.context_url && + context_operation_time_millis == o.context_operation_time_millis && + context_cpu_percentage == o.context_cpu_percentage && + context_memory_percentage == o.context_memory_percentage && + context_cross_app_correlation_id == o.context_cross_app_correlation_id && + context_device == o.context_device && + context_app_sku == o.context_app_sku && custom_properties == o.custom_properties && custom_segments == o.custom_segments end @@ -342,7 +423,7 @@ def eql?(o) # Calculates hash code according to all attributes. # @return [Fixnum] Hash code def hash - [api_key, log_level, classification, event_type, event_message, event_time, event_stacktrace, event_user, event_session, context_app_version, deployment_stage, context_env_name, context_env_language, context_env_version, context_env_hostname, context_app_browser, context_app_browser_version, context_app_os, context_app_os_version, context_data_center, context_data_center_region, custom_properties, custom_segments].hash + [api_key, log_level, classification, event_type, event_message, event_time, event_stacktrace, event_user, event_session, context_app_version, deployment_stage, context_env_name, context_env_language, context_env_version, context_env_hostname, context_app_browser, context_app_browser_version, context_app_os, context_app_os_version, context_data_center, context_data_center_region, context_tags, context_url, context_operation_time_millis, context_cpu_percentage, context_memory_percentage, context_cross_app_correlation_id, context_device, context_app_sku, custom_properties, custom_segments].hash end # Builds the object from hash diff --git a/generated/lib/trakerr_client/models/stack_trace_lines.rb b/generated/lib/trakerr_client/models/stack_trace_lines.rb index 05e6928..50c0ce0 100644 --- a/generated/lib/trakerr_client/models/stack_trace_lines.rb +++ b/generated/lib/trakerr_client/models/stack_trace_lines.rb @@ -25,7 +25,7 @@ module Trakerr - class StackTraceLines < Array + class StackTraceLines # Attribute mapping from ruby-style variable name to JSON key. def self.attribute_map diff --git a/generated/lib/trakerr_client/models/stacktrace.rb b/generated/lib/trakerr_client/models/stacktrace.rb index 641f8b1..1df1f29 100644 --- a/generated/lib/trakerr_client/models/stacktrace.rb +++ b/generated/lib/trakerr_client/models/stacktrace.rb @@ -25,7 +25,7 @@ module Trakerr - class Stacktrace < Array + class Stacktrace # Attribute mapping from ruby-style variable name to JSON key. def self.attribute_map From 9ba826224066541b50cdd16afb01c57514b6e44a Mon Sep 17 00:00:00 2001 From: RMSD Date: Fri, 26 May 2017 17:59:42 -0700 Subject: [PATCH 08/10] Update README.md Update spelling, shifted examples, update comments. --- README.md | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index da28de0..2612ec3 100644 --- a/README.md +++ b/README.md @@ -105,7 +105,10 @@ Require the package: require 'trakerr/lib/trakerr' ``` -### Option 1: Sending a default error to Trakerr +### Option 1: Use the logger +See [above](#3-minute-Integration-Guide) to learn how to integrate with the built in ruby logger. + +### Option 2: Sending a default error to Trakerr A trivial case would involve calling `log` for a caught exception. ```ruby def main() @@ -115,17 +118,17 @@ def main() rescue ZeroDivisionError => exception #You can leave the hash empty if you would like to use the default values. #We recommend that you supply a user and a session for all events, - #and supplying an "evntname" and "evntmessage" for non errors. + #and supplying an "eventname" and "eventmessage" for non errors. testApp.log({"user"=>"jack@trakerr.io", "session"=>"7"}, exception) end end ``` -Along with the `"user"` and `"session"`; the hash can also take `"evntname"` and `"evntmessage"`. Note that these two will be filled in automatically for errors you rescue if you do not provide them, so we suggest giving them for non-errors. +Along with the `"user"` and `"session"`; the hash can also take `"eventname"` and `"eventmessage"`. Note that these two will be filled in automatically for errors you rescue if you do not provide them, so we suggest giving them for non-errors. `log` may also take in a log_level and a classification (We recommend you providing this **especially** if you send a warning or below), but will otherwise default all of the AppEvent properties. -### Option 2: Sending an error to Trakerr with Custom Data +### Option 3: Sending an error to Trakerr with Custom Data If you want to populate the `AppEvent` fully with custom properties (log only accepts the minimum set of useful custom properties to utilize Trakerr's rich feature set), you can manually create an `AppEvent` and populate it's fields. Pass it to the `SendEvent` to then send the AppEvent to Trakerr. See the `AppEvent` API for more information on it's properties. ```ruby @@ -143,7 +146,7 @@ def main() end ``` -### Option 3: Send a non-exception to Trakerr +### Option 4: Send a non-exception to Trakerr Trakerr accepts events that aren't errors. To do so, pass false to the CreateAppEvent Exception field to not attach a stacktrace to the event (if you don't need it). Be sure to pass values in to the rest of the parameters since the default values will most likely not be useful for you if you don't have a stacktrace! ```ruby def main() From 2aea4dccb5ca707c8e90c4ab6b9737d580ec51cf Mon Sep 17 00:00:00 2001 From: RMSD Date: Wed, 31 May 2017 13:30:46 -0700 Subject: [PATCH 09/10] Finishes v3 changes. Finishes v3 changes, and makes sure it integrates with the logger. --- README.md | 41 +++++++++++++------ .../models/stack_trace_lines.rb | 2 +- .../lib/trakerr_client/models/stacktrace.rb | 2 +- test_app.rb | 1 + trakerr/lib/trakerr.rb | 15 ++++++- trakerr/lib/trakerr_formatter.rb | 11 ++--- trakerr/lib/trakerr_writer.rb | 36 ++-------------- trakerr_client.gemspec | 2 +- 8 files changed, 52 insertions(+), 58 deletions(-) diff --git a/README.md b/README.md index 2612ec3..72da082 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,6 @@ First install [git and curl](#Install-git-and-curl). Once you have that complete ```bash gem "trakerr_client", :git => "git://github.com/trakerr-io/trakerr-ruby.git" ``` - or ```bash @@ -25,16 +24,20 @@ Then import the packages: require_relative 'trakerr/lib/trakerr_formatter' require_relative 'trakerr/lib/trakerr_writer' ``` -Finally, the integration. Trakerr uses a string stream to catch the output of a logger and send it to trakerr. Trakerr afterwards writes the code to the stream, so it's possible to write that data elsewhere as required from the stream. +Finally, the integration. Trakerr uses a string stream to catch the output of a logger and send it to trakerr. Trakerr afterwards writes the code to the stream, so it's possible to write that data elsewhere as required from the stream. ```ruby -stream = Trakerr::TrakerrWriter.new(api_key, "2.0", "development") +stream = Trakerr::TrakerrWriter.new("", #api key you use for your program + "2.0", #Version of your app + "development", #Deployment stage of your app (production, development, ect) + "error" #log level at which the event and above should send. The log levels mirror that of the logger. (unknown > fatal > error > warn/warning, info > debug). + true, #Formatter switch. Should be the same value as the true false value passed to TrakerrFormatter. + nil)#method to parse the stream with. Only is called if the above is set to false. rlog = Logger.new(stream) rlog.formatter = Trakerr::TrakerrFormatter.new ``` Note that the formatter does change the layout of the output to make easier for Trakerr to parse. The layout will be at the bottom. Wherever you use the logger, it will also send it to trakerr. If you want to use the string stream afterwards, you may. - ```ruby begin raise IOError, "Failed to open file" @@ -47,16 +50,31 @@ log = stream.read puts log ``` -The layout of the output is as follows +There are two ways that the stream can configured to read. The first looks as close to the standard ruby log statement as possible that we provide as an out of the box simple formatter: ``` -Severity -progname -errormessage (errortype) +severityid, [time] severity progname : msg [Stacktrace] -... ``` -where the stacktrace can be multiple lines +The stacktrace is optional, depending on if the logger is passed an error. You may use your own formatter, but you also need to provide a method to parse it. To enable it, provide the following: +```ruby +stream = Trakerr::TrakerrWriter.new("", + "2.0", + "development", + "error", + false, + method(your_method_here(_str))) +rlog = Logger.new(stream) +``` +The last parameter expects a method object of which it can call. The method takes in one parameter, which is a string that holds the entire error information (often on multiple lines). The method should return a list formatted as follows +```ruby +# loglevel is the string level of the event. +# classification is the string small descriptor of the event. +# evname is the string name of the event. +# evmessage is the string message of the event. +# stacktrace is an array of strings which contain each line of the stactrace as an element. This should only have the file, message, and line number, not the prog name or other event info. +[loglevel, classification, evname, evmessage, stacktrace] +``` ## Installation & Usage ### 1) Install git and curl @@ -89,7 +107,6 @@ gem install trakerr_client for the latest stable release. ## Detailed Integration Guide - Please follow the [installation procedure](#installation--usage) and you're set to add Trakerr to your project. All of these examples are included in test_app.rb. If you would like to generate some quick sample events, you may download test_app.rb and run it from the command line like so: @@ -100,7 +117,6 @@ ruby test_app.rb ### Package dependency Require the package: - ```ruby require 'trakerr/lib/trakerr' ``` @@ -190,7 +206,6 @@ Name | Type | Description | Notes **contextDataCenterRegion** | **string** | Data center region. | Defaults to `nil` ## Documentation For Models - - [AppEvent](https://github.com/trakerr-io/trakerr-python/blob/master/generated/docs/AppEvent.md) ## Author diff --git a/generated/lib/trakerr_client/models/stack_trace_lines.rb b/generated/lib/trakerr_client/models/stack_trace_lines.rb index 50c0ce0..05e6928 100644 --- a/generated/lib/trakerr_client/models/stack_trace_lines.rb +++ b/generated/lib/trakerr_client/models/stack_trace_lines.rb @@ -25,7 +25,7 @@ module Trakerr - class StackTraceLines + class StackTraceLines < Array # Attribute mapping from ruby-style variable name to JSON key. def self.attribute_map diff --git a/generated/lib/trakerr_client/models/stacktrace.rb b/generated/lib/trakerr_client/models/stacktrace.rb index 1df1f29..641f8b1 100644 --- a/generated/lib/trakerr_client/models/stacktrace.rb +++ b/generated/lib/trakerr_client/models/stacktrace.rb @@ -25,7 +25,7 @@ module Trakerr - class Stacktrace + class Stacktrace < Array # Attribute mapping from ruby-style variable name to JSON key. def self.attribute_map diff --git a/test_app.rb b/test_app.rb index 87a6bee..8f695f2 100644 --- a/test_app.rb +++ b/test_app.rb @@ -81,6 +81,7 @@ def main() appev2.event_session = "3" appev2.context_app_browser = "Edge" appev2.context_app_browser_version = "40.15063.0.0" + appev2.context_operation_time_millis = 5000 testApp.send_event(appev2) diff --git a/trakerr/lib/trakerr.rb b/trakerr/lib/trakerr.rb index 132c5c8..5722961 100644 --- a/trakerr/lib/trakerr.rb +++ b/trakerr/lib/trakerr.rb @@ -235,7 +235,20 @@ def fill_defaults(appEvent) appEvent.context_data_center_region = appEvent.context_data_center_region || @context_data_center_region appEvent.event_time = DateTime.now.strftime('%Q').to_i - appEvent + + output = %x(uptime) + + begin + output = %x(uptime) + appEvent.context_cpu_percentage = appEvent.context_cpu_percentage || (output.split(" ")[8].tr!(',','').to_f*100).round + + output = %x(free) + appEvent.context_memory_percentage = appEvent.context_memory_percentage || ((output.split(" ")[8].to_f/output.split(" ")[7].to_f) * 100).round + rescue + #fail silently for all standard error here. + end + + appEvent #return appEvent end private diff --git a/trakerr/lib/trakerr_formatter.rb b/trakerr/lib/trakerr_formatter.rb index 37da678..992e655 100644 --- a/trakerr/lib/trakerr_formatter.rb +++ b/trakerr/lib/trakerr_formatter.rb @@ -2,18 +2,13 @@ module Trakerr class TrakerrFormatter < Logger::Formatter - def initialize(standard_format = true) + def initialize() super() - @standard_format = standard_format end def call(severity, time, progname, msg) - if @standard_format - severityid = severity[0] - "#{severityid}, [#{time}] #{severity} #{progname} : #{msg2str(msg)}\n" - else - "#{severity}\n#{progname}\n#{msg2str(msg)}\n" - end + severityid = severity[0] + "#{severityid}, [#{time}] #{severity} #{progname} : #{msg2str(msg)}\n" end end end diff --git a/trakerr/lib/trakerr_writer.rb b/trakerr/lib/trakerr_writer.rb index f7b10bb..06f8570 100644 --- a/trakerr/lib/trakerr_writer.rb +++ b/trakerr/lib/trakerr_writer.rb @@ -3,12 +3,13 @@ module Trakerr class TrakerrWriter < StringIO - def initialize(api_key, context_app_version = '1.0', context_deployment_stage = 'development', log_limit = 'warn', standard_format = true) + def initialize(api_key, context_app_version = '1.0', context_deployment_stage = 'development', log_limit = 'warn', standard_format = true, parse_function = nil) super() @client = Trakerr::TrakerrClient.new(api_key, context_app_version, context_deployment_stage) @log_limit = str2level(log_limit) @standard_format = standard_format + @parse_function = parse_function end def write(str) @@ -19,7 +20,7 @@ def write(str) if @standard_format loglevel, classification, evname, evmessage, stacktrace = parse_standard(strarray) else - loglevel, classification, evname, evmessage, stacktrace = parse_custom(strarray) + loglevel, classification, evname, evmessage, stacktrace = @parse_function.call(strarray) end if str2level(loglevel) >= @log_limit @@ -33,37 +34,6 @@ def write(str) end private - - def parse_custom(_buffer) - loglevel = nil - classification = nil - evname = nil - evmessage = nil - stacktrace = [] - - _buffer.each_index do |i| - if i == 0 # TrakerrFormatter dictates severity as the first line. - loglevel = _buffered_string[i] - - elsif i == 1 # TrakerrFormatter dictates progname as the second line. This is optional, but will be used as a classification. - classification = _buffer[i] - - elsif i == 2 # TrakerrFormatter dictates `message` as the their line. Message is actually the error message AND the name of the error in parenthesis. - ob = _buffer[i].match(/(?.*)(?\(.*\))/) - evname = ob[:name].gsub(/^\(+|\)+$/, '') - evmessage = ob[:message] - - else # All following lines are stacktrace shoved into the buffer automatically if provided. - # This is only given if the logger gets an error object, - # but I don't believe the TrakerrFormatter has access to it - - stacktrace << _buffer[i] - end - end - - [loglevel, classification, evname, evmessage, stacktrace] - end - def parse_standard(_buffer) loglevel = nil classification = nil diff --git a/trakerr_client.gemspec b/trakerr_client.gemspec index 41eb5af..cecc955 100644 --- a/trakerr_client.gemspec +++ b/trakerr_client.gemspec @@ -51,7 +51,7 @@ Gem::Specification.new do |s| s.add_development_dependency 'autotest-fsevent', '~> 0.2', '>= 0.2.11' #s.files = `find *`.split("\n").uniq.sort.select{|f| !f.empty? } - #REQUIRES GIT INSTALLED + #REQUIRES GIT INSTALLED, works on windows. s.files = `git ls-files`.split("\n").delete_if {|file| file.include? "spec"} s.test_files = `git ls-files`.split("\n").delete_if {|file| not file.include? "spec"} s.executables = [] From 5bafddc0a5b3afa26db6cf7078009c1e79c956a8 Mon Sep 17 00:00:00 2001 From: Unknown Date: Thu, 6 Jul 2017 01:51:57 -0700 Subject: [PATCH 10/10] Logger Rework Rework logger to a simpler design pattern. --- README.md | 53 +++----------------- trakerr/lib/trakerr.rb | 4 +- trakerr/lib/trakerr_logger.rb | 94 +++++++++++++++++++++++++++++++++++ 3 files changed, 104 insertions(+), 47 deletions(-) create mode 100644 trakerr/lib/trakerr_logger.rb diff --git a/README.md b/README.md index b938fa8..eaabc5e 100644 --- a/README.md +++ b/README.md @@ -21,59 +21,22 @@ gem install trakerr_client Then import the packages: ```ruby -require_relative 'trakerr/lib/trakerr_formatter' -require_relative 'trakerr/lib/trakerr_writer' +require_relative 'trakerr_logger.rb' ``` Finally, the integration. Trakerr uses a string stream to catch the output of a logger and send it to trakerr. Trakerr afterwards writes the code to the stream, so it's possible to write that data elsewhere as required from the stream. ```ruby -stream = Trakerr::TrakerrWriter.new("", #api key you use for your program - "2.0", #Version of your app - "development", #Deployment stage of your app (production, development, ect) - "error" #log level at which the event and above should send. The log levels mirror that of the logger. (unknown > fatal > error > warn/warning, info > debug). - true, #Formatter switch. Should be the same value as the true false value passed to TrakerrFormatter. - nil)#method to parse the stream with. Only is called if the above is set to false. -rlog = Logger.new(stream) -rlog.formatter = Trakerr::TrakerrFormatter.new +logger = Trakerr::TrakerrLogger.new(Logger.new(STDOUT)) ``` - -Note that the formatter does change the layout of the output to make easier for Trakerr to parse. The layout will be at the bottom. Wherever you use the logger, it will also send it to trakerr. If you want to use the string stream afterwards, you may. +Simply use the logger as normal afterwards: ```ruby +logger.fatal('oops') + +#If you pass an exception into the logger, the backtrace will become more specific. begin - raise IOError, "Failed to open file" -rescue IOError => err - rlog.fatal err +rescue StandardError => e + logger.fatal(e) end - -stream.rewind -log = stream.read -puts log -``` - -There are two ways that the stream can configured to read. The first looks as close to the standard ruby log statement as possible that we provide as an out of the box simple formatter: -``` -severityid, [time] severity progname : msg -[Stacktrace] -``` -The stacktrace is optional, depending on if the logger is passed an error. You may use your own formatter, but you also need to provide a method to parse it. To enable it, provide the following: -```ruby -stream = Trakerr::TrakerrWriter.new("", - "2.0", - "development", - "error", - false, - method(your_method_here(_str))) -rlog = Logger.new(stream) -``` - -The last parameter expects a method object of which it can call. The method takes in one parameter, which is a string that holds the entire error information (often on multiple lines). The method should return a list formatted as follows -```ruby -# loglevel is the string level of the event. -# classification is the string small descriptor of the event. -# evname is the string name of the event. -# evmessage is the string message of the event. -# stacktrace is an array of strings which contain each line of the stactrace as an element. This should only have the file, message, and line number, not the prog name or other event info. -[loglevel, classification, evname, evmessage, stacktrace] ``` ## Installation & Usage diff --git a/trakerr/lib/trakerr.rb b/trakerr/lib/trakerr.rb index 5722961..a17558d 100644 --- a/trakerr/lib/trakerr.rb +++ b/trakerr/lib/trakerr.rb @@ -10,7 +10,7 @@ # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# WITHOUT WARRANTIES OR CO|| Backtrace.java_exception?(ex)NDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. @@ -78,7 +78,7 @@ def initialize(api_key, @context_deployment_stage = context_deployment_stage @context_env_language = 'Ruby' - +|| Backtrace.java_exception?(ex) if RUBY_PLATFORM == 'java' @context_env_name = 'jruby' @context_env_version = JRUBY_VERSION diff --git a/trakerr/lib/trakerr_logger.rb b/trakerr/lib/trakerr_logger.rb new file mode 100644 index 0000000..c2643e1 --- /dev/null +++ b/trakerr/lib/trakerr_logger.rb @@ -0,0 +1,94 @@ +require 'logger' +require 'delegate' +require 'event_trace_builder' +require 'trakerr_client' + +module Trakerr + class TrakerrLogger < SimpleDelegator + attr_accessor :min_severity + attr_accessor :trakerr_client + + def initalize(logger) + __setobj__(logger) + @min_severity = Logger::WARN + #@trakerr_client TODO: Finish send to trakerr. + end + + ## + # @see Logger#debug + def debug(progname = nil, &block) + send_to_trakerr(Logger::DEBUG, progname) + super + end + + ## + # @see Logger#info + def info(progname = nil, &block) + send_to_trakerr(Logger::INFO, progname) + super + end + + ## + # @see Logger#warn + def warn(progname = nil, &block) + send_to_trakerr(Logger::WARN, progname) + super + end + + ## + # @see Logger#error + def error(progname = nil, &block) + send_to_trakerr(Logger::ERROR, progname) + super + end + + ## + # @see Logger#fatal + def fatal(progname = nil, &block) + send_to_trakerr(Logger::FATAL, progname) + super + end + + ## + # @see Logger#unknown + def unknown(progname = nil, &block) + send_to_trakerr(Logger::UNKNOWN, progname) + super + end + + private + + def send_to_trakerr(ex) + if ex.is_a?(Exception)#TODO: Work on recognizing the java logger, and it having a backtrace. + ex.set_backtrace(build_backtrace()) unless ex.backtrace + return ex + end + + err = RuntimeError.new(ex.to_s) + err.set_backtrace(build_backtrace()) + err + end + + def build_backtrace() + backtrace = Kernal.caller + backtrace.drop_while { |frame| frame[:file] =~ %r{/logger.rb\z} } + backtrace + end + + def severity_to_string(severity) + case severity + when Logger::DEBUG + "debug" + when Logger::INFO + "info" + when Logger::WARN + "warn" + when Logger::ERROR + "error" + when Logger::Fatal + "fatal" + else + "fatal"#If logging "UNKNOWN" + end + end +end \ No newline at end of file