├── .github └── pull_request_template.md ├── .gitignore ├── README.md ├── UPGRADE_FROM_RATCHET.md ├── build.sh ├── build ├── swc │ └── Rollbar.swc └── swf │ ├── RollbarNotifer.swf │ └── test.swf ├── index.html ├── libs ├── asuuid.swc └── flexunit-4.1.0-8-as3_4.1.0.16076.swc ├── src └── com │ └── rollbar │ ├── json │ └── JSONEncoder.as │ ├── notifier │ ├── Rollbar.as │ ├── RollbarNotifier.as │ └── Test.as │ ├── stacktrace │ ├── StackTrace.as │ ├── StackTraceLine.as │ └── StackTraceParser.as │ └── tests │ ├── ErrorCausingConstructorClass.as │ ├── StackTraceParserTest.as │ └── TestRunner.as └── static ├── js └── swfobject.js └── swf └── expressInstall.swf /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | ## Description of the change 2 | 3 | > Please include a summary of the change and which issues are fixed. 4 | > Please also include relevant motivation and context. 5 | 6 | ## Type of change 7 | 8 | - [ ] Bug fix (non-breaking change that fixes an issue) 9 | - [ ] New feature (non-breaking change that adds functionality) 10 | - [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) 11 | - [ ] Maintenance 12 | - [ ] New release 13 | 14 | ## Related issues 15 | 16 | > ClubHouse stories and GitHub issues (delete irrelevant) 17 | 18 | - Fix [ch] 19 | - Fix #1 20 | 21 | ## Checklists 22 | 23 | ### Development 24 | 25 | - [ ] Lint rules pass locally 26 | - [ ] The code changed/added as part of this pull request has been covered with tests 27 | - [ ] All tests related to the changed code pass in development 28 | 29 | ### Code review 30 | 31 | - [ ] This pull request has a descriptive title and information useful to a reviewer. There may be a screenshot or screencast attached 32 | - [ ] "Ready for review" label attached to the PR and reviewers assigned 33 | - [ ] Issue from task tracker has a link to this pull request 34 | - [ ] Changes have been reviewed by at least one other engineer 35 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.swp 2 | *.swo 3 | # Project property files 4 | .actionScriptProperties 5 | .flexProperties 6 | .settings/ 7 | .project 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Attention: 2 | 3 | As of May 2024, Rollbar will not be actively updating this repository and plans to archive it in January of 2025. We encourage our community to fork this repo if you wish to continue its development. While Rollbar will no longer be engaging in active development, we remain committed to reviewing and merging pull requests related to security updates. If an actively maintained fork emerges, please reach out to our support team and we will link to it from our documentation. 4 | 5 | 6 | # Rollbar notifier for Flash (AS3) 7 | 8 | 9 | Flash (ActionScript 3) library for reporting exceptions, errors, and log messages to [Rollbar](https://rollbar.com). 10 | 11 | 12 | 13 | ## Quick start 14 | 15 | 1. Download the [flash_rollbar](https://github.com/rollbar/flash_rollbar/tree/master/src) code or just the [Rollbar.swc](https://github.com/rollbar/flash_rollbar/blob/master/build/swc/Rollbar.swc) file. 16 | 2. Place the ```flash_rollbar/src``` directory in your source path or place the ```Rollbar.swc``` file in your project's library path. 17 | 3. Call ```Rollbar.init(this, accessToken, environment);``` from your top-level ```DisplayObject```. 18 | 19 | ```actionscript 20 | package { 21 | import com.rollbar.notifier.Rollbar; 22 | 23 | public class MyApp extends Sprite { 24 | 25 | public static const ROLLBAR_ACCESS_TOKEN:String = "POST_CLIENT_ITEM_ACCESS_TOKEN"; 26 | 27 | public function MyApp() { 28 | var environment:String = isDebug() ? "development" : "production"; 29 | var person:Object = {id: getUserId(), email: getEmail(), name: getName()}; // optional 30 | Rollbar.init(this, ROLLBAR_ACCESS_TOKEN, environment, person); 31 | } 32 | } 33 | } 34 | ``` 35 | 36 | 37 | ```Rollbar.init()``` installed a global error handler, so you don't need to do anything else. 38 | 39 | 40 | Be sure to replace ```POST_CLIENT_ITEM_ACCESS_TOKEN``` with your project's ```post_client_item``` access token, which you can find in the Rollbar.com interface. 41 | 42 | 43 | ## Requirements 44 | 45 | - Flash Player 10.1+ 46 | - May work on 9, but not tested. 47 | - mxmlc/compc if you plan on building from the command-line 48 | - A [Rollbar](http://rollbar.com) account 49 | 50 | ## Reporting caught errors 51 | 52 | If you want to instrument specific parts of your code, call ```Rollbar.handleError(err)```: 53 | 54 | ```actionscript 55 | private function onEnterFrame(event:Event) { 56 | try { 57 | gameLoop(event); 58 | } catch (err:Error) { 59 | Rollbar.handleError(err); 60 | } 61 | } 62 | ``` 63 | 64 | **Advanced:** to override parts of the payload before it is sent to the Rollbar API, pass them in the second argument to `handleError()`. For example, to control how your data will be grouped, you can pass a custom `fingerprint`: 65 | 66 | ```actionscript 67 | Rollbar.handleError(err, {fingerprint: "a string to uniquely identify this error"}); 68 | ``` 69 | 70 | The second argument, `extraData`, should be an object. Each key in `extraData` will overwrite the previous contents of the payload. For all options, see the [API documentation](https://rollbar.com/docs/api/items_post/). 71 | 72 | 73 | ## Configuration 74 | 75 | At the topmost level of your display list, instantiate the Rollbar singleton. 76 | 77 | ```actionscript 78 | Rollbar.init(this, accessToken, environment); 79 | ``` 80 | 81 | Here's the full list of constructor parameters (in order): 82 | 83 |
Note, if you get an error about a security violation, make sure to give this movie access to your local file settings by right clicking and updating the movie's settings.
29 | 30 | 31 | -------------------------------------------------------------------------------- /libs/asuuid.swc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rollbar/flash_rollbar/0a053c362621e6ea1bfdd76373f27b500f1dd1ae/libs/asuuid.swc -------------------------------------------------------------------------------- /libs/flexunit-4.1.0-8-as3_4.1.0.16076.swc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rollbar/flash_rollbar/0a053c362621e6ea1bfdd76373f27b500f1dd1ae/libs/flexunit-4.1.0-8-as3_4.1.0.16076.swc -------------------------------------------------------------------------------- /src/com/rollbar/json/JSONEncoder.as: -------------------------------------------------------------------------------- 1 | /* 2 | Originally from: https://github.com/mikechambers/as3corelib/ 3 | 4 | Copyright (c) 2008, Adobe Systems Incorporated 5 | All rights reserved. 6 | 7 | Redistribution and use in source and binary forms, with or without 8 | modification, are permitted provided that the following conditions are 9 | met: 10 | 11 | * Redistributions of source code must retain the above copyright notice, 12 | this list of conditions and the following disclaimer. 13 | 14 | * Redistributions in binary form must reproduce the above copyright 15 | notice, this list of conditions and the following disclaimer in the 16 | documentation and/or other materials provided with the distribution. 17 | 18 | * Neither the name of Adobe Systems Incorporated nor the names of its 19 | contributors may be used to endorse or promote products derived from 20 | this software without specific prior written permission. 21 | 22 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 23 | IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 24 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 25 | PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 26 | CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 27 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 28 | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 29 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 30 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 31 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 32 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 33 | */ 34 | 35 | package com.rollbar.json { 36 | 37 | import flash.utils.describeType; 38 | 39 | public class JSONEncoder { 40 | 41 | /** The string that is going to represent the object we're encoding */ 42 | private var jsonString:String; 43 | 44 | public static function encode(o:Object):String { 45 | return new JSONEncoder(o).getString(); 46 | } 47 | 48 | /** 49 | * Creates a new JSONEncoder. 50 | * 51 | * @param o The object to encode as a JSON string 52 | * @langversion ActionScript 3.0 53 | * @playerversion Flash 9.0 54 | * @tiptext 55 | */ 56 | public function JSONEncoder(value:*) { 57 | jsonString = convertToString( value ); 58 | } 59 | 60 | /** 61 | * Gets the JSON string from the encoder. 62 | * 63 | * @return The JSON string representation of the object 64 | * that was passed to the constructor 65 | * @langversion ActionScript 3.0 66 | * @playerversion Flash 9.0 67 | * @tiptext 68 | */ 69 | public function getString():String { 70 | return jsonString; 71 | } 72 | 73 | /** 74 | * Converts a value to it's JSON string equivalent. 75 | * 76 | * @param value The value to convert. Could be any 77 | * type (object, number, array, etc) 78 | */ 79 | private function convertToString(value:*):String { 80 | // determine what value is and convert it based on it's type 81 | if (value is String) { 82 | // escape the string so it's formatted correctly 83 | return escapeString(value as String); 84 | } 85 | else if (value is Number) { 86 | // only encode numbers that finate 87 | return isFinite( value as Number ) ? value.toString() : "null"; 88 | } else if (value is Boolean) { 89 | // convert boolean to string easily 90 | return value ? "true" : "false"; 91 | } else if (value is Array) { 92 | // call the helper method to convert an array 93 | return arrayToString( value as Array ); 94 | } else if (value is Object && value != null) { 95 | // call the helper method to convert an object 96 | return objectToString( value ); 97 | } 98 | 99 | return "null"; 100 | } 101 | 102 | /** 103 | * Escapes a string according to the JSON specification. 104 | * 105 | * @param str The string to be escaped 106 | * @return The string with escaped special characters 107 | * according to the JSON specification 108 | */ 109 | private function escapeString(str:String):String { 110 | // create a string to store the string's jsonstring value 111 | var s:String = ""; 112 | // current character in the string we're processing 113 | var ch:String; 114 | // store the length in a local variable to reduce lookups 115 | var len:Number = str.length; 116 | 117 | // loop over all of the characters in the string 118 | for (var i:int = 0; i < len; i++) { 119 | // examine the character to determine if we have to escape it 120 | ch = str.charAt(i); 121 | switch ( ch ) { 122 | case '"': // quotation mark 123 | s += "\\\""; 124 | break; 125 | 126 | //case '/': // solidus 127 | // s += "\\/"; 128 | // break; 129 | 130 | case '\\': // reverse solidus 131 | s += "\\\\"; 132 | break; 133 | 134 | case '\b': // bell 135 | s += "\\b"; 136 | break; 137 | 138 | case '\f': // form feed 139 | s += "\\f"; 140 | break; 141 | 142 | case '\n': // newline 143 | s += "\\n"; 144 | break; 145 | 146 | case '\r': // carriage return 147 | s += "\\r"; 148 | break; 149 | 150 | case '\t': // horizontal tab 151 | s += "\\t"; 152 | break; 153 | 154 | default: // everything else 155 | 156 | // check for a control character and escape as unicode 157 | if (ch < ' ') { 158 | // get the hex digit(s) of the character (either 1 or 2 digits) 159 | var hexCode:String = ch.charCodeAt(0).toString(16); 160 | 161 | // ensure that there are 4 digits by adjusting 162 | // the # of zeros accordingly. 163 | var zeroPad:String = hexCode.length == 2 ? "00" : "000"; 164 | 165 | // create the unicode escape sequence with 4 hex digits 166 | s += "\\u" + zeroPad + hexCode; 167 | } else { 168 | 169 | // no need to do any special encoding, just pass-through 170 | s += ch; 171 | 172 | } 173 | } // end switch 174 | 175 | } // end for loop 176 | 177 | return "\"" + s + "\""; 178 | } 179 | 180 | /** 181 | * Converts an array to it's JSON string equivalent 182 | * 183 | * @param a The array to convert 184 | * @return The JSON string representation ofa
185 | */
186 | private function arrayToString(a:Array):String {
187 | // create a string to store the array's jsonstring value
188 | var s:String = "";
189 |
190 | // loop over the elements in the array and add their converted
191 | // values to the string
192 | var length:int = a.length;
193 | for (var i:int = 0; i < length; i++) {
194 | // when the length is 0 we're adding the first element so
195 | // no comma is necessary
196 | if (s.length > 0) {
197 | // we've already added an element, so add the comma separator
198 | s += ","
199 | }
200 |
201 | // convert the value to a string
202 | s += convertToString(a[i]);
203 | }
204 |
205 | // KNOWN ISSUE: In ActionScript, Arrays can also be associative
206 | // objects and you can put anything in them, ie:
207 | // myArray["foo"] = "bar";
208 | //
209 | // These properties aren't picked up in the for loop above because
210 | // the properties don't correspond to indexes. However, we're
211 | // sort of out luck because the JSON specification doesn't allow
212 | // these types of array properties.
213 | //
214 | // So, if the array was also used as an associative object, there
215 | // may be some values in the array that don't get properly encoded.
216 | //
217 | // A possible solution is to instead encode the Array as an Object
218 | // but then it won't get decoded correctly (and won't be an
219 | // Array instance)
220 |
221 | // close the array and return it's string value
222 | return "[" + s + "]";
223 | }
224 |
225 | /**
226 | * Converts an object to it's JSON string equivalent
227 | *
228 | * @param o The object to convert
229 | * @return The JSON string representation of o
230 | */
231 | private function objectToString(o:Object):String {
232 | // create a string to store the object's jsonstring value
233 | var s:String = "";
234 |
235 | // determine if o is a class instance or a plain object
236 | var classInfo:XML = describeType(o);
237 | if (classInfo.@name.toString() == "Object") {
238 | // the value of o[key] in the loop below - store this
239 | // as a variable so we don't have to keep looking up o[key]
240 | // when testing for valid values to convert
241 | var value:Object;
242 |
243 | // loop over the keys in the object and add their converted
244 | // values to the string
245 | for (var key:String in o) {
246 | // assign value to a variable for quick lookup
247 | value = o[key];
248 |
249 | // don't add function's to the JSON string
250 | if (value is Function) {
251 | // skip this key and try another
252 | continue;
253 | }
254 |
255 | // when the length is 0 we're adding the first item so
256 | // no comma is necessary
257 | if (s.length > 0) {
258 | // we've already added an item, so add the comma separator
259 | s += ","
260 | }
261 |
262 | s += escapeString(key) + ":" + convertToString(value);
263 | }
264 | } else { // o is a class instance
265 | // Loop over all of the variables and accessors in the class and
266 | // serialize them along with their values.
267 | for each (var v:XML in classInfo..*.(name() == "variable" ||
268 | (
269 | name() == "accessor"
270 | // Issue #116 - Make sure accessors are readable
271 | && attribute("access").charAt(0) == "r")
272 | )) {
273 | // Issue #110 - If [Transient] metadata exists, then we should skip
274 | if (v.metadata && v.metadata.(@name == "Transient").length() > 0) {
275 | continue;
276 | }
277 |
278 | // When the length is 0 we're adding the first item so
279 | // no comma is necessary
280 | if (s.length > 0) {
281 | // We've already added an item, so add the comma separator
282 | s += ","
283 | }
284 |
285 | s += escapeString(v.@name.toString()) + ":" + convertToString(o[v.@name]);
286 | }
287 | }
288 |
289 | return "{" + s + "}";
290 | }
291 | }
292 | }
293 |
--------------------------------------------------------------------------------
/src/com/rollbar/notifier/Rollbar.as:
--------------------------------------------------------------------------------
1 | package com.rollbar.notifier {
2 |
3 | import flash.display.DisplayObjectContainer;
4 | import flash.events.ErrorEvent;
5 |
6 | import com.rollbar.notifier.RollbarNotifier;
7 |
8 | /**
9 | * Static wrapper around a RollbarNotifier singleton.
10 | *
11 | * Use this unless you have some specific reason to use RollbarNotifer directly.
12 | */
13 | public final class Rollbar {
14 |
15 | private static var notifier:RollbarNotifier = null;
16 |
17 | /**
18 | * Initialize the Rollbar notifier. Constructs a RollbarNotifier instance and adds it to the stage,
19 | * which will trigger final initialization.
20 | * All arguments after 'stage' are passed to RollbarNotifier's constructor.
21 | *
22 | * @param stage The stage
23 | * @param accessToken Rollbar project access token
24 | * @param environment Environment name (i.e. "development", "production")
25 | * @param person Person identifier string or object or function which returns an object.
26 | * @param rootPath Path to the application code root, not including the final slash.
27 | * @param srcPath Path to the source code root, not including the final slash.
28 | * @param codeBranch Code branch name, e.g. "master"
29 | * @param serverData Object containing server information, will be passed along with error reports
30 | * @param maxItemCount Max number of items to report per load.
31 | */
32 | public static function init(parent:DisplayObjectContainer,
33 | accessToken:String, environment:String, person:* = null,
34 | rootPath:String = null, srcPath:String = null,
35 | codeBranch:String = null, serverData:Object = null,
36 | maxItemCount:int = 5, endpointUrl:String = null):void {
37 |
38 | if (notifier !== null) {
39 | trace("WARNING: Rollbar.init() called more than once. Subsequent calls ignored.");
40 | return;
41 | }
42 |
43 | notifier = new RollbarNotifier(accessToken, environment, person, rootPath, srcPath,
44 | codeBranch, serverData, maxItemCount, endpointUrl);
45 | parent.addChild(notifier);
46 | }
47 |
48 | public static function handleError(err:Error, extraData:Object = null):String {
49 | if (notifier === null) {
50 | trace("WARNING: Rollbar.handleError() called before init(). Call ignored.");
51 | return null;
52 | }
53 | return notifier.handleError(err, extraData);
54 | }
55 |
56 | public static function handleErrorEvent(event:ErrorEvent):String {
57 | if (notifier === null) {
58 | trace("WARNING: Rollbar.handleErrorEvent() called before init(). Call ignored.");
59 | return null;
60 | }
61 | return notifier.handleErrorEvent(event);
62 | }
63 |
64 | public static function handleOtherEvent(event:*):String {
65 | if (notifier === null) {
66 | trace("WARNING: Rollbar.handleOtherEvent() called before init(). Call ignored.");
67 | return null;
68 | }
69 | return notifier.handleOtherEvent(event);
70 | }
71 |
72 | public static function setCodeVersion(codeVersion:String):void {
73 | if (notifier === null) {
74 | trace("WARNING: Rollbar.setCodeVersion() called before init(). Call ignored.");
75 | return;
76 | }
77 | notifier.setCodeVersion(codeVersion);
78 | }
79 |
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/src/com/rollbar/notifier/RollbarNotifier.as:
--------------------------------------------------------------------------------
1 | package com.rollbar.notifier {
2 |
3 | import flash.display.Sprite;
4 | import flash.display.LoaderInfo;
5 |
6 | import flash.errors.IllegalOperationError;
7 |
8 | import flash.events.Event;
9 | import flash.events.ErrorEvent;
10 | import flash.events.EventDispatcher;
11 | import flash.events.HTTPStatusEvent;
12 | import flash.events.IOErrorEvent;
13 | import flash.events.IEventDispatcher;
14 | import flash.events.SecurityErrorEvent;
15 | import flash.events.UncaughtErrorEvent;
16 |
17 | import flash.external.ExternalInterface;
18 |
19 | import flash.net.URLLoader;
20 | import flash.net.URLLoaderDataFormat;
21 | import flash.net.URLRequest;
22 | import flash.net.URLRequestMethod;
23 | import flash.net.URLVariables;
24 |
25 | import flash.system.Capabilities;
26 | import flash.system.System;
27 |
28 | import flash.utils.getTimer;
29 |
30 | import com.laiyonghao.Uuid;
31 |
32 | import com.rollbar.json.JSONEncoder;
33 | import com.rollbar.stacktrace.StackTrace;
34 | import com.rollbar.stacktrace.StackTraceParser;
35 |
36 | [Event(name="complete", type="flash.events.Event")]
37 | [Event(name="httpStatus", type="flash.events.HTTPStatusEvent")]
38 | [Event(name="ioError", type="flash.events.IOErrorEvent")]
39 | [Event(name="securityError", type="flash.events.SecurityErrorEvent")]
40 | public final class RollbarNotifier extends Sprite {
41 |
42 | private static const API_ENDPONT_URL:String = "https://api.rollbar.com/api/1/item/";
43 | private static const NOTIFIER_DATA:Object = {name: "flash_rollbar", version: "0.9.2"};
44 | private static const MAX_ITEM_COUNT:int = 5;
45 |
46 | private static var instance:RollbarNotifier = null;
47 |
48 | // Keep URLLoaders from being garbage collected before they finish
49 | private var loaders:Array;
50 |
51 | private var accessToken:String;
52 | private var environment:String;
53 | private var swfUrl:String;
54 | private var embeddedUrl:String;
55 | private var queryString:String;
56 | private var serverData:Object;
57 | private var itemCount:int = 0;
58 | private var endpointUrl:String;
59 | private var maxItemCount:int;
60 | private var personFn:Function;
61 | private var userId:String;
62 | private var person:Object;
63 | private var startTime:int;
64 | private var branch:String;
65 | private var rootPath:String;
66 | private var srcPath:String;
67 | private var codeVersion:String;
68 |
69 | public function RollbarNotifier(accessToken:String,
70 | environment:String,
71 | person:* = null,
72 | rootPath:String = null,
73 | srcPath:String = null,
74 | codeBranch:String = null,
75 | serverData:Object = null,
76 | maxItemCount:int = 5,
77 | endpointUrl:String = null) {
78 | this.accessToken = accessToken;
79 | this.environment = environment;
80 | this.serverData = serverData || {};
81 | this.endpointUrl = endpointUrl || API_ENDPONT_URL;
82 | this.maxItemCount = maxItemCount || MAX_ITEM_COUNT;
83 | this.branch = codeBranch || "master";
84 | this.rootPath = rootPath;
85 | this.srcPath = srcPath;
86 |
87 | this.loaders = new Array();
88 |
89 | if (person) {
90 | if (person is Function) {
91 | this.personFn = person;
92 | } else if (person is String) {
93 | this.userId = person;
94 | } else if (person is Object) {
95 | this.person = person;
96 | this.userId = resolveField(['id', 'userId', 'user_id', 'user'], person);
97 | } else {
98 | this.userId = '' + person;
99 | }
100 | }
101 |
102 | addEventListener(Event.ADDED_TO_STAGE, function(event:Event):void {
103 | swfUrl = unescape(parent.loaderInfo.url);
104 | embeddedUrl = getEmbeddedUrl();
105 | queryString = getQueryString();
106 |
107 | // Register for uncaught errors if >= 10.1.
108 | if (parent.loaderInfo.hasOwnProperty('uncaughtErrorEvents')) {
109 | var uncaughtErrorEvents:IEventDispatcher = IEventDispatcher(parent.loaderInfo.uncaughtErrorEvents);
110 | uncaughtErrorEvents.addEventListener(UncaughtErrorEvent.UNCAUGHT_ERROR, handleUncaughtError);
111 | }
112 | });
113 | }
114 |
115 | public function dispose():void {
116 | }
117 |
118 | public function handleError(err:Error, extraData:Object = null):String {
119 | var stackTrace:String = err.getStackTrace();
120 | if (stackTrace !== null) {
121 | // we got a stack trace (we're in the debug player).
122 | return handleStackTrace(stackTrace, extraData);
123 | } else {
124 | // no stack trace. just report the basics.
125 | return handlePlainError(err.errorID, err.name, err.message, extraData);
126 | }
127 | }
128 |
129 | public function handleErrorEvent(event:ErrorEvent):String {
130 | var newError:Error = new Error("An ErrorEvent was thrown and not caught: " + event.toString());
131 | return handleError(newError);
132 | }
133 |
134 | public function handleOtherEvent(event:*):String {
135 | var newError:Error = new Error("A non-Error or ErrorEvent was thrown and not caught: " + event.toString());
136 | return handleError(newError);
137 | }
138 |
139 | public function setCodeVersion(codeVersion:String):void {
140 | this.codeVersion = codeVersion;
141 | }
142 |
143 | private function handleUncaughtError(event:UncaughtErrorEvent):String {
144 | if (event.error is Error) {
145 | var error:Error = event.error as Error;
146 | return handleError(error);
147 | } else if (event.error is ErrorEvent) {
148 | var errorEvent:ErrorEvent = event.error as ErrorEvent;
149 | return handleErrorEvent(errorEvent);
150 | } else {
151 | // Inform the user that a non-error event was thrown and not caught.
152 | return handleOtherEvent(event);
153 | }
154 | }
155 |
156 | private function handleStackTrace(stackTrace:String, extraData:Object):String {
157 | var payload:Object = buildDebugPayload(stackTrace, extraData);
158 | sendPayload(payload);
159 | return payload['data']['uuid'];
160 | }
161 |
162 | private function handlePlainError(errorID:int, name:String, message:String, extraData:Object):String {
163 | var payload:Object = buildReleasePayload(errorID, name, message, extraData);
164 | sendPayload(payload);
165 | return payload['data']['uuid'];
166 | }
167 |
168 | private function sendPayload(payload:Object):void {
169 | if (itemCount < this.maxItemCount) {
170 | var request:URLRequest = new URLRequest();
171 | request.method = URLRequestMethod.POST;
172 | request.data = JSONEncoder.encode(payload);
173 | request.url = this.endpointUrl;
174 |
175 | var loader:URLLoader = new URLLoader();
176 | loader.dataFormat = URLLoaderDataFormat.TEXT;
177 |
178 | var handler:Function = function(event:Event):void {
179 | for (var i:int = 0; i < loaders.length; ++i) {
180 | if (loaders[i] == loader) {
181 | loaders.splice(i, 1);
182 | break;
183 | }
184 | }
185 |
186 | loader.removeEventListener(SecurityErrorEvent.SECURITY_ERROR, handler);
187 | loader.removeEventListener(IOErrorEvent.IO_ERROR, handler);
188 | loader.removeEventListener(Event.COMPLETE, handler);
189 |
190 | dispatchEvent(event);
191 | }
192 |
193 | loader.addEventListener(SecurityErrorEvent.SECURITY_ERROR, handler);
194 | loader.addEventListener(IOErrorEvent.IO_ERROR, handler);
195 | loader.addEventListener(Event.COMPLETE, handler);
196 |
197 | loaders.push(loader);
198 |
199 | loader.load(request);
200 | itemCount++;
201 | } else {
202 | // too many handled items
203 | }
204 | }
205 |
206 | private function getEmbeddedUrl():String {
207 | if (ExternalInterface.available) {
208 | return ExternalInterface.call("window.location.href.toString");
209 | }
210 | return null;
211 | }
212 |
213 | private function getQueryString():String {
214 | if (ExternalInterface.available) {
215 | return ExternalInterface.call("window.location.search.substring", 1);
216 | }
217 | return null;
218 | }
219 |
220 | private function getBrowserUserAgent():String {
221 | if (ExternalInterface.available) {
222 | return ExternalInterface.call("window.navigator.userAgent.toString");
223 | }
224 | return null;
225 | }
226 |
227 | /**
228 | * Builds and returns a payload object using the information available in the Release player.
229 | *
230 | * errorID, name, and message should come from the relevant Error object.
231 | */
232 | private function buildReleasePayload(errorID:int, name:String, message:String, extraData:Object):Object {
233 | var messageTitle:String = name + ": " + message;
234 | var messageBody:String = "Error ID: " + errorID + "\n" + messageTitle;
235 |
236 | var payload:Object = buildCommonPayload(extraData);
237 | payload.data.body = {
238 | message: {
239 | body: messageBody,
240 | error_id: errorID
241 | }
242 | };
243 | payload.data.title = messageTitle;
244 | return payload;
245 | }
246 |
247 | /**
248 | * Builds and returns a payload object using the information available in the Debug player.
249 | *
250 | * stackTrace should come from error.getStackTrace()
251 | */
252 | private function buildDebugPayload(stackTrace:String, extraData:Object):Object {
253 | var stackTraceObj:StackTrace = StackTraceParser.parseStackTrace(srcPath, stackTrace);
254 |
255 | var payload:Object = buildCommonPayload(extraData);
256 | payload.data.body = {
257 | trace: {
258 | frames: stackTraceObj.frames,
259 | exception: {
260 | 'class': stackTraceObj.errorClassName,
261 | message: stackTraceObj.message
262 | }
263 | }
264 | };
265 | return payload;
266 | }
267 |
268 | private function resolveField(fieldNames:Array, storage:Object):String {
269 | var index:Number;
270 | var len:Number = fieldNames.length;
271 | for (index = 0; index < len; ++index) {
272 | try {
273 | return storage[fieldNames[index]].toString();
274 | } catch (e:*) {
275 | // ignore
276 | }
277 | }
278 | return null;
279 | }
280 |
281 | /**
282 | * Builds and returns common payload data. Used by buildReleasePayload and buildDebugPayload.
283 | */
284 | private function buildCommonPayload(extraData:Object):Object {
285 | var tmpPerson:Object = this.person || (this.personFn != null ? this.personFn() : null);
286 | var userId:String = this.userId || resolveField(['id', 'userId', 'user_id', 'user'], tmpPerson);
287 | var person:Object;
288 |
289 | if (userId) {
290 | person = {id: userId};
291 | var email:String = resolveField(['email', 'emailAddress', 'email_address',
292 | 'userEmail', 'user_email'], tmpPerson);
293 | var username:String = resolveField(['username', 'userName', 'user_name', 'name'], tmpPerson);
294 |
295 | if (email) {
296 | person['email'] = email;
297 | }
298 | if (username) {
299 | person['username'] = username;
300 | }
301 | }
302 |
303 | var payload:Object = {
304 | access_token: accessToken,
305 | data: {
306 | environment: environment,
307 | platform: "flash",
308 | language: "as3",
309 | request: {
310 | url: embeddedUrl,
311 | query_string: queryString,
312 | user_ip: "$remote_ip",
313 | user_id: userId
314 | },
315 | client: {
316 | runtime_ms: getTimer(),
317 | timestamp: int((new Date()).getTime() / 1000),
318 | root: rootPath,
319 | branch: branch,
320 | flash: {
321 | browser: getBrowserUserAgent(),
322 | swf_url: swfUrl,
323 | player: {
324 | freeMemory: System.freeMemory,
325 | privateMemory: System.privateMemory,
326 | totalMemory: System.totalMemory,
327 | capabilities: {
328 | avHardwareDisable: Capabilities.avHardwareDisable,
329 | cpuArchitecture: Capabilities.cpuArchitecture,
330 | externalInterfaceAvailable: ExternalInterface.available,
331 | hasAccessibility: Capabilities.hasAccessibility,
332 | hasAudio: Capabilities.hasAudio,
333 | isDebugger: Capabilities.isDebugger,
334 | language: Capabilities.language,
335 | localFileReadDisable: Capabilities.localFileReadDisable,
336 | manufacturer: Capabilities.manufacturer,
337 | os: Capabilities.os,
338 | pixelAspectRatio: Capabilities.pixelAspectRatio,
339 | playerType: Capabilities.playerType,
340 | screenDPI: Capabilities.screenDPI,
341 | screenResolutionX: Capabilities.screenResolutionX,
342 | screenResolutionY: Capabilities.screenResolutionY,
343 | version: Capabilities.version
344 | }
345 | }
346 | }
347 | },
348 | server: serverData,
349 | notifier: NOTIFIER_DATA,
350 | uuid: new Uuid().toString().toLowerCase()
351 | }
352 | };
353 |
354 | if (this.codeVersion) {
355 | payload['data']['code_version'] = this.codeVersion;
356 | }
357 |
358 | if (person) {
359 | payload['data']['person'] = person;
360 | }
361 |
362 | if (extraData) {
363 | for (var k:String in extraData) {
364 | payload['data'][k] = extraData[k];
365 | }
366 | }
367 |
368 | return payload;
369 | }
370 |
371 | }
372 | }
373 |
--------------------------------------------------------------------------------
/src/com/rollbar/notifier/Test.as:
--------------------------------------------------------------------------------
1 | package com.rollbar.notifier {
2 | import flash.display.Sprite;
3 | import flash.text.TextField;
4 | import flash.text.TextFieldAutoSize;
5 | import flash.text.TextFormat;
6 | import flash.text.TextFormatAlign;
7 | import flash.events.*;
8 |
9 | import com.rollbar.notifier.Rollbar;
10 |
11 | public class Test extends Sprite {
12 | // change to your own access token
13 | public static const ACCESS_TOKEN:String = '2f5ce4c49aec41c2bba8c3c5c448f8b2';
14 | public static const ENV:String = 'production';
15 |
16 | protected var caughtButton:Sprite = new Sprite();
17 | protected var uncaughtButton:Sprite = new Sprite();
18 |
19 | public function Test() {
20 | trace('Building Rollbar test...');
21 |
22 | // Initialize the Rollbar notifier.
23 | /*
24 | Rollbar.init(this, // pass this sprite as first param
25 | ACCESS_TOKEN, // your rollbar project access token
26 | ENV, // environment name - i.e. "production" or "development"
27 | function():Object {
28 | return {user_id: "user123", name: "Cory Virok"}
29 | }, // user fn/id (optional).
30 | "/Users/coryvirok/Development/flash_rollbar", // the path to the project root,
31 | // not including the final slash.
32 | // Note: if the SWF/SWC is compiled with compiler.verbose-stacktraces=true
33 | // or -debug, you'll want to have this path reflect the root path from the
34 | // user who published the SWF/SWC file. Otherwise, you can set it to the
35 | // source directory of your project, e.g. "src".
36 | "/Users/coryvirok/Development/flash_rollbar/src" // the source code path
37 | );
38 | */
39 |
40 | Rollbar.init(this, ACCESS_TOKEN, ENV);
41 | Rollbar.handleError(new Error("flash test error", 666));
42 |
43 | mouseEnabled = true;
44 | mouseChildren = true;
45 |
46 | // Draw some buttons.
47 |
48 | caughtButton.graphics.clear();
49 | caughtButton.graphics.beginFill(0xD4D4D4);
50 | caughtButton.graphics.drawRoundRect(0, 100, 200, 50, 20, 20);
51 | caughtButton.graphics.endFill();
52 | addChild(caughtButton);
53 |
54 | uncaughtButton.graphics.clear();
55 | uncaughtButton.graphics.beginFill(0xD4D4D4);
56 | uncaughtButton.graphics.drawRoundRect(0, 200, 200, 50, 20, 20);
57 | uncaughtButton.graphics.endFill();
58 | addChild(uncaughtButton);
59 |
60 | var format:TextFormat = new TextFormat();
61 | format.size = 20;
62 |
63 | var format2:TextFormat = new TextFormat();
64 | format2.size = 20;
65 |
66 | var caughtErrText:TextField = new TextField();
67 | caughtErrText.defaultTextFormat = format;
68 | caughtErrText.autoSize = TextFieldAutoSize.RIGHT;
69 | caughtErrText.text = 'Cause Error';
70 | caughtErrText.x = 10;
71 | caughtErrText.y = 105;
72 | caughtErrText.selectable = false;
73 | caughtButton.addChild(caughtErrText);
74 |
75 | var uncaughtErrText:TextField = new TextField();
76 | uncaughtErrText.defaultTextFormat = format2;
77 | uncaughtErrText.autoSize = TextFieldAutoSize.RIGHT;
78 | uncaughtErrText.text = 'Cause Uncaught Error';
79 | uncaughtErrText.x = 10;
80 | uncaughtErrText.y = 205;
81 | uncaughtErrText.selectable = false;
82 | uncaughtButton.addChild(uncaughtErrText);
83 |
84 | caughtButton.addEventListener(MouseEvent.MOUSE_DOWN, caughtMouseDownHandler);
85 | uncaughtButton.addEventListener(MouseEvent.MOUSE_DOWN, uncaughtMouseDownHandler);
86 | }
87 |
88 | private function caughtMouseDownHandler(event:MouseEvent):void {
89 | try {
90 | trace('causing error within try/catch');
91 | causeError();
92 | } catch (e:Error) {
93 | trace('caught error within try/catch');
94 | Rollbar.handleError(e);
95 | }
96 | }
97 |
98 | private function uncaughtMouseDownHandler(event:MouseEvent):void {
99 | trace('caught uncaught error');
100 | causeError();
101 | }
102 |
103 | private function causeError():void {
104 | throw new Error('dummy');
105 | }
106 | }
107 | }
108 |
--------------------------------------------------------------------------------
/src/com/rollbar/stacktrace/StackTrace.as:
--------------------------------------------------------------------------------
1 | package com.rollbar.stacktrace {
2 | import flash.system.Capabilities;
3 | public class StackTrace {
4 | public var errorClassName:String = "";
5 | public var message:String = "";
6 | public var lines:Vector.