{"id":758,"date":"2017-09-12T10:00:55","date_gmt":"2017-09-12T10:00:55","guid":{"rendered":"http:\/\/smartfoxserver.com\/blog\/?p=758"},"modified":"2017-09-12T10:00:55","modified_gmt":"2017-09-12T10:00:55","slug":"better-ways-of-logging-exceptions-in-sfs2x","status":"publish","type":"post","link":"https:\/\/smartfoxserver.com\/blog\/better-ways-of-logging-exceptions-in-sfs2x\/","title":{"rendered":"Better ways of logging Exceptions in SFS2X"},"content":{"rendered":"<p>A recent discussion on our support forum brought up the struggle of sending\u00a0enough details to the log files when an Exception is caught on the\u00a0server side. In this article we\u00a0provide a few options to improve error reporting in your Extensions and avoid those puzzling,\u00a0single-line errors.<!--more--><\/p>\n<h3>\u00bb The problem<\/h3>\n<p>Let&#8217;s consider this example:<\/p>\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\r\ntry\r\n{\r\n\tCreateRoomSettings crs = new CreateRoomSettings();\r\n\tcrs.setName(&quot;The Lobby&quot;);\r\n\tcrs.setGame(false);\r\n\tcrs.setMaxUsers(60);\r\n\t\r\n\tgetApi().createRoom(getParentZone(), crs, null);\r\n}\r\ncatch(SFSCreateRoomException ex)\r\n{\r\n\ttrace(&quot;Room creation failure: &quot; + ex);\r\n}\r\n<\/pre>\n<p>If an error occurs in the API call, this is what we will get in the log files:<\/p>\n<pre>10:14:15,712 INFO  [main] Extensions     - {javaTest}: Room creation failure: com.smartfoxserver.v2.exceptions.SFSCreateRoomException: Room creation error. A room with the same name already exists: The Lobby, { Zone: BasicExamples }, null<\/pre>\n<p><sup>(scroll horizontally to read the full error message)<\/sup><\/p>\n<p>While the description is clear\u00a0and tells you exactly what problem has occurred we have no stack trace to understand where exactly in our code this happened. If this operation is wrapped in a method called by multiple classes it will be pretty difficult to understand where this exception is generated.<\/p>\n<p>In these cases having a full stack trace would help substantially, so let&#8217;s see how we can improve our error reporting accordingly.<\/p>\n<h4>1)\u00a0Using the inherited logger<\/h4>\n<p>The SFSExtension base class provides access to the underlying logging system via the <strong>getLogger()<\/strong> method. With one line of code we can immediately add a stack trace to our error reporting:<\/p>\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\r\ntry\r\n{\r\n\t\/\/ Potentially &quot;dangerous&quot; code\r\n}\r\ncatch(Exception ex)\r\n{\r\n\tgetLogger().warn(&quot;My custom message&quot;, ex);\r\n}\r\n<\/pre>\n<p>This in turn will generate an output like this in the log files:<\/p>\n<pre>10:45:47,965 WARN  [Thread-3] Extensions     - Room creation error\r\ncom.smartfoxserver.v2.exceptions.SFSCreateRoomException: Room creation error. A room with the same name already exists: The Lobby, { Zone: BasicExamples }, null\r\n\tat com.smartfoxserver.v2.api.SFSApi.createRoom(SFSApi.java:776)\r\n\tat sfs2x.extension.logging.CustomLoggerExtension.init(CustomLoggerExtension.java:24)\r\n\tat com.smartfoxserver.v2.entities.managers.SFSExtensionManager.createExtension(SFSExtensionManager.java:303)\r\n\tat com.smartfoxserver.v2.entities.managers.SFSExtensionManager.reloadExtension(SFSExtensionManager.java:533)\r\n\tat com.smartfoxserver.v2.entities.managers.SFSExtensionManager$ExtensionFileChangeListener.fileChanged(SFSExtensionManager.java:111)\r\n\tat org.apache.commons.vfs.events.ChangedEvent.notify(ChangedEvent.java:34)\r\n\tat org.apache.commons.vfs.provider.AbstractFileSystem.fireEvent(AbstractFileSystem.java:519)\r\n\tat org.apache.commons.vfs.provider.AbstractFileSystem.fireFileChanged(AbstractFileSystem.java:487)\r\n\tat org.apache.commons.vfs.impl.DefaultFileMonitor$FileMonitorAgent.check(DefaultFileMonitor.java:647)\r\n\tat org.apache.commons.vfs.impl.DefaultFileMonitor$FileMonitorAgent.access$200(DefaultFileMonitor.java:418)\r\n\tat org.apache.commons.vfs.impl.DefaultFileMonitor.run(DefaultFileMonitor.java:373)\r\n\tat java.lang.Thread.run(Thread.java:745)\r\n<\/pre>\n<h4>2)\u00a0ExceptionMessageComposer<\/h4>\n<p>If you want an even more flexible and fine grained system, SFS2X provides a custom helper class called <strong>ExceptionMessageComposer<\/strong>, which can be imported from\u00a0<em>com.smartfoxserver.v2.exceptions<\/em><\/p>\n<p>The class adds the ability to attach multiple lines of info to every reported error and provide a better description of the issue in a readable format.<\/p>\n<p>Let&#8217;s take a look at an example:<\/p>\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\r\ntry\r\n{\r\n   \/\/ some code\r\n}\r\ncatch(IllegalStateException ex)\r\n{\r\n\tExceptionMessageComposer emc = new ExceptionMessageComposer(ex);\r\n\temc.setDescription(&quot;Unexpected error during game turn&quot;);\r\n\temc.addInfo(&quot;Player 1 State: &quot; + player1.getState());\r\n\temc.addInfo(&quot;Player 2 State: &quot; + player2.getState());\r\n\temc.addInfo(&quot;Last P1 position: &quot; + player1.getPosition());\r\n\temc.addInfo(&quot;Last P2 position: &quot; + player2.getPosition());\r\n\temc.addInto(&quot;Pieces on board: &quot; + board.countPieces());\r\n\r\n\t  \/\/ ...\r\n  \r\n\tlog.warn(emc.toString());\r\n}\r\n<\/pre>\n<ul>\n<li><strong>setDescription()<\/strong> allows us to provide the main description of the error<\/li>\n<li><strong>addInfo()<\/strong> allows to add optional lines of info providing context to the error<\/li>\n<\/ul>\n<p>In this case we&#8217;re adding game state information to the error report so that we can better understand what might have happened. \u00a0Below is the formatted error found in the logs:<\/p>\n<pre>11:09:34,075 WARN  [Thread-3] logging.CustomLoggerExtension     - java.lang.IllegalStateException:\r\n::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::\r\nException: java.lang.IllegalStateException\r\nMessage: Invalid move from Player2\r\nDescription: Inconsistent state during game turn\r\nPlayer 1 State: Waiting\r\nPlayer 2 State: Playing\r\nLast P1 position: G4\r\nLast P2 position: H9\r\nPieces on board: 15\r\n+--- --- ---+\r\nStack Trace:\r\n+--- --- ---+\r\nsfs2x.extension.logging.CustomLoggerExtension.init(CustomLoggerExtension.java:25)\r\ncom.smartfoxserver.v2.entities.managers.SFSExtensionManager.createExtension(SFSExtensionManager.java:303)\r\ncom.smartfoxserver.v2.entities.managers.SFSExtensionManager.reloadExtension(SFSExtensionManager.java:533)\r\ncom.smartfoxserver.v2.entities.managers.SFSExtensionManager$ExtensionFileChangeListener.fileChanged(SFSExtensionManager.java:111)\r\norg.apache.commons.vfs.events.ChangedEvent.notify(ChangedEvent.java:34)\r\norg.apache.commons.vfs.provider.AbstractFileSystem.fireEvent(AbstractFileSystem.java:519)\r\norg.apache.commons.vfs.provider.AbstractFileSystem.fireFileChanged(AbstractFileSystem.java:487)\r\norg.apache.commons.vfs.impl.DefaultFileMonitor$FileMonitorAgent.check(DefaultFileMonitor.java:647)\r\norg.apache.commons.vfs.impl.DefaultFileMonitor$FileMonitorAgent.access$200(DefaultFileMonitor.java:418)\r\norg.apache.commons.vfs.impl.DefaultFileMonitor.run(DefaultFileMonitor.java:373)\r\njava.lang.Thread.run(Thread.java:745)\r\n::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::\r\n<\/pre>\n<h3>Performance and optimization<\/h3>\n<p>One side effect of using more detailed error reporting is that your log files will blow up in size very quickly, especially on a live server with high traffic. This in turn can hinder performance and fill your hard drive very quickly.<\/p>\n<p>The foremost common sense suggestion is to attach a full stack trace only where needed and avoid to do it everywhere. In general errors that are bound to happen in a single specific spot don&#8217;t need the stack trace, as you will know exactly where they happened and why.<br \/>\nOn the other hand unexpected\/runtime errors will likely be good candidates for extensive reporting.<\/p>\n<p><strong>Logger Configuration<\/strong><br \/>\nWhen accessing the inherited logger via <strong>getLogger()<\/strong> you can use <a href='http:\/\/docs2x.smartfoxserver.com\/GettingStarted\/log-configuration' target='_blank'>the DEBUG level<\/a> to easily switch on\/off the detailed logging.<br \/>\nHere&#8217;s an example:<\/p>\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\r\ntry\r\n{\r\n\t\/\/ Potentially &quot;dangerous&quot; code\r\n}\r\ncatch(Exception ex)\r\n{\r\n\tif(getLogger().isDebugEnabled()) \r\n\t\tgetLogger().warn(&quot;My custom message&quot;, ex);\r\n\telse\r\n\t\tgetLogger().warn(&quot;My custom message&quot;);\r\n}\r\n<\/pre>\n<p>Here we generate a detailed report only if the logger is configured in DEBUG mode, otherwise we use the single line, default mode. This strategy can be useful for switching between a test environment and a live setup<\/p>\n<p><strong>ExceptionMessageComposer global settings<\/strong><br \/>\nWhen using the ExceptionMessageComposer class we can rely on a couple of global settings in SFS2X, namely:<\/p>\n<ul>\n<li><strong>User friendly exceptions<\/strong>: toggles additional info messages<\/li>\n<li><strong>Use debug mode:<\/strong> toggles the stack trace<\/li>\n<\/ul>\n<p>Both options are enabled by default and can be modified from the AdminTool&#8217;s Server Configurator panel. When both options are turned off you will only get the standard message from the generated Exception.<\/p>\n<p>With this new information under your belt you should now be able to optimize your Extension logging and improve the ability of tracking issues in your code. <\/p>\n","protected":false},"excerpt":{"rendered":"<p>A recent discussion on our support forum brought up the struggle of sending\u00a0enough details to the log files when an Exception is caught on the\u00a0server side. In this article we\u00a0provide a few options to improve error reporting in your Extensions and avoid those puzzling,\u00a0single-line errors.<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":[],"categories":[23,5],"tags":[31,36,7],"_links":{"self":[{"href":"https:\/\/smartfoxserver.com\/blog\/wp-json\/wp\/v2\/posts\/758"}],"collection":[{"href":"https:\/\/smartfoxserver.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/smartfoxserver.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/smartfoxserver.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/smartfoxserver.com\/blog\/wp-json\/wp\/v2\/comments?post=758"}],"version-history":[{"count":24,"href":"https:\/\/smartfoxserver.com\/blog\/wp-json\/wp\/v2\/posts\/758\/revisions"}],"predecessor-version":[{"id":782,"href":"https:\/\/smartfoxserver.com\/blog\/wp-json\/wp\/v2\/posts\/758\/revisions\/782"}],"wp:attachment":[{"href":"https:\/\/smartfoxserver.com\/blog\/wp-json\/wp\/v2\/media?parent=758"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/smartfoxserver.com\/blog\/wp-json\/wp\/v2\/categories?post=758"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/smartfoxserver.com\/blog\/wp-json\/wp\/v2\/tags?post=758"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}